`
奔跑的羚羊
  • 浏览: 576237 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

nginx模块开发入门(十) -5 Load-balancers

 
阅读更多
5. Load-Balancers

    Load-balancer用来决定哪一个后端将会收到请求;具体的实现是round-robin方式或者把请求进行hash。本节将介绍load-balancer模块的装载及其调用。我们将用upstream_hash_module(full source)作例子。upstream_hash将对nginx.conf里配置的变量进行 hash,来选择后端服务器。

    一个load-balancer分为六个部分:

   1. 启用配置指令 (e.g, hash;) 将会调用注册函数
   2. 注册函数将定义一些合法的server 参数 (e.g., weight=) 并注册一个 upstream初始化函数
   3. upstream初始化函数将在配置经过验证后被调用,并且:
          * 解析 server 名称为特定的IP地址
          * 为每个sokcet连接分配空间
          * 设置peer(对端)初始化函数的回调入口
   4. peer(对端)初始化函数将在每次请求时被调用一次,它主要负责设置一些负载均衡函数将会使用的数据结构。
   5. 负载均衡函数决定把请求分发到哪里;每个请求将至少调用一次这个函数(如果后端服务器失败了,那就是多次了),有意思的事情就是在这里做的。
   6. 最后,peer(对端)释放函数 可以在与对应的后端服务器结束通信之后更新统计信息 (成功或失败)

    好像很多嘛,我来逐一讲讲。

5.1. 启用指令
The enabling directive


    指令声明,既确定了他们在哪里生效又确定了一旦流程遇到指令将要调用什么函数。load-balancer的指令需要置NGX_HTTP_UPS_CONF标志位,一遍让Nginx知道这个指令只会在upstream块中有效。同时它需要提供一个指向注册函数的指针。下面列出的是upstream_hash模块的指令声明:
    { ngx_string("hash"),
      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
      ngx_http_upstream_hash,
      0,
      0,
      NULL },


    都是些很眼熟的东西。

5.2. 注册函数
The registration function


    上面的回调函数ngx_http_upstream_hash就是所谓的注册函数。之所以这样叫(我起得名字)是因为它注册了把upstream初始化函数和周边的upstream配置注册到了一块。另外,注册函数还定义了特定upstream块中的server指令的一些选项(如weight=, fail_timeout=),下面是upstream_hash模块的注册函数:
ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
    ngx_http_upstream_srv_conf_t  *uscf;
    ngx_http_script_compile_t      sc;
    ngx_str_t                     *value;
    ngx_array_t                   *vars_lengths, *vars_values;

    value = cf->args->elts;

    /* the following is necessary to evaluate the argument to "hash" as a $variable */
    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

    vars_lengths = NULL;
    vars_values = NULL;

    sc.cf = cf;
    sc.source = &value[1];
    sc.lengths = &vars_lengths;
    sc.values = &vars_values;
    sc.complete_lengths = 1;
    sc.complete_values = 1;

    if (ngx_http_script_compile(&sc) != NGX_OK) {
        return NGX_CONF_ERROR;
    }
    /* end of $variable stuff */

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

    /* the upstream initialization function */
    uscf->peer.init_upstream = ngx_http_upstream_init_hash;

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE;

    /* OK, more $variable stuff */
    uscf->values = vars_values->elts;
    uscf->lengths = vars_lengths->elts;

    /* set a default value for "hash_method" */
    if (uscf->hash_function == NULL) {
        uscf->hash_function = ngx_hash_key;
    }

    return NGX_CONF_OK;
 }


    除了依葫芦画瓢的用来计算$variable的代码,剩下的都很简单,就是分配一个回调函数,设置一些标志位。哪些标志位是有效的呢?

    * NGX_HTTP_UPSTREAM_CREATE: 让upstream块中有 server 指令。我实在想不出那种情形会用不到它。
    * NGX_HTTP_UPSTREAM_WEIGHT: 让server指令获取选项 weight=
    * NGX_HTTP_UPSTREAM_MAX_FAILS: 允许选项max_fails=
    * NGX_HTTP_UPSTREAM_FAIL_TIMEOUT: 允许选项fail_timeout=
    * NGX_HTTP_UPSTREAM_DOWN: 允许选项 down
    * NGX_HTTP_UPSTREAM_BACKUP: 允许选项backup

    每一个模块都可以访问这些配置值。一切都取决于模块自己的决定 。也就是说,max_fails不会被自动强制执行;所有的失败逻辑都是由模块作者决定的。过会我们再说这个。目前,我们还没有完成对回调函数的追踪呢。接下来,我们来看upstream初始化函数 (上面的函数中的回调函数init_upstream )。


5.3. upstream 初始化函数
The upstream initialization function


在刚刚上一面的方法ngx_http_upstream_hash中调用ngx_http_upstream_init_hash
    /* the upstream initialization function */
    uscf->peer.init_upstream = ngx_http_upstream_init_hash;


    upstream 初始化函数的目的是,解析主机名,为socket分配空间,分配(另一个)回调函数。下面是upstream_hash:
ngx_int_t
ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
    ngx_uint_t                       i, j, n;
    ngx_http_upstream_server_t      *server;
    ngx_http_upstream_hash_peers_t  *peers;

    /* set the callback */
    us->peer.init = ngx_http_upstream_init_upstream_hash_peer;

    if (!us->servers) {
        return NGX_ERROR;
    }

    server = us->servers->elts;

    /* figure out how many IP addresses are in this upstream block. */
    /* remember a domain name can resolve to multiple IP addresses. */
    for (n = 0, i = 0; i < us->servers->nelts; i++) {
        n += server[i].naddrs;
    }

    /* allocate space for sockets, etc */
    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_peers_t)
            + sizeof(ngx_peer_addr_t) * (n - 1));

    if (peers == NULL) {
        return NGX_ERROR;
    }

    peers->number = n;

    /* one port/IP address per peer */
    for (n = 0, i = 0; i < us->servers->nelts; i++) {
        for (j = 0; j < server[i].naddrs; j++, n++) {
            peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
            peers->peer[n].socklen = server[i].addrs[j].socklen;
            peers->peer[n].name = server[i].addrs[j].name;
        }
    }

    /* save a pointer to our peers for later */
    us->peer.data = peers;

    return NGX_OK;
}


    这个函数包含的东西貌似比我们期望的多些。大部分的工作貌似都该被抽象出来,但事实却不是,我们只能忍受这一点。倒是有一种简化的策略:调用另一个模块的upstream初始化函数,把这些脏活累活(peer的分配等等)都让它干了,然后再覆盖其us->peer.init这个回调函数。例子可以参见http/modules/ngx_http_upstream_ip_hash_module.c

    在我们这个观点中的关键点是设置peer初始化函数的指向,在我们这个例子里是ngx_http_upstream_init_upstream_hash_peer

5.4. peer初始化函数
The peer initialization function


在刚刚上一面的方法ngx_http_upstream_init_hash中调用ngx_http_upstream_init_hash_peer
    /* set the callback */
    us->peer.init = ngx_http_upstream_init_upstream_hash_peer;


    peer初始化函数每个请求会被调用一次。它会构造一个数据结构,模块会用这个数据结构来选择合适的后端服务器;这个数据结构保存着和后端交互的重试次数,通过它可以很容易的跟踪链接失败次数或者是计算好的哈希值。这个结构体习惯性地被命名为ngx_http_upstream_<module name>_peer_data_t。

    另外,对端初始化函数还会构建两个回调函数:

    * get: load-balancing 函数
    * free: peer释放函数 (通常只是在连接完成后更新一些统计信息)

    似乎还不止这些,它同时还初始化了一个叫做tries的变量。只要tries是正数,Nginx将继续重试当前的load-banlancer。当tries变为0时,Nginx将放弃重试。一切都取决于getfree 如何设置合适的tries

    下面是upstream_hash中peer初始化函数的例子:
static ngx_int_t
ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_upstream_hash_peer_data_t     *uhpd;
    
    ngx_str_t val;

    /* evaluate the argument to "hash" */
    if (ngx_http_script_run(r, &val, us->lengths, 0, us->values) == NULL) {
        return NGX_ERROR;
    }

    /* data persistent through the request */
    uhpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t)
	    + sizeof(uintptr_t) 
	      * ((ngx_http_upstream_hash_peers_t *)us->peer.data)->number 
                  / (8 * sizeof(uintptr_t)));
    if (uhpd == NULL) {
        return NGX_ERROR;
    }

    /* save our struct for later */
    r->upstream->peer.data = uhpd;

    uhpd->peers = us->peer.data;

    /* set the callbacks and initialize "tries" to "hash_again" + 1*/
    r->upstream->peer.free = ngx_http_upstream_free_hash_peer;
    r->upstream->peer.get = ngx_http_upstream_get_hash_peer;
    r->upstream->peer.tries = us->retries + 1;

    /* do the hash and save the result */
    uhpd->hash = us->hash_function(val.data, val.len);

    return NGX_OK;
}


    看上去不错,我们现在可以来选择一台upstream服务器了。

5.5. 负载均衡函数
The load-balancing function


    主要部分现在才开始。货真价实的哦。模块就是在这里选择upstream服务器的。负载均衡函数的原型看上去是这样的:
static ngx_int_t 
ngx_http_upstream_get_<module_name>_peer(ngx_peer_connection_t *pc, void *data);


    data是我们存放所关注的客户端连接中有用信息的结构体。pc则是要存放我们将要去连接的server的相关信息。负载均衡函数做的事情就是填写pc->sockaddr, pc->socklen, 和 pc->name。如果你懂一点网络编程的话,这些东西应该都比较熟悉了;但实际上他们跟我们手头上的任务来比并不算很重要。我们不关心他们代表什么;我们只想知道从哪里找到合适的值来填写他们。

    这个函数必须找到一个可用server的列表,挑一个分配给pc。我们来看看upstream_hash是怎么做的吧:

    upstream_hash模块之前已经通过调用ngx_http_upstream_init_hash,把server列表存放在了ngx_http_upstream_hash_peer_data_t 这一结构中。这个结构就是现在的data:
ngx_http_upstream_hash_peer_data_t *uhpd = data;


    peer列表现在在uhpd->peers->peer中了。我们通过对哈希值与 server总数取模来从这个数组中取得最终的peer服务器:
ngx_peer_addr_t *peer = &uhpd->peers->peer[uhpd->hash % uhpd->peers->number];



    现在终于大功告成了:
    pc->sockaddr = peer->sockaddr;
    pc->socklen  = peer->socklen;
    pc->name     = &peer->name;

    return NGX_OK;


    就是这样!如果load-balancer模块返回 NGX_OK,则意味着”来吧,上这个 server吧!“。如果返回的是NGX_BUSY,说明所有的后端服务器目前都不可用,此时Nginx应该重试。

    但是……我们怎么记录哪些个服务器不可用了?我们如果不想重试了怎么办?

5.6. peer释放函数
The peer release function


    peer释放函数在upstream连接就绪之后开始运行,它的目的是跟踪失败。函数原型如下:
void 
ngx_http_upstream_free_<module name>_peer(ngx_peer_connection_t *pc, void *data, 
    ngx_uint_t state);


    前两个参数和我们在load-balancer函数中看到的一样。第三个参数是一个state变量,它表明了当前连接是成功还是失败。它可能是NGX_PEER_FAILED (连接失败) 和 NGX_PEER_NEXT (连接失败或者连接成功但程序返回了错误)按位或的结果。如果它是0则代表连接成功。

    这些失败如何处理则由模块的开发者自己定。如果根本不再用,那结果则应存放到data中,这是一个指向每个请求自定义的结构体。

    但是peer释放函数的关键作用是可以设置pc->tries为 0来阻止Nginx在load-balancer模块中重试。最简单的对端释放函数应该是这样的:
pc->tries = 0;


    这样就保证了如果发往后端服务器的请求遇到了错误,客户端将得到一个502 Bad Proxy的错误。

    这里还有一个更为复杂的例子,是从upstream_hash模块中拿来的。如果后端连接失败,它会在位向量 (叫做 tried,一个 uintptr_t类型的数组)中标示失败,然后继续选择一个新的后端服务器直至成功。

#define ngx_bitvector_index(index) index / (8 * sizeof(uintptr_t))
#define ngx_bitvector_bit(index) (uintptr_t) 1 << index % (8 * sizeof(uintptr_t))

static void
ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, void *data,
    ngx_uint_t state)
{
    ngx_http_upstream_hash_peer_data_t  *uhpd = data;
    ngx_uint_t                           current;

    if (state & NGX_PEER_FAILED
            && --pc->tries)
    {
        /* the backend that failed */
        current = uhpd->hash % uhpd->peers->number;

       /* mark it in the bit-vector */
        uhpd->tried[ngx_bitvector_index(current)] |= ngx_bitvector_bit(current);

        do { /* rehash until we're out of retries or we find one that hasn't been tried */
            uhpd->hash = ngx_hash_key((u_char *)&uhpd->hash, sizeof(ngx_uint_t));
            current = uhpd->hash % uhpd->peers->number;
        } while ((uhpd->tried[ngx_bitvector_index(current)] & ngx_bitvector_bit(current)) && --pc->tries);
    }
}


    因为load-balancer函数只会看新的uhpd->hash的值,所以这样是行之有效的。

    许多应用程序不提供重试功能,或者在更高层的逻辑中进行了控制。但其实你也看到了,只需这么几行代码这个功能就可以实现了。
分享到:
评论

相关推荐

    nginx + nginx-http-flv-module-1.2.9

    nginx-http-flv-module 是由 nginx 开发社区创建的一个第三方模块,用于在 Nginx 上实现 HTTP 直播(HTTP Live Streaming,HLS)和FLV格式的视频流。FLV(Flash Video)是 Adobe Flash 平台广泛使用的视频格式,...

    添加nginx-http-flv-module模块并重新编译后的nginx(windows版)

    本资源提供的是一款针对Windows平台的Nginx,其中已经集成了`nginx-http-flv-module`模块,这个模块主要用于支持HTTP实时流(HTTP Live Streaming, HLS)和Flash视频流(Flash Video, FLV)。现在我们将深入探讨这一...

    nginx带nginx-http-flv模块windows编译版rtmp

    Nginx-RTMP是Nginx的一个扩展模块,由Adobe Systems开发,用于支持Real-Time Messaging Protocol (RTMP)。RTMP是一种协议,常用于在线流媒体传输,如视频直播服务。Nginx-RTMP模块允许Nginx接收来自Flash Player或...

    nginx-sticky-module-1.25.zip

    nginx sticky是nginx的module,可以实现基于cookie的负载均衡。 下载后,在编译安装nginx时,用--add-module... ./configure --prefix=/usr/local/nginx-1.6.0 --add-module=../nginx-sticky-module-1.25 --without-...

    win环境使用nginx的nginx-http-flv-module.zip

    在windows 7 64位 环境下使用nginx的nginx-http-flv-module搭建flv视频流播放所有的安装包,参考:https://blog.csdn.net/qq_33071429/article/details/102628008

    nginx-1.19.3_nginx-http-flv-module.rar

    标题中的"nginx-1.19.3_nginx-http-flv-module.rar"表明这是一个关于Nginx服务器的软件包,特别地,它包含了Nginx的1.19.3版本,并且已经集成了`nginx-http-flv-module`模块。这个模块是用于支持HTTP FLV(Flash ...

    nginx上传下载之nginx-upload-module-2.3.0

    cp -r ../nginx-upload-module-2.3.0 nginx-1.21.x/ cd nginx-1.21.x/ ./configure --add-module=../nginx-upload-module-2.3.0 \ --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-pcre make ...

    nginx-rtmp模块源码包nginx-rtmp-module-master

    **Nginx-RTMP 模块详解** Nginx-RTMP 模块是 Nginx 的一个扩展,用于处理 Real-Time Messaging Protocol (RTMP) 流,它允许 Nginx 作为 RTMP 服务器运行,支持直播和点播服务。这个源码包 "nginx-rtmp-module-...

    nginx-http-flv-module(windows版)

    --&gt; nginx-1.21.6 ======================== 在网上查找半天都只有教程,没有可免费下载的版本,深知没有积分遍地找资源的痛苦,无奈之下只好自己按照教程一步一个坑编译出来的,供大家免费下载使用。(无毒放心使用...

    集成了nginx-http-flv-module 1.2.9模块的64位nginx-1.21.4程序

    `nginx-http-flv-module`是Nginx的一个第三方模块,由张洪君开发,用于处理FLV格式的流媒体数据。通过这个模块,Nginx可以支持RTMP协议,实现实时流媒体的推拉流,为Flash Player和其他支持RTMP的客户端提供服务。...

    windows下编译nginx-http-flv-moudle

    3. **nginx-http-flv-module**: 这是Nginx的一个第三方模块,由社区开发,用于支持HTTP FLV直播。它提供了处理RTMP流并将它们转化为适应HTTP的FLV流的能力,让客户端可以无插件播放。 4. **编译过程**: 在Windows上...

    windows平台nginx编译nginx-http-flv-module

    在给定的压缩包文件中,"使用必看.txt"可能包含了编译和使用过程中的注意事项,而"nginx-rtmp.zip"可能是包含了RTMP模块的源代码,这在搭建流媒体服务器时也会用到,因为HTTP FLV Module通常与RTMP模块结合使用,为...

    nginx-upstream-jvm-route 和 nginx 对应版本,亲测可用

    此资源有两个文件,含 nginx-upstream-jvm-route 和 nginx 对应版本,都是tar.gz文件。 安装方法网上很多就不写了,亲测可用。 不用担心版本不匹配造成安装失败,再浪费积分去到处下载尝试的烦恼。 此资源有两个文件...

    nginx-upstream-jvm-route-1.15

    【标题】"nginx-upstream-jvm-route-1.15" 涉及的核心知识点是Nginx的upstream模块与JVM路由的整合,特别针对Nginx 1.15版本。这个项目旨在解决在配置Nginx时遇到的特定错误提示“nginx: [emerg] invalid parameter ...

    nginx-goodies-nginx-sticky-module-ng-08a395c66e42.zip

    Sticky是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route (a)客户端首次发起访问请求,nginx接收后,发现...

    nginx-sticky-module-ng-1.2.6.tar.gz

    在 `nginx-sticky-module-ng-1.2.6` 压缩包中,通常包含以下组件: 1. `src`: 这是源代码目录,包含了模块的核心代码,如 `ngx_http_sticky_module.c`,它是实现会话保持功能的主要源文件。 2. `config`: 配置脚本...

    nginx-http-flv-module(nginx1.19.3&amp;http-flv-module1.2.7&amp;windows)

    1、nginx-http-flv-module(windows可执行程序,含http-flv-module:1.2.7,nginx 1.19.3) 2、不要放置于中文路径下,否则无法启动 3、说明文档,请下载查看。

    nginx-1.19.3-http-flv.zip

    1. 采用nginx最新版编译,包含最新的nginx-http-flv-module,以及基础模块openssl、prce、zlib 2. 整体打包,已配置好nginx.conf的http-flv直播流,以及http web环境。无需任何配置即可使用 3. 自带windows的服务...

    nginx-http-flv-module-1.2.10(包含nginx-rtmp-module)

    而nginx-rtmp-module则是一个额外的Nginx模块,它增加了对Real-Time Messaging Protocol (RTMP)的支持,使Nginx能够作为RTMP服务器接收并分发直播流。 1. **HTTP FLV 模块详解** HTTP FLV模块使得Nginx可以处理FLV...

    (修复bug模块)nginx-http-flv-module-master 支持flv模块直播

    【标题】:“(修复bug模块)nginx-http-flv-module-master 支持flv模块直播” 指的是一个专门针对Nginx服务器的扩展模块,该模块允许Nginx支持流媒体服务,特别是针对FLV(Flash Video)格式的实时流媒体。...

Global site tag (gtag.js) - Google Analytics