溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

發(fā)布時間:2020-07-19 08:08:31 來源:網(wǎng)絡(luò) 閱讀:13328 作者:lilugoodjob 欄目:軟件技術(shù)

概述

在項目運行過程中,有些操作對系統(tǒng)資源消耗較大,比如建立數(shù)據(jù)庫連接、建立Redis連接等操作,我們希望一次性創(chuàng)建多個連接對象,并在以后需要使用時能直接使用已創(chuàng)建好的連接,達(dá)到提高性能的目的。池技術(shù)通過提前將一些占用較多資源的對象初始化,并將初始化后的對象保存到池中備用,達(dá)到提高應(yīng)用服務(wù)性能的目的,數(shù)據(jù)庫的JDBC連接池和Jedis連接池等都使用了池技術(shù)。
Apache-Commons-Pool2提供了一套池技術(shù)的規(guī)范接口和實現(xiàn)的通用邏輯,我們只需要實現(xiàn)其抽象出來的方法就可以了。這篇博文主要分享基于Apache-Commons-Pool2來實現(xiàn)Grpc連接池的應(yīng)用。
關(guān)于Grpc相關(guān)的內(nèi)容,大家如想了解基本的實現(xiàn)方法,可以參考我的另一篇博客(傳送門):https://blog.51cto.com/andrewli/2058908

核心組件

我們先來了解一下Apache-Commons-Pool2規(guī)范接口中涉及到的幾個核心組件,包括:

  • ObjectPool
    對象池,用于存儲對象,并管理對象的入池和出池。對象池的實現(xiàn)類是 GenericObjectPool<T>;
  • PoolConfig
    池屬性,用于設(shè)置連接池的一些配置信息,比如最大池容量、超過池容量后的處理邏輯等。池屬性的實現(xiàn)類是:GenericObjectPoolConfig;
  • ObjectFactory
    對象工廠,在需要的時候生成新的對象實例,并放入池中。對象工廠的接口是:interface PooledObjectFactory<T>;
  • ClientObject
    池對象,由對象工廠負(fù)責(zé)創(chuàng)建,并放入到對象池中;需要使用時從對象池中取出,執(zhí)行對象的業(yè)務(wù)邏輯,使用完后再放回對象池。池對象的接口是:interface PooledObject<T>。

    核心組件依賴關(guān)系及其工作流程

    接口與類之間的依賴關(guān)系

    在梳理連接池相關(guān)的核心組件工作流程之前,我們先來了解一下核心組件涉及到的類和接口之間的繼承和實現(xiàn)關(guān)系。

  • 對象池類的繼承關(guān)系
    對象池的最頂層接口是ObjectPool<T>,里面定義了對象池的基本方法,包括對象的添加、取出、校驗、返還,以及獲取處于Idle休眠狀態(tài)的對象數(shù)量、獲取處于Active狀態(tài)的對象數(shù)量、清空池、關(guān)閉池。
    抽象類BaseGenericObjectPool<T>,定義了對象池的初始配置,并實現(xiàn)了對象池的基本接口方法。
    池類GenericObjectPool<T>繼承了抽象類BaseGenericObjectPool<T>,并實現(xiàn)了ObjectPool<T>接口。其中添加了對象工廠、存儲所有對象的Map、存儲Idle對象的鏈?zhǔn)阶枞犃?、?dāng)前已創(chuàng)建的對象數(shù)等屬性。
    由于GenericObjectPool<T>類支持范型,我們要做的,就是指定GenericObjectPool<T>池類返回的池對象類型<T>,并設(shè)置對象工廠類、配置類等池屬性;或者繼承GenericObjectPool類以添加更多的自定義池特性。
    基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

  • 池屬性類的繼承關(guān)系
    池屬性的最上層接口是interface Cloneable,抽象類BaseObjectPoolConfig實現(xiàn)了這個接口,并定義了默認(rèn)的池配置屬性。
    GenericObjectPoolConfig類繼承了BaseObjectPoolConfig,同樣定義了默認(rèn)的池配置屬性值。
    我們可以直接使用GenericObjectPoolConfig類,或者繼承GenericObjectPoolConfig類,根據(jù)自己的需求設(shè)置自定義池配置屬性。
    基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

  • 池內(nèi)對象類的繼承關(guān)系
    池內(nèi)對象類實現(xiàn)了上層的PooledObject<T>接口,這個接口里面定義了一個池對象需要實現(xiàn)的各種方法。
    另外,池內(nèi)對象類還需要定義類本身需要具備的成員屬性和需要實現(xiàn)的業(yè)務(wù)方法。
    基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

  • 對象工廠類的繼承關(guān)系
    對象工廠類實現(xiàn)了最上層的PooledObjectFactory<T>接口,該接口定義了對象工廠的核心功能方法,包括:創(chuàng)建對象、銷毀對象、校驗對象、激活對象、鈍化對象。
    基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

工作流程

根據(jù)上述對核心組件的類繼承關(guān)系分析,我們可以梳理出一個流程,逐步實現(xiàn)各個組件,并組合成一套適用于我們業(yè)務(wù)的連接池架構(gòu)。我們來看看這個流程該如何定義。
(1)定義我們的池內(nèi)對象類 ClientObject,并結(jié)合我們的實際業(yè)務(wù)來實現(xiàn)上層接口的方法。
(2)定義對象工廠類ClientFactory,并結(jié)合我們的實際業(yè)務(wù)來實現(xiàn)上層接口的方法。
(3)定義池屬性類ClientPoolConfig,結(jié)合我們的實際需求來設(shè)置屬性值。
(4)使用對象池GenericObjectPool,指定泛型類型GenericObjectPool<ClientObject>。

基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

連接池內(nèi)部的核心業(yè)務(wù)邏輯:
基于Apache-Commons-Pool2實現(xiàn)Grpc客戶端連接池

池內(nèi)對象的創(chuàng)建和返回邏輯是池技術(shù)里的關(guān)鍵,可以查看池對象的borrowObject方法去了解這部分細(xì)節(jié)內(nèi)容。

應(yīng)用實踐

代碼實現(xiàn)

根據(jù)上述對Apache-commons-pool2的特點和實現(xiàn)流程的分析,我們基于Grpc客戶端連接池的應(yīng)用場景,來進(jìn)行代碼實踐,主要包括實現(xiàn)池內(nèi)對象類 ClientObject和實現(xiàn)對象工廠類ClientFactory。
具體代碼可以進(jìn)入我的百度網(wǎng)盤下載,鏈接如下:
https://pan.baidu.com/s/1eaGpz6XN2a3ssw0eYsNLww

代碼測試

為了驗證我們的Grpc連接池的作用,我編寫了一個測試方法,模擬以下場景,即開啟10個線程,每個線程循環(huán)10次使用Grpc連接發(fā)送消息給grpc服務(wù)端,然后查看線程池中累計創(chuàng)建的連接對象個數(shù)、線程池中每個連接對象的被使用次數(shù)等信息。
通過測試輸出的信息,我得到的結(jié)論是:不使用連接池時,總共需要進(jìn)行100次Grpc連接并發(fā)送消息;使用連接池后,總共僅需要建立2次Grpc連接來發(fā)送100次消息,每個連接被調(diào)用了50次。
測試代碼如下。

package com.cmcc.littlec.grpc.poolclient;
import com.cmcc.littlec.grpc.util.Constants;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class test {
    @SuppressWarnings("unchecked")
    public static GenericObjectPool<ClientObject> getClientPool(){
        ClientPoolFactory factory = new ClientPoolFactory(Constants.grpcHost, Constants.grpcPort);
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(8);
        config.setMinIdle(3);
        config.setMaxTotal(18);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        GenericObjectPool<ClientObject> clientPool = new GenericObjectPool<ClientObject>(factory, config);
        return clientPool;
    }

    public static void  main(String[] args ){
        final GenericObjectPool<ClientObject> clientPool = getClientPool();
        for(int i=0; i<10; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i = 0; i < 10; i++) {
                            ClientObject client = clientPool.borrowObject();                                                        
                            String str = "hello, grpc client_" + i;//參數(shù)
                            try {
                                client.sayHello(str);
                            }catch(Exception e){
                                client.invalidate();
                            }
                            System.out.println("Thread : " + Thread.currentThread().getName() + "; clientPool size : " + clientPool.getCreatedCount());
                            System.out.println("clientObj : "+client.toString());
                            clientPool.returnObject(client);
                        }
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
            });
            t.start();
            try {
                if(i%2==0) {
                    Thread.sleep(5000L);//每隔兩個線程創(chuàng)建后停頓5S
                }
            }catch(Exception e){ }
        }
    }
}
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI