Memcached 介绍与分析
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
)的空间,用来建酒架,酒架根据这个酒瓶的大小分割酒架为多个小格子安放酒瓶,将同样大小范围内的酒瓶都放置在一类酒架上面。例如
20cm
半径的酒瓶放置在可以容纳
20-25cm
的酒架
A
上,
30cm
半径的酒瓶就放置在容纳
25-30cm
的酒架
B
上。回收机制也很简单,首先新酒入库,看看酒架是否有可以回收的地方,如果有直接使用,如果没有申请新的地方,如果申请不到,采用配置的过期策略。这个特点来看,如果要放的内容大小十分离散,同时大小比例相差梯度很明显,那么可能对于使用空间来说不好,可能在酒架
A
上就放了一瓶酒,但占用掉了一个酒架的位置。
Cache
机制简单:有时候很多开源的项目做的面面俱到,但是最后也就是因为过于注重一些非必要性的功能而拖累了性能,这里要提到的就是
Memcached
的简单性。首先它没有什么同步,消息分发,两阶段提交等等,它就是一个很简单的
Cache
,把东西放进去,然后可以取出来,如果发现所提供的
Key
没有命中,那么就很直白的告诉你,你这个
key
没有任何对应的东西在缓存里,去数据库或者其他地方取,当你在外部数据源取到的时候,可以直接将内容置入到
Cache
中,这样下次就可以命中了。这里会提到怎么去同步这些数据,两种方式,一种就是在你修改了以后立刻更新
Cache
内容,这样就会即时生效。另一种是说容许有失效时间,到了失效时间,自然就会将内容删除,此时再去去的时候就会命中不了,然后再次将内容置入
Cache
,用来更新内容。后者用在一些时时性要求不高,写入不频繁的情况。
客户端的重要性:
Memcached
是用
C
写的一个服务端,客户端没有规定,反正是
Socket
传输,只要语言支持
Socket
通信,通过
Command
的简单协议就可以通信,但是客户端设计的合理十分重要,同时也给使用者提供了很大的空间去扩展和设计客户端来满足各种场景的需要,包括容错,权重,效率,特殊的功能性需求,嵌入框架等等。
几个应用点:小对象的缓存(用户的
token
,权限信息,资源信息)。小的静态资源缓存。
Sql
结果的缓存(这部分用的好,性能提高相当大,同时由于
Memcached
自身提供
scale out
,那么对于
db scale out
的老大难问题无疑是一剂好药)。
ESB
消息缓存。
集成设计
为什么需要集成?直接使用现有的两个
Java
实现
Memcached
是否就可以了?
当前集成主要为了两方面考虑,首先是方便的配置使用,如何将
Memcached
内嵌到类似于
ASF
以及其他框架中去,并且通过配置文件方便使用,这就需要作部分的集成工作,这部分工作主要是定义了配置文件以及通过
Stax
去解析配置的功能。然后是如何管理
Memcached
,这部分内容包括了初始化,运行期检测,资源回收的工作。最后是扩展,这里的扩展分成两部分(功能的扩展以及框架实现的扩展),功能扩展例如当前扩展了虚拟的
cluster
,可以让多个
memcached Client
组成一个虚拟的
cluster
,如果通过放入
cluster
的方式放入到其中一个
Cache Client
中的话,那么就可以在整个
cluster
都作好备份,这样其实可以根据
memcached
的单机多实例以及多机多实例作交互备份,提高可靠性。当然后续还有很多可以扩展的内容,这里只是一个开头。框架实现的扩展指的是这里采用了类似于
Jdk
的
JAXP
的框架设计,只是规定了框架
API
结构,至于实现者动态载入,这个和
ASF
等现在可扩展的框架一样,提供了很方便的扩展点,后续的设计中会提到。
接口设计类图:
4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600">
图
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
|
1月3日 11:13:25
|