1 Memcache是什么
Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。
它可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个HashTable,Memcached自管理这些HashTable。
为什么会有Memcache和memcached两种名称?
其实Memcache是这个项目的名称,而memcached是它服务器端的主程序文件名,
Memcache官方网站:http://www.danga.com/memcached,
2 Memcache工作原理
首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中的,并不是保存在 cache 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些对象并不是持久的,服务停止之后,里边的数据就会丢失。
与许多 cache 工具类似,Memcached 的原理并不复杂。它采用了C/S的模式,在 server 端启动服务进程,在启动时可以指定监听的 ip,自己的端口号,所使用的内存大小等几个关键参数。一旦启动,服务就一直处于可用状态。Memcached 的目前版本是通过C实现,采用了单进程,单线程,异步I/O,基于事件 (event_based) 的服务方式.使用 libevent 作为事件通知实现。多个 Server 可以协同工作,但这些 Server 之间是没有任何通讯联系的,每个 Server 只是对自己的数据进行管理。Client 端通过指定 Server 端的 ip 地址(通过域名应该也可以)。需要缓存的对象或数据是以 key->value 对的形式保存在Server端。key 的值通过 hash 进行转换,根据 hash 值把 value 传递到对应的具体的某个 Server 上。当需要获取对象数据时,也根据 key 进行。首先对 key 进行 hash,通过获得的值可以确定它被保存在了哪台 Server 上,然后再向该 Server 发出请求。Client 端只需要知道保存 hash(key) 的值在哪台服务器上就可以了。
其实说到底,memcache 的工作就是在专门的机器的内存里维护一张巨大的 hash 表,来存储经常被读写的一些数组与文件,从而极大的提高网站的运行效率。
今天先研究研究缓存工具类的改造,在旧框架中部分函数用了ehcache对执行结果进行了缓存处理,现在目标是提供一个缓存工具类,在配置文件中配置使用哪种缓存(memcached或ehcached),使其它程序对具体的缓存不依赖,同时使用AOP方式来对方法执行结果进行缓存。
首先是工具类的实现:
在Spring中配置
- <!-- EhCache Manager -->
- <bean id="cacheManager"
- class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- <property name="configLocation">
- <value>classpath:ehcache.xml</value>
- </property>
- </bean>
- <bean id="localCache"
- class="org.springframework.cache.ehcache.EhCacheFactoryBean">
- <property name="cacheManager" ref="cacheManager" />
- <property name="cacheName"
- value="×××.cache.LOCAL_CACHE" />
- </bean>
- <bean id="cacheService"
- class="×××.core.cache.CacheService" init-method="init" destroy-method="destory">
- <property name="cacheServerList" value="${cache.servers}"/>
- <property name="cacheServerWeights" value="${cache.cacheServerWeights}"/>
- <property name="cacheCluster" value="${cache.cluster}"/>
- <property name="localCache" ref="localCache"/>
- </bean>
在properties文件中配置${cache.servers} ${cache.cacheServerWeights} ${cache.cluster}
具体工具类的代码
- /**
- * @author Marc
- *
- */
- public class CacheService {
- private Log logger = LogFactory.getLog(getClass());
- private Cache localCache;
- String cacheServerList;
- String cacheServerWeights;
- boolean cacheCluster = false;
- int initialConnections = 10;
- int minSpareConnections = 5;
- int maxSpareConnections = 50;
- long maxIdleTime = 1000 * 60 * 30; // 30 minutes
- long maxBusyTime = 1000 * 60 * 5; // 5 minutes
- long maintThreadSleep = 1000 * 5; // 5 seconds
- int socketTimeOut = 1000 * 3; // 3 seconds to block on reads
- int socketConnectTO = 1000 * 3; // 3 seconds to block on initial
- // connections. If 0, then will use blocking
- // connect (default)
- boolean failover = false; // turn off auto-failover in event of server
- // down
- boolean nagleAlg = false; // turn off Nagle's algorithm on all sockets in
- // pool
- MemCachedClient mc;
- public CacheService(){
- mc = new MemCachedClient();
- mc.setCompressEnable(false);
- }
- /**
- * 放入
- *
- */
- public void put(String key, Object obj) {
- Assert.hasText(key);
- Assert.notNull(obj);
- Assert.notNull(localCache);
- if (this.cacheCluster) {
- mc.set(key, obj);
- } else {
- Element element = new Element(key, (Serializable) obj);
- localCache.put(element);
- }
- }
- /**
- * 删除
- */
- public void remove(String key){
- Assert.hasText(key);
- Assert.notNull(localCache);
- if (this.cacheCluster) {
- mc.delete(key);
- }else{
- localCache.remove(key);
- }
- }
- /**
- * 得到
- */
- public Object get(String key) {
- Assert.hasText(key);
- Assert.notNull(localCache);
- Object rt = null;
- if (this.cacheCluster) {
- rt = mc.get(key);
- } else {
- Element element = null;
- try {
- element = localCache.get(key);
- } catch (CacheException cacheException) {
- throw new DataRetrievalFailureException("Cache failure: "
- + cacheException.getMessage());
- }
- if(element != null)
- rt = element.getValue();
- }
- return rt;
- }
- /**
- * 判断是否存在
- *
- */
- public boolean exist(String key){
- Assert.hasText(key);
- Assert.notNull(localCache);
- if (this.cacheCluster) {
- return mc.keyExists(key);
- }else{
- return this.localCache.isKeyInCache(key);
- }
- }
- private void init() {
- if (this.cacheCluster) {
- String[] serverlist = cacheServerList.split(",");
- Integer[] weights = this.split(cacheServerWeights);
- // initialize the pool for memcache servers
- SockIOPool pool = SockIOPool.getInstance();
- pool.setServers(serverlist);
- pool.setWeights(weights);
- pool.setInitConn(initialConnections);
- pool.setMinConn(minSpareConnections);
- pool.setMaxConn(maxSpareConnections);
- pool.setMaxIdle(maxIdleTime);
- pool.setMaxBusyTime(maxBusyTime);
- pool.setMaintSleep(maintThreadSleep);
- pool.setSocketTO(socketTimeOut);
- pool.setSocketConnectTO(socketConnectTO);
- pool.setNagle(nagleAlg);
- pool.setHashingAlg(SockIOPool.NEW_COMPAT_HASH);
- pool.initialize();
- logger.info("初始化memcached pool!");
- }
- }
- private void destory() {
- if (this.cacheCluster) {
- SockIOPool.getInstance().shutDown();
- }
- }
- }
然后实现函数的AOP拦截类,用来在函数执行前返回缓存内容
- public class CachingInterceptor implements MethodInterceptor {
- private CacheService cacheService;
- private String cacheKey;
- public void setCacheKey(String cacheKey) {
- this.cacheKey = cacheKey;
- }
- public void setCacheService(CacheService cacheService) {
- this.cacheService = cacheService;
- }
- public Object invoke(MethodInvocation invocation) throws Throwable {
- Object result = cacheService.get(cacheKey);
- //如果函数返回结果不在Cache中,执行函数并将结果放入Cache
- if (result == null) {
- result = invocation.proceed();
- cacheService.put(cacheKey,result);
- }
- return result;
- }
- }
Spring的AOP配置如下:
- <aop:config proxy-target-class="true">
- <aop:advisor
- pointcut="execution(* ×××.PoiService.getOne(..))"
- advice-ref="PoiServiceCachingAdvice" />
- </aop:config>
- <bean id="BasPoiServiceCachingAdvice"
- class="×××.core.cache.CachingInterceptor">
- <property name="cacheKey" value="PoiService" />
- <property name="cacheService" ref="cacheService" />
- </bean>