开发规范server-list
添加Maven依赖,引用如下Starter POM
<dependency>
<groupId>com.ztesoft.zsmart.core</groupId>
<artifactId>core-boot-starter-cache</artifactId>
<version>${core.version}</version>
<exclusions>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
Demo路径
http://gitlab.ztesoft.com/platform/doc/tree/master/99-Demo/demo-parent/demo-cache
缓存对象需要实现Serializable接口
为方便扩展,需要缓存的对象最好实现Serializable接口。
缓存对象要有无参构造函数
为方便扩展,需要缓存的对象最好有无参构造函数,否则有些序列化工具无法工作。
正确实现getter/setter方法
由于部分序列化工具是根据getter/setter方法来序列化/反序列化对象的,所以需要正确实现对象的getter/setter方法,即需要符合JavaBean命名规范。
@Cacheable需要用在幂等方法上
幂等性是指一次和多次调用某一个方法应该具有同样的副作用,例如,对于要记录操作日志的查询方法不要使用@Cacheable。
基于proxy的spring aop 带来的内部调用问题
Spring Cache是基于动态生成的 proxy 代理机制来对方法的调用进行切面时,如@Caching果对象的方法是内部调用(即 this 引用)而不是外部引用,则会导致 proxy 失效,那么我们的切面就失效,缓存操作也会失效。所以,需要避免带缓存操作的方法的内部调用。
public Account getAccountByName2(String userName) {
return this.getAccountByName\(userName\); //内部调用带缓存的方法
}
@Cacheable(value="accountCache", key = "#result.id")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return new Account\(\);
}
@CacheEvict 的可靠性问题
@CacheEvict 注释有一个属性 beforeInvocation,缺省为 false,即缺省情况下,都是在实际的方法执行完成后,才对缓存进行清空操作。期间如果执行方法出现异常,则会导致缓存清空不被执行。推荐设置beforeInvocation为false.(此处只是为了示例,实际用缺省值即可)
@CacheEvict(cacheNames = "Billing:AcctItemTypeDto:id", key = "#acctItemTypeId",
condition = "\#acctItemTypeId ne null", beforeInvocation = false\)
public Boolean delAcctItemTypeById(Long acctItemTypeId) throws BaseAppException {
logger.info\("delAcctItemTypeById invoked!"\);
acctItemTypeDoMapper.deleteByPrimaryKey\(acctItemTypeId\);
return true;
}
非 public 方法问题
和内部调用问题类似,非public方法如果想实现基于注释的缓存,必须采用基于 AspectJ 的 AOP 机制。请避免。
禁止@CachePut和@Cacheable注解同时使用
如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取,所以要混合多个注解使用时,不能组合使用@CachePut和@Cacheable。请避免。
禁止错误使用RedisConnectionFactory
禁止对同一个ZCache集群创建多个RedisConnectionFactory,同时,也禁止直接从RedisConnectionFactory中获取连接进行Redis操作,这样会存在连接泄露的风险。
不建议直接使用ZCache Client
请优先使用Spring Cahe和RedisTemplate进行编程,不建议直接使用ZCache Client。Spring Cahe和RedisTemplate的使用示例请参考前面相应章节。
慎用ZCache事务
对于事务,分布式缓存集群并不能很好的支持,单机版的缓存支持事务,但此事务跟关系数据库的事务还是差别很大,单机版缓存事务仅仅保证事务内的命令可以原子执行(也就是不会被其他客户端的命令打断),并不保证事务内的所有命令同时成功或失败。
ZCache不支持单节点
ZCache不支持单节点环境访问,需要部署集群环境,集群方式访问。
ZCache的异常
通过Spring Data Redis的方式(主要是RedisTemplate)操作ZCache时,若在底层(Jedis)发生Exception,Spring Data Redis会将此Exception翻译成DataAccessException后抛出。此外,由于RedisTemplate可能存在序列化/反序列化操作,所以处理数据时会抛出SerializationException,而这两种异常都是RuntimeException。
通过Spring Cache操作ZCache时,除了可能会抛出以上两种异常外,特别的,若通过<T> T get(Object key, Callable<T> valueLoader)方法获取缓存,可能会抛出Cache.ValueRetrievalException,这也是RuntimeException。若对同一key存储的value类型不同,会抛出ClassCastException(业务设计错误或key重名)。
综上,框架对ZCache的异常处理主要都是往上抛出RuntimeException,分为DataAccessException和SerializationException。
ZCache的直接访问
方式一:通过登录ZCache Server所在系统,进入到ZCache的src目录,使用其下的redis-cli命令来连接到ZCache服务器,对ZCache的进行访问,支持修改等操作。示例如下:
10.45.82.64 cache/cache
localhost:/usr/local/redis/bin $ ./redis-cli -c -h 10.45.82.64 -p 7670
10.45.82.64:7670> keys *
(empty list or set)
10.45.82.64:7670> set keyxh valuexh
-> Redirected to slot [14963] located at 10.45.82.64:7672
OK
10.45.82.64:7672> get keyxh
"valuexh"
10.45.82.64:7672> quit
localhost:/usr/local/redis/bin $
注:1.集群方式访问时redis-cli需要加上-c参数
2.zcache的操作命令可以参考[https://redis.io/commands](https://redis.io/commands\
方式二:通过部署ZCache监控系统ZCacheManager可以通过Web方式直接访问ZCache集群,但是这种情况下只能查看缓存信息,不能做修改操作。
ZCacheManager的项目访问路径:http://gitlab.ztesoft.com/zc-manager/zcache-manager
示例如下:
调用缓存方法的日志跟踪
对于使用注解式缓存操作的开发环境下,要想查看缓存方法的调用情况,可以在日志配置中增加一个logger来判断缓存调用情况,此日志名是org.springframework.cache.interceptor.CacheInterceptor,日志级别必须是trace。
日志配置:
<logger name="org.springframework.cache.interceptor.CacheInterceptor" level="trace"/>
日志样例:
15:40:36.907 [main] TRACE o.s.c.interceptor.CacheInterceptor - No cache entry for key '9000' in cache(s) [Billing:AcctItemTypeDto:id]
15:40:36.907 [main] TRACE o.s.c.interceptor.CacheInterceptor - Computed cache key '9000' for operation Builder[public com.ztesoft.zsmart.bss.crm.bcdc.acct.param.AcctItemTypeDto com.ztesoft.zsmart.bss.crm.bcdc.acct.service.impl.AcctItemTypeServiceImpl.qryAcctItemType(com.ztesoft.zsmart.bss.crm.bcdc.acct.param.AcctItemTypeDto) throws com.ztesoft.zsmart.core.exception.BaseAppException] caches=[Billing:AcctItemTypeDto:id] | key='#acctItemTypeDto.acctItemTypeId' | keyGenerator='' | cacheManager='cacheManager' | cacheResolver='' | condition='#acctItemTypeDto.acctItemTypeId ne null' | unless='' | sync='false'