memcahe本身提供有对getMulti的支持 但是memecahed java client中的getMulti方法是使用多线程并发请求来实现的 为了减少socket的消耗添加了一个对getMulti(协议中为get key1 key2 ...)协议支持的方法
在AscIIClient中增加下面方法
private Map<String, Object> doMget(MultiKeyBuffer multiKeyBuffer) {
Map<String, Object> valuesMap = new HashMap<String, Object>();
while(multiKeyBuffer.hasNext()) {
String multiKey = multiKeyBuffer.next();
if(multiKey == null) return null;
// get SockIO obj using cache key
SchoonerSockIO sock = pool.getSock(multiKey, null);
if (sock == null) {
if (errorHandler != null)
errorHandler.handleErrorOnGet(this, new IOException("no socket to server available"), multiKey);
return null;
}
String cmdLine = "get " + multiKey;
try {
sock.writeBuf.clear();
sock.writeBuf.put(cmdLine.getBytes());
sock.writeBuf.put(B_RETURN);
// write buffer to server
sock.flush();
SockInputStream input = new SockInputStream(sock, Integer.MAX_VALUE);
int oneTimeReadMaxCount = 32;
jump:
while(oneTimeReadMaxCount-- > 0){
String responseKey = "";
int dataSize = 0;
int flag = 0;
boolean stop = false;
StringBuilder sb = new StringBuilder();
int b;
int index = 0;
while (!stop) {
/*
* Critical block to parse the response header.
*/
b = input.read();
if (b == ' ' || b == '\r') {
switch (index) {
case 0:
if (END.startsWith(sb.toString()))
break jump;
case 1:
responseKey = sb.toString();
break;
case 2:
flag = Integer.parseInt(sb.toString());
break;
case 3:
// get the data size
dataSize = Integer.parseInt(sb.toString());
break;
}
index++;
sb = new StringBuilder();
if (b == '\r') {
input.read();
stop = true;
}
continue;
}
sb.append((char) b);
}
Object o = null;
input.willRead(dataSize);
// we can only take out serialized objects
if (dataSize > 0) {
if (NativeHandler.isHandled(flag)) {
// decoding object
byte[] buf = input.getBuffer();
if ((flag & F_COMPRESSED) == F_COMPRESSED) {
GZIPInputStream gzi = new GZIPInputStream(new ByteArrayInputStream(buf));
ByteArrayOutputStream bos = new ByteArrayOutputStream(buf.length);
int count;
byte[] tmp = new byte[2048];
while ((count = gzi.read(tmp)) != -1) {
bos.write(tmp, 0, count);
}
// store uncompressed back to buffer
buf = bos.toByteArray();
gzi.close();
}
if (primitiveAsString) {
o = new String(buf, defaultEncoding);
} else
o = NativeHandler.decode(buf, flag);
} else if (transCoder != null) {
// decode object with default transcoder.
InputStream in = input;
if ((flag & F_COMPRESSED) == F_COMPRESSED)
in = new GZIPInputStream(in);
if (classLoader == null)
o = transCoder.decode(in);
else
o = ((ObjectTransCoder) transCoder).decode(in, classLoader);
}
valuesMap.put(responseKey, o);
}
input.willRead(Integer.MAX_VALUE);
input.getLine();//跳过每一个value发送完成的\r\n
}
} catch (Exception ce) {
try {
sock.trueClose();
} catch (IOException e) {
log.error("++++ failed to close socket : " + sock.toString());
}
sock = null;
} finally {
if (sock != null) {
sock.close();
sock = null;
}
}
}
return valuesMap;
}
@Override
public Map<String, Object> mget(String[] keys) {
if (keys == null || keys.length == 0) {
log.error("keys is null for mget()");
return null;
}
MultiKeyBuffer buf = new MultiKeyBuffer(keys);
return doMget(buf);
}
/*这里设置每次使用getMulti最多执行多少个key(忘了memcache支持一次多少个key了...)*/
private static int per_multikey_max_size = 32;
class MultiKeyBuffer{
/**原始数据*/
private final String[] keys;
/**当前已经取到了哪个位置(从0开始)*/
private int position;
/**数组的长度*/
private final int size;
public MultiKeyBuffer(String[] keys) {
this.keys = keys;
size = keys.length;
}
/**
* 获取下一个multiKey
* @return
*/
public String next(){
if(hasNext()){
int limit = Math.min(size, position + per_multikey_max_size);
StringBuilder multiKeySb = new StringBuilder();
while(position < limit){
String key = keys[position];
try {
key = sanitizeKey(key);
} catch (UnsupportedEncodingException e) {
log.error("failed to sanitize your key!", e);
return null;
}
multiKeySb.append(" ").append(key);
position ++;
}
return multiKeySb.substring(1);
}
return null;//调用hasNext()防止执行到该行
}
public boolean hasNext(){
return position < size;
}
}
在父类MemcachdClient中增加
public Map<String, Object> mget(String[] keys) {
return client.mget(keys);
}