日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php redis 源码分析,从源码中分析关于phpredis中的连接池可持有数目

發布時間:2024/1/23 php 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php redis 源码分析,从源码中分析关于phpredis中的连接池可持有数目 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近寫個東西期間用到redis拓展時,看到文檔里提到有連接池到方式,所以本想看看別人用時,基本沒看到如何設置連接池到數量的地方,無奈之下看看phpredis手冊也只有如何連接,沒有相關介紹如何控制連接池中數量,最后辦法,查源碼

在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;

}

//創建連接池

/* 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;

}

在創建連接池階段首先聲明了一塊大小為ConnectionPool這個結構體的內存塊,但這里并沒有連接池數量進行設置,因為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是一個包含雙向鏈表和活躍計數的結構體。

下面回到第一段代碼的初始化連接池階段,這里 zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1);方法對ConnectionPool中的zend_llist開始初始化,也就是開始給連接池填充,而sizeof(php_stream *)是zend_llist->size,不是數量具體如下

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()這個方法,通過查找發現

/**

* redis_sock_connect

*redis創建連接

*/

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創建連接部分

......

//省略redis創建連接部分

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–。

下面判斷是根據redis.pconnect.connection_limit設定的值和nb_active判斷是否達到自定義上限。

在未設定limit和未到上限情況下,由于上面未獲得鏈接,所以接下來還要新創建一個redis連接,在連接創建成功時,有連接池情況下nb_active++。

這里會發現只是對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字段存儲是當前已經創建了多少連接,至于有多少在池里是在zend_list的count中 ;

phpredis的連接池中的數量由兩方面決定:一個redis.pconnect.connection_limit這個參數,雖然文檔里沒有可以設置該值的提示,但它是可以設置的,默認值0是不自定義限制;另一個決定面是由當前系統可以創建的redis的連接數目確定的。(簡單說就是在不使用連接池時你能創建多少連接,在用池子時每創建一個,正常回收時它就放入池子,當然這里涉及到池子中存在的連接失活的)

到這里本該結尾了,只有在查找中好奇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 協議》,轉載必須注明作者和本文鏈接

總結

以上是生活随笔為你收集整理的php redis 源码分析,从源码中分析关于phpredis中的连接池可持有数目的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。