高併發服務器-鏈接池的設計

高併發服務器-鏈接池的設計sql

高併發服務器須要有一些池的設計,如內存池,鏈接池,數據庫鏈接池。數據庫

池(pool)的設計主要考慮到一些資源的頻繁申請和釋放,尤爲是在高併發的服務器中,幾萬甚至幾十萬併發每秒,設計人員不得不去考慮這些。服務器

好比數據庫鏈接池(sql pool),是經過TCP來通訊的,屬於IO類,有必定的延時,在高併發系統中頻繁的建立會嚴重影響系統性能。併發

內存( mem )的分配是要涉及鎖( mutex )的,有鎖就會有延時,所以能夠在開始申請一大塊內存,後面進行分配與釋放,來節省鎖開銷。函數

服務器的鏈接處理不單單涉及內存,還涉及到一些屬性的賦值,這些是要佔用CPU時間的,若是在一開始就建立大量的鏈接,就方便之後複用了。高併發

下面我以數據庫鏈接池爲例,先定義鏈接的結構:性能

  1. typedef struct tst_sql_s tst_sql_t;  
  2. struct tst_sql_s{  
  3.     MYSQL     *sql;  
  4.     tst_sql_t   *next;  
  5.     tst_sql_t   *prev;  
  6. };  

現實開發中,我發現有些喜歡用( free-busi ) 模式來設計池。設計

  1. struct  tst_sql_pool_s  
  2. {  
  3.     tst_sql_t *free_sql;  
  4.     tst_sql_t *busi_sql;  
  5.     …  
  6. };  


將池中的鏈接分紅兩個部分,一部分是空閒的(free),一部分是正在用的(busi),相函數函數:接口

  1. tst_sql_t* tst_sql_pool_get( tst_sql_pool_t* pool )  
  2. {  
  3.     tst_sql_t *sql;  
  4.     if( !pool ){  
  5.         return 0;  
  6.     }  
  7.       
  8.     sql = pool->free_sql;  
  9.       
  10.     if( !sql ){  
  11.         return 0;  
  12.     }  
  13.   
  14.     pool->free_sql = sql->next;  
  15.     sql->next = pool->busi_sql;  
  16.     sql->prev = 0;  
  17.     if( pool->busi_sql ){  
  18.         pool->busi_sql->prev = sql;  
  19.     }  
  20.     pool->busi_sql = sql;  
  21.       
  22.     return sql;  
  23. }  
  24.   
  25. int tst_sql_pool_put( tst_sql_pool_t* pool, tst_sql_t* sql )  
  26. {  
  27.     if( !pool || !sql ){  
  28.         return 0;  
  29.     }  
  30.   
  31.     if( sql->prev ){  
  32.         sql->prev->next = sql->next;  
  33.     }  
  34.     else{  
  35.         pool->busi_sql = sql->next;  
  36.     }  
  37.   
  38.     if( sql->next ){  
  39.         sql->next->prev = sql->prev;  
  40.     }  
  41.   
  42.     sql->next = pool->free_sql;  
  43.     pool->free_sql = sql;  
  44.       
  45.     return 0;  
  46. }  

 

 

基本就完成了池的管理了,可是咱們也能夠看出來這個判斷其實很麻煩,有沒有不用這麼麻煩的呢。內存

從上面的函數也能夠看出,麻煩主要在 busi 池上,free池的處理其實挺簡單的,因而就有了下面的設計:

鏈接池只存放空閒鏈接,不在保存鏈接的狀態,而應該把狀態的分別交給管理函數。

下面咱們以鏈接池舉例

我從新設計了鏈接池的結構:

  1. typedef struct tst_conn_s tst_conn_t;  
  2. typedef struct tst_conn_pool_s tst_conn_pool_t;  
  3. struct tst_conn_s  
  4. {  
  5.     int  fd;  
  6.     ……..  
  7.     ……..  
  8.     tst_conn_t* next;  
  9. };  
  10.   
  11. struct tst_conn_pool_s  
  12. {  
  13.     ………  
  14.     ……….  
  15.     tst_conn_t*  conns;  
  16. };  

 

池的管理函數:

  1. tst_conn_t* tst_conn_pool_get( tst_conn_pool_t* pool )  
  2. {  
  3.     tst_conn_t* conn;  
  4.   
  5.     if( !pool ){  
  6.         return 0;  
  7.     }  
  8.   
  9.     conn = pool->conns;  
  10.     if( !conn ){  
  11.         return 0;  
  12.     }  
  13.   
  14.     pool->conns = conn->next;  
  15.       
  16.     return conn;  
  17.   
  18. }  
  19. #define TST_CONN_POOL_ERROR -1  
  20. #define TST_CONN_POOL_OK 0  
  21.   
  22. int tst_conn_pool_put( tst_conn_pool_t* pool, tst_conn_t* conn )  
  23. {  
  24.     if( !pool || !conn ){  
  25.         return TST_CONN_POOL_ERROR;  
  26.     }  
  27.   
  28.     conn->next = pool->conns;  
  29.     pool->conns = conn;  
  30.       
  31.     return TST_CONN_POOL_OK;  
  32. }     


 

 

 

這樣,就起到了鏈接池的分配與回收的功能。

通常在設計上提升模塊的透明性和下降耦合,我會把池的管理放在模塊內部,對外只提供一致性接口:

  1. #define TST_CONN_POOL_ERROR -1  
  2. #define TST_CONN_POOL_OK 0  
  3. tst_conn_t* tst_conn_get();  
  4. int tst_conn_free( tst_conn_t* conn );  

模塊內部用一個全局的池,在模塊內統一的管理。 

相關文章
相關標籤/搜索