千家信息网

nginx如何动态修改upstream ngx_http_dyups_module

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,小编给大家分享一下nginx如何动态修改upstream ngx_http_dyups_module,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面
千家信息网最后更新 2026年10月31日nginx如何动态修改upstream ngx_http_dyups_module

小编给大家分享一下nginx如何动态修改upstream ngx_http_dyups_module,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

简介

nginx 动态修改upstream不reload nginx模块,ngx_http_dyups_module分析。

主要围绕https://github.com/yzprofile/ngx_http_dyups_module/blob/master/ngx_http_dyups_module.c进行分析记录下来。

开整......

在create_main_conf的时候初始化这个数组

static void * ngx_http_dyups_create_main_conf(ngx_conf_t *cf)

{

...

if (ngx_array_init(&dmcf->dy_upstreams, cf->pool, 1024, sizeof(ngx_http_dyups_srv_conf_t)) != NGX_OK)

{

return NULL;

}

...

}

ngx_http_dyups_init

static ngx_http_module_t ngx_http_dyups_module_ctx = {

ngx_http_dyups_pre_conf, /* preconfiguration */

ngx_http_dyups_init, /* postconfiguration */

ngx_http_dyups_create_main_conf, /* create main configuration */

ngx_http_dyups_init_main_conf, /* init main configuration */

ngx_http_dyups_create_srv_conf, /* create server configuration */

NULL, /* merge server configuration */

NULL, /* create location configuration */

NULL /* merge location configuration */

};

在dyups init的时把upstream中的conf取出来放进去。

初始化dy_upstream链以及全局ngx_http_dyups_deleted_upstream。

static ngx_int_t ngx_http_dyups_init(ngx_conf_t *cf)

{

...

dmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_dyups_module);

umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);

uscfp = umcf->upstreams.elts;

for (i = 0; i < umcf->upstreams.nelts; i++) {

duscf = ngx_array_push(&dmcf->dy_upstreams);

// 清零

ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));

duscf->pool = NULL;

// 赋值

duscf->upstream = uscfp[i];

duscf->dynamic = (uscfp[i]->port == 0

&& uscfp[i]->srv_conf && uscfp[i]->servers

&& uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE);

duscf->deleted = 0;

// 赋值index

duscf->idx = i;

}

...

}

dyups share memory同步机制

shm初始化是在ngx_http_dyups_init_main_conf函数中实现的,同时设置了read_mesg的超时时间,并且指定了大小。

static char *ngx_http_dyups_init_main_conf(ngx_conf_t *cf, void *conf)

{

...

if (dmcf->read_msg_timeout == NGX_CONF_UNSET_MSEC) {

// 一秒一次

dmcf->read_msg_timeout = 1000;

}

if (dmcf->shm_size == NGX_CONF_UNSET_UINT) {

dmcf->shm_size = 2 * 1024 * 1024;

}

return ngx_http_dyups_init_shm(cf, conf);

...

}

static char *ngx_http_dyups_init_shm(ngx_conf_t *cf, void *conf)

{

...

shm_zone = ngx_shared_memory_add(cf, &dmcf->shm_name, dmcf->shm_size,

&ngx_http_dyups_module);

shm_zone->data = cf->pool;

// 加进去的这个名头的共享内存块的init函数会在初始化的时候统一调用

shm_zone->init = ngx_http_dyups_init_shm_zone;

...

}

static ngx_int_t ngx_http_dyups_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)

{

...

shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

sh = ngx_slab_alloc(shpool, sizeof(ngx_dyups_shctx_t));

if (sh == NULL) {

return NGX_ERROR;

}

// 全局变量,sh和shpool

ngx_dyups_global_ctx.sh = sh;

ngx_dyups_global_ctx.shpool = shpool;

// 初始化msg->queue

ngx_queue_init(&sh->msg_queue);

sh->version = 0;

sh->status = NULL;

...

}

ngx_http_dyups_init_process

该函数在启动进程时候调用,设定了一些定时器。

初始化共享内存,判断如果是非正常退出的,那么重新加载upstream配置。

static ngx_int_t ngx_http_dyups_init_process(ngx_cycle_t *cycle)

{

...

// 设定定时器来定时read msg,同步信息

timer = &ngx_dyups_global_ctx.msg_timer;

timer->handler = ngx_http_dyups_read_msg;

ngx_add_timer(timer, dmcf->read_msg_timeout);

// 拿到全局的pool和sh

shpool = ngx_dyups_global_ctx.shpool;

sh = ngx_dyups_global_ctx.sh;

ngx_shmtx_lock(&shpool->mutex);

// 初始化的时候肯定是NULL,,申请对应数量进程数的内存

if (sh->status == NULL) {

sh->status = ngx_slab_alloc_locked(shpool,

sizeof(ngx_dyups_status_t) * ccf->worker_processes);

if (sh->status == NULL) {

ngx_shmtx_unlock(&shpool->mutex);

return NGX_ERROR;

}

ngx_memzero(sh->status,

sizeof(ngx_dyups_status_t) * ccf->worker_processes);

ngx_shmtx_unlock(&shpool->mutex);

return NGX_OK;

}

ngx_shmtx_unlock(&shpool->mutex);

// 判断version,如果不是0的话,说明version已经在同步中被++了,所以是进程挂掉再被拉起来

if (sh->version != 0) {

//...

}

最核心的是ngx_http_dyups_read_msg函数,里面的是ngx_http_dyups_read_msg_locked函数

static void ngx_http_dyups_read_msg_locked(ngx_event_t *ev)

{

...

sh = ngx_dyups_global_ctx.sh;

shpool = ngx_dyups_global_ctx.shpool;

for (i = 0; i < ccf->worker_processes; i++) {

status = &sh->status[i];

if (status->pid == 0 || status->pid == ngx_pid) {

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,

"[dyups] process %P update time %ui",

status->pid, status->time);

// 遍历全部进程,将对应的pid赋值

status->pid = ngx_pid;

status->time = now;

break;

}

}

// 遍历消息队列

for (q = ngx_queue_last(&sh->msg_queue);

q != ngx_queue_sentinel(&sh->msg_queue);

q = ngx_queue_prev(q))

{

// 如果该msg的count和进程数一致,就是大家都同步过了,把这个msg删掉

if (msg->count == ccf->worker_processes) {

t = ngx_queue_next(q); ngx_queue_remove(q); q = t;

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, "[dyups] destroy msg %V:%V",&msg->name, &msg->content);

ngx_dyups_destroy_msg(shpool, msg);

continue;

}

found = 0;

for (i = 0; i < msg->count; i++) {

ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,"[dyups] msg pids [%P]", msg->pid[i]);

if (msg->pid[i] == ngx_pid) {

found = 1;

break;

}

}

// 如果发现该进程了,就说明已经同步过了。

if (found) {

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, "[dyups] msg %V count %ui found", &msg->name, msg->count);

continue;

}

// 如果没发现的话,count++,pid更新

msg->pid[i] = ngx_pid;

msg->count++;

ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, "[dyups] msg %V count %ui", &msg->name, msg->count);

// 取出来name和content

name = msg->name;

content = msg->content;

// 执行同步

rc = ngx_dyups_sync_cmd(pool, &name, &content, msg->flag);

if (rc != NGX_OK) {

ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "[dyups] read msg error, may cause the " "config inaccuracy,name:%V, content:%V",&name, &content);

}

}

...

}

static ngx_int_t ngx_dyups_sync_cmd(ngx_pool_t *pool, ngx_str_t *name, ngx_str_t *content, ngx_uint_t flag)

{

...

} else if (flag == NGX_DYUPS_ADD) {

body.start = body.pos = content->data;

body.end = body.last = content->data + content->len;

body.temporary = 1;

rc = ngx_dyups_do_update(name, &body, &rv);

ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "[dyups] sync add: %V rv: %V rc: %i", name, &rv, rc);

if (rc != NGX_HTTP_OK) {

return NGX_ERROR;

}

return NGX_OK;

}

...

}

同步其他进程接受的信息,如果是当前进程处理的就要把信息添加到消息队列中。


ngx_dyups_update_upstream

ngx_int_t ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)

{

...

ngx_http_dyups_read_msg_locked(timer);

// 沙箱测试配置

status = ngx_dyups_sandbox_update(buf, rv);

if (status != NGX_HTTP_OK) {

goto finish;

}

status = ngx_dyups_do_update(name, buf, rv);

if (status == NGX_HTTP_OK) {

//把操作发到队列中去

if (ngx_http_dyups_send_msg(name, buf, NGX_DYUPS_ADD)) {

ngx_str_set(rv, "alert: update success "

"but not sync to other process");

status = NGX_HTTP_INTERNAL_SERVER_ERROR;

}

}

...

}

ngx_http_dyups_send_msg函数分析

static ngx_int_t ngx_http_dyups_send_msg(ngx_str_t *name, ngx_buf_t *body, ngx_uint_t flag)

{

...

// 初始化整个msg,将name和body填充进去

sh->version++;

ngx_queue_insert_head(&sh->msg_queue, &msg->queue);

...

}

ngx_dyups_do_update

在update之前先find寻找对应的upstream。

static ngx_http_dyups_srv_conf_t * ngx_dyups_find_upstream(ngx_str_t *name, ngx_int_t *idx)

{

...

duscfs = dumcf->dy_upstreams.elts;

for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {

duscf = &duscfs[i];

uscf = duscf->upstream;

if (uscf->host.len != name->len

|| ngx_strncasecmp(uscf->host.data, name->data, uscf->host.len)

!= 0)

{

continue;

}

*idx = i;

return duscf;

}

...

}

如果寻找到了idx赋值。

一旦发现寻找到了对应name的dy_upstream就先判断。

然后调用的是ngx_dyups_mark_upstream_delete函数

static void ngx_dyups_mark_upstream_delete(ngx_http_dyups_srv_conf_t *duscf)

{

...

// 获取umcf和uscf

uscf = duscf->upstream;

umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,

ngx_http_upstream_module);

// us获取这个dynamic upstream下的servers

us = uscf->servers->elts;

for (i = 0; i < uscf->servers->nelts; i++) {

// 标志位置1

us[i].down = 1;

#if (NGX_HTTP_UPSTREAM_CHECK)

if (us[i].addrs) {

// 关闭peer,看宏定义主要关闭健康检查的peer

ngx_http_upstream_check_delete_dynamic_peer(&uscf->host,

us[i].addrs);

}

#endif

}

// 将upstream对应的index的配置变成一个dummy配置

uscfp[duscf->idx] = &ngx_http_dyups_deleted_upstream;

#if (NGX_HTTP_UPSTREAM_RBTREE)

ngx_rbtree_delete(&umcf->rbtree, &uscf->node);

#endif

duscf->deleted = NGX_DYUPS_DELETING;

...

}

其中最重要的是check_delete_dynamic_peer

void ngx_http_upstream_check_delete_dynamic_peer(ngx_str_t *name,ngx_addr_t *peer_addr)

{

...

/* 一堆比较 找到choosen*/

chosen = &peer[i];

chosen->shm->ref--;

if (chosen->shm->ref <= 0 && chosen->shm->delete != PEER_DELETED) {

ngx_http_upstream_check_clear_dynamic_peer_shm(chosen->shm);

chosen->shm->delete = PEER_DELETED;

}

ngx_shmtx_unlock(&chosen->shm->mutex);

ngx_http_upstream_check_clear_peer(chosen);

...

}

这样子删完一次之后,再find一次,idx大概率就变成-1了,就可以进行创建

static ngx_int_t ngx_dyups_do_update(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)

{

...

if (idx == -1) {

duscf = ngx_array_push(&dumcf->dy_upstreams);

uscfp = ngx_array_push(&umcf->upstreams);

ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));

// 这里为了获取在umcf中的新upstream的index值。

idx = umcf->upstreams.nelts - 1;

}

duscf->idx = idx;

rc = ngx_dyups_init_upstream(duscf, name, idx);

rc = ngx_dyups_add_server(duscf, buf);

...

}

最重要的就是init_upstream和add_server。

首先是init ipstream,他的传参其实是dy_srv_conf_t。upstream的name,以及upstream链表中对应的index

static ngx_int_t ngx_dyups_init_upstream(ngx_http_dyups_srv_conf_t *duscf, ngx_str_t *name, ngx_uint_t index)

{

...

umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,

ngx_http_upstream_module);

uscfp = umcf->upstreams.elts;

/*初始化uscf 也就是upstream的各个结构体*/

uscfp[index] = uscf; // 赋值

duscf->dynamic = 1;

duscf->upstream = uscf;

ctx = ngx_pcalloc(duscf->pool, sizeof(ngx_http_conf_ctx_t));

// 存放ctx

duscf->ctx = ctx;

// insert进去uscf

uscf->node.key = ngx_crc32_short(uscf->host.data, uscf->host.len);

ngx_rbtree_insert(&umcf->rbtree, &uscf->node);

...

}

static ngx_int_t ngx_dyups_add_server(ngx_http_dyups_srv_conf_t *duscf, ngx_buf_t *buf)

{

...

ngx_dyups_parse_upstream(&cf, buf)

...

}

static char * ngx_dyups_parse_upstream(ngx_conf_t *cf, ngx_buf_t *buf)

{

...

b = *buf;

ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));

conf_file.file.fd = NGX_INVALID_FILE;

conf_file.buffer = &b;

cf->conf_file = &conf_file;

return ngx_conf_parse(cf, NULL);

...

}

ngx_dyups_do_delete

static ngx_int_t ngx_dyups_do_delete(ngx_str_t *name, ngx_str_t *rv)

{

...

duscf = ngx_dyups_find_upstream(name, &dumy);

// 如果查出来的是NULL,或者是一个已经被标记删除,或者彻底删除的,就说明要删这个有异常

if (duscf == NULL || duscf->deleted) {

ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, "[dyups] not find upstream %V %p", name, duscf);

ngx_str_set(rv, "not found uptream");

return NGX_HTTP_NOT_FOUND;

}

// 没问题的话就执行正常删除

ngx_dyups_mark_upstream_delete(duscf);

...

}

ngx_dyups_find_upstream

find upstream做了很多事还做了一部分的删除操作。

static ngx_http_dyups_srv_conf_t * ngx_dyups_find_upstream(ngx_str_t *name, ngx_int_t *idx)

{

...

dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_dyups_module);

duscfs = dumcf->dy_upstreams.elts;

for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {

// 这里是在mark_upstream中被标记的

if (duscf->deleted == NGX_DYUPS_DELETING) {

// 确认可以删除,主要看这个ref的引用计数

if (*(duscf->ref) == 0) {

ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "[dyups] free dynamic upstream in find upstream" " %ui", duscf->idx);

duscf->deleted = NGX_DYUPS_DELETED;

if (duscf->pool) {

ngx_destroy_pool(duscf->pool);

duscf->pool = NULL;

}

}

}

// 如果是deleted或者是deleting,算是没有找到,除非遍历完没找到返回一个deleted。

if (duscf->deleted == NGX_DYUPS_DELETING) {

continue;

}

if (duscf->deleted == NGX_DYUPS_DELETED) {

*idx = i;

duscf_del = duscf;

continue;

}

// 如果找到了就正常返回

if (uscf->host.len != name->len

|| ngx_strncasecmp(uscf->host.data, name->data, uscf->host.len)

!= 0)

{

continue;

}

*idx = i;

return duscf;

}

...

}

引用计数ref

dyups的peer init get和free函数。

static ngx_int_t ngx_http_dyups_init_peer(ngx_http_request_t *r,

ngx_http_upstream_srv_conf_t *us)

{

...

// 设置上下文

ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dyups_ctx_t));

if (ctx == NULL) {

return NGX_ERROR;

}

// scf指向对应的dscf,ctx的data指向了自己

ctx->scf = dscf;

ctx->data = r->upstream->peer.data;

ctx->get = r->upstream->peer.get;

ctx->free = r->upstream->peer.free;

r->upstream->peer.data = ctx;

r->upstream->peer.get = ngx_http_dyups_get_peer;

r->upstream->peer.free = ngx_http_dyups_free_peer;

// 与客户端的连接的这个pool注册一个销毁函数

cln = ngx_pool_cleanup_add(r->pool, 0);

if (cln == NULL) {

return NGX_ERROR;

}

// 引用计数加一

dscf->ref++;

// 等调用这个就会将ref--。

cln->handler = ngx_http_dyups_clean_request;

cln->data = &dscf->ref;

...

}

// 这个函数是在ngx_dyups_add_server初始化upstream中被赋值的

uscf->peer.init = ngx_http_dyups_init_peer;

// 调用的位置是在这里

static void ngx_http_upstream_init_request(ngx_http_request_t *r)

{

...

if (uscf->peer.init(r, uscf) != NGX_OK) {

ngx_http_upstream_finalize_request(r, u,

NGX_HTTP_INTERNAL_SERVER_ERROR);

return;

}

ngx_http_upstream_connect(r, u);

...

}

static ngx_int_t ngx_http_dyups_get_peer(ngx_peer_connection_t *pc, void *data)

{

ngx_http_dyups_ctx_t *ctx = data;

// 就用之前的peer get来。

return ctx->get(pc, ctx->data);

}

static void ngx_http_dyups_free_peer(ngx_peer_connection_t *pc, void *data,

ngx_uint_t state)

{

ngx_http_dyups_ctx_t *ctx = data;

ngx_pool_cleanup_t *cln;

/* upstream connect failed */

if (pc->connection == NULL) {

goto done;

}

if (pc->cached) {

goto done;

}

// free的时候先给ref++,等调用handler后ref--

ctx->scf->ref++;

// pool设置一个销毁pool的数据结构,赋值给pool->cleanup

cln = ngx_pool_cleanup_add(pc->connection->pool, 0);

if (cln == NULL) {

ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "[dyups] dynamic upstream free peer may cause memleak %i",ctx->scf->ref);

goto done;

}

// 销毁的递归函数

cln->handler = ngx_http_dyups_clean_request;

cln->data = &ctx->scf->ref;

done:

// 结束后调用之前保存的free

ctx->free(pc, ctx->data, state);

}

static void ngx_http_dyups_clean_request(void *data)

{

ngx_uint_t *ref = data;

(*ref)--;

ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "[dyups] http clean request count %i", *ref);

}

pool在被destroy的时候,会调用这个handler将引用计数减掉。

void ngx_destroy_pool(ngx_pool_t *pool)

{

ngx_pool_t *p, *n;

ngx_pool_large_t *l;

ngx_pool_cleanup_t *c;

for (c = pool->cleanup; c; c = c->next) {

if (c->handler) {

ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

"run cleanup: %p", c);

c->handler(c->data);

}

}

...

}

以上是"nginx如何动态修改upstream ngx_http_dyups_module"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

函数 进程 同步 时候 是在 配置 动态 信息 全局 内存 篇文章 队列 分析 重要 位置 内容 定时器 就是 指向 标记 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广州淘业互联网络科技 科技和互联网公司 新华互联网科技学院 互联网科技有限公司图片 互联网科技金融公司 奇酷互联网科技 互联网金融科技平台 互联网科技小额贷款 撒么互联网科技公司 广州恒生互联网科技有限 福建科技产业互联网发展 河南猎农互联网科技有限公司 呼和浩特新华互联网科技学院 互联网科技的全部知识 国大互联网教育科技有限公司薪资 沐思想互联网科技有限公司 广州百诚互联网科技有限公司官网 吉林省码上互联网科技电话 互联网科技机房 银川新华互联网科技学校招聘 上海卓越科技互联网 互联网科技logo 广州善班互联网科技 科技大学互联网专业好不好 顶级互联网科技成果有哪些 消费医药和互联网科技 珠海东科互联网科技有限公司 云南互联网科技学费 杭州融税互联网科技有限公司 互联网科技创业计划的风险分析
0