`
张立军
  • 浏览: 35407 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Nginx模块开发 从url变量中key的值 来确定upstream服务器

阅读更多

源文件(从Miller的文件修改而来)

 

 

/*

 * Hash a variable to choose an upstream server.

 *

 * Copyright (C) Evan Miller

 *

 * This module can be distributed under the same terms as Nginx itself.

 */

 

 

#include <ngx_config.h>

#include <ngx_core.h>

#include <ngx_http.h>

 

ngx_int_t ngx_http_hi_upstream_module_init(ngx_conf_t *cf);

 

#define ngx_bitvector_index(index) index / (8 * sizeof(uintptr_t))

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

 

typedef struct {

    struct sockaddr                *sockaddr;

    socklen_t                       socklen;

    ngx_str_t                       name;

} ngx_http_upstream_hash_peer_t;

 

typedef struct {

    ngx_uint_t                        number;

    ngx_http_upstream_hash_peer_t     peer[0];

} ngx_http_upstream_hash_peers_t;

 

typedef struct {

    ngx_http_upstream_hash_peers_t   *peers;

    ngx_uint_t                        hash;

    ngx_str_t                         current_key;

    ngx_str_t                         original_key;

    ngx_uint_t                        try_i;

    uintptr_t                         tried[1];

} ngx_http_upstream_hash_peer_data_t;

 

 

static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us);

static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,

    void *data);

static void ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc,

    void *data, ngx_uint_t state);

static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,

    void *conf);

static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,

    ngx_http_upstream_srv_conf_t *us);

static ngx_uint_t ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen);

 

// 获得URL参数

static ngx_int_t  ngx_http_key_index;

static ngx_str_t  ngx_http_key_value = ngx_string("key");

ngx_http_variable_value_t      *key_vv;

 

static ngx_int_t

ngx_http_hi_upstream_module_variable_not_found(ngx_http_request_t *r,

        ngx_http_variable_value_t *v, uintptr_t data)

{

    v->not_found = 1;

    return NGX_OK;

}

 

static ngx_int_t

ngx_http_hi_upstream_module_add_variable(ngx_conf_t *cf, ngx_str_t *name) {

    ngx_http_variable_t         *v;

 

    v = ngx_http_add_variable(cf, name, NGX_HTTP_VAR_CHANGEABLE);

    if (v == NULL) {

        return NGX_ERROR;

    }

 

    v->get_handler = ngx_http_hi_upstream_module_variable_not_found;

 

    return ngx_http_get_variable_index(cf, name);

}

 

// 指令声明,既确定了他们在哪里生效又确定了一旦流程遇到指令将要调用什么函数。

// load-balancer的指令需要置NGX_HTTP_UPS_CONF标志位,

// 以便让Nginx知道这个指令只会在upstream块中有效。

// 同时它需要提供一个指向注册函数的指针。下面列出的是upstream_hash模块的指令声明

// 第一步

static ngx_command_t  ngx_http_upstream_hash_commands[] = {

    { ngx_string("hash"),

      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,

      ngx_http_upstream_hash,

      0,

      0,

      NULL },

 

      ngx_null_command

};

 

 

static ngx_http_module_t  ngx_http_hi_upstream_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_hi_upstream_module_init,               /* postconfiguration 从配置文件中读取key定义 */

 

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

 

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

 

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

 

//////////////////////

//     定义模块              //

//////////////////////

ngx_module_t  ngx_http_hi_upstream_module = {

    NGX_MODULE_V1,

    &ngx_http_hi_upstream_module_ctx,    /* module context */

    ngx_http_upstream_hash_commands,       /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

 

// upstream 初始化函数的目的是,解析主机名,为socket分配空间,分配(另一个)回调函数。

// 下面是upstream_hash:

// 第三步

static 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;

 

    us->peer.init = ngx_http_upstream_init_hash_peer; // 回调函数 去第四步

 

    if (!us->servers) {

 

        return NGX_ERROR;

    }

 

    server = us->servers->elts;

 

    for (n = 0, i = 0; i < us->servers->nelts; i++) {

        n += server[i].naddrs;

    }

 

    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_hash_peers_t)

            + sizeof(ngx_http_upstream_hash_peer_t) * n);

 

    if (peers == NULL) {

        return NGX_ERROR;

    }

 

    peers->number = n;

 

    /* 同一个主机名可能对应多个IP地址 */

    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;

        }

    }

 

    us->peer.data = peers;

 

    return NGX_OK;

}

 

// 对端初始化函数每个请求调用一次。它会构造一个数据结构,模块会用这个数据结构来选择合适的后端服务器;

// 这个数据结构保存着和后端交互的重试次数,通过它可以很容易的跟踪链接失败次数或者是计算好的哈希值。

// 这个结构体习惯性地被命名为ngx_http_upstream_<module name>_peer_data_t。

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

// * get: load-balancing 函数

// * free: 对端释放函数 (通常只是在连接完成后更新一些统计信息)

// 似乎还不止这些,它同时还初始化了一个叫做tries的变量。只要tries是正数,Nginx将继续重试当前的load-banlancer。

// 当tries变为0时,Nginx将放弃重试。一切都取决于get 和 free 如何设置合适的tries。

// 下面是upstream_hash中对端初始化函数的例子:

 

static ngx_int_t

ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,

    ngx_http_upstream_srv_conf_t *us)

{

 

    ////////////////////////////////////////////

    //        读取URL里面具体的key变量的值

    ////////////////////////////////////////////

    key_vv = ngx_http_get_indexed_variable(r,

     ngx_http_key_index);

 

ngx_http_upstream_hash_peer_data_t     *uhpd;

 

    ngx_str_t val;

 

    // 获得hash变量对应的本次请求的值

    if (ngx_http_script_run(r, &val, us->lengths, 0, us->values) == NULL) {

        return NGX_ERROR;

    }

 

    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;

    }

 

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

 

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

 

    r->upstream->peer.get = ngx_http_upstream_get_hash_peer; // 回调函数 去第五步

    r->upstream->peer.free = ngx_http_upstream_free_hash_peer; // 回调函数 去第六步

    r->upstream->peer.tries = us->retries + 1;

 

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

                   "upstream_hash: hashing \"%V\"", &val);

 

    uhpd->hash = ngx_http_upstream_hash_crc32(val.data, val.len); // 准备hash的数值

 

    return NGX_OK;

}

 

 

static ngx_int_t

ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)

{

    ngx_http_upstream_hash_peer_data_t  *uhpd = data;

    ngx_http_upstream_hash_peer_t       *peer;

    ngx_uint_t                           peer_index;

 

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "upstream_hash: get upstream request hash peer try %ui", pc->tries);

 

    pc->cached = 0;

    pc->connection = NULL;

 

//    peer_index = uhpd->hash % uhpd->peers->number;

    peer_index = atoi((char *) key_vv->data) % uhpd->peers->number;

 

    peer = &uhpd->peers->peer[peer_index];

 

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,

                   "upstream_hash: chose peer %ui w/ hash %ui", peer_index, uhpd->hash);

 

    pc->sockaddr = peer->sockaddr;

    pc->socklen = peer->socklen;

    pc->name = &peer->name;

 

    return NGX_OK;

}

 

/* 不进行重试 */

static void

ngx_http_upstream_free_hash_peer(ngx_peer_connection_t *pc, void *data,

    ngx_uint_t state)

{

pc->tries = 0;

}

 

/* bit-shift, bit-mask, and non-zero requirement are for libmemcache compatibility */

static ngx_uint_t

ngx_http_upstream_hash_crc32(u_char *keydata, size_t keylen)

{

    ngx_uint_t crc32 = (ngx_crc32_short(keydata, keylen) >> 16) & 0x7fff;

    return crc32 ? crc32 : 1;

}

 

// 回调函数ngx_http_upstream_hash就是所谓的注册函数。

// 之所以这样叫(我起得名字)是因为它注册了把upstream初始化函数和周边的upstream配置注册到了一块。

// 另外,注册函数还定义了特定upstream块中的server指令的一些选项(如weight=, fail_timeout=),

// 下面是upstream_hash模块的注册函数:

// 第二步

static char *

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; // 获得nginx.conf中的hash配置的值

 

    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;

    }

 

    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

 

    uscf->peer.init_upstream = ngx_http_upstream_init_hash; // 回调函数 去第三步

 

    uscf->flags = NGX_HTTP_UPSTREAM_CREATE;

 

    uscf->values = vars_values->elts;

    uscf->lengths = vars_lengths->elts;

 

    if ((ngx_http_key_index = ngx_http_hi_upstream_module_add_variable(

                cf, &ngx_http_key_value)) == NGX_ERROR)

    {

        return NGX_CONF_ERROR;

    }

 

    return NGX_CONF_OK;

}

 

//////////////////////

// 从配置文件中读取key定义

//////////////////////

ngx_int_t

ngx_http_hi_upstream_module_init(ngx_conf_t *cf)

{

    printf("called:ngx_http_hi_upstream_module_init\n");

 

    // 读key参数

    if ((ngx_http_key_index = ngx_http_hi_upstream_module_add_variable(

                cf, &ngx_http_key_value)) == NGX_ERROR)

    {

        return NGX_ERROR;

    }

 

    return NGX_OK;

 

}

 

 

 

config文件

 

 

 

ngx_addon_name=ngx_http_hi_upstream_module

HTTP_MODULES="$HTTP_MODULES ngx_http_hi_upstream_module"

NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hi_upstream_module.c"


 

 

Nginx配置文件

 

worker_processes  1;

daemon off; 

master_process  off;

error_log  /tmp/error.log debug;

pid /tmp/nginx_demo.pid;

events {

    worker_connections  1024;

}

http {

    #include       /etc/nginx/mime.types;

    sendfile        on;

    keepalive_timeout  65;

    tcp_nodelay        on;

 

upstream backend {

    server 127.0.0.1:8889;

    server 127.0.0.1:8890;

    hash   $request_uri;

}

 

server {

        listen   8100;

        server_name  localhost;

        access_log  /tmp/access.log;

        error_log  /tmp/error.log debug;

        location /hello {

            set $key $arg_key;

            #echo "abcd";

   proxy_pass      http://backend;

        }

    }

}

 

0
0
分享到:
评论

相关推荐

    nginx_upstream_hash-0.3.2.tar.gz

    nginx_upstream_hash模块是nginx官方模块的一个扩展,它的核心功能在于通过哈希算法对请求进行分发,使得具有相同哈希值的请求总是被定向到同一台后端服务器。这种策略在处理会话持久化、缓存一致性等问题时非常有用...

    实战Nginx取代Apache的高性能Web服务器_文字版

    3. 健康检查:Nginx可监控后端服务器状态,自动将故障服务器从负载均衡池中移除。 四、HTTPS与SSL/TLS配置 1. SSL证书:配置`ssl_certificate`和`ssl_certificate_key`指令加载SSL证书和私钥。 2. 安全配置:启用`...

    nginx优化配置,搭建高性能服务器

    5. **性能监控**:可以使用`stub_status`模块开启Nginx的简单状态监控,以便实时查看服务器负载和请求处理情况。 三、实例配置 下面是一个基本的Nginx与FastCGI结合的配置示例: ```nginx http { upstream ...

    nginx笔记.zip

    安装完成后,Nginx的主配置文件位于`/etc/nginx/nginx.conf`,在这个文件中可以设置全局变量、服务器块和location块。 **反向代理** Nginx的一个重要功能是作为反向代理,它可以将客户端请求转发到后端服务器集群...

    Nginx从入门到精通.pdf

    ### Nginx从入门到精通 #### 一、Nginx简介 Nginx是一款由俄罗斯程序员Igor Sysoev开发的高度轻量级HTTP服务器,它不仅具备高性能的HTTP服务功能,同时也支持反向代理、负载均衡以及IMAP/POP3/SMTP代理等功能。...

    nginx-1.11.4.zip/Windows

    在 Windows 上安装 Nginx,首先需要解压 "nginx-1.11.4.zip" 文件,然后将解压后的目录添加到系统的 PATH 环境变量中。接着,可以通过双击 "nginx.exe" 来启动服务器。如果需要自定义配置,可以修改 "conf/nginx....

    Nginx学习.zip

    9. **健康检查**:Nginx可以定期检查后端服务器的健康状态,通过`health_check`指令,当检测到某个服务器异常时,自动将其从负载均衡池中移除。 10. **GZIP压缩**:Nginx支持GZIP压缩,能减小传输的数据量,提升...

    weblogic集群安装及nginx https反向代理及负载均衡配置

    - **配置环境变量**:在 `/etc/profile` 文件中添加相应的 JAVA_HOME、CLASSPATH 和 PATH 环境变量,并使更改立即生效。 - **验证安装**:通过 `java -version` 命令验证 JDK 的安装情况。 ##### 1.7 解决 WebLogic...

    Nginx 工作原理.docx

    Nginx 提供的核心模块包括 HTTP、EVENT 和 MAIL 模块,基础模块包括 HTTP Access、FastCGI、Proxy 和 Rewrite 模块,而第三方模块如 HTTP Upstream Request Hash、Notice 和 HTTP AccessKey 模块则扩展了 Nginx 的...

    Tengine(Nginx)的使用

    Nginx的rewrite模块用于URL重写,基于正则表达式。如`rewrite ^/old/(.*) /new/$1 permanent;`将所有以"/old/"开头的URL重定向到"/new/"。`break`用于终止当前规则,`last`则结束所有重写规则,`redirect`和`...

    Nginx+Tomcat+Https 服务器负载均衡配置实践方案详解

    这可以通过在`nginx.conf`中的`upstream`块来实现,定义一组服务器节点。然后,在`server`块中,使用`proxy_pass`指令指向这个`upstream`。可以采用轮询、权重轮询、最少连接数等多种策略进行负载分配。 6. **测试...

    服务器安全配置ngx配置https反向代理

    **Upstream** 是Nginx中的一个重要概念,用于定义一组后端服务器,这些服务器通常负责处理实际的服务请求。在配置HTTPS反向代理时,需要定义一个上游组: ```nginx upstream backend { server localhost:8080; # ...

    redis集群部署

    - 在Nginx的配置文件中设置upstream,定义多个Tomcat服务器的地址列表。 - 设置location规则,将特定的请求转发给指定的Tomcat服务器。 - 配置Tomcat集群,实现Session复制功能,确保会话数据能够在多台Tomcat...

    openresty最佳实践

    OpenResty是一款基于Nginx与Lua的高性能Web平台,它集成了众多有用的模块与第三方组件,使开发人员能够轻松地构建可扩展的Web应用程序和服务。OpenResty的核心在于其高度灵活的LuaJIT脚本接口,这使得开发者可以利用...

Global site tag (gtag.js) - Google Analytics