高併發服務器-鏈接池的設計sql
高併發服務器須要有一些池的設計,如內存池,鏈接池,數據庫鏈接池。數據庫
池(pool)的設計主要考慮到一些資源的頻繁申請和釋放,尤爲是在高併發的服務器中,幾萬甚至幾十萬併發每秒,設計人員不得不去考慮這些。服務器
好比數據庫鏈接池(sql pool),是經過TCP來通訊的,屬於IO類,有必定的延時,在高併發系統中頻繁的建立會嚴重影響系統性能。併發
內存( mem )的分配是要涉及鎖( mutex )的,有鎖就會有延時,所以能夠在開始申請一大塊內存,後面進行分配與釋放,來節省鎖開銷。函數
服務器的鏈接處理不單單涉及內存,還涉及到一些屬性的賦值,這些是要佔用CPU時間的,若是在一開始就建立大量的鏈接,就方便之後複用了。高併發
下面我以數據庫鏈接池爲例,先定義鏈接的結構:性能
- typedef struct tst_sql_s tst_sql_t;
- struct tst_sql_s{
- MYSQL *sql;
- tst_sql_t *next;
- tst_sql_t *prev;
- };
現實開發中,我發現有些喜歡用( free-busi ) 模式來設計池。設計
- struct tst_sql_pool_s
- {
- tst_sql_t *free_sql;
- tst_sql_t *busi_sql;
- …
- };
將池中的鏈接分紅兩個部分,一部分是空閒的(free),一部分是正在用的(busi),相函數函數:接口
- tst_sql_t* tst_sql_pool_get( tst_sql_pool_t* pool )
- {
- tst_sql_t *sql;
- if( !pool ){
- return 0;
- }
-
- sql = pool->free_sql;
-
- if( !sql ){
- return 0;
- }
-
- pool->free_sql = sql->next;
- sql->next = pool->busi_sql;
- sql->prev = 0;
- if( pool->busi_sql ){
- pool->busi_sql->prev = sql;
- }
- pool->busi_sql = sql;
-
- return sql;
- }
-
- int tst_sql_pool_put( tst_sql_pool_t* pool, tst_sql_t* sql )
- {
- if( !pool || !sql ){
- return 0;
- }
-
- if( sql->prev ){
- sql->prev->next = sql->next;
- }
- else{
- pool->busi_sql = sql->next;
- }
-
- if( sql->next ){
- sql->next->prev = sql->prev;
- }
-
- sql->next = pool->free_sql;
- pool->free_sql = sql;
-
- return 0;
- }
基本就完成了池的管理了,可是咱們也能夠看出來這個判斷其實很麻煩,有沒有不用這麼麻煩的呢。內存
從上面的函數也能夠看出,麻煩主要在 busi 池上,free池的處理其實挺簡單的,因而就有了下面的設計:
鏈接池只存放空閒鏈接,不在保存鏈接的狀態,而應該把狀態的分別交給管理函數。
下面咱們以鏈接池舉例
我從新設計了鏈接池的結構:
- typedef struct tst_conn_s tst_conn_t;
- typedef struct tst_conn_pool_s tst_conn_pool_t;
- struct tst_conn_s
- {
- int fd;
- ……..
- ……..
- tst_conn_t* next;
- };
-
- struct tst_conn_pool_s
- {
- ………
- ……….
- tst_conn_t* conns;
- };
池的管理函數:
- tst_conn_t* tst_conn_pool_get( tst_conn_pool_t* pool )
- {
- tst_conn_t* conn;
-
- if( !pool ){
- return 0;
- }
-
- conn = pool->conns;
- if( !conn ){
- return 0;
- }
-
- pool->conns = conn->next;
-
- return conn;
-
- }
- #define TST_CONN_POOL_ERROR -1
- #define TST_CONN_POOL_OK 0
-
- int tst_conn_pool_put( tst_conn_pool_t* pool, tst_conn_t* conn )
- {
- if( !pool || !conn ){
- return TST_CONN_POOL_ERROR;
- }
-
- conn->next = pool->conns;
- pool->conns = conn;
-
- return TST_CONN_POOL_OK;
- }
這樣,就起到了鏈接池的分配與回收的功能。
通常在設計上提升模塊的透明性和下降耦合,我會把池的管理放在模塊內部,對外只提供一致性接口:
- #define TST_CONN_POOL_ERROR -1
- #define TST_CONN_POOL_OK 0
- tst_conn_t* tst_conn_get();
- int tst_conn_free( tst_conn_t* conn );
模塊內部用一個全局的池,在模塊內統一的管理。