談談連接池、線程池技術原理
這么多數據庫、http、netty連接池,jdk線程池,本質上都是連接池技術,連接池技術核心是連接或者說創建的資源復用。
連接池技術核心:通過減少連接創建、關閉來提升性能。用於用戶后續使用,好處是后續使用不用在創建連接以及線程,因為這些都需要相關很多文件、連接資源、操作系統內核資源支持來完成構建,會消耗大量資源,並且創建、關閉會消耗應用程序大量性能。
網絡連接本身會消耗大量內核資源,在linux系統下,網絡連接創建本身tcp/ip協議棧在內核里面,連接創建關閉會消耗大量文件句柄(linux中萬物皆文件,一種厲害抽象手段)系統資源。當下更多是應用tcp技術完成網絡傳輸,反復打開關閉,需要操作系統維護大量tcp協議棧狀態。
連接池本質上是構建一個容器,容器來存儲創建好的線程、http連接、數據庫連接、netty連接等。對於使用方相當於黑盒,按照接口進行使用就可以了。各個連接池構建、使用管理詳細過程大概分成以下三部分。
第一部分:首先初始化連接池,根據設置相應參數,連接池大小、核心線程數、核心連接數等參數,初始化創建數據庫、http、netty連接以及jdk線程。
第二部分:連接池使用,前邊初始化好的連接池、線程池,直接從連接池、線程中取出資源即可進行使用,使用完后要記得交還連接池、線程池,通過池容器來對資源進行管理。
第三部分:對於連接池維護,連接池、線程池來維護連接、線程狀態,不可用連接、線程進行銷毀,正在使用連接、線程進行狀態標注,連接、線程不夠后並且少於設置最大連接、線程數,要進行新連接、線程創建。
通過上邊可以了解到各種連接池技術以及線程池原理或者說套路,理解原理才能不被紛繁復雜表象掩蓋。
下面談談構建自己連接池,其實理解了連接池、線程原理,可以使用ArrayList來構建自己連接池、線程池。初始化時創建配置連接數、線程,存儲在ArrayList容器中,使用時從ArrayList從取出連接、線程進行使用,執行完任務后,提交回ArrayList容器。前提條件是單線程,在多線程狀態下要用線程安全容器。
前邊根據原理介紹了一個簡單連接池、線程池怎樣構建,實際工業級別線程池還要考慮到連接狀態,短連接重連,線程池維護管理高效,線程池穩定等多個因素。
需要用到連接池而又沒有相關開源產品可用時,java連接池可以使用common-pool2來構建,比如google開源gRPC技術,本身是高性能跨平台技術,但目前作為微服務使用,沒有連接池、負載均衡等相應配套,這時可以根據需要自己基於Java容器構建自己連接池。也可以利用common-pool2構建連接池來提升應用性能,以及保持高可用。common-pool2本身不僅僅可以構建連接池使用,還可以用來構建對象池。
連接池還有一個副作用就是實現了高可用,在微服務場景下一個連接不可用,那么再從netty連接池中取出一個進行使用,避免了連接不可用問題。
掌握原理從比較全面掌握各種池技術,避免數據庫連接池,再到httpclient http連接池,再到微服務netty連接池,redis客戶端連接池,以及jdk中線程池,對象池各種各樣池技術,使我們眼花繚亂,花費過多時間,掌握原理機制以不變應萬變。
推廣一下這個方法,其他技術也是類似,深入掌握其原理,就可以明白其他類似技術相似原理,避免疲於應對各種新技術。但每一種架構設計與實現又與領域有着關系,也不可講原理不顧實際情況擴展。理論與架構設計、源碼學習相結合才是最好的,希望有幫助。
享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候可以減少內存的開銷,通常與工廠模式一起使用。
FlyWeightFactory負責創建和管理享元單元,當一個客戶端請求時,工廠需要檢查當前對象池中是否有符合條件的對象,如果有,就返回已經存在的對象,如果沒有,則創建一個新對象,FlyWeight是超類。
一提到共享池,我們很容易聯想到Java里面的JDBC連接池,想想每個連接的特點,我們不難總結出:適用於作共享的一些個對象,他們有一些共有的屬性,就拿數據庫連接池來說,url、driverClassName、username、password及dbname,這些屬性對於每個連接來說都是一樣的,所以就適合用享元模式來處理,建一個工廠類,將上述類似屬性作為內部數據,其它的作為外部數據,在方法調用時,當做參數傳進來,這樣就節省了空間,減少了實例的數量。
看個例子:
看下數據庫連接池的代碼:
[java] view plaincopy
- public class ConnectionPool {
- private Vector<Connection> pool;
- /*公有屬性*/
- private String url = "jdbc:mysql://localhost:3306/test";
- private String username = "root";
- private String password = "root";
- private String driverClassName = "com.mysql.jdbc.Driver";
- private int poolSize = 100;
- private static ConnectionPool instance = null;
- Connection conn = null;
- /*構造方法,做一些初始化工作*/
- private ConnectionPool() {
- pool = new Vector<Connection>(poolSize);
- for (int i = 0; i < poolSize; i++) {
- try {
- Class.forName(driverClassName);
- conn = DriverManager.getConnection(url, username, password);
- pool.add(conn);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- /* 返回連接到連接池 */
- public synchronized void release() {
- pool.add(conn);
- }
- /* 返回連接池中的一個數據庫連接 */
- public synchronized Connection getConnection() {
- if (pool.size() > 0) {
- Connection conn = pool.get(0);
- pool.remove(conn);
- return conn;
- } else {
- return null;
- }
- }
- }
通過連接池的管理,實現了數據庫連接的共享,不需要每一次都重新創建連接,節省了數據庫重新創建的開銷,提升了系統的性能!