设为首页 收藏本站
查看: 1312|回复: 0

[经验分享] memcache-client-forjava 源码分析之DefaultCacheImpl分析

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-8-26 09:24:05 | 显示全部楼层 |阅读模式
最近在研究memcached,有兴趣的朋友,欢迎围观我的memcached演练系列。
memcached演练(1) 搭建memcached服务memcached演练(2) 访问memcached服务 memcached演练(3) 使用apache+搭建双节点tomcat集群memcached演练(4) 使用apache+memcached-session-manager+t..memcached演练(5) 内存管理memcached演练(6) 高可用实例HA(伪集群方案 )memcache-client-forjava 是一款memcached高可用解决方案,由一位阿里的一个牛人开源的一个框架。项目源码发布在google.code。需要翻墙下载。如果不能下载,留下邮箱。
周所周知,memcache是一个分布式数据缓存组件,节点的一致性和高可用均需要在客户端实现。
这个项目已经很长时间没有更新了,最新版是2.5.1。但项目不庞大,是研究源码,提高设计能力的一个好的选择。
主要内容
1.为什么要分析缓存源码
2.将memcache-client-forjava demo maven化
3.分析DefaultCacheImpl作为起点,进行分析

1.为什么要分析缓存源码

就不说虚的啦,互联网大环境等等。如果你在各大招聘网站,刷过招聘JD,应该对一些招聘要求要求有很深的印象。
**最少熟悉一款nosql数据库,如redis,memcached等
**要求具备数据库调优,web服务调优经验...
可见缓存是我们不得不掌握的一项技能了。而且我有过一次面试,直接让“如何设计一款缓存服务”。现在想想,回答不太理想,这也是我对缓存这一块耿耿于怀的一个诱因。
分享些关于缓存的若干资源,大家有兴趣补补。
几个著名Java开源缓存框架介绍

维基百科(缓存)

【Web缓存机制系列】

JSR107

缓存一致性(Cache Coherency)入门

按一个正常访问请求来说,缓存发生地点:浏览器缓存,代理缓存(CDN缓存),web缓存(本地缓存),web分布式缓存,数据库缓存,操作系统缓存,CPU缓存。
缓存的意义:适配CPU和磁盘随机访问的速度差异,重用计算结果,降低重复计算等。
评价缓存的一些指标:命中率,过期数据清除算法等。

2.将memcache-client-forjava demo maven化
个人由于比较讨厌维护jar之间的依赖,比较喜欢使用maven管理之。
2.1 先从https://code.google.com/archive/ ... t-forjava/downloads下载
2.2 由于memcache-client-forjava 并没有提交到中央仓库,所以需要我们手动上传至本地仓库。
1
2
3
mvn install:install-file -DgroupId=com.alisoft -DartifactId=alisoft-xplatform-asf-cache -Dversion=2.5.1 -Dpackaging=jar -Dfile=C:\Users\zhaoguoyu\Downloads\alisoft-xplatform-asf-cache-2.5.1.jar

mvn install:install-file -DgroupId=com.alisoft -DartifactId=alisoft-xplatform-asf-cache -Dversion=2.5.1 -Dpackaging=jar -Dfile=C:\Users\zhaoguoyu\Downloads\alisoft-xplatform-asf-cache-2.5.1-src.jar -DgeneratePom=true -Dclassifier=sources



2.3 pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.alisoft.examples</groupId>
    <artifactId>asftest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>asftest</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alisoft</groupId>
            <artifactId>alisoft-xplatform-asf-cache</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.woodstox</groupId>
            <artifactId>wstx-asl</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>stax</groupId>
            <artifactId>stax-api</artifactId>
            <version>1.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.7</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>



2.4 复制cache-demo-2.5代码到test目录
2.5 最后的项目结构
wKiom1e-sn-Q_bQCAABJi_7w3e8320.jpg
3.分析DefaultCacheImpl

3.1 DefaultCacheImpl类图
wKiom1e-s1_jJFfpAACy5wtJZIo166.jpg

1
<span style="font-size:16px;">ICache<K,V>: Cache统一接口,支持泛型。具有常用的数据的增删改查接口。<br>DefaultCacheImpl:实现了ICache接口,是默认的本地Cache的实现,线程安全。<br>CheckOutOfDateSchedule:过期数据检查任务,定期清除过期数据。</span><br><span style="font-size:16px;">接下来,重点分析下DefaultCacheImpl具体实现</span>



3.2 主要属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DefaultCacheImpl implements ICache<String,Object>
{
/**
* 具体内容存放的地方
*/
ConcurrentHashMap<String, Object>[] caches;
/**
* 超期信息存储
*/
ConcurrentHashMap<String, Long> expiryCache;

/**
* 清理超期内容的服务
*/
private  ScheduledExecutorService scheduleService;

/**
* 清理超期信息的时间间隔,默认10分钟
*/
private int expiryInterval = 10;

/**
* 内部cache的个数,根据key的hash对module取模来定位到具体的某一个内部的Map,
* 减小阻塞情况发生。
*/
private int moduleSize = 10;
...
}



  • 维护了2个ConcurrentHashMap,分别存储数据有效信息和实际数据,而ConcurrentHashMap本身是线程安全的。
  • scheduleService驱动CheckOutOfDateSchedule任务执行
  • 考虑热快情况,加入了取模逻辑,降低数据争用。

3.3 初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public DefaultCacheImpl()
{
   init();
}

public DefaultCacheImpl(int expiryInterval,int moduleSize)
{
   this.expiryInterval = expiryInterval;
   this.moduleSize = moduleSize;
   init();
}


@SuppressWarnings("unchecked")
private void init()
{
   caches = new ConcurrentHashMap[moduleSize];
   //初始化数据存放桶
   for(int i = 0 ; i < moduleSize ;i ++)
      caches = new ConcurrentHashMap<String, Object>();

   expiryCache = new ConcurrentHashMap<String, Long>();

   scheduleService = Executors.newScheduledThreadPool(1);
   //启动定时任务
   scheduleService.scheduleAtFixedRate(new CheckOutOfDateSchedule(caches,expiryCache),
         0, expiryInterval * 60, TimeUnit.SECONDS);

   if (Logger.isInfoEnabled())
      Logger.info("DefaultCache CheckService is start!");
}



3.4 数据的存取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
...
public boolean containsKey(String key)
{
   checkValidate(key);
   return getCache(key).containsKey(key);
}


public Object get(String key)
{
   checkValidate(key);
   return getCache(key).get(key);
}

public Object put(String key, Object value)
{
   Object result = getCache(key).put(key, value);
   expiryCache.put(key,(long)-1);

   return result;
}

public Object put(String key, Object value, Date expiry)
{
   Object result = getCache(key).put(key, value);
   expiryCache.put(key,expiry.getTime());

   return result;
}
private ConcurrentHashMap<String, Object>getCache(String key)
{
   long hashCode = (long)key.hashCode();

   if (hashCode < 0)
      hashCode = -hashCode;

   int moudleNum = (int)hashCode % moduleSize;

   return caches[moudleNum];
}

/**
检查key是否过期,过期则删除
*/
private void checkValidate(String key)
{
   if (expiryCache.get(key) != null && expiryCache.get(key) != -1
         && new Date(expiryCache.get(key)).before(new Date()))
   {
      getCache(key).remove(key);
      expiryCache.remove(key);
   }
}
...



数据的存储:首先根据key 的hashCode选择合适的数据桶,还需要在expiryCache中记录过期信息。
数据的读取:首先根据key expiryCache记录,判断是否过期,过期则删除。
逻辑非常简单,但是这各默认实现,没有考虑数据回收情况。当缓存大批量数据,容易出现内存溢出。
3.5 缓存溢出再现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DefaultCacheImplTest{
ICache<String,Object> cache;

@Before
public void setUpBeforeClass() throws Exception
{
   cache = new DefaultCacheImpl();
}

@After
public void tearDownAfterClass() throws Exception
{
   cache.clear();
}
@Test
public void testGet()
{
   int i=0;
       while(true){
           i++;
           cache.put("key1"+i, "value"+i);
           System.out.println(""+i);;
       }

// Assert.assertEquals("value1", cache.get("key1"));
}  
...
}



为了容易再现问题,加入“-Xmx5m -Xms5m”
经过测试接近30000时,出现java.lang.OutOfMemoryError: Java heap space。
个人认为一个缓存必须要具备以下组件
  • 命中率统计

  • 老数据回收
  • 数据管理
  • 过期数据清除



而DefaultCacheImpl 简单实现了3 和4.
但不可否认,它为我们研究缓存提供了入门参考。








运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-263155-1-1.html 上篇帖子: 安装memcached 下篇帖子: Memcached群集部署
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表