Sky9

balancer_by_lua analysis

This project is maintained by wangfakang

balancer_by_lua的开发

该模块基本思想:
  就是完全绕过了之前的upstream,自己设置相应的回调

该模块的运用:
  动态管理nginx的upstream方案如下:
  这里我感觉玩法还是蛮多的,其一就是把相应的后端server信息进行存放在第三方库中[redis等],然后相应 的信息加载到share memory进行缓存,然后可以在location中为每一个请求进行设置当前请求的server信息 [可以记录在ngx.ctx中],然后在balancer_by_lua中进行读取ngx.ctx进行调用set_current_peer进行相 应的后端server的路由.

下面就相应的源码进行简单解析下:

这是其balancer的路由数据结构:

+typedef struct {
+    /* the round robin data must be first */
+    ngx_http_upstream_rr_peer_data_t    rrp;//复用了round_robin的数据结构,当后面没有设置sockaddr的时候就会调用round_robin进行路由
+
+    ngx_http_lua_srv_conf_t            *conf;
+    ngx_http_request_t                 *request;
+
+    ngx_event_get_peer_pt               get_rr_peer;
+
+    ngx_uint_t                          more_tries;
+    ngx_uint_t                          total_tries;//当前总的重试次数
+
+    struct sockaddr                    *sockaddr;
+    socklen_t                           socklen;
+
+    ngx_str_t                           host;
+    in_port_t                           port;
+
+    int                                 last_peer_state;
+} ngx_http_lua_balancer_peer_data_t;
+
balancer_by_lua的初始化函数.在配置文件解析的时候执行
char *
+ngx_http_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf)
+{
+ .........
+    value = cf->args->elts;
+
+    lscf->balancer.handler = (ngx_http_lua_srv_conf_handler_pt) cmd->post;
+
+    if (cmd->post == ngx_http_lua_balancer_handler_file) {
+        /* Lua code in an external file */
+
+        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
+                                        value[1].len);
+        if (name == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        lscf->balancer.src.data = name;
+        lscf->balancer.src.len = ngx_strlen(name);
+
+        p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
+        if (p == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        lscf->balancer.src_key = p;
+
+        p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
+        p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
+        *p = '\0';
+
+    } else {
+        /* inlined Lua code */
+
+        lscf->balancer.src = value[1];
+
+        p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
+        if (p == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        lscf->balancer.src_key = p;
+
+        p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
+        p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
+        *p = '\0';
+    }
+
+    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+    if (uscf->peer.init_upstream) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "load balancing method redefined");
+    }
+    //主要就是这个回调的设置 这个主要就是在init_upstream的时候进行判断 然后进行执行[解析upstream模块的时候]
+    uscf->peer.init_upstream = ngx_http_lua_balancer_init;
+
+    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+                  |NGX_HTTP_UPSTREAM_WEIGHT
+                  |NGX_HTTP_UPSTREAM_MAX_FAILS
+                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+                  |NGX_HTTP_UPSTREAM_DOWN;
+
+    return NGX_CONF_OK;
+}
+

在配置文件解析饿时候执行,也就是上面设置饿回调[upstream解析的时候]

static ngx_int_t
+ngx_http_lua_balancer_init(ngx_conf_t *cf,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /* this callback is called upon individual requests */
+    us->peer.init = ngx_http_lua_balancer_init_peer;//每一个请求的初始化函数
+
+    return NGX_OK;
+}

每一个请求都会执行的,主要就是设置相应的回调

+static ngx_int_t
+ngx_http_lua_balancer_init_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    ngx_http_lua_srv_conf_t            *bcf;
+    ngx_http_lua_balancer_peer_data_t  *bp;
+    //给自己的轮子造空间
+    bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t));
+    if (bp == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.data = &bp->rrp;
+
+    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {//默认的round_robin的初始化
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.get = ngx_http_lua_balancer_get_peer;//设置每一个请求获取后端server的方法
+    r->upstream->peer.free = ngx_http_lua_balancer_free_peer;//设置每一个请求获取后端server执行玩的方法
+
+    bcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);
+
+    bp->conf = bcf;
+    bp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;/默认的round_robin方式获取
+    bp->request = r;
+
+    return NGX_OK;
+}
+

这个函数就是每个请求的回调钩子---如何获取后端的server

static ngx_int_t
+ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data)
+{
+    lua_State                          *L;
+    ngx_int_t                           rc;
+    ngx_http_request_t                 *r;
+    ngx_http_lua_ctx_t                 *ctx;
+    ngx_http_lua_srv_conf_t            *lscf;
+    ngx_http_lua_balancer_peer_data_t  *bp = data;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                   "lua balancer peer, try: %ui", pc->tries);
+
+    lscf = bp->conf;
+
+    r = bp->request;
+
+    ngx_http_lua_assert(lscf->balancer.handler && r);
+
+    L = ngx_http_lua_get_lua_vm(r, NULL);
+
+    bp->sockaddr = NULL;
+    bp->socklen = 0;
+    bp->more_tries = 0;
+    bp->total_tries++;
+
+    rc = lscf->balancer.handler(r, lscf, L);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
+
+    if (ctx->exited && ctx->exit_code != NGX_OK) {
+        rc = ctx->exit_code;
+        if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+            return rc;
+        }
+
+        if (rc > NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (bp->sockaddr && bp->socklen) { //如果设置了[执行了set_current_peer]就会直接选择
+        pc->sockaddr = bp->sockaddr;
+        pc->socklen = bp->socklen;
+        pc->name = &bp->host;
+        bp->rrp.peers->single = 0;
+
+        if (bp->more_tries) {
+            r->upstream->peer.tries += bp->more_tries;
+        }
+
+        dd("tries: %d", (int) r->upstream->peer.tries);
+
+        return NGX_OK;
+    }
+
+    return bp->get_rr_peer(pc, &bp->rrp);//没有设置的话就采用round_robin的方式来获取
+}
+

欢迎一起交流学习

在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流

Thx

Author