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的方式来获取
+}
+
在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流