Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。
下面,笔者分以下几个步骤来进行介绍:
一、安装以及启动
这里为了学习的方便,大家可以去下载一个windows版本的,地址如下:服务端下载地址,另外还有一个可视化工具memadmin,不过需要依赖php的环境,地址如下:可视化工具地址。
停止和启动的命令如下:
memcached.exe -d stop
memcached.exe -d start
二、java memcached客户端的使用
首先,maven依赖如下:
<!-- Memcached客户端 -->
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>
memcached服务端默认的端口为11211,代码如下:
/**
* Project Name:qyk_testJava
* File Name:MemcachedClient.java
* Package Name:com.qiyongkang.memcached
* Date:2017年2月22日下午1:32:26
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package com.qiyongkang.memcached;
import com.whalin.MemCached.MemCachedClient;
import com.whalin.MemCached.SockIOPool;
/**
* ClassName:MemcachedClient <br/>
* Function: Memcached客户端. <br/>
* Date: 2017年2月22日 下午1:32:26 <br/>
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class MemcachedClient {
public static void main(String[] args) {
// 初始化SockIOPool,管理memcached的连接池
String[] servers = { "127.0.0.1:11211" };
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
// 建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
// 将对象加入到memcached缓存
boolean success = memCachedClient.set("name", "qiyongkang");
// 从memcached缓存中按key值取对象
String result = (String) memCachedClient.get("name");
System.out.println(String.format("set:" + success));
System.out.println(String.format("get:" + result));
}
}
运行结果如下:
三、spring中的集成
首先,我们把相关的配置属性放在属性文件memcached.properties中,如下:
#######################Memcached配置#######################
#服务器地址
memcached.server=127.0.0.1
memcached.port=11211
#初始化时对每个服务器建立的连接数目
memcached.initConn=20
#每个服务器建立最小的连接数
memcached.minConn=10
#每个服务器建立最大的连接数
memcached.maxConn=50
#自查线程周期进行工作,其每次休眠时间
memcached.maintSleep=3000
#Socket的参数,如果是true在写数据时不缓冲,立即发送出去
memcached.nagle=false
#Socket阻塞读取数据的超时时间
memcached.socketTO=3000
然后,再来看看集成在spring中的memcachedContext.xml配置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- properties config -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:memcached.properties</value>
</list>
</property>
</bean>
<!-- Memcached配置 -->
<bean id="memcachedPool" class="com.whalin.MemCached.SockIOPool"
factory-method="getInstance" init-method="initialize" destroy-method="shutDown">
<property name="servers">
<list>
<value>${memcached.server}:${memcached.port}</value>
</list>
</property>
<property name="initConn">
<value>${memcached.initConn}</value>
</property>
<property name="minConn">
<value>${memcached.minConn}</value>
</property>
<property name="maxConn">
<value>${memcached.maxConn}</value>
</property>
<property name="maintSleep">
<value>${memcached.maintSleep}</value>
</property>
<property name="nagle">
<value>${memcached.nagle}</value>
</property>
<property name="socketTO">
<value>${memcached.socketTO}</value>
</property>
</bean>
</beans>
然后,我们再封装一个MemcacheUtil工具类,如下:
/**
* Project Name:qyk_testSpring
* File Name:MemcacheUtil.java
* Package Name:org.qiyongkang.spring.util
* Date:2017年2月22日下午2:50:05
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.whalin.MemCached.MemCachedClient;
/**
* ClassName:MemcacheUtil <br/>
* Function: memcache缓存工具类. <br/>
* Date: 2017年2月22日 下午2:50:05 <br/>
*
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class MemcacheUtil {
private static Logger logger = LogManager.getLogger(MemcacheUtil.class);
private static String MEMCACHE_POOL_NAME = "default";
/**
* cachedClient.
*/
private static MemCachedClient cachedClient;
static {
if (cachedClient == null) {
cachedClient = new MemCachedClient(MEMCACHE_POOL_NAME);
}
}
/**
* 构造函数.
*/
private MemcacheUtil() {
}
/**
* 向缓存添加新的键值对。如果键已经存在,则之前的值将被替换.
*
* @param key
* 键
* @param value
* 值
* @return boolean
*/
public static boolean set(String key, Object value) {
return setExp(key, value, null);
}
/**
* 向缓存添加新的键值对。如果键已经存在,则之前的值将被替换.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
public static boolean set(String key, Object value, Date expire) {
return setExp(key, value, expire);
}
/**
* 向缓存添加新的键值对。如果键已经存在,则之前的值将被替换.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
private static boolean setExp(String key, Object value, Date expire) {
boolean flag = false;
try {
flag = cachedClient.set(key, value, expire);
} catch (Exception e) {
logger.error("Memcached set方法报错,key值:" + key + "\r\n" + exceptionWrite(e));
}
return flag;
}
/**
* 仅当缓存中不存在键时,add 命令才会向缓存中添加一个键值对.
*
* @param key
* 键
* @param value
* 值
* @return boolean
*/
public static boolean add(String key, Object value) {
return addExp(key, value, null);
}
/**
* 仅当缓存中不存在键时,add 命令才会向缓存中添加一个键值对.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
public static boolean add(String key, Object value, Date expire) {
return addExp(key, value, expire);
}
/**
* 仅当缓存中不存在键时,add 命令才会向缓存中添加一个键值对.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
private static boolean addExp(String key, Object value, Date expire) {
boolean flag = false;
try {
flag = cachedClient.add(key, value, expire);
} catch (Exception e) {
logger.error("Memcached add方法报错,key值:" + key + "\r\n" + exceptionWrite(e));
}
return flag;
}
/**
* 仅当键已经存在时,replace 命令才会替换缓存中的键.
*
* @param key
* 键
* @param value
* 值
* @return boolean
*/
public static boolean replace(String key, Object value) {
return replaceExp(key, value, null);
}
/**
* 仅当键已经存在时,replace 命令才会替换缓存中的键.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
public static boolean replace(String key, Object value, Date expire) {
return replaceExp(key, value, expire);
}
/**
* 仅当键已经存在时,replace 命令才会替换缓存中的键.
*
* @param key
* 键
* @param value
* 值
* @param expire
* 过期时间 New Date(1000*10):十秒后过期
* @return boolean
*/
private static boolean replaceExp(String key, Object value, Date expire) {
boolean flag = false;
try {
flag = cachedClient.replace(key, value, expire);
} catch (Exception e) {
logger.error("Memcached replace方法报错,key值:" + key + "\r\n" + exceptionWrite(e));
}
return flag;
}
/**
* get 命令用于检索与之前添加的键值对相关的值.
*
* @param key
* 键
* @return boolean
*/
public static Object get(String key) {
Object obj = null;
try {
obj = cachedClient.get(key);
} catch (Exception e) {
logger.error("Memcached get方法报错,key值:" + key + "\r\n" + exceptionWrite(e));
}
return obj;
}
/**
* 删除 memcached 中的任何现有值.
*
* @param key
* 键
* @return boolean
*/
public static boolean delete(String key) {
return cachedClient.delete(key);
}
/**
* 清理缓存中的所有键/值对.
*
* @return boolean
*/
public static boolean flashAll() {
boolean flag = false;
try {
flag = cachedClient.flushAll();
} catch (Exception e) {
logger.error("Memcached flashAll方法报错\r\n" + exceptionWrite(e));
}
return flag;
}
/**
* 返回异常栈信息,String类型.
*
* @param e
* Exception
* @return boolean
*/
private static String exceptionWrite(Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
return sw.toString();
}
}
这里需要说明一点,memcached连接池被创建的时候,可以传入一个poolName,如果没传就使用default,因此这个值要么为空,要么就是default,不要随便写,否则会报错的。这个大家可以去看下实例化memcached连接池的代码。
最后,再来使用下,测试代码如下:
/**
* Project Name:qyk_testSpring
* File Name:MemcacheClientTest.java
* Package Name:org.qiyongkang.spring.test
* Date:2017年2月22日下午2:23:47
* Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
*
*/
package org.qiyongkang.spring.test;
import org.qiyongkang.spring.util.MemcacheUtil;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName:MemcacheClientTest <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON. <br/>
* Date: 2017年2月22日 下午2:23:47 <br/>
* @author qiyongkang
* @version
* @since JDK 1.6
* @see
*/
public class MemcacheClientTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
new ClassPathXmlApplicationContext("applicationContext.xml");
//设置key
boolean isSuccess = MemcacheUtil.set("name", "qiyongkang");
System.out.println("是否成功:" + isSuccess);
Object obj = MemcacheUtil.get("name");
System.out.println("获取name:" + obj);
}
}
运行结果如下:
好了,就介绍到这儿了。另外关于memcached与spring的集成还有很多相关的东西,使用配置起来也比较方便,大家也可以去了解下。由于memcached缓存的数据全部是保存在内存中,所以一旦服务重启数据就全部遗失了,需要再次进行缓存。笔者平时用得比较多的是redis,关于两者之间的适用场景,大家可以去自行了解下~