feat(proxy): 连接池大小可配置(默认4,最大16)

- proxy.h: COCOON_MAX_POOL_SIZE → COCOON_POOL_MAX_CAPACITY(16) + COCOON_POOL_DEFAULT_SIZE(4)
- proxy.c: proxy_pool_init 接收 max_pool_size,自动裁剪到上限
- proxy.c: proxy_add_rule 新增 pool_size 参数
- cocoon.h: 配置结构新增 pool_size 字段
- config.c: JSON 解析 pool_size 数字字段,默认值为 4
- server.c: 调用 proxy_add_rule 时传入配置值
- cocoon.json: 示例配置添加 pool_size 展示

编译零警告,142 单元测试 + 90 集成测试全部通过。
This commit is contained in:
xfy911 2026-06-09 03:06:50 +08:00
parent ff1b56fc9e
commit 23574ab2ba
7 changed files with 51 additions and 23 deletions

View File

@ -64,13 +64,24 @@
- 当前仅做编译和测试验证,不添加新代码 - 当前仅做编译和测试验证,不添加新代码
## 待办池(活跃) ## 待办池(活跃)
1. **HTTP/2 代理支持** — 将反向代理接入 HTTP/2 流(当前仅 HTTP/1.1 1. ~~连接池最大连接数可配置 — 当前固定为4~~ ✅ 2026-06-09
2. 虚拟主机 / 多站点 — 低优先级 2. **HTTP/2 代理支持** — 将反向代理接入 HTTP/2 流(当前仅 HTTP/1.1
3. 加权轮询 / 主动健康检查 — 负载均衡进阶(当前为被动检测) 3. 虚拟主机 / 多站点 — 低优先级
4. 连接池最大连接数可配置 — 当前固定为4 4. 加权轮询 / 主动健康检查 — 负载均衡进阶(当前为被动检测)
## 最近行动记录 ## 最近行动记录
- 2026-06-09 03:00: **连接池大小可配置**
- `proxy.h`: `COCOON_MAX_POOL_SIZE``COCOON_POOL_MAX_CAPACITY`上限16+ `COCOON_POOL_DEFAULT_SIZE`默认4`cocoon_conn_pool_t` 新增 `max_size` 字段
- `proxy.c`: `proxy_pool_init` 接收 `max_pool_size` 参数,自动裁剪到上限;所有循环改用 `backend->pool.max_size`
- `proxy.c`: `proxy_add_rule` 新增 `pool_size` 参数,传递给 `parse_backend_url``proxy_pool_init`
- `cocoon.h`: `proxies` 配置结构新增 `uint32_t pool_size` 字段
- `config.c`: JSON 解析器新增 `pool_size` 数字字段解析,默认值为 4
- `server.c`: 调用 `proxy_add_rule` 时传入配置的 `pool_size`
- `cocoon.json`: 示例配置添加 `pool_size` 展示(`/api` → 8`/backend` → 4
- 编译零警告142 单元测试 + 90 集成测试全部通过
- 提交已推送 `main`
- 2026-06-09 00:15: **后端连接池/keep-alive** - 2026-06-09 00:15: **后端连接池/keep-alive**
- `proxy.h`: 新增 `cocoon_pooled_conn_t` / `cocoon_conn_pool_t`pthread_mutex 保护),后端嵌入连接池 - `proxy.h`: 新增 `cocoon_pooled_conn_t` / `cocoon_conn_pool_t`pthread_mutex 保护),后端嵌入连接池
- `proxy.c`: 实现 `proxy_pool_init`/`destroy`/`acquire`/`release` + `proxy_config_destroy` - `proxy.c`: 实现 `proxy_pool_init`/`destroy`/`acquire`/`release` + `proxy_config_destroy`

View File

@ -56,6 +56,7 @@ typedef struct cocoon_config {
struct { struct {
char prefix[256]; char prefix[256];
char target[256]; char target[256];
uint32_t pool_size; /* 连接池大小默认4最大16 */
} proxies[COCOON_MAX_PROXY_RULES]; } proxies[COCOON_MAX_PROXY_RULES];
size_t num_proxies; size_t num_proxies;
} cocoon_config_t; } cocoon_config_t;

View File

@ -11,8 +11,8 @@
"plugins": ["plugins/hello.so"], "plugins": ["plugins/hello.so"],
"access_log": "-", "access_log": "-",
"proxies": [ "proxies": [
{"prefix": "/api", "target": "http://localhost:3000"}, {"prefix": "/api", "target": "http://localhost:3000", "pool_size": 8},
{"prefix": "/api", "target": "http://localhost:3001"}, {"prefix": "/api", "target": "http://localhost:3001"},
{"prefix": "/backend", "target": "https://localhost:8443"} {"prefix": "/backend", "target": "https://localhost:8443", "pool_size": 4}
] ]
} }

View File

@ -436,6 +436,9 @@ bool config_load_from_file(const char *path, cocoon_config_t *config) {
} else if (strcmp(pk, "target") == 0 && pval.type == TOKEN_STRING) { } else if (strcmp(pk, "target") == 0 && pval.type == TOKEN_STRING) {
char *v = token_str_dup(&pval); char *v = token_str_dup(&pval);
if (v) { strncpy(target, v, sizeof(target)-1); free(v); } if (v) { strncpy(target, v, sizeof(target)-1); free(v); }
} else if (strcmp(pk, "pool_size") == 0 && pval.type == TOKEN_NUMBER) {
char *v = token_str_dup(&pval);
if (v) { config->proxies[config->num_proxies].pool_size = (uint32_t)atoi(v); free(v); }
} }
free(pk); free(pk);
token_t psep = parser_next_token(&p); token_t psep = parser_next_token(&p);
@ -454,6 +457,9 @@ bool config_load_from_file(const char *path, cocoon_config_t *config) {
if (target_len >= sizeof(config->proxies[0].target)) target_len = sizeof(config->proxies[0].target) - 1; if (target_len >= sizeof(config->proxies[0].target)) target_len = sizeof(config->proxies[0].target) - 1;
memcpy(config->proxies[config->num_proxies].target, target, target_len); memcpy(config->proxies[config->num_proxies].target, target, target_len);
config->proxies[config->num_proxies].target[target_len] = '\0'; config->proxies[config->num_proxies].target[target_len] = '\0';
if (config->proxies[config->num_proxies].pool_size == 0) {
config->proxies[config->num_proxies].pool_size = 4; /* 默认连接池大小 */
}
config->num_proxies++; config->num_proxies++;
} }
} else { } else {

29
proxy.c
View File

@ -32,10 +32,17 @@ static void backend_init_health(cocoon_proxy_backend_t *backend) {
backend->last_check = 0; backend->last_check = 0;
} }
void proxy_pool_init(cocoon_proxy_backend_t *backend) { void proxy_pool_init(cocoon_proxy_backend_t *backend, size_t max_pool_size) {
memset(&backend->pool, 0, sizeof(backend->pool)); memset(&backend->pool, 0, sizeof(backend->pool));
backend->pool.max_size = max_pool_size;
if (backend->pool.max_size > COCOON_POOL_MAX_CAPACITY) {
backend->pool.max_size = COCOON_POOL_MAX_CAPACITY;
}
if (backend->pool.max_size == 0) {
backend->pool.max_size = COCOON_POOL_DEFAULT_SIZE;
}
pthread_mutex_init(&backend->pool.mutex, NULL); pthread_mutex_init(&backend->pool.mutex, NULL);
for (size_t i = 0; i < COCOON_MAX_POOL_SIZE; i++) { for (size_t i = 0; i < COCOON_POOL_MAX_CAPACITY; i++) {
backend->pool.conns[i].fd = COCOON_INVALID_SOCKET; backend->pool.conns[i].fd = COCOON_INVALID_SOCKET;
backend->pool.conns[i].tls_conn = NULL; backend->pool.conns[i].tls_conn = NULL;
backend->pool.conns[i].in_use = false; backend->pool.conns[i].in_use = false;
@ -46,7 +53,7 @@ void proxy_pool_init(cocoon_proxy_backend_t *backend) {
void proxy_pool_destroy(cocoon_proxy_backend_t *backend) { void proxy_pool_destroy(cocoon_proxy_backend_t *backend) {
if (!backend) return; if (!backend) return;
pthread_mutex_lock(&backend->pool.mutex); pthread_mutex_lock(&backend->pool.mutex);
for (size_t i = 0; i < COCOON_MAX_POOL_SIZE; i++) { for (size_t i = 0; i < backend->pool.max_size; i++) {
cocoon_pooled_conn_t *pc = &backend->pool.conns[i]; cocoon_pooled_conn_t *pc = &backend->pool.conns[i];
if (pc->fd != COCOON_INVALID_SOCKET) { if (pc->fd != COCOON_INVALID_SOCKET) {
cocoon_socket_close(pc->fd); cocoon_socket_close(pc->fd);
@ -74,7 +81,7 @@ bool proxy_pool_acquire(cocoon_proxy_backend_t *backend, cocoon_socket_t *pfd, p
pthread_mutex_lock(&backend->pool.mutex); pthread_mutex_lock(&backend->pool.mutex);
/* 1. 查找可用空闲连接(未超时) */ /* 1. 查找可用空闲连接(未超时) */
for (size_t i = 0; i < COCOON_MAX_POOL_SIZE; i++) { for (size_t i = 0; i < backend->pool.max_size; i++) {
cocoon_pooled_conn_t *pc = &backend->pool.conns[i]; cocoon_pooled_conn_t *pc = &backend->pool.conns[i];
if (pc->in_use) continue; if (pc->in_use) continue;
if (pc->fd == COCOON_INVALID_SOCKET && !pc->tls_conn) continue; if (pc->fd == COCOON_INVALID_SOCKET && !pc->tls_conn) continue;
@ -141,7 +148,7 @@ void proxy_pool_release(cocoon_proxy_backend_t *backend, cocoon_socket_t fd, pro
pthread_mutex_lock(&backend->pool.mutex); pthread_mutex_lock(&backend->pool.mutex);
/* 先尝试找到这个连接的槽位(如果它原本就在池中) */ /* 先尝试找到这个连接的槽位(如果它原本就在池中) */
for (size_t i = 0; i < COCOON_MAX_POOL_SIZE; i++) { for (size_t i = 0; i < backend->pool.max_size; i++) {
cocoon_pooled_conn_t *pc = &backend->pool.conns[i]; cocoon_pooled_conn_t *pc = &backend->pool.conns[i];
if (pc->in_use && if (pc->in_use &&
((use_https && pc->tls_conn == tls_conn) || ((use_https && pc->tls_conn == tls_conn) ||
@ -155,7 +162,7 @@ void proxy_pool_release(cocoon_proxy_backend_t *backend, cocoon_socket_t fd, pro
} }
/* 如果是新建连接,找一个空槽位放入 */ /* 如果是新建连接,找一个空槽位放入 */
for (size_t i = 0; i < COCOON_MAX_POOL_SIZE; i++) { for (size_t i = 0; i < backend->pool.max_size; i++) {
cocoon_pooled_conn_t *pc = &backend->pool.conns[i]; cocoon_pooled_conn_t *pc = &backend->pool.conns[i];
if (pc->fd == COCOON_INVALID_SOCKET && !pc->tls_conn) { if (pc->fd == COCOON_INVALID_SOCKET && !pc->tls_conn) {
pc->fd = fd; pc->fd = fd;
@ -179,7 +186,7 @@ void proxy_pool_release(cocoon_proxy_backend_t *backend, cocoon_socket_t fd, pro
} }
} }
static void parse_backend_url(const char *target_url, cocoon_proxy_backend_t *backend) { static void parse_backend_url(const char *target_url, cocoon_proxy_backend_t *backend, size_t pool_size) {
/* 解析目标URL */ /* 解析目标URL */
bool https = false; bool https = false;
const char *url = target_url; const char *url = target_url;
@ -237,10 +244,10 @@ static void parse_backend_url(const char *target_url, cocoon_proxy_backend_t *ba
} }
/* 初始化连接池 */ /* 初始化连接池 */
proxy_pool_init(backend); proxy_pool_init(backend, pool_size);
} }
bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *target_url) { bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *target_url, size_t pool_size) {
if (!prefix || !target_url || prefix[0] == '\0' || target_url[0] == '\0') { if (!prefix || !target_url || prefix[0] == '\0' || target_url[0] == '\0') {
return false; return false;
} }
@ -261,7 +268,7 @@ bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *
return false; return false;
} }
cocoon_proxy_backend_t *backend = &rule->backends[rule->backend_count]; cocoon_proxy_backend_t *backend = &rule->backends[rule->backend_count];
parse_backend_url(target_url, backend); parse_backend_url(target_url, backend, pool_size);
backend_init_health(backend); backend_init_health(backend);
log_info("追加后端: %s -> %s://%s:%d%s", log_info("追加后端: %s -> %s://%s:%d%s",
rule->path_prefix, rule->path_prefix,
@ -286,7 +293,7 @@ bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *
rule->current_index = 0; rule->current_index = 0;
cocoon_proxy_backend_t *backend = &rule->backends[rule->backend_count]; cocoon_proxy_backend_t *backend = &rule->backends[rule->backend_count];
parse_backend_url(target_url, backend); parse_backend_url(target_url, backend, pool_size);
backend_init_health(backend); backend_init_health(backend);
rule->backend_count++; rule->backend_count++;

13
proxy.h
View File

@ -17,7 +17,9 @@
#include <pthread.h> #include <pthread.h>
#include <time.h> #include <time.h>
#define COCOON_MAX_POOL_SIZE 4 /* === 连接池可配置 === */
#define COCOON_POOL_MAX_CAPACITY 16 /**< 连接池最大容量上限 */
#define COCOON_POOL_DEFAULT_SIZE 4 /**< 默认连接池大小 */
#define COCOON_POOL_IDLE_TIMEOUT_MS 30000 #define COCOON_POOL_IDLE_TIMEOUT_MS 30000
#define COCOON_MAX_PROXY_BACKENDS 8 #define COCOON_MAX_PROXY_BACKENDS 8
@ -38,8 +40,9 @@ typedef struct {
* cocoon_conn_pool_t - * cocoon_conn_pool_t -
*/ */
typedef struct { typedef struct {
cocoon_pooled_conn_t conns[COCOON_MAX_POOL_SIZE]; /**< 空闲连接数组 */ cocoon_pooled_conn_t conns[COCOON_POOL_MAX_CAPACITY]; /**< 空闲连接数组上限16 */
pthread_mutex_t mutex; /**< 保护锁 */ size_t max_size; /**< 实际配置大小默认4 */
pthread_mutex_t mutex; /**< 保护锁 */
} cocoon_conn_pool_t; } cocoon_conn_pool_t;
/** /**
@ -90,7 +93,7 @@ void proxy_init(cocoon_proxy_config_t *cfg);
* @param target_url URL "http://localhost:3000" * @param target_url URL "http://localhost:3000"
* @return true * @return true
*/ */
bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *target_url); bool proxy_add_rule(cocoon_proxy_config_t *cfg, const char *prefix, const char *target_url, size_t pool_size);
/** /**
* proxy_match - * proxy_match -
@ -129,7 +132,7 @@ bool proxy_forward(cocoon_socket_t client_fd, const http_request_t *req,
/** /**
* proxy_pool_init - * proxy_pool_init -
*/ */
void proxy_pool_init(cocoon_proxy_backend_t *backend); void proxy_pool_init(cocoon_proxy_backend_t *backend, size_t max_pool_size);
/** /**
* proxy_pool_destroy - * proxy_pool_destroy -

View File

@ -1265,7 +1265,7 @@ server_context_t *server_create(const cocoon_config_t *config) {
/* 初始化反向代理配置 */ /* 初始化反向代理配置 */
proxy_init(&ctx->proxy_config); proxy_init(&ctx->proxy_config);
for (size_t i = 0; i < ctx->config.num_proxies; i++) { for (size_t i = 0; i < ctx->config.num_proxies; i++) {
proxy_add_rule(&ctx->proxy_config, ctx->config.proxies[i].prefix, ctx->config.proxies[i].target); proxy_add_rule(&ctx->proxy_config, ctx->config.proxies[i].prefix, ctx->config.proxies[i].target, ctx->config.proxies[i].pool_size);
} }
/* 加载插件 */ /* 加载插件 */