php redis 源码分析,从源码中分析关于phpredis中的连接池可持有数目
最近寫個東西期間用到redis拓展時,看到文檔里提到有連接池到方式,所以本想看看別人用時,基本沒看到如何設置連接池到數(shù)量的地方,無奈之下看看phpredis手冊也只有如何連接,沒有相關介紹如何控制連接池中數(shù)量,最后辦法,查源碼
在phpredis的library.c中有這樣一個方法
static ConnectionPool *
redis_sock_get_connection_pool(RedisSock *redis_sock)
{
ConnectionPool *pool;
zend_resource *le;
zend_string *persistent_id;
/* Generate our unique pool id depending on configuration */
persistent_id = redis_pool_spprintf(redis_sock, INI_STR("redis.pconnect.pool_pattern"));
//查找連接池
/* Return early if we can find the pool */
if ((le = zend_hash_find_ptr(&EG(persistent_list), persistent_id))) {
zend_string_release(persistent_id);
return le->ptr;
}
//創(chuàng)建連接池
/* Create the pool and store it in our persistent list */
pool = pecalloc(1, sizeof(*pool), 1);
//初始化連接池
zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1);
redis_register_persistent_resource(persistent_id, pool, le_redis_pconnect);
zend_string_release(persistent_id);
return pool;
}
在創(chuàng)建連接池階段首先聲明了一塊大小為ConnectionPool這個結構體的內存塊,但這里并沒有連接池數(shù)量進行設置,因為ConnectionPool指記錄了連接池的指針,具體看下面代碼
typedef struct {
zend_llist list;
int nb_active;
} ConnectionPool;
下面來自PHP7.2的源碼
typedef struct _zend_llist_element {
struct _zend_llist_element *next;
struct _zend_llist_element *prev;
char data[1]; /* Needs to always be last in the struct */
} zend_llist_element;
typedef struct _zend_llist {
zend_llist_element *head;
zend_llist_element *tail;
size_t count;
size_t size;
llist_dtor_func_t dtor;
unsigned char persistent;
zend_llist_element *traverse_ptr;
} zend_llist;
從上面可以看出zend_llist是一個雙向鏈表的結構體,而ConnectionPool是一個包含雙向鏈表和活躍計數(shù)的結構體。
下面回到第一段代碼的初始化連接池階段,這里 zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1);方法對ConnectionPool中的zend_llist開始初始化,也就是開始給連接池填充,而sizeof(php_stream *)是zend_llist->size,不是數(shù)量具體如下
void zend_llist_init(zend_llist *l, size_t size, llist_dtor_func_t dtor, unsigned char persistent)
{
l->head = NULL;
l->tail = NULL;
l->count = 0;
l->size = size;
l->dtor = dtor;
l->persistent = persistent;
}
再回到第一段代碼中redis_register_persistent_resource(persistent_id, pool, le_redis_pconnect);這個是注冊持久資源的。
到此為止只是搭了一個連接池的框子并沒有往池子里放入對象,那么只能查誰調用了redis_sock_get_connection_pool()這個方法,通過查找發(fā)現(xiàn)
/**
* redis_sock_connect
*redis創(chuàng)建連接
*/
PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
這個方法里中有調用該方法下面只截取相關部分:
if (redis_sock->persistent) {
if (INI_INT("redis.pconnect.pooling_enabled")) {
p = redis_sock_get_connection_pool(redis_sock);
if (zend_llist_count(&p->list) > 0) {
redis_sock->stream = *(php_stream **)zend_llist_get_last(&p->list);
zend_llist_remove_tail(&p->list);
if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
return SUCCESS;
}
p->nb_active--;
}
int limit = INI_INT("redis.pconnect.connection_limit");
if (limit > 0 && p->nb_active >= limit) {
redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1);
return FAILURE;
}
gettimeofday(&tv, NULL);
persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec);
} else {
//省略
}
//省略redis創(chuàng)建連接部分
......
//省略redis創(chuàng)建連接部分
if (!redis_sock->stream) {
if (estr) {
redis_sock_set_err(redis_sock, ZSTR_VAL(estr), ZSTR_LEN(estr));
zend_string_release(estr);
}
return FAILURE;
}
if (p) p->nb_active++;
看著有點亂,其實這塊原理就是,當從想池中獲取一個鏈接時,首先判斷池子里是否有,如果有并且驗證沒有失活就直接返回成功,如果驗證活性失敗,就nb_active–。
下面判斷是根據(jù)redis.pconnect.connection_limit設定的值和nb_active判斷是否達到自定義上限。
在未設定limit和未到上限情況下,由于上面未獲得鏈接,所以接下來還要新創(chuàng)建一個redis連接,在連接創(chuàng)建成功時,有連接池情況下nb_active++。
這里會發(fā)現(xiàn)只是對nb_active++但并沒有往池子里放對象,再往下找:
/**
* redis_sock_disconnect */PHP_REDIS_API int
redis_sock_disconnect(RedisSock *redis_sock, int force)
{
if (redis_sock == NULL) {
return FAILURE;
} else if (redis_sock->stream) {
if (redis_sock->persistent) {
ConnectionPool *p = NULL;
if (INI_INT("redis.pconnect.pooling_enabled")) {
p = redis_sock_get_connection_pool(redis_sock);
}
if (force) {
php_stream_pclose(redis_sock->stream);
if (p) p->nb_active--;
} else if (p) {
zend_llist_prepend_element(&p->list, &redis_sock->stream);
}
} else {
php_stream_close(redis_sock->stream);
}
redis_sock->stream = NULL;
}
redis_sock->mode = ATOMIC;
redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
redis_sock->watching = 0;
return SUCCESS;
}
連接斷開時,這里有兩種情況對于客戶端斷開的連接如果強制關閉的,那么連接池nb_active直接自減一,如果非強制的并且連接池正常情況下,則把該連接重新放回連接池。
到這里可以總結下了:
ConnectionPool的nb_active字段存儲是當前已經(jīng)創(chuàng)建了多少連接,至于有多少在池里是在zend_list的count中 ;
phpredis的連接池中的數(shù)量由兩方面決定:一個redis.pconnect.connection_limit這個參數(shù),雖然文檔里沒有可以設置該值的提示,但它是可以設置的,默認值0是不自定義限制;另一個決定面是由當前系統(tǒng)可以創(chuàng)建的redis的連接數(shù)目確定的。(簡單說就是在不使用連接池時你能創(chuàng)建多少連接,在用池子時每創(chuàng)建一個,正常回收時它就放入池子,當然這里涉及到池子中存在的連接失活的)
到這里本該結尾了,只有在查找中好奇php_stream到底是什么結構,而它的大小是多少,查了許久也沒看到看到一個說法,只能再看php源碼:
struct _php_stream {
php_stream_ops *ops;
void *abstract; /* convenience pointer for abstraction */
php_stream_filter_chain readfilters, writefilters;
php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */
void *wrapperthis; /* convenience pointer for a instance of a wrapper */
zval wrapperdata; /* fgetwrapperdata retrieves this */
uint8_t is_persistent:1;
uint8_t in_free:2; /* to prevent recursion during free */
uint8_t eof:1;
uint8_t __exposed:1; /* non-zero if exposed as a zval somewhere */
/* so we know how to clean it up correctly. This should be set to
* PHP_STREAM_FCLOSE_XXX as appropriate */
uint8_t fclose_stdiocast:2;
uint8_t fgetss_state; /* for fgetss to handle multiline tags */
char mode[16]; /* "rwb" etc. ala stdio */
uint32_t flags; /* PHP_STREAM_FLAG_XXX */
zend_resource *res; /* used for auto-cleanup */
FILE *stdiocast; /* cache this, otherwise we might leak! */
char *orig_path;
zend_resource *ctx;
/* buffer */
zend_off_t position; /* of underlying stream */
unsigned char *readbuf;
size_t readbuflen;
zend_off_t readpos;
zend_off_t writepos;
/* how much data to read when filling buffer */
size_t chunk_size;
#if ZEND_DEBUG
const char *open_filename;
uint32_t open_lineno;
#endif
struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */
}; /* php_stream */
可以大概了解到這個結構應該是保存流的上下文等相關信息的,而phpredis中的這個php_stream應該是打開redis連接時的stream。
到這里結束。。
本作品采用《CC 協(xié)議》,轉載必須注明作者和本文鏈接
總結
以上是生活随笔為你收集整理的php redis 源码分析,从源码中分析关于phpredis中的连接池可持有数目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node.js 执行php,node.j
- 下一篇: php 目录管理,[PHP文件管理器]①