- 请求合并(可选):当热点数据失效时,会有大量请求同请求同一个key,合并请求之后只会有一个请求真正得到处理,其他相同的请求只需等待结果即可---注意选择必须复写方法参数或key的hashcode!
- 多级缓存:目前只有本地(可选)和redis(可选)两级
- loadOnStartUp:支持缓存在应用启动时加载(只支持无参方法)
- metrics:hit/miss;hit.local/miss.local;hit.remote/miss.remote;load/error;load.time/error.time
- 多环境配置:配合qconfig实现多环境配置,因为FAT的数据量太小而且qps也不够,配置的参数值与线上通常需要有所区别
- 自动打clog日志:配合ContextAwareClogger & ThreadLocalVariableManager可以将自己想要打的tag打在缓存日志中
- 自动刷新(可选):当数据的超时时间小于数据的刷新间隔时,数据永不过时(如果设置较大的redis失效时间的话,即时服务方挂掉了也能“苟活”一段时间),同时能保证一定的时效性,对于一些数据量比较小的热点数据建议使用这种配置
- retries:数据刷新防抖动,会在抛出异常时进行重试
- 随机interval:为了防止数据同时失效,刷新的初始延迟为interval之内的随机数
- 缓存同步:一台机器更新某个key的缓存后会通过redis pub/sub 刷新其他机器
- refreshTimeout:对于超过限定时间没有再次访问的key会被移除刷新队列
- 依 8000 于Spring @Cacheable实现自动存取
- 本地缓存使用caffeine,分布式缓存使用credis
- 使用覆盖取代过期,尽量保证数据的可用性
- Redis sub/pub同步数据
- ScheduledThreadPoolExecutor调度刷新任务
- 在key相同的请求间使用锁和future合并请求
- Jackson序列化redis key/value,借助于公司的SSJsonSerializer支持契约对象
<groupId>com.ctrip.train.tieyouflight</groupId>
<artifactId>domino</artifactId>
<version>1.0.20</version>
使用springboot autoconfigure,只需引入jar包即可。
依托于spring @Cacheable, 使用meta-annotation二次开发,支持@Cacheable的大多数配置参数:@Domino(cond)=@Cacheable(condition);@Domino(except)=@Cacheable(unless)
@Domino(refreshable = true, name = "CommonPassengerStub.getCommonPassengers", except = "#result==null||#result.size()<=20")
public List<CommonPassengerBO> getCommonPassengers(String ctripUId) throws Exception {}
待补充
- springcache基于代理实现,所以类内调用(同一类的A方法调B方法)不会走缓存;如果需要走缓存,请在bean内配置当前bean,调用缓存方法使用bean.B. 示例如下
@Component
public class CommonPassengerStub {
private static final String LTP = "CommonPassengerStub.";
private static BbzmbrcommonpassengerClient client = BbzmbrcommonpassengerClient.getInstance();
@Resource
private CommonPassengerStub self;
public static BbzmbrcommonpassengerClient getClient() {
return client;
}
static {
client.setConnectTimeout(1000);
client.setRequestTimeout(1000);
client.setSocketTimeout(1000);
client.setFormat("json");
}
public List<CommonPassengerBO> getCommonPassengers(String ctripUId, int limit) throws Exception {
List<CommonPassengerBO> commonPassengers = self.getCommonPassengers(ctripUId);
if (limit > 0 && commonPassengers != null) {
return commonPassengers.stream().limit(limit).collect(Collectors.toList());
}
return commonPassengers;
}
@Domino(refreshable = true, name = "CommonPassengerStub.getCommonPassengers", except = "#result==null||#result.size()<=20")
public List<CommonPassengerBO> getCommonPassengers(String ctripUId) throws Exception {
String logTitle = LTP + "getCommonPassengers";
GetCommonPassengerRequestType request = new GetCommonPassengerRequestType();
request.setUID(ctripUId);
ParameterItem parameterItem = new ParameterItem("BizType", "TRA");
request.setParameterList(Arrays.asList(parameterItem));
QueryCondition queryCondition1 = new QueryCondition("PageIndex", "1");
QueryCondition queryCondition2 = new QueryCondition("PageSize", "50");
request.setQueryConditionList(Arrays.asList(queryCondition1, queryCondition2));
GetCommonPassengerResponseType response = client.getCommonPassenger(request);
List<CommonPassenger> commonPassengers = MoreObjects.firstNonNull(response.getCommonPassengers(), Lists.newArrayList());
List<CommonPassengerBO> commonPassengerBOS = Lists.newArrayList();
for (CommonPassenger commonPassenger : commonPassengers) {
if (CollectionUtils.isEmpty(commonPassenger.getCommonPassengerCardList())) {
continue;
}
CommonPassengerCard commonPassengerCard = commonPassenger.getCommonPassengerCardList().stream().filter(passengerCard ->
"1".equals(passengerCard.getCardType()) && StringUtils.isNotBlank(passengerCard.getCardNo())).findFirst().orElse(null);
if (commonPassengerCard != null) {
commonPassengerBOS.add(new CommonPassengerBO(commonPassenger.getCNName(), PassportType.Identity_Card.getName(), commonPassengerCard.getCardNo()));
}
}
CLogger.logRequestResponse(logTitle, request, StringUtils.EMPTY);
return commonPassengerBOS;
}
}
- 缓存的数据模型发生变化需要修改缓存名称,防止反序列化错误。例如方法返回的响应由class A换到了class B,则需要修改注解中的缓存名称
- 优化metric和日志
- 分布式锁实现的请求合并
- 缓存支持手动刷新
- support separate or manual configured cache
- 支持@CachePut
- 支持@CacheEvict
- redisPubSub改造(事件驱动,线程池)
- 增加扩展点:metric/cache provider/cache manager