首页 技术 正文
技术 2022年11月7日
0 收藏 833 点赞 389 浏览 9563 个字

前言

上次总结了下本地缓存Guava Cache的简单应用, 这次来继续说下项目中使用的DCache的简单使用.
这里分为几部分进行总结, 1)DCache介绍; 2)DCache配置及使用; 3)使用实例.

1, Dcache 介绍

  • Dcache是Distribute Cache System的缩写,既分布式缓存系统。具有高性能,大容量,弹性扩容,服务隔离等特点。
  • Dcache是一个高速缓存系统,单机测试qps可达到10000以上。可无限扩容,目前配置最高可达8T容量。不同服务间数据隔离保证安全性。
  • Dcache目前支持的底层的实现包括memcache/redis。
  • Dcache尽量保证数据的稳定存储。但在底层的cache出现问题重启,或是Dcache全部内存写满等情况会导致数据丢失。
  • Dcache适用场景为key量较大,可以均匀分片,单个value大小不大的情况。如:用户的jupiter特征;社区话题的内容;用户最近一小时看到的广告等。不适用于key很少,单value很大的情况。

Dcache 模块介绍

  • Dcache Client是一个java sdk,需要要调用的系统依赖Dcache Client并进行缓存数据的get, set, incr操作
  • Raw Cache Client是Dcache Client的底层实现,可以是memcache client,可以是redis template,可以是其它no sql client。
  • Dcache Server管理众多的Raw Cache Client。
    对于一个实际的get/set请求,会先根据key进行分片,决定使用具体的Raw Cache Client,再调用其进行实际的get/set操作。
  • Dcache Server是一个web服务,地址为xxxx。提供两个功能,一是提供Dcache Client需要的配置信息,包括Raw Cache Client的地址,端口等信息,支持读写的属性信息,写数据token等。二是提供Dcache Client的访问统计打点接口,记录Dcache Client的成功或失败的读写数目及大小。

Dcache 性能测试

底层使用4台1G的Memcache,单台4核32G公用测试机测试Dcache性能如下。(最终瓶颈为测试机的cpu) 考虑到单台Memcache的内存越大,单台支持qps成倍数增长,所以远没有达到Dcache的性能上限,仅供参考。
| 方法 | 并发线程数 | value字节数 | qps |
| ——| ———- |————-|—–|
| GET | 10 | 32 | 6400 |
| GET | 50 | 32 | 11000 |
| GET | 50 | 1k | 10000 |
| GET | 50 | 10k | 7600 |
| SET | 10 | 1k | 6000 |
| SET | 10 | 10k | 4700 |
| SET | 50 | 1k | 11000 |
| SET | 50 | 10k | 6200 |

2, DCache配置及使用

这里因为我们公司内部给配置好了DCache服务器, 所以我们需要直接接入使用.
使用方式很简单, 申请一个前缀和属性名

  • DcacheClientKey
    |参数 |类型| 备注|
    |——-|—-|——-|
    |prefix| String| 属性前缀,如’a’|
    |property| String |属性名称|
    |key |String |具体的key,不建议太长,可以是md5或是appuser,贴子id等.

这里给出配置后台页面的截图:
[Java 缓存] Java Cache之 DCache的简单应用.

这里都是自己申请前缀和属性名, 然后设置最大缓存时间和单个Key的最大value大小.

DCache中的使用配置

dcache.env=TESTdcache.group=0dcache.write.token=test_token

参数解读:
|参数 |取值| 备注|
|——-|—-|——-|
|env| PRODUCTION, TEST, STAGING, DEV| 不要在测试时指向线上的配置|
|group| 集群组别, 后台管理系统配置的, 一般有Redis和Memcache两种 |
|token |写数据要求的token |token正确才能对属性进行写,否则只能读。token和项目绑定,需申请

dcache-client使用方式:

<!-- spring注入方式,在application.xml里配置,通过FactoryBean方式生成DcacheClient。--><bean id="dcacheFactoryBean" class="cn.mucang.dcache.client.factory.DcacheClientFactory" >    <property name="env">        <value>${dcache.env}</value>    </property>    <property name="group">        <value>${dcache.group}</value>    </property>    <property name="token">        <value>${dcache.write.token}</value>    </property></bean>//代码中使用自动注入即可://注意不要在测试时指向线上的配置,会造成数据污染!!!@Autowiredprivate DcacheClient dcacheClient;//代码直接生成DcacheClient的方式,注意不要build多个dcacheClient对象DcacheClient dcacheClient = new DcacheClientBuilder().setEnv(env)            .setGroup(group)            .setToken(token).build();

DCache方法详解

/**    * 设置缓存里的值    *    * @param key    * @param value       键值    * @param cacheSecond 缓存时间,要求正数    * @return 如果set成功返回true,否则返回false    */   boolean set(DcacheClientKey key, String value, int cacheSecond);   /**    * 设置缓存里的值    *    * @param key    * @param value 键值    * @return 如果set成功返回true,否则返回false, 缓存时间为server设置的最大缓存时间    */   boolean set(DcacheClientKey key, String value);   /**    * 设置缓存里的值,失败则抛出exception    *    * @param key    * @param value 键值    * @return 如果set成功返回true,否则返回false, 缓存时间为server设置的最大缓存时间    */   boolean setOrException(DcacheClientKey key, String value) throws DcacheException;   /**    * 设置缓存里的值,失败则抛出exception    * @param key    * @param value    * @param cacheSecond    * @return    * @throws DcacheException    */   boolean setOrException(DcacheClientKey key, String value, int cacheSecond) throws DcacheException;   /**    * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num    *    * @param key    * @param num         要增加的值    * @param cacheSecond 缓存时间,要求正数    * @return 增加后的值,失败返回-1    */   long incr(DcacheClientKey key, long num, int cacheSecond);   /**    * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num    *    * @param key    * @param num 要增加的值    * @return 增加后的值,失败返回-1,缓存时间是server设置的最大缓存时间    */   long incr(DcacheClientKey key, long num);   /**    * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num。如果有异常抛出exception    *    * @param key    * @param num 要增加的值    * @return 增加后的值,失败返回-1,缓存时间是server设置的最大缓存时间    */   long incrOrException(DcacheClientKey key, long num) throws DcacheException;   /**    * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num。如果有异常抛出exception    * @param key    * @param num    * @param cacheSecond    * @return    * @throws DcacheException    */   long incrOrException(DcacheClientKey key, long num, int cacheSecond) throws DcacheException;   /**    * 得到缓存里的值    *    * @param key    * @return 缓存的值,如有异常或值不存在返回null    */   String get(DcacheClientKey key);   /**    * 得到缓存里的值,如果请求异常则抛出exception    */   String getOrException(DcacheClientKey key) throws DcacheException;   /**    * 得到一组缓存里的值    *    * @param keyList    * @return map, key是请求,value是缓存里的值。如果请求不合法或是在缓存里不存在的值不会出现在map里    */   Map<DcacheClientKey, String> batchGet(List<DcacheClientKey> keyList);   /**    * 得到一组缓存里的值    *    * @param keyList    * @param timeoutMills 每个key请求的超时时间(ms)    * @return map, key是请求,value是缓存里的值。如果请求不合法或是在缓存里不存在的值不会出现在map里    */   Map<DcacheClientKey, String> batchGet(List<DcacheClientKey> keyList, long timeoutMills);   /**    * 删除一个key及对应的value    *    * @param key    * @return    */   boolean remove(DcacheClientKey key);   /**    * 删除一个key及对应的value,如果失败返回异常    * @param key    * @return    * @throws DcacheException    */   boolean removeOrException(DcacheClientKey key) throws DcacheException;   /**    * 得到一个属性下的全部的keys.    * MEMCACHE类型的DcacheClient不支持本方法    *    * @param property    * @return    */   List<DcacheClientKey> keys(DcacheClientPrefixProperty property);   /**    * 得到一个属性的key的cursor,支持对key的遍历    * MEMCACHE类型的DcacheClient不支持本方法    *    * @param property    * @return    */   DcacheCursor scan(DcacheClientPrefixProperty property); //使用方法和iterator一致,如下   while(cursor.hasNext()) {          DcacheClientKey key = cursor.next();   }

看到上面所有方法, 大家可以看出DCache对于Multiple Get支持的不是很好..

3, 使用实例

现在我自己所维护的一个小系统中主要使用了两种缓存, 第一个是GuavaCache, 第二个是DCache.
这里有只使用GuavaCache, 有只使用DCache, 也有GuavaCache和DCache组成二级缓存使用的. 这里会用实例说明后两种的使用.

  • 首先申请项目前缀和属性名称, 以及配置缓存大小和缓存时间.
  • 配置文件:
    [Java 缓存] Java Cache之 DCache的简单应用.
    在本地环境配置:(说明: 这个0和2都是对应后台管理系统中配置的Redis, 其中0是使用本地Redis, 2是部署在aliyun上的.)

    dcache.env=TESTdcache.group=0dcache.write.token=test_token

    线上环境配置:

    dcache.env=PRODUCTIONdcache.group=2dcache.write.token=xxxxx
  • Spring配置文件中引入DCacheClient

    <description>DCache配置文件</description><bean id="dcacheFactoryBean" class="cn.mucang.dcache.client.factory.DcacheClientFactory" ><property name="env">    <value>${dcache.env}</value></property><property name="group">    <value>${dcache.group}</value></property><property name="token">    <value>${dcache.write.token}</value></property></bean>
  • 自己写一个DCacheClient包装类(这里只是封装了DCacheClient中的方法)

    @Servicepublic class DCacheManagerService {protected final Logger logger = LoggerFactory.getLogger(this.getClass());/** * 默认的前缀 */protected String prefix = "c";/** * 默认的属性 */protected String property = "cartype-extend";@Autowiredprotected DcacheClient dcacheClient;public <T> T get(String rawKey, Type type) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    String result = dcacheClient.get(dcacheClientKey);    return JSON.parseObject(result, type);}public <T> T get(String rawKey, Class<T> clazz) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    String result = dcacheClient.get(dcacheClientKey);    return JSON.parseObject(result, clazz);}public <T> T get(String rawKey, Callable<T> callable) {    return get(rawKey, callable, -1);}public <T> T get(String rawKey, Callable<T> callable, int cacheSecond) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    return get(dcacheClientKey, callable, cacheSecond);}public <T> T get(DcacheClientKey clientKey, Callable<T> callable) {    return get(clientKey, callable, -1);}public <T> T get(DcacheClientKey clientKey, Callable<T> callable, int cacheSecond) {    Preconditions.checkNotNull(callable, "callable is null");    String result = dcacheClient.get(clientKey);    if (result == null) {        try {            T data = callable.call();            if (cacheSecond == -1) {                dcacheClient.set(clientKey, JSON.toJSONString(data));            } else {                dcacheClient.set(clientKey, JSON.toJSONString(data), cacheSecond);            }            return data;        } catch (Exception e) {            e.printStackTrace();        }    }    final Type type = ((ParameterizedType) callable.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];    try {        return JSON.parseObject(result, type);    } catch (Exception e) {        logger.info("Json转换异常: {}, {}", result, e);        throw e;    }}public <K, V> Map<K, V> batchGet(List<DcacheClientKey> clientKeys, List<K> mapKey, Type mapType) {    Map<DcacheClientKey, String> maps = dcacheClient.batchGet(clientKeys);    Map<K, V> result = Maps.newLinkedHashMap();    for (int i = 0; i < clientKeys.size(); i++) {        V value = JSON.parseObject(maps.get(clientKeys.get(i)), mapType);        result.put(mapKey.get(i), value);    }    return result;}public <K, V> Map<K, V> batchGet(String prefixKey, List rowKeys, List<K> mapKey, Type mapType) {    List<DcacheClientKey> clientKeys = Lists.newArrayList();    for (Object rowKey : rowKeys) {        DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, prefixKey + rowKey);        clientKeys.add(dcacheClientKey);    }    return batchGet(clientKeys, mapKey, mapType);}public boolean set(String rawKey, String value) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    return set(dcacheClientKey, value);}public boolean set(String rawKey, String value, int cacheSecond) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    return set(dcacheClientKey, value, cacheSecond);}public boolean set(DcacheClientKey clientKey, String value) {    return set(clientKey, value, -1);}public boolean set(DcacheClientKey clientKey, String value, int cacheSecond) {    if (cacheSecond != -1) {        return dcacheClient.set(clientKey, value, cacheSecond);    } else {        return dcacheClient.set(clientKey, value);    }}public boolean remove(String rawKey) {    DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);    return dcacheClient.remove(dcacheClientKey);}public List<DcacheClientKey> keys(DcacheClientPrefixProperty property) {    return dcacheClient.keys(property);}/** * 构建cache key */protected String buildCacheRawKey(String prefix, Object... keys) {    StringBuilder rawKeyBuilder = new StringBuilder(prefix);    for (Object key : keys) {        rawKeyBuilder.append(".").append(key);    }    return rawKeyBuilder.toString();}}

好了, 这些准备工作做完之后就可以正式使用DCache了.

@Autowiredprivate DCacheManagerService managerService;@Transactionalpublic List<ArticleComingCarDto> getComingCars() {    final String key = "getComingCars";    return managerService.get(key, new Callable<List<ArticleComingCarDto>>() {        @Override        public List<ArticleComingCarDto> call() {            //Do your logic here.        }    });}

剩下还有很多可以延伸, 比如说可以写一个action, 支持手动刷新DCache缓存, 这里get 方法有好几种实现, 按照自己的需求可以使用不同的get方法.

再说就是GuavaCache和DCache的组合使用二级缓存的情况.
当项目刚启动刷入GuavaCache(一级缓存), 如果访问量过大, 那么系统压力就会很大, 这时我们可以用DCache当做二级缓存来缓解压力, 然后将DCache中取出的值放入到GuavaCache中. 而GuavaCache的多重取值又比较与DCache有很大的优势. 所以当我们的一组缓存key值较多时且经常需要多重取值时, 那么这两种缓存的组合形式将是不二之选了.

关于DCache的东西就说这么多了, 更多的是展示出的代码.

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,983
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,500
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,344
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,127
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,762
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,838