Memcache-Java-Client-Release源码阅读(之二)

一、主要内容
本章节的主要内容是介绍Memcache 的初始化过程。

二、准备工作
1、memcache服务端搭建,至少启动两个memcache实例。
2、将下载的memcache-java-client-release的源码导入到Eclipse工程里。
3、memcache-client代码,可以从源码包中的测试类抽取一小部分,如下:

public static void main(String[] args) {
    String[] servers = { "192.168.0.106:11211","192.168.0.106:11212" };
    SockIOPool pool = SockIOPool.getInstance();
    pool.setServers(serverlist);

    pool.setInitConn(5);
    pool.setMinConn(5);
    pool.setMaxConn(50);

    pool.setNagle(false);
    pool.initialize();
}

SockIOPool 的参数尽可能少一些,这样有利于排除干扰,pool.initialize();语句执行表示正式进入客户端初始化。

三、源码阅读
1、SockIOPool的实例化方法
主要是调用SchoonerSockIOPool的getInstance(“default”)方法,使用了
ConcurrentMap集合,并且方法内对该集合进行同步锁控制,连接池的实现类是SchoonerSockIOPool ,默认使用TCP方式,如代码所示:

/**
 * Factory to create/retrieve new pools given a unique poolName.
 *
 * @param poolName
 *            unique name of the pool
 * @return instance of SockIOPool
 */
public static SchoonerSockIOPool getInstance(String poolName) {
    SchoonerSockIOPool pool;

    synchronized (pools) {
        if (!pools.containsKey(poolName)) {
            pool = new SchoonerSockIOPool(true);
            pools.putIfAbsent(poolName, pool);
        }
    }

    return pools.get(poolName);
}

其实也可以创建多个连接池对象,同步安全可以放心的创建,可以根据实际的场景需求创建多个,但一般场景下创建一个就够用了。

2、初始化方法
接下来我们看最核心的initialize()方法,同样进行了锁控制,请注意这点,hash算法会影响Buckets的创建逻辑:一致性Hash(CONSISTENT_HASH )和另外三个Hash算法(NATIVE_HASH、OLD_COMPAT_HASH、NEW_COMPAT_HASH)是不一样的,代码如下:

/**
 * Initializes the pool.
 */
public void initialize() {
    initDeadLock.lock();
    try {

        // if servers is not set, or it empty, then
        // throw a runtime exception
        if (servers == null || servers.length <= 0) {
            log.error("++++ trying to initialize with no servers");
            throw new IllegalStateException("++++ trying to initialize with no servers");
        }
        // pools
        socketPool = new HashMap<String, GenericObjectPool>(servers.length);
        hostDead = new ConcurrentHashMap<String, Date>();
        hostDeadDur = new ConcurrentHashMap<String, Long>();
        // only create up to maxCreate connections at once

        // initalize our internal hashing structures
        if (this.hashingAlg == CONSISTENT_HASH)
            populateConsistentBuckets();
        else
            populateBuckets();

        // mark pool as initialized
        this.initialized = true;

    } finally {
        initDeadLock.unlock();
    }

}

3、Buckets的创建逻辑
这里我们先讨论其他三个Hash算法的情况,一致性Hash算法的Buckets创建逻辑我们会在后面的章节单独讨论。
获取配置信息中的servers配置项(或是前面main方法中的servers数组)生成实例节点,注意这里权重(weights)的用法,权重会增加Buckets集合实例节点的个数,以调整该节点的命中概述。对象池使用的是commons-pool的GenericObjectPool类。如下代码所示:

private void populateBuckets() {
    // store buckets in tree map
    buckets = new ArrayList<String>();
    // servers表示配置文件中的memcache实例服务节点
    for (int i = 0; i < servers.length; i++) {
        if (this.weights != null && this.weights.length > i) {
            for (int k = 0; k < this.weights[i].intValue(); k++) {
                // 这里会根据权重的数量来生成虚拟节点
                buckets.add(servers[i]);
            }
        } else {
            buckets.add(servers[i]);
        }

        // Create a socket pool for each host
        // Create an object pool to contain our active connections
        GenericObjectPool gop;
        SchoonerSockIOFactory factory;
        if (authInfo != null) {
            factory = new AuthSchoonerSockIOFactory(servers[i], isTcp, bufferSize, socketTO, socketConnectTO,
                    nagle, authInfo);
        } else {
            factory = new SchoonerSockIOFactory(servers[i], isTcp, bufferSize, socketTO, socketConnectTO, nagle);
        }
        gop = new GenericObjectPool(factory, maxConn, GenericObjectPool.WHEN_EXHAUSTED_BLOCK, maxIdle, maxConn);
        factory.setSockets(gop);
        socketPool.put(servers[i], gop);
    }
}

这样最基本的创建过程就完成了。

四、FAQ
以下是在阅读代码过程中提的疑问,阅读完之后自行作的解答,仅供参考。
Q1、初始化:权重是如何利用的?
A1:初始化时根据权的数量来填充buckets的数量,权重的关键在于比例,基本上权重越大,在获取服务节点的命中率越高,最后就是各权重累加不一定要是100%。
Q2、SockIOPool的实例化时默认使用TCP协议,如果服务端启动时选择UDP会怎么样?
A2:启动时不会报错,在调用set/get操作时会报错,会报java.io.IOException异常。
Q3、SockIOPool的初始连接数是在什么时候完成的?
A3:本来是应该在初始化时就会创建连接的,但代码中维持连接池连接数据的线程类MaintThread没有找到启动的执行语句,所以刚初始化完是不会有连接的,要等到发起set/get操作时,才会创建连接。查看连接可以在服务端用lsof -i:11211命令或netstat -anp|grep 11211等命令查看。
Q4、memcache客户端的最大连接数是如何实现的?
A4:在创建GenericObjectPool时,就指定了最大连接数maxConn,这部分逻辑是交给commons-pool的GenericObjectPool类来实现的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值