文章出處

對象池是一種很實用的技術,經典的例子就是數據庫連接池。去年曾經從零開始寫過一個thrift客戶端連接池如果不想重造輪子,可以直接在apache開源項目commons-pool的基礎上開發。 

步驟:

一、定義對象工廠

package test.cn.mwee.service.paidui.pool;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

public class TProtocolFactory
        extends BasePooledObjectFactory<TProtocol> {


    private String host;
    private int port;
    private boolean keepAlive = true;

    public TProtocolFactory(String host, int port, boolean keepAlive) {
        this.host = host;
        this.port = port;
        this.keepAlive = keepAlive;
    }

    @Override
    public TProtocol create() throws TTransportException {
        TSocket tSocket = new TSocket(host, port);
        TTransport tTransport = new TFramedTransport(tSocket);
        tTransport.open();
        return new TCompactProtocol(tTransport);
    }


    @Override
    public PooledObject<TProtocol> wrap(TProtocol protocol) {
        return new DefaultPooledObject<>(protocol);
    }

    /**
     * 對象鈍化(即:從激活狀態轉入非激活狀態,returnObject時觸發)
     *
     * @param pooledObject
     * @throws TTransportException
     */
    @Override
    public void passivateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
        if (!keepAlive) {
            pooledObject.getObject().getTransport().flush();
            pooledObject.getObject().getTransport().close();
        }
    }


    /**
     * 對象激活(borrowObject時觸發)
     *
     * @param pooledObject
     * @throws TTransportException
     */
    @Override
    public void activateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
        if (!pooledObject.getObject().getTransport().isOpen()) {
            pooledObject.getObject().getTransport().open();
        }
    }


    /**
     * 對象銷毀(clear時會觸發)
     * @param pooledObject
     * @throws TTransportException
     */
    @Override
    public void destroyObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
        passivateObject(pooledObject);
        pooledObject.markAbandoned();
    }


    /**
     * 驗證對象有效性
     *
     * @param p
     * @return
     */
    @Override
    public boolean validateObject(PooledObject<TProtocol> p) {
        if (p.getObject() != null) {
            if (p.getObject().getTransport().isOpen()) {
                return true;
            }
            try {
                p.getObject().getTransport().open();
                return true;
            } catch (TTransportException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}

有二個關鍵的方法,需要重寫:activateObject(對象激活) 及 passivateObject(對象鈍化)

 

二、定義對象池

package test.cn.mwee.service.paidui.pool;

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * Created by yangjunming on 6/7/16.
 */
public class AutoClearGenericObjectPool<T> extends GenericObjectPool<T> {

    public AutoClearGenericObjectPool(PooledObjectFactory<T> factory) {
        super(factory);
    }

    public AutoClearGenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig config) {
        super(factory, config);
    }

    @Override
    public void returnObject(T obj) {
        super.returnObject(obj);
        //空閑數>=激活數時,清理掉空閑連接
        if (getNumIdle() >= getNumActive()) {
            clear();
        }
    }

}

common-pools提供了對象池的默認實現:GenericObjectPool 但是該對象池中,對于處于空閑的對象,需要手動調用clear來釋放空閑對象,如果希望改變這一行為,可以自己派生自己的子類,重寫returnObject方法,上面的代碼中,每次歸還對象時,如果空閑的對象比激活的對象還要多(即:一半以上的對象都在打醬油),則調用clear方法。

 

三、使用示例:

package test.cn.mwee.service.paidui.pool;

import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.thrift.protocol.TProtocol;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * thrift 連接池測試
 */
public class ProtocolPoolTest {


    public static void main(String[] args) throws Exception {

        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(10);
        poolConfig.setMinIdle(1);
        poolConfig.setTestOnBorrow(true);

        ObjectPool<TProtocol> pool = new AutoClearGenericObjectPool<>(
                new TProtocolFactory("127.0.0.1", 13041, true), poolConfig);

        List<TProtocol> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            TProtocol protocol = pool.borrowObject();
            System.out.println(protocol.toString());
            if (i % 2 == 0) {
                //10個連接中,將偶數歸還
                pool.returnObject(protocol);
            } else {
                list.add(protocol);
            }
        }

        Random rnd = new Random();
        while (true) {
            System.out.println(String.format("active:%d,idea:%d", pool.getNumActive(), pool.getNumIdle()));
            Thread.sleep(5000);
            //每次還一個
            if (list.size() > 0) {
                int i = rnd.nextInt(list.size());
                pool.returnObject(list.get(i));
                list.remove(i);
            }

            //直到全部還完
            if (pool.getNumActive() <= 0) {
                break;
            }
        }

        System.out.println("------------------------");


        list.clear();
        //連接池為空,測試是否能重新創建新連接
        for (int i = 1; i <= 10; i++) {
            TProtocol protocol = pool.borrowObject();
            System.out.println(protocol.toString());
            if (i % 2 == 0) {
                pool.returnObject(protocol);
            } else {
                list.add(protocol);
            }
        }

        while (true) {
            System.out.println(String.format("active:%d,idea:%d", pool.getNumActive(), pool.getNumIdle()));
            Thread.sleep(5000);
            if (list.size() > 0) {
                int i = rnd.nextInt(list.size());
                pool.returnObject(list.get(i));
                list.remove(i);
            }

            if (pool.getNumActive() <= 0) {
                pool.close();
                break;
            }
        }

    }
}

注:需要從對象池取一個對象時,調用borrowObject(背后會調用activeObject激活對象),類似的,對象使用完之后,需要調用returnObject將對象放回對象池(背后會調用passivateObject使對象鈍化)

輸出:

org.apache.thrift.protocol.TCompactProtocol@146044d7
org.apache.thrift.protocol.TCompactProtocol@1e9e725a
org.apache.thrift.protocol.TCompactProtocol@516be40f
org.apache.thrift.protocol.TCompactProtocol@3c0a50da
org.apache.thrift.protocol.TCompactProtocol@3c0a50da
org.apache.thrift.protocol.TCompactProtocol@646be2c3
org.apache.thrift.protocol.TCompactProtocol@646be2c3
org.apache.thrift.protocol.TCompactProtocol@797badd3
org.apache.thrift.protocol.TCompactProtocol@797badd3
org.apache.thrift.protocol.TCompactProtocol@77be656f
active:5,idea:1
active:4,idea:2
active:3,idea:0
active:2,idea:1
active:1,idea:0
------------------------
org.apache.thrift.protocol.TCompactProtocol@221af3c0
org.apache.thrift.protocol.TCompactProtocol@62bd765
org.apache.thrift.protocol.TCompactProtocol@23a5fd2
org.apache.thrift.protocol.TCompactProtocol@78a2da20
org.apache.thrift.protocol.TCompactProtocol@78a2da20
org.apache.thrift.protocol.TCompactProtocol@dd3b207
org.apache.thrift.protocol.TCompactProtocol@dd3b207
org.apache.thrift.protocol.TCompactProtocol@551bdc27
org.apache.thrift.protocol.TCompactProtocol@551bdc27
org.apache.thrift.protocol.TCompactProtocol@58fdd99
active:5,idea:1
active:4,idea:2
active:3,idea:0
active:2,idea:1
active:1,idea:0

Process finished with exit code 0

從輸出上看,歸還對象后,再次取出時,并沒有創建新對象,而是直接使用了對象池中已經空閑的對象。當對象池中的所有對象都歸還變成空閑并被clear后,再次從對象池中借對象時,會重新創建對象。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()