Java memcache Client 数据操作源码剖析



在学习使用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

时间: 2024-10-26 05:10:42

Java memcache Client 数据操作源码剖析的相关文章

java多线程17:ThreadLocal源码剖析

ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是干什么用的.再使用.最后总结,讲解ThreadLocal采取这样的思路. 三个理论基础 在剖析ThreadLocal源码前,先讲一下ThreadLocal的三个理论基础: 1.每个线程都有一个自己的ThreadLocal.ThreadLocalMap对象 2.每一个ThreadLocal对象都有一个

Java多线程9:ThreadLocal源码剖析

http://www.cnblogs.com/xrq730/p/4854813.html ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是干什么用的.再使用.最后总结,讲解ThreadLocal采取这样的思路. 三个理论基础 在剖析ThreadLocal源码前,先讲一下ThreadLocal的三个理论基础: 1.每个线程都有一个自己的ThreadLocal.ThreadLoca

【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,

【Java集合源码剖析】LinkedHashmap源码剖析

LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同. LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析). LinkedHashMap同样是非线程安全的,只在单线程环境下使用. LinkedHashMap源码剖析 LinkedHashM

转:【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动

转:【Java集合源码剖析】LinkedHashmap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985   前言:有网友建议分析下LinkedHashMap的源码,于是花了一晚上时间研究了下,分享出此文(这个系列的最后一篇博文了),希望大家相互学习.LinkedHashMap的源码理解起来也不难(当然,要建立在对HashMap源码有较好理解的基础上). LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加

转:【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使

【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全的,只在单线程下适合使用. LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆. Linked

【Java集合源码剖析】HashMap源码剖析(转)

HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆. HashMap源码剖析 HashMap的源码如下(加入了比较详细的注释): [ja