开发规范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'

results matching ""

    No results matching ""