目录
(一) 集成前配置
引入依赖
配置application.yml
自定义properties类
自定义Memcached连接配置类
MemcachedClien
tBuilder 相关属性含义
(二) MemcachedClient相关操作
插入Set
获取Get
touch更新数据超时时间
CAS原子操作
综合性操作例子
迭代所有key(不建议)
Incr/Decr原子递增/原子递减
查看统计信息
保存数据体量较大时压缩
(三) 监视服务器运行状况stats命令详解
com.googlecode.xmemcached
xmemcached
2.4.5
spring:
memcache:
# 服务地址
servers: 127.0.0.1:11211
# 连接数量
poolSize: 10
# 操作超时时长
opTimeout: 5000
sanitizeKeys: false # 是否启用url encode机制
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* XMemcached
* 为了缩短篇幅 , 笔者删减了getter 和 setter
* @author HUANG
*/
@Component
// 用于读取配置文件
@ConfigurationProperties(prefix = "spring.memcache")
public class IMemcachedProperties {
/**
* memcached服务地址
*/
private String servers;
/**
* nio连接池的数量
*/
private Integer poolSize;
/**
* 设置默认操作超时
*/
private Long opTimeout;
/**
* 是否启用url encode机制
*/
private Boolean sanitizeKeys;
}
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.command.BinaryCommandFactory;
import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator;
import net.rubyeye.xmemcached.utils.AddrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* Memcached 配置
*/
@Configuration
public class IMemcachedConfig {
/**
* 日志
*/
private static Logger logger = LoggerFactory.getLogger(IMemcachedConfig.class);
@Autowired
private IMemcachedProperties iMemcachedProperties;
@Bean
public MemcachedClient getMemcachedClinet() {
MemcachedClient memcachedClient = null;
try {
// 获取连接
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(iMemcachedProperties.getServers()));
// 从1.3版本开始,xmemcached支持failure模式。所谓failure模式是指,当一个memcached节点down掉的时候,发往这个节点的请求将直接失败,而不是发送给下一个有效的memcached节点 , 默认不启用failure模式
builder.setFailureMode(false);
builder.setSanitizeKeys(iMemcachedProperties.getSanitizeKeys());
// 启用连接池 , 设置nio连接池连接数量, Xmemcached是基于java nio的client实现,默认对一个memcached节点只有一个连接,这在通常情况下已经有非常优异的表现。但是在典型的高并发环境下,nio的单连接也会遇到性能瓶颈。因此XMemcached支持设置nio的连接池,允许建立多个连接到同一个memcached节点,但是请注意,这些连接之间是不同步的,因此你的应用需要自己保证数据更新的同步,
builder.setConnectionPoolSize(iMemcachedProperties.getPoolSize());
// 设置操作超时时长
builder.setOpTimeout(iMemcachedProperties.getOpTimeout());
// 设置连接时长
builder.setConnectTimeout(3000);
// 设置数据一致性hash算法
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 启用二进制协议 Memcached 1.4开始正式启用二进制协议,xmemcached 1.2开始支持二进制协议
builder.setCommandFactory(new BinaryCommandFactory());
memcachedClient = builder.build();
} catch (IOException e) {
logger.error("init MemcachedClient fail:", e);
}
return memcachedClient;
}
}
配置选项参数表:
属性名 | 值 |
---|---|
servers | memcached节点列表,形如“主节点1:port,备份节点1:port 主节点2:port,备份节点2:port“的字符串,可以不设置备份节点,主备节点逗号隔开,不同分组空格隔开。 |
weights | 与servers对应的节点的权重 |
authInfoMap | 授权验证信息,仅在xmemcached 1.2.5及以上版本有效 |
connectionPoolSize | nio连接池大小,默认为1 |
commandFactory | 协议工厂,net.rubyeye.xmemcached.command.BinaryCommandFactory,TextCommandFactory(默认),KestrelCommandFactory |
sessionLocator | 分布策略,一致性哈希net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator或者ArraySessionLocator(默认) |
transcoder | 序列化转换器,默认使用net.rubyeye.xmemcached.transcoders.SerializingTranscoder,更多选项参见javadoc |
---|---|
bufferAllocator | IoBuffer分配器,默认为net.rubyeye.xmemcached.buffer.SimpleBufferAllocator,可选CachedBufferAllocator(不推荐) |
failureMode | 是否启用failure模式,true为启用,默认不启用 |
客户不能存储所有类型的复杂结构。对象通常是不可序列化的,例如从mysql查询返回的行对象。必须先将数据转换为纯数组或哈希/表类型结构,然后才能存储或检索它。key最长250 , 存储体量单个最大1M(因为这是典型slab 的最大值)
// 使用到的引入类
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.exception.MemcachedException;
// 注入客户端实例
@Autowired
private MemcachedClient memcachedClient;
@PostMapping("/testMem001")
public void testMem001(String key, String value){
try {
memcachedClient.set(key, 0, value, 5000);
Object o = memcachedClient.get(key);
System.out.println("查询出来的数据是" + o);
} catch (TimeoutException e) {
// 此处若是set进去的数据已经超时失效 get方法就会出现TimeOutException
logger.error("数据失效,查询失败,key:[{}]" , key);
e.printStackTrace();
} catch (InterruptedException | MemcachedException e) {
e.printStackTrace();
}
}
value=client.get(“hello”,3000);
就是等待3秒超时,如果3秒超时就跑出TimeoutException,用户需要自己处理这个异常。因为等待是通过调用CountDownLatch.await(timeout)方法,因此用户还需要处理中断异常InterruptException。最后的MemcachedException表示Xmemcached内部发生的异常,如解码编码错误、网络断开等等异常情况。
client.touch(key,new-expire-time);
client.getAndTouch(key,new-expire-time);
Memcached是通过cas协议实现原子更新,所谓原子更新就是compare and set,原理类似乐观锁,每次请求存储某个数据同时要附带一个cas值,memcached比对这个cas值与当前存储数据的cas值是否相等,如果相等就让新的数据覆盖老的数据,如果不相等就认为更新失败,这在并发环境下特别有用。XMemcached提供了对CAS协议的支持(无论是文本协议还是二进制协议),XMemcached提供了一个*CASOperation*接口包装了这部分操作,允许你尝试N次去原子更新某个key存储的数据,无需显式地调用gets获取cas值
CASOpertion接口只有两个方法,一个是设置最大尝试次数的getMaxTries方法,这里是尝试一次,如果尝试超过这个次数没有更新成功将抛出一个TimeoutException,如果你想无限尝试(理论上),可以将返回值设定为Integer.MAX_VALUE;另一个方法是根据当前获得的GetsResponse来决定更新数据的getNewValue方法,如果更新成功,这个方法返回的值将存储成功,这个方法的两个参数是最新一次gets返回的GetsResponse结果。
//cas 操作 CASOperation String 就是你将要修改后的值类型 也是getNewValue的return值
boolean huang = this.memcachedClient.cas("hxk3", new CASOperation() {
@Override
public int getMaxTries() {
return 0;
}
@Override
public String getNewValue(long currentCAS, String currentValue) {
return "这是新的CAS值";
}
});
// 首先存储 hello 对应的world字符串
if (!client.set("hello", 0, "world")) {
System.err.println("set error");
}
// 然后调用add添加,因为数据已经存在,因此add会失败
if (client.add("hello", 0, "dennis")) {
System.err.println("Add error,key is existed");
}
// 然后调用replace替换 , replace在数据存在的情况才会成功,也就是将hello对应的数据更新为dennis
if (!client.replace("hello", 0, "dennis")) {
System.err.println("replace error");
}
// 然后通过prepend方法在dennis前加上了字符串hello
client.append("hello", " good");
// 然后通过append方法在dennis后加上了字符串good
client.prepend("hello", "hello ");
// 获取
String name = client.get("hello", new StringTranscoder());
// 结果 hello dennis good。
System.out.println(name);
// 而删除数据则是通过deleteWithNoReply方法,这个方法删除数据并且告诉memcached不用返回应答,因此这个方法不会等待应答直接返回,特别适合于批量处理;同样地,set、add、replace等方法也有相应的withNoReply重载版本
client.deleteWithNoReply(“hello”);
迭代器是memcached所有key的快照,它不是实时的。“ stats cachedump”具有长度限制,因此如果有很多密钥,迭代器将无法访问所有密钥. 应用程序不应依赖于此功能。
不推荐使用 memcached 1.6.x将删除cachedump stats命令,因此以后将删除此方法
Memcached本身并没有提供迭代所有key的方法,但是通过"stats items"和"stats cachedump"统计协议可以做到迭代所有的key,这个迭代过程是低效,因此如无必要,并不推荐使用此方法。XMemcached仅提供了文本协议的迭代支持,其他协议暂未支持。
想迭代所有的key,你只需要获取一个KeyIterator即可:
KeyIterator it=client.getKeyIterator(AddrUtil.getOneAddress("localhost:11211"));
while(it.hasNext())
{
String key=it.next();
}
incr/decr 两个操作都类似Java中的原子类如AtomicIntger,用于原子递增或者递减变量数值:
this.memcachedClient.incr("a", 5, 1) == 1;
this.memcachedClient.incr("a", 5) == 6;
this.memcachedClient.incr("a", 4) == 10;
this.memcachedClient.decr("a", 1) == 9;
this.memcachedClient.decr("a", 2) == 7;
incr和decr都有三个参数的方法,第一个参数指定递增的key名称,第二个参数指定递增的幅度大小,第三个参数指定当key不存在的情况下的初始值。两个参数的重载方法省略了第三个参数,默认指定为0。
Xmemcached还提供了一个称为计数器的封装,它封装了incr/decr方法,使用它就可以类似AtomicLong那样去操作计数:
Counter counter=memcachedClient.getCounter("counter",0);
counter.incrementAndGet();
counter.decrementAndGet();
counter.addAndGet(-10);
其中getCounter的第二个参数是计数器的初始值。
Memcached提供了统计协议用于查看统计信息:
Map> result=client.getStats();
getStats方法返回一个map,其中存储了所有已经连接并且有效的memcached节点返回的统计信息,你也可以统计具体的项目,如统计items项目:
Map> result=client.getStatsByItem("items");
只要向getStatsByItem传入需要统计的项目名称即可。
返回出局格式如下
数据压缩
memcached存储大数据的效率是比较低的,当数据比较大的时候xmemcached会帮你压缩在存储,取出来的时候自动解压并反序列化,这个大小阀值默认是16K,可以通过Transcoder接口的setCompressionThreshold(1.2.1引入)方法修改阀值,
压缩时Memcached会设置一个特殊的标志位。然后压缩数据并存储。由于使用了特殊标志,客户端将自动知道是否在返回时解压缩该值。
比如设置为1K:
builder.getTranscoder()).setCompressionThreshold(1024);
sanitizeKeys
在官方客户端有提供一个sanitizeKeys选项,当选择用URL当key的时候,MemcachedClient会自动将URL encode再存储。默认是关闭的,想启用可以通过:
builder.setSanitizeKeys(true);
flush_all
命令时,缓存中的所有项目都会失效,并且此计数器会增加。