前言

上次总结了下本地缓存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等.

这里给出配置后台页面的截图:

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

DCache中的使用配置

  1. dcache.env=TEST
  2. dcache.group=0
  3. dcache.write.token=test_token

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

dcache-client使用方式:

  1. <!-- spring注入方式,在application.xml里配置,通过FactoryBean方式生成DcacheClient。-->
  2. <bean id="dcacheFactoryBean" class="cn.mucang.dcache.client.factory.DcacheClientFactory" >
  3. <property name="env">
  4. <value>${dcache.env}</value>
  5. </property>
  6. <property name="group">
  7. <value>${dcache.group}</value>
  8. </property>
  9. <property name="token">
  10. <value>${dcache.write.token}</value>
  11. </property>
  12. </bean>
  13. //代码中使用自动注入即可:
  14. //注意不要在测试时指向线上的配置,会造成数据污染!!!
  15. @Autowired
  16. private DcacheClient dcacheClient;
  17. //代码直接生成DcacheClient的方式,注意不要build多个dcacheClient对象
  18. DcacheClient dcacheClient = new DcacheClientBuilder().setEnv(env)
  19. .setGroup(group)
  20. .setToken(token).build();

DCache方法详解

  1. /**
  2. * 设置缓存里的值
  3. *
  4. * @param key
  5. * @param value 键值
  6. * @param cacheSecond 缓存时间,要求正数
  7. * @return 如果set成功返回true,否则返回false
  8. */
  9. boolean set(DcacheClientKey key, String value, int cacheSecond);
  10. /**
  11. * 设置缓存里的值
  12. *
  13. * @param key
  14. * @param value 键值
  15. * @return 如果set成功返回true,否则返回false, 缓存时间为server设置的最大缓存时间
  16. */
  17. boolean set(DcacheClientKey key, String value);
  18. /**
  19. * 设置缓存里的值,失败则抛出exception
  20. *
  21. * @param key
  22. * @param value 键值
  23. * @return 如果set成功返回true,否则返回false, 缓存时间为server设置的最大缓存时间
  24. */
  25. boolean setOrException(DcacheClientKey key, String value) throws DcacheException;
  26. /**
  27. * 设置缓存里的值,失败则抛出exception
  28. * @param key
  29. * @param value
  30. * @param cacheSecond
  31. * @return
  32. * @throws DcacheException
  33. */
  34. boolean setOrException(DcacheClientKey key, String value, int cacheSecond) throws DcacheException;
  35. /**
  36. * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num
  37. *
  38. * @param key
  39. * @param num 要增加的值
  40. * @param cacheSecond 缓存时间,要求正数
  41. * @return 增加后的值,失败返回-1
  42. */
  43. long incr(DcacheClientKey key, long num, int cacheSecond);
  44. /**
  45. * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num
  46. *
  47. * @param key
  48. * @param num 要增加的值
  49. * @return 增加后的值,失败返回-1,缓存时间是server设置的最大缓存时间
  50. */
  51. long incr(DcacheClientKey key, long num);
  52. /**
  53. * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num。如果有异常抛出exception
  54. *
  55. * @param key
  56. * @param num 要增加的值
  57. * @return 增加后的值,失败返回-1,缓存时间是server设置的最大缓存时间
  58. */
  59. long incrOrException(DcacheClientKey key, long num) throws DcacheException;
  60. /**
  61. * 增加缓存里的值,如果在cache里没有此key,会设为num并返回num。如果有异常抛出exception
  62. * @param key
  63. * @param num
  64. * @param cacheSecond
  65. * @return
  66. * @throws DcacheException
  67. */
  68. long incrOrException(DcacheClientKey key, long num, int cacheSecond) throws DcacheException;
  69. /**
  70. * 得到缓存里的值
  71. *
  72. * @param key
  73. * @return 缓存的值,如有异常或值不存在返回null
  74. */
  75. String get(DcacheClientKey key);
  76. /**
  77. * 得到缓存里的值,如果请求异常则抛出exception
  78. */
  79. String getOrException(DcacheClientKey key) throws DcacheException;
  80. /**
  81. * 得到一组缓存里的值
  82. *
  83. * @param keyList
  84. * @return map, key是请求,value是缓存里的值。如果请求不合法或是在缓存里不存在的值不会出现在map里
  85. */
  86. Map<DcacheClientKey, String> batchGet(List<DcacheClientKey> keyList);
  87. /**
  88. * 得到一组缓存里的值
  89. *
  90. * @param keyList
  91. * @param timeoutMills 每个key请求的超时时间(ms)
  92. * @return map, key是请求,value是缓存里的值。如果请求不合法或是在缓存里不存在的值不会出现在map里
  93. */
  94. Map<DcacheClientKey, String> batchGet(List<DcacheClientKey> keyList, long timeoutMills);
  95. /**
  96. * 删除一个key及对应的value
  97. *
  98. * @param key
  99. * @return
  100. */
  101. boolean remove(DcacheClientKey key);
  102. /**
  103. * 删除一个key及对应的value,如果失败返回异常
  104. * @param key
  105. * @return
  106. * @throws DcacheException
  107. */
  108. boolean removeOrException(DcacheClientKey key) throws DcacheException;
  109. /**
  110. * 得到一个属性下的全部的keys.
  111. * MEMCACHE类型的DcacheClient不支持本方法
  112. *
  113. * @param property
  114. * @return
  115. */
  116. List<DcacheClientKey> keys(DcacheClientPrefixProperty property);
  117. /**
  118. * 得到一个属性的key的cursor,支持对key的遍历
  119. * MEMCACHE类型的DcacheClient不支持本方法
  120. *
  121. * @param property
  122. * @return
  123. */
  124. DcacheCursor scan(DcacheClientPrefixProperty property); //使用方法和iterator一致,如下
  125. while(cursor.hasNext()) {
  126. DcacheClientKey key = cursor.next();
  127. }

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

3, 使用实例

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

  • 首先申请项目前缀和属性名称, 以及配置缓存大小和缓存时间.
  • 配置文件:

    在本地环境配置:(说明: 这个0和2都是对应后台管理系统中配置的Redis, 其中0是使用本地Redis, 2是部署在aliyun上的.)

    1. dcache.env=TEST
    2. dcache.group=0
    3. dcache.write.token=test_token

    线上环境配置:

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

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

    1. @Service
    2. public class DCacheManagerService {
    3. protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    4. /**
    5. * 默认的前缀
    6. */
    7. protected String prefix = "c";
    8. /**
    9. * 默认的属性
    10. */
    11. protected String property = "cartype-extend";
    12. @Autowired
    13. protected DcacheClient dcacheClient;
    14. public <T> T get(String rawKey, Type type) {
    15. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    16. String result = dcacheClient.get(dcacheClientKey);
    17. return JSON.parseObject(result, type);
    18. }
    19. public <T> T get(String rawKey, Class<T> clazz) {
    20. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    21. String result = dcacheClient.get(dcacheClientKey);
    22. return JSON.parseObject(result, clazz);
    23. }
    24. public <T> T get(String rawKey, Callable<T> callable) {
    25. return get(rawKey, callable, -1);
    26. }
    27. public <T> T get(String rawKey, Callable<T> callable, int cacheSecond) {
    28. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    29. return get(dcacheClientKey, callable, cacheSecond);
    30. }
    31. public <T> T get(DcacheClientKey clientKey, Callable<T> callable) {
    32. return get(clientKey, callable, -1);
    33. }
    34. public <T> T get(DcacheClientKey clientKey, Callable<T> callable, int cacheSecond) {
    35. Preconditions.checkNotNull(callable, "callable is null");
    36. String result = dcacheClient.get(clientKey);
    37. if (result == null) {
    38. try {
    39. T data = callable.call();
    40. if (cacheSecond == -1) {
    41. dcacheClient.set(clientKey, JSON.toJSONString(data));
    42. } else {
    43. dcacheClient.set(clientKey, JSON.toJSONString(data), cacheSecond);
    44. }
    45. return data;
    46. } catch (Exception e) {
    47. e.printStackTrace();
    48. }
    49. }
    50. final Type type = ((ParameterizedType) callable.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
    51. try {
    52. return JSON.parseObject(result, type);
    53. } catch (Exception e) {
    54. logger.info("Json转换异常: {}, {}", result, e);
    55. throw e;
    56. }
    57. }
    58. public <K, V> Map<K, V> batchGet(List<DcacheClientKey> clientKeys, List<K> mapKey, Type mapType) {
    59. Map<DcacheClientKey, String> maps = dcacheClient.batchGet(clientKeys);
    60. Map<K, V> result = Maps.newLinkedHashMap();
    61. for (int i = 0; i < clientKeys.size(); i++) {
    62. V value = JSON.parseObject(maps.get(clientKeys.get(i)), mapType);
    63. result.put(mapKey.get(i), value);
    64. }
    65. return result;
    66. }
    67. public <K, V> Map<K, V> batchGet(String prefixKey, List rowKeys, List<K> mapKey, Type mapType) {
    68. List<DcacheClientKey> clientKeys = Lists.newArrayList();
    69. for (Object rowKey : rowKeys) {
    70. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, prefixKey + rowKey);
    71. clientKeys.add(dcacheClientKey);
    72. }
    73. return batchGet(clientKeys, mapKey, mapType);
    74. }
    75. public boolean set(String rawKey, String value) {
    76. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    77. return set(dcacheClientKey, value);
    78. }
    79. public boolean set(String rawKey, String value, int cacheSecond) {
    80. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    81. return set(dcacheClientKey, value, cacheSecond);
    82. }
    83. public boolean set(DcacheClientKey clientKey, String value) {
    84. return set(clientKey, value, -1);
    85. }
    86. public boolean set(DcacheClientKey clientKey, String value, int cacheSecond) {
    87. if (cacheSecond != -1) {
    88. return dcacheClient.set(clientKey, value, cacheSecond);
    89. } else {
    90. return dcacheClient.set(clientKey, value);
    91. }
    92. }
    93. public boolean remove(String rawKey) {
    94. DcacheClientKey dcacheClientKey = new DcacheClientKey(prefix, property, rawKey);
    95. return dcacheClient.remove(dcacheClientKey);
    96. }
    97. public List<DcacheClientKey> keys(DcacheClientPrefixProperty property) {
    98. return dcacheClient.keys(property);
    99. }
    100. /**
    101. * 构建cache key
    102. */
    103. protected String buildCacheRawKey(String prefix, Object... keys) {
    104. StringBuilder rawKeyBuilder = new StringBuilder(prefix);
    105. for (Object key : keys) {
    106. rawKeyBuilder.append(".").append(key);
    107. }
    108. return rawKeyBuilder.toString();
    109. }
    110. }

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

  1. @Autowired
  2. private DCacheManagerService managerService;
  3. @Transactional
  4. public List<ArticleComingCarDto> getComingCars() {
  5. final String key = "getComingCars";
  6. return managerService.get(key, new Callable<List<ArticleComingCarDto>>() {
  7. @Override
  8. public List<ArticleComingCarDto> call() {
  9. //Do your logic here.
  10. }
  11. });
  12. }

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

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

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

[Java 缓存] Java Cache之 DCache的简单应用.的更多相关文章

  1. [Java 缓存] Java Cache之 Guava Cache的简单应用.

    前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...

  2. 从Java视角理解CPU缓存(CPU Cache)

    从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态众所周知, CPU是计算机的大脑, 它负责执行程序的指令; 内存负责存数据, 包括程序自身数据. 同样大家都知道, 内存比CPU慢很多 ...

  3. Map实现java缓存机制的简单实例

    缓存是Java中主要的内容,主要目的是缓解项目访问数据库的压力以及提升访问数据的效率,以下是通过Map实现java缓存的功能,并没有用cache相关框架. 一.缓存管理类 CacheMgr.java ...

  4. Java缓存相关memcached、redis、guava、Spring Cache的使用

    随笔分类 - Java缓存相关 主要记录memcached.redis.guava.Spring Cache的使用 第十二章 redis-cluster搭建(redis-3.2.5) 摘要: redi ...

  5. (转)java缓存技术,记录

    http://blog.csdn.net/madun/article/details/8569860 最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇 ...

  6. JAVA缓存技术

    介绍 JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacOS ...

  7. JAVA缓存技术之EhCache

    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/ar ...

  8. JAVA缓存技术之EhCache(转)

    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/ar ...

  9. Java缓存框架

      JBossCache/TreeCache  JBossCache是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的改善性能.缓存数据被自动复制,让你轻松进行Jboss服务器之间的集群工作 ...

随机推荐

  1. REST简介

    一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...

  2. TODO:GitHub创建组织的步骤

    TODO:GitHub创建组织的步骤 使用GitHub进行团队合作,写这个步骤主要作用是为了OneTODO作为一个团队组织进行代码的分享,让更多人来参与. 使用帐号.密码登录GitHub 2.右上角加 ...

  3. ASP.NET Core应用针对静态文件请求的处理[1]: 以Web的形式发布静态文件

    虽然ASP.NET Core是一款"动态"的Web服务端框架,但是在很多情况下都需要处理针对静态文件的请求,最为常见的就是这对JavaScript脚本文件.CSS样式文件和图片文件 ...

  4. so 问题来了,你现在值多少钱?

    年底了一大帮人都写着年底总结,总结一年做过的事.错过的事和做错的事.增长了多少本事,找没找到女朋友……来年做好升职加薪,要么做跳槽的准备,程序猿又开始浮躁了……. so 问题来了,你现在值多少钱? 这 ...

  5. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  6. [开发笔记]yum错误

    yum 错误TypeError: rpmdb open failed 解决办法 是因为RPM数据库出现损坏导致的,它导致所有的软件的升级.安装甚至是删除都会出现问题,终端出现乱码,YUMEX也用不成, ...

  7. jquery.cookie的使用

    今天想到了要为自己的影像日记增加赞的功能,并且需要用到cookie. 记得原生的js操作cookie也不是很麻烦的,但似乎jquery更简单,不过相比原生js,需要额外引入2个文件,似乎又不是很好,但 ...

  8. 程序员必须要知道的Hadoop的一些事实

    程序员必须要知道的Hadoop的一些事实.现如今,Apache Hadoop已经无人不知无人不晓.当年雅虎搜索工程师Doug Cutting开发出这个用以创建分布式计算机环境的开源软...... 1: ...

  9. maven-sprigmvc-mybatis配置

    pom.xml配置 <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.ap ...

  10. 海鑫智圣:物联网漫谈之MQTT协议

    什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...