高并发服务器的设计--连接池的设计

高并发服务器需要有一些池的设计,如内存池,连接池,数据库连接池。

池(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 );


模块内部用一个全局的池,在模块内统一的管理。



你可能感兴趣的:(服务器开发,架构设计,性能,大并发服务器开发)