標籤:
在學習使用Java_Memcache操作memcache後,饒有興趣的研究了一下Java_Memcache的源碼。Java_Memcache在類AscIIClient中封裝了資料操作方法set/add/delete/append/get等。
儲存資料set
由Memcache命令詳解,我們知道memcache原始的set命令格式為
set <key> <flag> <expiretime> <bytes> \r\n
<value> \r\n
而在Java_Memcache中set操作資料最始調用的是set(String, String, Object, Date, Integer, Long, flag)方法。
private boolean set(String s, String s1, Object obj, Date date, Integer integer, Long long1, boolean flag){ SchoonerSockIO schoonersockio; int i; String s2; ...... s1 = sanitizeKey(s1); ...... schoonersockio = pool.getSock(s1, integer); if(schoonersockio == null) { if(errorHandler != null) errorHandler.handleErrorOnSet(this, new IOException("no socket to server available"), s1); return false; } if(date == null) date = new Date(0L); i = NativeHandler.getMarkerFlag(obj); s2 = s + " " + s1 + " " + i + " " + date.getTime() / 1000L + " "; boolean flag1; schoonersockio.writeBuf.clear(); schoonersockio.writeBuf.put(s2.getBytes()); int j = schoonersockio.writeBuf.position(); schoonersockio.writeBuf.put(BLAND_DATA_SIZE); if(long1.longValue() != 0L) schoonersockio.writeBuf.put((new StringBuilder()).append(" ").append(long1.toString()).toString().getBytes()); schoonersockio.writeBuf.put(B_RETURN); SockOutputStream sockoutputstream = new SockOutputStream(schoonersockio); int k = 0; if(i != 0) { byte abyte0[]; if(flag) abyte0 = obj.toString().getBytes(defaultEncoding); else abyte0 = NativeHandler.encode(obj); sockoutputstream.write(abyte0); k = abyte0.length; } else { k = transCoder.encode(sockoutputstream, obj); } schoonersockio.writeBuf.put(B_RETURN); byte abyte1[] = (new Integer(k)).toString().getBytes(); int l = schoonersockio.writeBuf.position(); schoonersockio.writeBuf.position(j); schoonersockio.writeBuf.put(abyte1); schoonersockio.writeBuf.position(l); schoonersockio.flush(); String s3 = (new SockInputStream(schoonersockio, 2147483647)).getLine(); if(!"STORED\r\n".equals(s3)) break MISSING_BLOCK_LABEL_538; ...... return false;}
當我們使用MemCacheClient.set(“name”,”abcdef”),通過對比上面的code,首先i=NativeHandler.getMarkerFlag(“abcdef”)值為32, 然後拼接字串s2=”set name 32 0 ”並將s2放入ByteBuffer中,然後調用schoonersockio.writeBuf.put(BLAND_DATA_SIZE),而BLADN_DATA_SIZE是" ".getBytes(),其實就是“set name 32 0 ”,最後schoonersockio.writeBuf.put(B_RETURN)即輸入\r\n,最後再通過transCoder.encode()將value寫入。
public void encode(OutputStream outputstream, Object obj)throws IOException{ ObjectOutputStream objectoutputstream = new ObjectOutputStream(outputstream); objectoutputstream.writeObject(obj); objectoutputstream.close();}
其實就是將對象序列化,因此在使用Java_Memcache操作的自訂對象必須都實現Serializable介面。
但是,如果我們直接運行”set name 32 0 \r\n“會提示錯誤資訊。
我很困惑set命令最後的<byte>參數是如何處理的????
反編譯後的代碼不全 or 反編譯有誤?
資料提取get
同樣,memcache原始的get命令很簡單,其基本格式為:
get <key> \r\n
而在Java_Memcache中,AscIIClient中是按如下處理的:
public Object get(String s){ return get(s, null);}public Object get(String s, Integer integer){ return get("get", s, integer, false);}
private Object get(String s, String s1, Integer integer, boolean flag){ SchoonerSockIO schoonersockio; String s2; .... s1 = sanitizeKey(s1); schoonersockio = pool.getSock(s1, integer); s2 = (new StringBuilder()).append(s).append(" ").append(s1).toString(); int i; int j; SockInputStream sockinputstream; boolean flag1; StringBuffer stringbuffer; int l; schoonersockio.writeBuf.clear(); schoonersockio.writeBuf.put(s2.getBytes()); schoonersockio.writeBuf.put(B_RETURN); schoonersockio.flush(); i = 0; j = 0; sockinputstream = new SockInputStream(schoonersockio, 2147483647); flag1 = false; stringbuffer = new StringBuffer(); l = 0; <span style="color:#006600;">//前面一部分就是構建字串 “get name \r\n”,即向Memcache server發出get命令</span>_L5: int k; if(flag1) break MISSING_BLOCK_LABEL_365; k = sockinputstream.read(); if(k != 32 && k != 13) break MISSING_BLOCK_LABEL_353; <span style="color:#006600;">//一直讀取到斷行符號或換行</span> l; JVM INSTR tableswitch 0 3: default 322 goto _L1 _L2 _L1 _L3 _L4 <span style="color:#006600;">//這裡是一個while迴圈接收從memcache server返回的訊息字元</span>_L1: break; /* Loop/switch isn't completed */_L2: Object obj; if(!"END\r\n".startsWith(stringbuffer.toString())) break; /* Loop/switch isn't completed */ <span style="color:#006600;">//如果不是END\r\r行,就繼續處理,否則就直接返回空</span> obj = null; if(schoonersockio != null) { schoonersockio.close(); schoonersockio = null; } return obj; <span style="color:#006600;">//這裡處理在memcache中沒有對應key值時取不到任何值,調用get命令後直接得到END/r/n,返回空</span>_L3: j = Integer.parseInt(stringbuffer.toString()); break; /* Loop/switch isn't completed */_L4: i = Integer.parseInt(stringbuffer.toString()); l++; stringbuffer = new StringBuffer(); if(k == 13) { sockinputstream.read(); flag1 = true; } goto _L5 stringbuffer.append((char)k); goto _L5 Object obj3; Object obj1 = null; sockinputstream.willRead(i); if(i > 0) if(NativeHandler.isHandled(j)) { byte abyte0[] = sockinputstream.getBuffer(); if((j & 2) == 2) { GZIPInputStream gzipinputstream = new GZIPInputStream(new ByteArrayInputStream(abyte0)); ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(abyte0.length); byte abyte1[] = new byte[2048]; int i1; while((i1 = gzipinputstream.read(abyte1)) != -1) bytearrayoutputstream.write(abyte1, 0, i1); abyte0 = bytearrayoutputstream.toByteArray(); <span style="color:#006600;">//讀取資料到位元組數組中</span> gzipinputstream.close(); } if(primitiveAsString || flag) obj1 = new String(abyte0, defaultEncoding); else obj1 = NativeHandler.decode(abyte0, j); <span style="color:#006600;">//最後調用decodeString解碼字串</span> } else if(transCoder != null) { Object obj2 = sockinputstream; if((j & 2) == 2) obj2 = new GZIPInputStream(((java.io.InputStream) (obj2))); if(classLoader == null) obj1 = transCoder.decode(((java.io.InputStream) (obj2))); else obj1 = ((ObjectTransCoder)transCoder).decode(((java.io.InputStream) (obj2)), classLoader); } sockinputstream.willRead(2147483647); sockinputstream.getLine(); sockinputstream.getLine(); obj3 = obj1; if(schoonersockio != null) { schoonersockio.close(); schoonersockio = null; } return obj3;}
我們可以對比直接使用memcache命令操作的輸入輸出來理解,就是在輸入完get命令後解析字元序列,編碼成實際的data。
使用反編譯後的代碼可能理解起來比較困難,我們可以查看Java_Memcache client原始代碼,學習起來更省心。
有些版本原代碼是在MemcacheClient中直接get/set data的。
https://code.csdn.net/dwzteam/dwz_springmvc/tree/f744cf61e31188e93c31644686a07ece5b84e1e4/src/dwz/cache/memcache/client/MemcachedClient.java
這份程式碼分析Memcache處理資料會更加簡單。
總結
至此,我們介紹了如何在windows安裝並操作Memcache,分析了Memcache分布式原理,探討了一般Hash演算法和一致性Hash演算法,如何操作資料。我們從整體上對Java_Memcache Client有了深刻的認識,其主要思想就是維護一套分布式Server的資訊,對每台Server維護其Socket串連,根據操作的資料進行Hash映射到某台Server上,最後構建memcache命令操作和解析資料。
相信在掌握Java_Memcache_Client的思想和實現過程後,您會使用得更加得心應手。
Windows環境下的Memcache實戰 http://blog.csdn.net/musa875643dn/article/details/45439417
Memcache命令詳解 http://blog.csdn.net/musa875643dn/article/details/45935895
Memcache分布式原理解析 http://blog.csdn.net/musa875643dn/article/details/45675711
Memcache Hash演算法揭秘 http://blog.csdn.net/musa875643dn/article/details/45796887
Java memcache Client 資料操作源碼剖析