高性能Java memcached客户端——Xmemcached发布2.0.0版本,主要改进如下:
- 性能优化,在测试中文本协议实现有接近10%的性能提升。
- 批量获取key如果包含相同的key,会缓存反序列化后的对象,减少重复反序列化的CPU开销。
- 修复getStats没办法获取cache dump的Bug,感谢machao9email。
- 修复ONE_AT_A_TIME没有正确处理无符号数的Bug,感谢spudone。
- 使用纳秒时间戳作为namespace值,避免高并发下的namespace值冲突。
Maven依赖:
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.0.0</version>
</dependency>
Spring + Xmemcached
<pre name="code" class="java"> <bean name="memcachedClient"
class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean">
<property name="servers">
<!-- 服务器列表,格式:(ip:端口 ip:端口),多个以空格分割 -->
<value>10.0.1.75:12000 10.0.1.76:12000 10.0.1.77:12000</value>
</property>
<!-- 和servers对应的节点的权重,weights是用来调节Memcached的负载,设置的权重越高,该Memcached节点存储的数据将越多,所承受的负载越大。 -->
<property name="weights">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<!-- nio连接池大小,默认一个Memcached节点只有一个连接池,通常条件是没有问题的。但是在典型的高并发环境下,nio的单连接也会遇到性能瓶颈。可通过调整连接池数量,<br>
当建立了多个连接到同一个Memcached节点,由于连接之间是不同步的,因此你的应用需要自己保证数据更新的同步 -->
<property name="connectionPoolSize">
<value>1</value>
</property>
<!-- 与服务器节点个数一致,密码不允许有空格 -->
<property name="authInfoMap">
<map>
<entry key-ref="serverA">
<bean class="net.rubyeye.xmemcached.auth.AuthInfo"
factory-method="typical">
<constructor-arg index="0">
<value>memcached</value>
</constructor-arg>
<constructor-arg index="1">
<value>123456</value>
</constructor-arg>
</bean>
</entry>
<entry key-ref="serverB">
<bean class="net.rubyeye.xmemcached.auth.AuthInfo"
factory-method="typical">
<constructor-arg index="0">
<value>memcached</value>
</constructor-arg>
<constructor-arg index="1">
<value>123456</value>
</constructor-arg>
</bean>
</entry>
<entry key-ref="serverC">
<bean class="net.rubyeye.xmemcached.auth.AuthInfo"
factory-method="typical">
<constructor-arg index="0">
<value>memcached</value>
</constructor-arg>
<constructor-arg index="1">
<value>123456</value>
</constructor-arg>
</bean>
</entry>
</map>
</property>
<!-- 分布策略,KetamaMemcachedSessionLocator(一致性哈希,增加主机时降低未命中率问题的解决);
还有实现了:ArrayMemcachedSessionLocator;ElectionMemcachedSessionLocator;
LibmemcachedMemcachedSessionLocator;PHPMemcacheSessionLocator -->
<property name="sessionLocator">
<bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator" />
</property>
<!-- 序列化转换器 -->
<property name="transcoder">
<bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" />
</property>
<!-- IoBuffer分配器(内容写入memcached时的内存控制策略) -->
<property name="bufferAllocator">
<bean class="net.rubyeye.xmemcached.buffer.SimpleBufferAllocator" />
</property>
<!-- 协议工厂net.rubyeye.xmemcached.command.BinaryCommandFactory(二进制),TextCommandFactory(文本),KestrelCommandFactory(可靠获取),本应用配置二进制方式 -->
<property name="commandFactory">
<bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory" />
</property>
</bean>
<bean name="serverA" class="java.net.InetSocketAddress">
<constructor-arg>
<value>10.0.1.75</value>
</constructor-arg>
<constructor-arg>
<value>12000</value>
</constructor-arg>
</bean>
<bean name="serverB" class="java.net.InetSocketAddress">
<constructor-arg>
<value>10.0.1.76</value>
</constructor-arg>
<constructor-arg>
<value>12000</value>
</constructor-arg>
</bean>
<bean name="serverC" class="java.net.InetSocketAddress">
<constructor-arg>
<value>10.0.1.77</value>
</constructor-arg>
<constructor-arg>
<value>12000</value>
</constructor-arg>
</bean>
调用
@Resource
public MemcachedClient memcachedClient;
public void testString() {
try {
for (int i = 1024; i < 1024 * 2; i++) {
memcachedClient.set("zy" + i, 0, i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
更多Memcached操作,请查看Memcached之存储命令(2),然后在Xmemcached客户端找对应的方法。
Java API调用
查看:https://github.com/killme2008/xmemcached
@Test
public void javaExample() throws MemcachedException, InterruptedException, TimeoutException, IOException {
XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:11211"));
builder.setCommandFactory(new BinaryCommandFactory());
builder.setCommandFactory(new KestrelCommandFactory());
XMemcachedClient client = (XMemcachedClient) builder.build();
client.setPrimitiveAsString(true);
client.addServer("10.0.1.75:12000 10.0.1.76:12000");
client.removeServer("10.0.1.75:12000 10.0.1.76:12000");
// get operation
String name = client.get("test");
// set add replace append prepend gets
client.add("hello", 0, "dennis");
client.replace("hello", 0, "dennis");
client.append("hello", " good");
client.prepend("hello", "hello ");
GetsResponse response = client.gets("hello");
long cas = response.getCas();
Object value = response.getValue();
// incr decr
client.set("a", 0, "1");
client.incr("a", 4);
client.decr("a", 4);
// cas
client.cas("a", 0, new CASOperation() {
@Override
public int getMaxTries() {
return 1; // max try times
}
@Override
public Object getNewValue(long currentCAS, Object currentValue) {
System.out.println("current value " + currentValue);
return 3; // return new value to update
}
});
// flush_all
client.flushAll();
// stats
Map<InetSocketAddress, Map<String, String>> result = client.getStats();
// get server versions
Map<InetSocketAddress, String> version = client.getVersions();
// bulk get
List<String> keys = new ArrayList<String>();
keys.add("hello");
keys.add("test");
Map<String, Object> map = client.get(keys);
}
常见问题
如果启动的协议是二进制的,必须在Spring配置文件指定(xmemcached默认的协议是:net.rubyeye.xmemcached.command.TextCommandFactory),不然会打印如下异常:
net.rubyeye.xmemcached.exception.MemcachedException: Session has been closed
at net.rubyeye.xmemcached.impl.MemcachedTCPSession.destroy(MemcachedTCPSession.java:111)
at net.rubyeye.xmemcached.impl.MemcachedHandler.onSessionClosed(MemcachedHandler.java:164)
at com.google.code.yanf4j.core.impl.AbstractSession.onClosed(AbstractSession.java:306)
at com.google.code.yanf4j.core.impl.AbstractSession.close(AbstractSession.java:294)
at com.google.code.yanf4j.nio.impl.NioTCPSession.decode(NioTCPSession.java:311)
at com.google.code.yanf4j.nio.impl.NioTCPSession.decodeAndDispatch(NioTCPSession.java:237)
at com.google.code.yanf4j.nio.impl.NioTCPSession.readFromBuffer(NioTCPSession.java:207)
at com.google.code.yanf4j.nio.impl.AbstractNioSession.onRead(AbstractNioSession.java:196)
at com.google.code.yanf4j.nio.impl.AbstractNioSession.onEvent(AbstractNioSession.java:341)
at com.google.code.yanf4j.nio.impl.SocketChannelController.dispatchReadEvent(SocketChannelController.java:56)
at com.google.code.yanf4j.nio.impl.NioController.onRead(NioController.java:157)
at com.google.code.yanf4j.nio.impl.Reactor.dispatchEvent(Reactor.java:323)
at com.google.code.yanf4j.nio.impl.Reactor.run(Reactor.java:180)
常见问题会持续更新。。。