Memcached是一种集中式Cache,支持分布式横向扩展。总结几个它的特色来理解一下它的优势和限制。算法
Memory:内存存储,不言而喻,速度快,对于内存的要求高,不指出的话所缓存的内容非持久化。对于CPU要求很低,因此经常采用将Memcached服务端和一些CPU高消耗Memory低消耗应用部属在一块儿。(做为咱们AEP正好有这样的环境,咱们的接口服务器有多台,接口服务器对于CPU要求很高(因为WS-Security),可是对于Memory要求很低,所以能够用做Memcached的服务端部属机器)数据库
集中式Cache:避开了分布式Cache的传播问题,可是须要非单点保证其可靠性,这个就是后面集成中所做的cluster的工做,能够将多个Memcached做为一个虚拟的cluster,同时对于cluster的读写和普通的memcached的读写性能没有差异。缓存
分布式扩展:Memcached的很突出一个优势,就是采用了可分布式扩展的模式。能够将部属在一台机器上的多个Memcached服务端或者部署在多个机器上的Memcached服务端组成一个虚拟的服务端,对于调用者来讲彻底屏蔽和透明。提升的单机器的内存利用率,也提供了scale out的方式。服务器
Socket通讯:传输内容的大小以及序列化的问题须要注意,虽然Memcached一般会被放置到内网做为Cache,Socket传输速率应该比较高(当前支持Tcp和udp两种模式,同时根据客户端的不一样能够选择使用nio的同步或者异步调用方式),可是序列化成本和带宽成本仍是须要注意。这里也提一下序列化,对于对象序列化的性能每每让你们头痛,可是若是对于同一类的Class对象序列化传输,第一次序列化时间比较长,后续就会优化,其实也就是说序列化最大的消耗不是对象序列化,而是类的序列化。若是穿过去的只是字符串,那么是最好的,省去了序列化的操做,所以在Memcached中保存的每每是较小的内容。框架
特殊的内存分配机制:首先要说明的是Memcached支持最大的存储对象为1M。它的内存分配比较特殊,可是这样的分配方式其实也是对于性能考虑的,简单的分配机制能够更容易回收再分配,节省对于CPU的使用。这里用一个酒窖比喻来讲明这种内存分配机制,首先在Memcached起来的时候能够经过参数设置使用的总共的Memory,这个就是建造一个酒窖,而后在有酒进入的时候,首先申请(一般是1M)的空间,用来建酒架,酒架根据这个酒瓶的大小分割酒架为多个小格子安放酒瓶,将一样大小范围内的酒瓶都放置在一类酒架上面。例如500px半径的酒瓶放置在能够容纳20-625px的酒架A上,750px半径的酒瓶就放置在容纳25-750px的酒架B上。回收机制也很简单,首先新酒入库,看看酒架是否有能够回收的地方,若是有直接使用,若是没有申请新的地方,若是申请不到,采用配置的过时策略。这个特色来看,若是要放的内容大小十分离散,同时大小比例相差梯度很明显,那么可能对于使用空间来讲很差,可能在酒架A上就放了一瓶酒,但占用掉了一个酒架的位置。异步
Cache机制简单:有时候不少开源的项目作的面面俱到,可是最后也就是由于过于注重一些非必要性的功能而拖累了性能,这里要提到的就是Memcached的简单性。首先它没有什么同步,消息分发,两阶段提交等等,它就是一个很简单的Cache,把东西放进去,而后能够取出来,若是发现所提供的Key没有命中,那么就很直白的告诉你,你这个key没有任何对应的东西在缓存里,去数据库或者其余地方取,当你在外部数据源取到的时候,能够直接将内容置入到Cache中,这样下次就能够命中了。这里会提到怎么去同步这些数据,两种方式,一种就是在你修改了之后马上更新Cache内容,这样就会即时生效。另外一种是说允许有失效时间,到了失效时间,天然就会将内容删除,此时再去去的时候就会命中不了,而后再次将内容置入Cache,用来更新内容。后者用在一些时时性要求不高,写入不频繁的状况。socket
客户端的重要性:Memcached是用C写的一个服务端,客户端没有规定,反正是Socket传输,只要语言支持Socket通讯,经过Command的简单协议就能够通讯,可是客户端设计的合理十分重要,同时也给使用者提供了很大的空间去扩展和设计客户端来知足各类场景的须要,包括容错,权重,效率,特殊的功能性需求,嵌入框架等等。分布式
几个应用点:小对象的缓存(用户的token,权限信息,资源信息)。小的静态资源缓存。Sql结果的缓存(这部分用的好,性能提升至关大,同时因为Memcached自身提供scale out,那么对于db scale out的老大难问题无疑是一剂好药)。ESB消息缓存。memcached
为何须要集成?直接使用现有的两个Java实现Memcached是否就能够了?性能
当前集成主要为了两方面考虑,首先是方便的配置使用,如何将Memcached内嵌到相似于ASF以及其余框架中去,而且经过配置文件方便使用,这就须要做部分的集成工做,这部分工做主要是定义了配置文件以及经过Stax去解析配置的功能。而后是如何管理Memcached,这部份内容包括了初始化,运行期检测,资源回收的工做。最后是扩展,这里的扩展分红两部分(功能的扩展以及框架实现的扩展),功能扩展例如当前扩展了虚拟的cluster,可让多个memcached Client组成一个虚拟的cluster,若是经过放入cluster的方式放入到其中一个Cache Client中的话,那么就能够在整个cluster都做好备份,这样其实能够根据memcached的单机多实例以及多机多实例做交互备份,提升可靠性。固然后续还有不少能够扩展的内容,这里只是一个开头。框架实现的扩展指的是这里采用了相似于Jdk的JAXP的框架设计,只是规定了框架API结构,至于实现者动态载入,这个和ASF等如今可扩展的框架同样,提供了很方便的扩展点,后续的设计中会提到。
接口设计类图:
图1 Cache接口包类图
ICache和IMemcachedCache实现的是最基本的Cache的功能,只是IMemcachedCache有所加强,提供了对于虚拟的Cluster的操做,批量操做,统计的功能。ICacheManager和IMemcachedCacheManager分别是对于上面两个Cache的管理类,根据配置文件解析,初始化客户端池,创建虚拟集群,销毁客户端池等工做。
图2 Memcached 实现包
省略了一些辅助类定义。这部分是具体的实现,同时能够在图上看到spi包内的CacheManagerFactory就是用来提供扩展使用的接口。只须要定义在jar的META-INF下面创建services目录,创建两个名为:com.alisoft.xplatform.asf.cache.IMemcachedCacheManager和com.alisoft.xplatform.asf.cache.spi.CacheManagerFactory的文件就能够替换MemcachedCacheManager和CacheManagerFactory的实现类,从而改变Memcached Client实现机制。若是没有这两个文件在Classpath目录下面,那么默认将会使用当前框架中的两个实现。
图3 Memcached的结构图
Memcached Server就是部署在不一样服务器或者在同一台服务器上的Memcached实例,通常采用后台守护进程方式运行。SocketPool是客户端链接到服务端的Socket通讯层,Memcached Client能够归属为虚拟的Cluster,MemcachedCacheManager做用是管理Cluster和Cache。从这个结构图能够看出客户端的每一层都是很独立,这样有利于层次的交互,以及组合扩展。
1. 配置:
须要有一个名为memcached.xml的文件在classpath中,能够在jar里面也能够在任意classpath能够找的到的地方,须要注意的是,CacheManager实现了对于多个memcached.xml merge的功能。
具体的配置内容以下:
<?xml version="1.0" encoding="UTF-8"?>
<memcached>//总标签
//memcached Client的配置,也就是一个IMemcachedCache的配置。Name必须填,表示Cache的名称,socketpool必须填,表示使用的远程通讯链接池是哪个,参看后面对于socketpool的定义。后面都是可选的,第三个参数表示传输的时候是否压缩,第四个参数表示默认的编码方式
<client name="mclient1" socketpool="pool1" compressEnable="true" defaultEncoding="UTF-8" >
<!--errorHandler></errorHandler-->//可定义错误处理类,通常不须要定义
</client>
<client name="mclient2" socketpool="pool2" compressEnable="true" defaultEncoding="UTF-8" >
</client>
//socketpool是通讯链接池定义,每个memcached Client要和服务端交互都必须有通讯链接池做为底层数据通讯的支持,name必填,表示名字,同时也是memcached client指定socketpool的依据,failover表示对于服务器出现问题时的自动修复。initConn初始的时候链接数,minConn表示最小闲置链接数,maxConn最大链接数,maintSleep表示是否须要延时结束(最好设置为0,若是设置延时的话那么就不可以马上回收全部的资源,若是此时重新启动一样的资源分配,就会出现问题),nagle是TCP对于socket建立的算法,socketTO是socket链接超时时间,aliveCheck表示心跳检查,肯定服务器的状态。Servers是memcached服务端开的地址和ip列表字符串,weights是上面服务器的权重,必须数量一致,不然权重无效
<socketpool name="pool1" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>10.0.68.210:12000,10.0.68.210:12222</servers>
<weights>5,5</weights>
</socketpool>
<socketpool name="pool2" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>10.0.68.210:22000,10.0.68.210:22222</servers>
<weights>5,5</weights>
</socketpool>
//虚拟集群设置,这里将几个client的cache设置为一个虚拟集群,当对这些IMemcachedCache做集群操做的时候,就会自动地对集群中全部的Cache做插入,寻找以及删除的操做,作一个虚拟交互备份
<cluster name="cluster1">
<memCachedClients>mclient1,mclient2</memCachedClients>
</cluster>
</memcached>
2. 测试代码,这里就附带一个单元测试类的代码就能够很清楚的知道使用方法。
没有很差的,只有不适合的,适合的场景使用,根据场景适合的使用,才是提升性能的最有效手段。后须要根据所需的应用场景继续对这部分集成内容做完善,实践完善设计。