欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

什么是commons-pool2池化技術(shù)

本篇內(nèi)容介紹了“什么是commons-pool2池化技術(shù)”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序定制開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了枝江免費(fèi)建站歡迎大家使用!

一、前言

我們經(jīng)常會(huì)接觸各種池化的技術(shù)或者概念,包括對象池、連接池、線程池等,池化技術(shù)最大的好處就是實(shí)現(xiàn)對象的重復(fù)利用,尤其是創(chuàng)建和使用大對象或者寶貴資源(HTTP連接對象,MySQL連接對象)等方面的時(shí)候能夠大大節(jié)省系統(tǒng)開銷,對提升系統(tǒng)整體性能也至關(guān)重要。

在并發(fā)請求下,如果需要同時(shí)為幾百個(gè)query操作創(chuàng)建/關(guān)閉MySQL的連接或者是為每一個(gè)HTTP請求創(chuàng)建一個(gè)處理線程或者是為每一個(gè)圖片或者XML解析創(chuàng)建一個(gè)解析對象而不使用池化技術(shù),將會(huì)給系統(tǒng)帶來極大的負(fù)載挑戰(zhàn)。

二、commons-pool2池化技術(shù)剖析

越來越多的框架在選擇使用apache commons-pool2進(jìn)行池化的管理,如jedis-cluster,commons-pool2工作的邏輯如下圖所示:

什么是commons-pool2池化技術(shù)

2.1 核心三元素

2.1.1 ObjectPool

對象池,負(fù)責(zé)對對象進(jìn)行生命周期的管理,并提供了對對象池中活躍對象和空閑對象統(tǒng)計(jì)的功能。

2.1.2 PooledObjectFactory

對象工廠類,負(fù)責(zé)具體對象的創(chuàng)建、初始化,對象狀態(tài)的銷毀和驗(yàn)證。commons-pool2框架本身提供了默認(rèn)的抽象實(shí)現(xiàn)BasePooledObjectFactory ,業(yè)務(wù)方在使用的時(shí)候只需要繼承該類,然后實(shí)現(xiàn)warp和create方法即可。

2.1.3 PooledObject

池化對象,是需要放到ObjectPool對象的一個(gè)包裝類。添加了一些附加的信息,比如說狀態(tài)信息,創(chuàng)建時(shí)間,激活時(shí)間等。commons-pool2提供了DefaultPooledObject和 PoolSoftedObject 2種實(shí)現(xiàn)。其中PoolSoftedObject繼承自DefaultPooledObject,不同點(diǎn)是使用SoftReference實(shí)現(xiàn)了對象的軟引用。獲取對象的時(shí)候使用也是通過SoftReference進(jìn)行獲取。

2.2 對象池邏輯分析

2.2.1 對象池接口說明

1)我們在使用commons-pool2的時(shí)候,應(yīng)用程序獲取或釋放對象的操作都是基于對象池進(jìn)行的,對象池核心接口主要包括如下:

/**
*向?qū)ο蟪刂性黾訉ο髮?shí)例
*/
void addObject() throws Exception, IllegalStateException,
      UnsupportedOperationException;
/**
* 從對象池中獲取對象
*/
T borrowObject() throws Exception, NoSuchElementException,
      IllegalStateException;
/**
* 失效非法的對象
*/
void invalidateObject(T obj) throws Exception;
/**
* 釋放對象至對象池
*/
void returnObject(T obj) throws Exception;

除了接口本身之外,對象池還支持對對象的最大數(shù)量,保留時(shí)間等等進(jìn)行設(shè)置。對象池的核心參數(shù)項(xiàng)包括maxTotal,maxIdle,minIdle,maxWaitMillis,testOnBorrow 等。

2.2.2 對象創(chuàng)建解耦

對象工廠是commons-pool2框架中用于生成對象的核心環(huán)節(jié),業(yè)務(wù)方在使用過程中需要自己去實(shí)現(xiàn)對應(yīng)的對象工廠實(shí)現(xiàn)類,通過工廠模式,實(shí)現(xiàn)了對象池與對象的生成與實(shí)現(xiàn)過程細(xì)節(jié)的解耦,每一個(gè)對象池應(yīng)該都有對象工廠的成員變量,如此實(shí)現(xiàn)對象池本身和對象的生成邏輯解耦。

可以通過代碼進(jìn)一步驗(yàn)證我們的思路:

public GenericObjectPool(final PooledObjectFactory<T> factory) {
      this(factory, new GenericObjectPoolConfig<T>());
  }
  
  public GenericObjectPool(final PooledObjectFactory<T> factory,
                            final GenericObjectPoolConfig<T> config) {

      super(config, ONAME_BASE, config.getJmxNamePrefix());

      if (factory == null) {
          jmxUnregister(); // tidy up
          throw new IllegalArgumentException("factory may not be null");
      }
      this.factory = factory;

      idleObjects = new LinkedBlockingDeque<>(config.getFairness());
      setConfig(config);
  }

  public GenericObjectPool(final PooledObjectFactory<T> factory,
                            final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
      this(factory, config);
      setAbandonedConfig(abandonedConfig);
  }

可以看到對象池的構(gòu)造方法,都依賴于對象構(gòu)造工廠PooledObjectFactory,在生成對象的時(shí)候,基于對象池中定義的參數(shù)和對象構(gòu)造工廠來生成。

/**
* 向?qū)ο蟪刂性黾訉ο?,一般在預(yù)加載的時(shí)候會(huì)使用該功能
*/
@Override
public void addObject() throws Exception {
  assertOpen();
  if (factory == null) {
      throw new IllegalStateException(
              "Cannot add objects without a factory.");
  }
  final PooledObject<T> p = create();
  addIdleObject(p);
}

create() 方法基于對象工廠來生成的對象,繼續(xù)往下跟進(jìn)代碼來確認(rèn)邏輯;

final PooledObject<T> p;
try {
  p = factory.makeObject();
  if (getTestOnCreate() && !factory.validateObject(p)) {
      createCount.decrementAndGet();
      return null;
  }
} catch (final Throwable e) {
  createCount.decrementAndGet();
  throw e;
} finally {
  synchronized (makeObjectCountLock) {
      makeObjectCount--;
      makeObjectCountLock.notifyAll();
  }
}

此處確認(rèn)了factory.makeObject()的操作,也印證了上述的推測,基于對象工廠來生成對應(yīng)的對象。

為了更好的能夠?qū)崿F(xiàn)對象池中對象的使用以及跟蹤對象的狀態(tài),commons-pool2框架中使用了池化對象PooledObject的概念,PooledObject本身是泛型類,并提供了getObject()獲取實(shí)際對象的方法。

2.2.3 對象池源碼分析

經(jīng)過上述分析我們知道了對象池承載了對象的生命周期的管理,包括整個(gè)對象池中對象數(shù)量的控制等邏輯,接下來我們通過GenericObjectPool的源碼來分析究竟是如何實(shí)現(xiàn)的。

什么是commons-pool2池化技術(shù)

對象池中使用了雙端隊(duì)列LinkedBlockingDeque來存儲(chǔ)對象,LinkedBlockingDeque對列支持FIFO和FILO兩種策略,基于AQS來實(shí)現(xiàn)隊(duì)列的操作的協(xié)同。

LinkedBlockingDeque提供了隊(duì)尾和隊(duì)頭的插入和移除元素的操作,相關(guān)操作都進(jìn)行了加入重入鎖的加鎖操作隊(duì)列中設(shè)置notFull 和 notEmpty兩個(gè)狀態(tài)變量,當(dāng)對隊(duì)列進(jìn)行元素的操作的時(shí)候會(huì)觸發(fā)對應(yīng)的執(zhí)行await和notify等操作。

/**
* 第一個(gè)節(jié)點(diǎn)
* Invariant: (first == null && last == null) ||
*           (first.prev == null && first.item != null)
*/
private transient Node<E> first; // @GuardedBy("lock")

/**
* 最后一個(gè)節(jié)點(diǎn)
* Invariant: (first == null && last == null) ||
*           (last.next == null && last.item != null)
*/
private transient Node<E> last; // @GuardedBy("lock")

/** 當(dāng)前隊(duì)列長度 */
private transient int count; // @GuardedBy("lock")

/** 隊(duì)列最大容量 */
private final int capacity;

/** 主鎖 */
private final InterruptibleReentrantLock lock;

/** 隊(duì)列是否為空狀態(tài)鎖 */
private final Condition notEmpty;

/** 隊(duì)列是否滿狀態(tài)鎖 */
private final Condition notFull;

隊(duì)列核心點(diǎn)為:

1.隊(duì)列中所有的移入元素、移出、初始化構(gòu)造元素都是基于主鎖進(jìn)行加鎖操作。

2.隊(duì)列的offer和pull支持設(shè)置超時(shí)時(shí)間參數(shù),主要是通過兩個(gè)狀態(tài)Condition來進(jìn)行協(xié)調(diào)操作。如在進(jìn)行offer操作的時(shí)候,如果操作不成功,則基于notFull狀態(tài)對象進(jìn)行等待。

public boolean offerFirst(final E e, final long timeout, final TimeUnit unit)
  throws InterruptedException {
  Objects.requireNonNull(e, "e");
  long nanos = unit.toNanos(timeout);
  lock.lockInterruptibly();
  try {
      while (!linkFirst(e)) {
          if (nanos <= 0) {
              return false;
          }
          nanos = notFull.awaitNanos(nanos);
      }
      return true;
  } finally {
      lock.unlock();
  }
}

如進(jìn)行pull操作的時(shí)候,如果操作不成功,則對notEmpty進(jìn)行等待操作。

public E takeFirst() throws InterruptedException {
  lock.lock();
  try {
      E x;
      while ( (x = unlinkFirst()) == null) {
          notEmpty.await();
      }
      return x;
  } finally {
      lock.unlock();
  }
}

反之當(dāng)操作成功的時(shí)候,則進(jìn)行喚醒操作,如下所示:

private boolean linkLast(final E e) {
  // assert lock.isHeldByCurrentThread();
  if (count >= capacity) {
      return false;
  }
  final Node<E> l = last;
  final Node<E> x = new Node<>(e, l, null);
  last = x;
  if (first == null) {
      first = x;
  } else {
      l.next = x;
  }
  ++count;
  notEmpty.signal();
  return true;
}

2.3 核心業(yè)務(wù)流程

2.3.1 池化對象狀態(tài)變更

上圖是PooledObject的狀態(tài)機(jī)圖,藍(lán)色表示狀態(tài),紅色表示與ObjectPool相關(guān)的方法.PooledObject的狀態(tài)為:IDLE、ALLOCATED、RETURNING、ABANDONED、INVALID、EVICTION、EVICTION_RETURN_TO_HEAD

所有狀態(tài)是在PooledObjectState類中定義的,其中一些是暫時(shí)未使用的,此處不再贅述。

2.3.2 對象池browObject過程

第一步、根據(jù)配置確定是否要為標(biāo)簽刪除調(diào)用removeAbandoned方法。

第二步、嘗試獲取或創(chuàng)建一個(gè)對象,源碼過程如下:

//1、嘗試從雙端隊(duì)列中獲取對象,pollFirst方法是非阻塞方法
p = idleObjects.pollFirst();
if (p == null) {
    p = create();
    if (p != null) {
        create = true;
    }
}
if (blockWhenExhausted) {
    if (p == null) {
        if (borrowMaxWaitMillis < 0) {
            //2、沒有設(shè)置最大阻塞等待時(shí)間,則無限等待
            p = idleObjects.takeFirst();
        } else {
            //3、設(shè)置最大等待時(shí)間了,則阻塞等待指定的時(shí)間
            p = idleObjects.pollFirst(borrowMaxWaitMillis,
                    TimeUnit.MILLISECONDS);
        }
    }
}

示意圖如下所示:

什么是commons-pool2池化技術(shù)

第三步、調(diào)用allocate使?fàn)顟B(tài)更改為ALLOCATED狀態(tài)。

第四步、調(diào)用工廠的activateObject來初始化對象,如果發(fā)生錯(cuò)誤,請調(diào)用destroy方法來銷毀對象,例如源代碼中的六個(gè)步驟。

第五步、調(diào)用TestFactory的validateObject進(jìn)行基于TestOnBorrow配置的對象可用性分析,如果不可用,則調(diào)用destroy方法銷毀對象。3-7步驟的源碼過程如下所示:

//修改對象狀態(tài)
if (!p.allocate()) {
    p = null;
}
if (p != null) {
    try {
        //初始化對象
        factory.activateObject(p);
    } catch (final Exception e) {
        try {
            destroy(p, DestroyMode.NORMAL);
        } catch (final Exception e1) {
        }
 
}
    if (p != null && getTestOnBorrow()) {
        boolean validate = false;
        Throwable validationThrowable = null;
        try {
            //驗(yàn)證對象的可用性狀態(tài)
            validate = factory.validateObject(p);
        } catch (final Throwable t) {
            PoolUtils.checkRethrow(t);
            validationThrowable = t;
        }
        //對象不可用,驗(yàn)證失敗,則進(jìn)行destroy
        if (!validate) {
            try {
                destroy(p, DestroyMode.NORMAL);
               destroyedByBorrowValidationCount.incrementAndGet();
            } catch (final Exception e) {
                // Ignore - validation failure is more important
            }
 
        }
    }
}
2.3.3 對象池returnObject的過程執(zhí)行邏輯

第一步、調(diào)用markReturningState方法將狀態(tài)更改為RETURNING。

第二步、基于testOnReturn配置調(diào)用PooledObjectFactory的validateObject方法以進(jìn)行可用性檢查。如果檢查失敗,則調(diào)用destroy消耗該對象,然后確保調(diào)用idle以確保池中有IDLE狀態(tài)對象可用,如果沒有,則調(diào)用create方法創(chuàng)建一個(gè)新對象。

第三步、調(diào)用PooledObjectFactory的passivateObject方法進(jìn)行反初始化操作。

第四步、調(diào)用deallocate將狀態(tài)更改為IDLE。

第五步、檢測是否已超過最大空閑對象數(shù),如果超過,則銷毀當(dāng)前對象。

第六步、根據(jù)LIFO(后進(jìn)先出)配置將對象放置在隊(duì)列的開頭或結(jié)尾。

(還原操作隊(duì)列示意圖)

2.4 拓展和思考

2.4.1 關(guān)于LinkedBlockingDeque的另種實(shí)現(xiàn)

上文中分析到commons-pool2中使用了雙端隊(duì)列以及java中的condition來實(shí)現(xiàn)隊(duì)列中對象的管理和不同線程對對象獲取和釋放對象操作之間的協(xié)調(diào),那是否有其他方案可以實(shí)現(xiàn)類似效果呢?答案是肯定的。

使用雙端隊(duì)列進(jìn)行操作,其實(shí)是想將空閑對象和活躍對象進(jìn)行隔離,本質(zhì)上將我們用兩個(gè)隊(duì)列來分別存儲(chǔ)空閑隊(duì)列和當(dāng)前活躍對象,然后再統(tǒng)一使用一個(gè)對象鎖,也是可以達(dá)成相同的目標(biāo)的,大概的思路如下:

1)雙端隊(duì)列改為兩個(gè)單向隊(duì)列分別用于存儲(chǔ)空閑的和活躍的對象,隊(duì)列之間的同步和協(xié)調(diào)可以通過對象鎖的wait和notify完成。

public  class PoolState {
 
protected final List<PooledObject> idleObjects = new ArrayList<>();
protected final List<PooledObject> activeObjects = new ArrayList<>();
 
 
//...
 
}

2)在獲取對象時(shí)候,原本對雙端隊(duì)列的LIFO或者FIFO變成了從空閑隊(duì)列idleObjects中獲取對象,然后在獲取成功并對象狀態(tài)合法后,將對象添加到活躍對象集合activeObjects 中,如果獲取對象需要等待,則PoolState對象鎖應(yīng)該通過wait操作,進(jìn)入等待狀態(tài)。

3)在釋放對象的時(shí)候,則首先從活躍對象集合activeObjects 刪除元素,刪除完成后,將對象增加到空閑對象集合idleObjects中,需要注意的是,在釋放對象過程中也需要去校驗(yàn)對象的狀態(tài)。當(dāng)對象狀態(tài)不合法的時(shí)候,對象應(yīng)該進(jìn)行銷毀,不應(yīng)該添加到idleObjects中。釋放成功后則PoolState通過notify或者notifyAll喚醒等待中的獲取操作。

4)為保障對活躍隊(duì)列和空閑隊(duì)列的操作線程安全性,獲取對象和釋放對象需要進(jìn)行加鎖操作,和commons2-pool中的一致。

2.4.2 對象池的自我保護(hù)機(jī)制

我們在使用commons-pool2中獲取對象的時(shí)候,會(huì)從雙端隊(duì)列中阻塞等待獲取元素(或者是創(chuàng)建新對象),但是如果是應(yīng)用程序的異常,一直未調(diào)用returnObject或者invalidObject的時(shí)候,那可能就會(huì)出現(xiàn)對象池中的對象一直上升,到達(dá)設(shè)置的上線之后再去調(diào)用borrowObject的時(shí)候就會(huì)出現(xiàn)一直等待或者是等待超時(shí)而無法獲取對象的情況。

commons-pool2為了避免上述分析的問題的出現(xiàn),提供了兩種自我保護(hù)機(jī)制:

2.4.2.1 基于閾值的檢測

從對象池中獲取對象的時(shí)候會(huì)校驗(yàn)當(dāng)前對象池的活躍對象和空閑對象的數(shù)量占比,當(dāng)空閑獨(dú)享非常少,活躍對象非常多的時(shí)候,會(huì)觸發(fā)空閑對象的回收,具體校驗(yàn)規(guī)則為:如果當(dāng)前對象池中少于2個(gè)idle狀態(tài)的對象或者 active數(shù)量>最大對象數(shù)-3 的時(shí)候,在borrow對象的時(shí)候啟動(dòng)泄漏清理。通過AbandonedConfig.setRemoveAbandonedOnBorrow 為 true 進(jìn)行開啟。

//根據(jù)配置確定是否要為標(biāo)簽刪除調(diào)用removeAbandoned方法
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3) ) {
    removeAbandoned(ac);
}

2.4.2.2 異步調(diào)度線程檢測

AbandonedConfig.setRemoveAbandonedOnMaintenance 設(shè)置為 true 以后,在維護(hù)任務(wù)運(yùn)行的時(shí)候會(huì)進(jìn)行泄漏對象的清理,通過設(shè)置setTimeBetweenEvictionRunsMillis 來設(shè)置維護(hù)任務(wù)執(zhí)行的時(shí)間間隔。

什么是commons-pool2池化技術(shù)

(異步檢測線程Evictor時(shí)序圖)

檢測和回收實(shí)現(xiàn)邏輯分析:

在構(gòu)造方法內(nèi)部邏輯的最后調(diào)用了startEvictor方法。這個(gè)方法的作用是在構(gòu)造完對象池后,啟動(dòng)回收器來監(jiān)控回收空閑對象。startEvictor定義在GenericObjectPool的父類BaseGenericObjectPool(抽象)類中,我們先看一下這個(gè)方法的源碼。

在構(gòu)造器中會(huì)執(zhí)行如下的設(shè)置參數(shù);

public final void setTimeBetweenEvictionRunsMillis(
      final long timeBetweenEvictionRunsMillis) {
  this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
  startEvictor(timeBetweenEvictionRunsMillis);
}

當(dāng)且僅當(dāng)設(shè)置了timeBetweenEvictionRunsMillis參數(shù)后才會(huì)開啟定時(shí)清理任務(wù)。

final void startEvictor(final long delay) {
  synchronized (evictionLock) {
      EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
      evictor = null;
      evictionIterator = null;
      //如果delay<=0則不會(huì)開啟定時(shí)清理任務(wù)
      if (delay > 0) {
          evictor = new Evictor();
          EvictionTimer.schedule(evictor, delay, delay);
      }
  }
}

繼續(xù)跟進(jìn)代碼可以發(fā)現(xiàn),調(diào)度器中設(shè)置的清理方法的實(shí)現(xiàn)邏輯實(shí)際在對象池中定義的,也就是由GenericObjectPool或者GenericKeyedObjectPool來實(shí)現(xiàn),接下來我們繼續(xù)探究對象池是如何進(jìn)行對象回收的。

a)、核心參數(shù):

minEvictableIdleTimeMillis:指定空閑對象最大保留時(shí)間,超過此時(shí)間的會(huì)被回收。不配置則不過期回收。

softMinEvictableIdleTimeMillis:一個(gè)毫秒數(shù)值,用來指定在空閑對象數(shù)量超過minIdle設(shè)置,且某個(gè)空閑對象超過這個(gè)空閑時(shí)間的才可以會(huì)被回收。

minIdle:對象池里要保留的最小空間對象數(shù)量。

b)、回收邏輯

以及一個(gè)對象回收策略接口EvictionPolicy,可以預(yù)料到對象池的回收會(huì)和上述的參數(shù)項(xiàng)及接口EvictionPolicy發(fā)生關(guān)聯(lián),繼續(xù)跟進(jìn)代碼會(huì)發(fā)現(xiàn)如下的內(nèi)容,可以看到在判斷對象池可以進(jìn)行回收的時(shí)候,直接調(diào)用了destroy進(jìn)行回收。

boolean evict;
try {
  evict = evictionPolicy.evict(evictionConfig, underTest,
  idleObjects.size());
} catch (final Throwable t) {
  // Slightly convoluted as SwallowedExceptionListener
  // uses Exception rather than Throwable
    PoolUtils.checkRethrow(t);
    swallowException(new Exception(t));
    // Don't evict on error conditions
    evict = false;
}
if (evict) {
    // 如果可以被回收則直接調(diào)用destroy進(jìn)行回收
    destroy(underTest);
    destroyedByEvictorCount.incrementAndGet();
}

為提升回收的效率,在回收策略判斷對象的狀態(tài)不是evict的時(shí)候,也會(huì)進(jìn)行進(jìn)一步的狀態(tài)判斷和處理,具體邏輯如下:

1.嘗試激活對象,如果激活失敗則認(rèn)為對象已經(jīng)不再存活,直接調(diào)用destroy進(jìn)行銷毀。

2.在激活對象成功的情況下,會(huì)通過validateObject方法取校驗(yàn)對象狀態(tài),如果校驗(yàn)失敗,則說明對象不可用,需要進(jìn)行銷毀。

boolean active = false;
try {
  // 調(diào)用activateObject激活該空閑對象,本質(zhì)上不是為了激活,
  // 而是通過這個(gè)方法可以判定是否還存活,這一步里面可能會(huì)有一些資源的開辟行為。
  factory.activateObject(underTest);
  active = true;
} catch (final Exception e) {
  // 如果激活的時(shí)候,發(fā)生了異常,就說明該空閑對象已經(jīng)失聯(lián)了。
  // 調(diào)用destroy方法銷毀underTest
  destroy(underTest);
  destroyedByEvictorCount.incrementAndGet();
}
if (active) {
  // 再通過進(jìn)行validateObject校驗(yàn)有效性
  if (!factory.validateObject(underTest)) {
      // 如果校驗(yàn)失敗,說明對象已經(jīng)不可用了
      destroy(underTest);
      destroyedByEvictorCount.incrementAndGet();
  } else {
      try {
          /*
            *因?yàn)樾r?yàn)還激活了空閑對象,分配了額外的資源,那么就通過passivateObject把在activateObject中開辟的資源釋放掉。
          */
          factory.passivateObject(underTest);
      } catch (final Exception e) {
          // 如果passivateObject失敗,也可以說明underTest這個(gè)空閑對象不可用了
          destroy(underTest);
          destroyedByEvictorCount.incrementAndGet();
      }
  }
}

“什么是commons-pool2池化技術(shù)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

網(wǎng)頁名稱:什么是commons-pool2池化技術(shù)
文章轉(zhuǎn)載:http://aaarwkj.com/article22/gghpcc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、微信公眾號(hào)、全網(wǎng)營銷推廣、響應(yīng)式網(wǎng)站、做網(wǎng)站品牌網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
国内自拍视频一区高清视频| 国产精品国产亚洲精品看不| 日韩美女后入式在线视频| 麻豆文化传媒免费网址| 欧美日韩伦理片在线观看| 国产高清视频不卡在线| 亚洲各类熟女们中文字幕| 日韩美女毛片啪啪响| 日韩中文字幕欧美国产| 日韩大片一区二区三区在线观看| 午夜视频在线播放一区二区三区| 欧美日韩亚洲国产专区精品| 国产av剧情同事肉体秘密| 人妻猛烈进入中文字幕| 三级国产大片在线观看| 熟妇激情欧美在线播放视频| 人人妻人人澡人人爽人人dvd| 最新日本免费久久精品| 欧美精品亚洲二区中文乱码| 99久久精品人妻少妇一| 成人三级中文字幕电影| 亚洲女同另类在线播放视频| 久久精品国产一区二区三区不卡| 日韩欧美一区二区中文字幕视频| 久久中文人妻丝袜不卡| 欧美高清成人一区二区三区| 亚洲一区二区三区免费在线视频| 日韩电影一区二区在线观看中文字幕 | 亚洲精品一区二区午夜| 日本韩国亚洲三级在线| 午夜福利视频一区久久久| 亚洲一区二区三区 码| 中文字幕乱码亚洲2019| 91精品手机在线观看| 天天操天天夜夜操夜夜| 18禁在线免费观看网站| 日韩黄色资源在线观看| 国产黄色片网站在线观看| 亚洲日本成人av在线观看| 国产日韩欧美精品激情| 欧美又粗又成人大视频|