现在的位置: 首页 > 综合 > 正文

memcached总结和与spring的集成

2017年11月15日 ⁄ 综合 ⁄ 共 5397字 ⁄ 字号 评论关闭

 

         Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻硬盘数据库的负载,基于一个存储键/值对的hashmap,守护进程用c写的,客户端可用各种语言实现。

         特点有以下几个:

        1.暂无认证以及安全管制,也没有冗余备份机制,考虑到时内存数据库,也就无所谓了。

          2.Value大小 1.4.2以后可以到128M

        3.节点之间不互相通信。

        4.c/s通信使用基于文本行的格式。

        5.使用LRU算法管理内存。

        6.客户端可以实现一致性哈希算法来决定数据在服务器节点的分布。

          7.Lazy Expiration,不主动检查数据的过期。

          原理图:

 

 

Perl:

整数哈希值根据余数计算分散

缺点:添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而影响缓存的命中率。

 

Php实现了Consistent Hashing:

 

 

首先求出memcached服务器(节点)的哈希值, 并将其配置到0~232的圆(continuum)上。add数据时,用同样的方法求出存储数据的键的哈希值,并映射到圆上。从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。 如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。

 

        上图可以看出增加服务器后受影响的只有部分区域,因为本来是属于node4的hash范围,加入机器后变为node5的范围。

 

memcached的监视工具可以用nagiosMemcachePHP

下面说明java客户端的用法,以跟spring集成为例:

 

          memcached的java客户端选用xmemcached,相关的包要下载下来。

一、客户端的bean配置:

	<!-- xMemcached集群配置 -->
	<bean id="cacheManager" class="com.cr.common.cache.base.CacheManager">
		<property name="clnt" ref="xMemcachedClient" />
		<property name="expire" value="3600" />
	</bean>
	<bean name="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder">
		<constructor-arg>
			<list>

				<bean class="java.net.InetSocketAddress">
					<constructor-arg>
						<value>192.168.0.110</value>
					</constructor-arg>
					<constructor-arg>
						<value>11211</value>
					</constructor-arg>
				</bean>
				<bean class="java.net.InetSocketAddress">
					<constructor-arg>
						<value>192.168.0.111</value>
					</constructor-arg>
					<constructor-arg>
						<value>11211</value>
					</constructor-arg>
				</bean>
				
			</list>
		</constructor-arg>
		<constructor-arg>
			<list>
				<value>1</value>
				<value>1</value>
			</list>
		</constructor-arg>
		<property name="connectionPoolSize" value="30"></property>
		<property name="commandFactory">
			<bean class="net.rubyeye.xmemcached.command.TextCommandFactory"></bean>
		</property>
		<property name="sessionLocator">
			<bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator">
				<constructor-arg index="0">
					<value>true</value>
				</constructor-arg>
			</bean>
		</property>
		<property name="transcoder">
			<bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"></bean>
		</property>
	</bean>
	<bean name="xMemcachedClient" factory-bean="memcachedClientBuilder"
		factory-method="build" destroy-method="shutdown">
		<property name="opTimeout" value="5000"></property>
	</bean>

 

        memcachedClientBuilder完成MemcachedClientBuilder,在constructor-arg里面配置集群的节点。然后通过memcachedClient节点配置用于连接的client。

cacheManger是memcached的管理类,持有xMemcachedClient的实例,提供get,set操作,当然是通过bean注入的。

主要代码如下:

 

public <T> T getCache(String key) {		
		try {	
			log.info("get " + key);
			return (T)(client.get(key));
		} catch (TimeoutException e) {
			log.error(e.getMessage(), e);
		} catch (InterruptedException e) {
			log.error(e.getMessage(), e);
		} catch (MemcachedException e) {
			log.error(e.getMessage(), e);
		}
		return null;
	}

	public  <T> boolean setCache(String key, T value) {
		try {			
			log.info("add cache:"+key);
			return client.set(key, expire, value);
		} catch (TimeoutException e) {
			log.error(e.getMessage(), e);
		} catch (InterruptedException e) {
			log.error(e.getMessage(), e);
		} catch (MemcachedException e) {
			log.error(e.getMessage(), e);
		}
		return false;
	}

二、spring的AOP切面配置:

<bean id="systemContext" class="com.cr.common.utils.SystemContext" >
		<property name="propertiesFile"
			value="/WEB-INF/spring/SystemContext.properties" />
	</bean>
	<!--BeanFactory 配置-->
	<bean class="com.cr.common.utils.BeanFactory"></bean>
      	<!-- 日志切面 -->
	<bean id="logAspect"
		class="com.cr.common.log.LogAspect">
		<property name="orderValue" value="1" />
	</bean>
        <!-- 缓存切面 -->
	<bean id="cacheAspect"
		class="com.cr.common.cache.core.CacheAspect">
		<property name="orderValue" value="2" />
		<property name="cacheManager" ref="cacheManager"/>
	</bean>
	<aop:aspectj-autoproxy>
		<aop:include name="cacheAspect" />
		<aop:include name="logAspect" />
	</aop:aspectj-autoproxy>

 

BeanFactory 类定义了通过名称获得Spring容器内的Bean实例:

public class BeanFactory implements ServletContextAware{
	private static ServletContext servletContext;
	public static <T> T getBean(String beanName) {
      WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		return wac.getBean(beanName);
	}

	@Override
	public void setServletContext(ServletContext servletContext) {
		BeanFactory.servletContext = servletContext;
	}
}

为了记录浏览器端的访问记录可以写一个log切面,最后是切面的申明配置。

以上两个配置文件都是作为spring bean管理的。

 

三、缓存切面类:

@Aspect
public class CacheAspect implements Ordered {
	private int orderValue = 2;
	private Cache cacheManager;
	private Logger log = Logger.getLogger(CacheAspect.class); 

	@Pointcut("@annotation(com.cr.common.cache.core.NeedCached)")
	public void needCached() {

	}

	@Around("needCached() && args(filter,..)")
	public Object aroundMethod(ProceedingJoinPoint pjp, QueryFilter filter)
			throws Throwable {
		if (filter.getValue() == null) {
			return null;
		}
		// 判断是否开启缓存
		boolean cacheEnabled = CommonUtil.parseBoolean(ContextConfig.getProperty("cache.enabled"), false);

		if (cacheEnabled) {
			String md5key = MD5Util.getMD5(filter.getValue().toString());
			Object value = cacheManager.getCacheInfo(md5key);
			boolean flag = false; 			if (null != value) 							JSONObject json = new JSONObject(value.toString());
				return json;
			} else if ("null".equals(value)) {
				return null;
			} else { 	value = pjp.proceed();
				
		if(null!=value){//此处省略若干业务码                                                                         cacheManager.setCacheInfo(md5key, value.toString());
						
						}
				return value;
			}
		} else {// 未开启缓存,执行数据库查询
			return pjp.proceed();
		}

	}

	public void setOrderValue(int orderValue) {
		this.orderValue = orderValue;
	}

	public int getOrder() {
		return orderValue;
	}

	public int getOrderValue() {
		return orderValue;
	}

	public Cache getCacheManager() {
		return cacheManager;
	}

	public void setCacheManager(Cache cacheManager) {
		this.cacheManager = cacheManager;
	}
}

needCached是自定义注解,用于需要执行缓存切面的方法上,aroundMethod环绕在含有此注解,及方法参数含有filter的方法上面。由于本来的key超过了250,所以md5后作为key,value是jsonObect。

前端web数据请求action若映射到含needCached注解的方法时,会进入此方法进行处理。若缓存有,直接返回,没有则执行数据库查询,再设置缓存。

抱歉!评论已关闭.