RPC-03-Thrift简单连接池实现

声明:原创文章,转载请注明出处。http://www.jianshu.com/u/e02df63eaa87

在上一节中,介绍了Thrift框架的基本情况和使用,本节针对上节中最后实现的四则运算服务实现一个简单的连接池。

1、Apache Common-pool2简介

Apache Common-pool2包提供了一个通用对象池实现,可方便地基于此实现对象池。对象的创建和销毁在一定程度上会消耗系统的资源,虽然JVM想性能得到了很大的提升,对于多数对象来说,没必要利用对象池进行对象的创建和管理,但是对于线程、TCP连接、数据库连接等对象,其创建与销毁的代价是很大的,因此对象池技术还是有其存在的意义。

Common-pool2由三大模块组成:ObjectPool、PooledObject和PooledObjectFactory。

  • ObjectPool:提供所有对象的存取管理。
  • PooledObject:池化的对象,是对对象的一个包装,加上了对象的一些其他信息,包括对象的状态(已用、空闲),对象的创建时间等。
  • PooledObjectFactory:工厂类,负责池化对象的创建,对象的初始化,对象状态的销毁和对象状态的验证。

ObjectPool会持有PooledObjectFactory,将具体的对象的创建、初始化、销毁等任务交给它处理,其操作对象是PooledObject,即具体的Object的包装类。

2、 Thrift连接池实现

2.1 Node节点

用于保存服务端的IP、Port等信息

public class Node {
    private static final Logger logger = LogManager.getLogger(Node.class);

    private String ip;
    private int port;

    public Node() {
    }

    public Node(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public static Logger getLogger() {
        return logger;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return "Node{" +
                "ip='" + ip + '\'' +
                ", port=" + port +
                '}';
    }
}
2.2 Factory

回调。用于对象创建,销毁,验证,激活、钝化等。

public class ConnectionFactory implements PooledObjectFactory<TServiceClient> {
    private static final Logger logger = LogManager.getLogger(ConnectionFactory.class);
    private Node node;
    private int timeout;

    public ConnectionFactory(Node node, int timeout) {
        this.node = node;
        this.timeout = timeout;
    }

    public PooledObject<TServiceClient> makeObject() throws Exception {
        try {
            TTransport tTransport = new TFramedTransport(new TSocket(node.getIp(), node.getPort(), timeout));
            tTransport.open();
            // 具体的连接池
            ComputeServer.Client client = new ComputeServer.Client(new TBinaryProtocol(tTransport));
            logger.info("connect to server success.");
            return new DefaultPooledObject<TServiceClient>(client);
        } catch (Exception e) {
            logger.error("connect to server failed.", e);
            throw e;
        }
    }

    public boolean validateObject(PooledObject<TServiceClient> pooledObject) {
        TServiceClient client = pooledObject.getObject();
        TTransport transport = client.getInputProtocol().getTransport();
        return transport.isOpen();
    }

    public void activateObject(PooledObject<TServiceClient> pooledObject) throws Exception {
        TServiceClient client = pooledObject.getObject();
        TTransport transport = client.getInputProtocol().getTransport();
        if (!transport.isOpen()) {
            logger.info("transport is closed, reopen");
            transport.open();
        }
    }

    public void destroyObject(PooledObject<TServiceClient> pooledObject) throws Exception {
        TServiceClient client = pooledObject.getObject();
        TTransport transport = client.getInputProtocol().getTransport();
        if (transport.isOpen()) {
            transport.close();
            logger.info("close renode connection." + node.toString());
        }
    }

    public void passivateObject(PooledObject<TServiceClient> pooledObject) throws Exception {
        TServiceClient client = pooledObject.getObject();
        TTransport transport = client.getInputProtocol().getTransport();
        if (!transport.isOpen()) {
            logger.info("transport is closed, reopen");
            transport.open();
        }
    }
}

备注:其中需要注意的地方是,在makeObject()中,根据不同的Client将进行创建。

2.3 代理

用于获取连接和归还连接。

public class ThriftClientProxy {
    private static final Logger logger = LogManager.getLogger(ThriftClientProxy.class);

    private static final int WAIT_TIME_MS = 20; // 等待时间
    GenericObjectPoolConfig poolConfig;         // 连接池配置

    // Thrift客户端连接池,ConcurrentHashMap用于存储所有的对象(不含销毁的对象)
    // LinkedBlockingDeque用于存储空闲的对象
    GenericObjectPool<TServiceClient> pool;
    ConnectionFactory connectionFactory;        // 连接
    Node node;                                  // 节点

    /* 构造连接池 */
    public ThriftClientProxy(Node node, int timeout, int minPoolSize, int maxPoolSize) {
        this.node = node;
        poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(maxPoolSize);
        poolConfig.setMinIdle(minPoolSize);

        connectionFactory = new ConnectionFactory(this.node, timeout);
        pool = new GenericObjectPool<TServiceClient>(connectionFactory, poolConfig);
    }

    /* 连接池初始化 */
    public boolean connect() {
        try {
            pool.preparePool();
        } catch (Exception e) {
            logger.error("prepare client connection pool failed.", e);
            close();
            return false;
        }
        return true;
    }
    /* 关闭连接池 */
    public void close() {
        pool.close();
    }

    /* 获取连接池中的连接 */
    public TServiceClient takeConnection() {
        try {
            return pool.borrowObject(WAIT_TIME_MS);
        } catch (Exception e) {
            logger.error("take connection from pool failed.", e);
            return null;
        }
    }

    /* 归还连接 */
    public void returnConnection(TServiceClient client) {
        if (client == null)
            return;
        pool.returnObject(client);
    }
}
2.4 Main

用于测试连接池的功能。

public class Main {
    private static final Logger logger = LogManager.getLogger(Main.class);

    public static void main(String[] args) {
        // 根据节点创建连接池
        Node node = new Node("127.0.0.1", 9000);
        ThriftClientProxy thriftClientProxy = new ThriftClientProxy(node, 200, 5, 5);

        if (!thriftClientProxy.connect()) {
            logger.error("connect to server failed.{}", node.toString());
            return;
        }
        // 获取连接并使用
        ComputeServer.Client computeClient = (ComputeServer.Client) thriftClientProxy.takeConnection();
        ComputeRequest request = new ComputeRequest();
        request.setX(1);
        request.setY(2);
        request.setComputeType(ComputeType.ADD);
        try {
            ComputeResponse response = computeClient.getComputeResult(request);
            if (response != null) {
                System.out.println(response.toString());
            }
        } catch (Exception e) {
            logger.error(e);
        } finally {
            // 归还连接
            thriftClientProxy.returnConnection(computeClient);
        }
    }
}
2.5 代码结构
包结构
2.6 测试
测试

引用
http://www.open-open.com/lib/view/open1415453575730.html
http://www.cnblogs.com/jinzhiming/p/5120623.html
http://commons.apache.org/proper/commons-pool/api-2.4.2/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容