`

阅读博客 添加收藏 nginx动态代理方案

阅读更多

       我做的是nginx +lua+redis

       0、需求:动态调整转发策略

    数据库存放着大量的用户数据,需要制定一个策略,负载均衡服务器可以根据用户信息,动态转发请求。

    比如A用户(001)的请求转发到A服务器(192.168.1.101),B用户(002)的请求转发到B服务器(192.168.1.102),C用户(003)的请求转发到A服务器(192.168.1.103),等等。

1、服务器上下文

   前端nginx服务器 + N台后端应用服务器。准备用单台服务器模拟。

   前端:192.168.1.101:80

   后端:192.168.1.101:81

 

2、技术头脑风暴

    程序员们脑袋里开始有好几个方案了,有的是直觉,有的是经验,如下:

a、写nginx模块,模块里实现读数据库或nosql,根据数据值做转发。

b、找现成的模块,看能不能直接改根据或脚本就可以解决。据说ngx_lua很强大,可以考虑。

c、服务器必须保证不能有任何阻塞,模块实现时得用nginx的subrequest机制。

d、blalala...

 

3、程序员的脑袋里装的什么呢? 简单

    任务1:web程序员A,写个http api接口,返回具体的服务器信息(ip+port)。

    任务2:系统工程师B,做个nginx配置,根据A的api让nginx根据返回信息实现动态转发。

 

4、开始实现:

   任务1:分分钟搞定,写个php脚本呗。

   任务2:继续拆解

             任务2.1:实现最简单转发

server {
    listen  80;

    location / {
        proxy_pass  192.168.1.101:81;
    }
}


server {
    listen  81;

    location / {
        root  html;
    }
}

            任务2.2:实现动态转发

                 看下源码proxy_pass能不能使用变量

static char *
ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ...

    value = cf->args->elts;

    url = &value[1];

    n = ngx_http_script_variables_count(url);  // 找到这里了,可以使用变量

    if (n) {
        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;
        sc.source = url;  
        ...
     }
     ...
}

        改下配置,测试ok,继续往下走

server {
    listen  80;

    location / {
        set  $url 192.168.1.101:81;
        proxy_pass  $url;
    }
}

            任务2.3:根据api设置变量$url的值

                 好像没有现成的模块,脑袋里过滤了一遍,有的话必须是跟subrequest有关的模块,想起了 auth_request 模块,它可以配置一个http请求,根据http请求的返回结果决定给客户端是否正常访问,去看下源码先。

static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{ 
    ...
    if (ctx != NULL) {
        ...
        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {  // 看函数名感觉这里有干货
            return NGX_ERROR;
        }

        /* return appropriate status */

        if (ctx->status == NGX_HTTP_FORBIDDEN) { // 403
            return ctx->status;
        }
        
        // 200 and ...
        if (ctx->status >= NGX_HTTP_OK
            && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
        {
            return NGX_OK;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "auth request unexpected status: %d", ctx->status);

        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }  ...

    return NGX_AGAIN;
}

           从源码我们掌握两个信息:1、api返回状态码要为200  2、ngx_http_auth_request_set_variables 可能有我们要的信息,继续查看:

static ngx_int_t
ngx_http_auth_request_set_variables(ngx_http_request_t *r,
    ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
{
    ...
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    v = cmcf->variables.elts;

    av = arcf->vars->elts;
    last = av + arcf->vars->nelts;

    // 遍历 arcf->vars 这个东东
    while (av < last) {  
        /*
         * explicitly set new value to make sure it will be available after
         * internal redirects
         */

        vv = &r->variables[av->index];

        if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
            != NGX_OK)
        {
            return NGX_ERROR;
        }

        vv->valid = 1;
        vv->not_found = 0;
        vv->data = val.data;
        vv->len = val.len;

        if (av->set_handler) {
            /*
             * set_handler only available in cmcf->variables_keys, so we store
             * it explicitly
             */

            av->set_handler(r, vv, v[av->index].data);  // 设置变量
        }

        av++;
    }

    return NGX_OK;
}

           代码逻辑很简单,遍历这个模块的配置的某个成员,肯定是跟变量有关的了,找配置信息了

static ngx_command_t  ngx_http_auth_request_commands[] = {

    { ngx_string("auth_request"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_http_auth_request,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("auth_request_set"), // 名称很像设置变量,就是它了
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
      ngx_http_auth_request_set, // 看一下这函数实现,印证了猜测是正确的
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};

       整理一下,就是当接收到api的返回信息后,模块处理了设置变量。so配置为如下,测试ok,继续

server {
    listen  80;

    location / {
        auth_request  /api.php; #用php模拟
        auth_request_set  $url 192.168.1.101:81; #保持简单,先用正确值模拟
        proxy_pass  $url;
    }

    location ~ \.php$ { # 为了处理上面的 /api.php 
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
        include        fastcgi_params;
    }
}

         任务2.4:让$url的值从api返回信息里获取

         (请不要在2.3里一步搞定整个模拟,保持每步正确和简单,是不是很像代码重构的原则)

         我们要解决这个:auth_request_set   $url  192.168.1.101:81;

         nginx有什么变量可以让我们获取请求的返回信息呢,头部信息也可以(其实这里心里已经判断肯定只能从头部信息里获取,以对nginx的代码熟悉了解程度)。去看下获取变量值的函数吧。

ngx_http_variable_value_t *
ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
{
    ...
    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);

    if (v) {
        if (v->flags & NGX_HTTP_VAR_INDEXED) {
            return ngx_http_get_flushed_variable(r, v->index);

        } else {

            vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));

            if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
                return vv;
            }

            return NULL;
        }
    }

    vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
    if (vv == NULL) {
        return NULL;
    }

    // 我清楚nginx可以通过 http_xxx 获取请求的头部信息 xxx: ...
    if (ngx_strncmp(name->data, "http_", 5) == 0) {

        if (ngx_http_variable_unknown_header_in(r, vv, (uintptr_t) name)
            == NGX_OK)
        {
            return vv;
        }

        return NULL;
    }

    // 这个没看过,不知道哪个版本加上的,感觉很意外,看名字应该就是我要找的
    if (ngx_strncmp(name->data, "sent_http_", 10) == 0) {
        // 这个函数怎么实现的,看了下类似上面的,上面用于获取header_in即请求,
           它用于获取header_out,就是我们要的响应信息
        if (ngx_http_variable_unknown_header_out(r, vv, (uintptr_t) name)
            == NGX_OK) 
        {
            return vv;
        }

        return NULL;
    }

    ...

    vv->not_found = 1;

    return vv;
}

         到这里心里已经很有数了,写php代码,并且直接访问测试正常

api.php

<?php

$url = "192.168.1.101:81";
header("url: $url");

         改nginx配置:        

server {
    listen  80;

    location / {
        auth_request  /api.php; #用php模拟
        auth_request_set  $url $sent_http_url; #保持简单,先用正确值模拟
        proxy_pass  $url;
    }

    location ~ \.php$ { # 为了处理上面的 /api.php 
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
        include        fastcgi_params;
    }
} 

        搞定,心情特好 ,预期半个小时验证方案,提前5分钟完成。


5、整个方案整理:

a、> ./configure --with-http_auth_request_module && make && ./objs/nginx

b、nginx.conf

server {
    listen  80;

    location / {
        auth_request  /api.php; #用php模拟
        auth_request_set  $url $sent_http_url; #保持简单,先用正确值模拟
        proxy_pass  $url;
    }

    location ~ \.php$ { # 为了处理上面的 /api.php 
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
        include        fastcgi_params;
    }
} 


server {
    listen  81;

    location / {
        root  html;
    }
}

   c、api.php

<?php

$url = "192.168.1.101:81";
header("url: $url")


转自:https://m.oschina.net/blog/347365
分享到:
评论

相关推荐

    nginx经过多层代理后获取真实来源ip过程详解

    问题 nginx取 $remote_addr 当做真实ip,而事实上,$http_X_Forwarded_For 才是用户真实ip,$remote_addr...添加之后启动nginx报错: nginx: [emerg] unknown directive set_real_ip_from in /home/lnidmp/nginx/conf/

    基于Nginx 反向代理获取真实IP的问题详解

    解决该问题,需要在Nginx反向代理配置中添加一些配置信息,目的将客户端的真实IP和域名传递到应用程序中。同时,也要修改获取IP地址的方法。 但是需要注意的是,通过Nginx反向代理后,如果访问IP通过了几层代理,...

    nginx下的代理服务器80端口被封的解决方案

    方法如下: 首先得准备一台VPS,俺用的是国外的,,早上我已经将环境搞定了,于是迫不及待的测试了下……???啥?你吖环境都不知道配置??没关系.看哥的教程,用SSH连接上终端,挨个执行centos下的LNMP环境一键安装...不懂得添加

    Nginx+Tomcat负载均衡

    3.这里只需要修改Nginx的配置,让它通过tomcat来转发,所以我们在这一步只需要在nginx.conf中添加几行代码就行。 如果看不懂 。建议直接拿我替来用。 Copy下面的内容 替换 你的nginx.conf注意黄色端口要改为你的。 ...

    docker-nginx-auto-ssl

    docker-nginx-auto-ssl 向您的站点添加SSL证书的最简单解决方案 使用Let's加密和Open Resty并具有合理的SSL设置,HTTP / 2和WebSockets开箱即用的自动生成SSL证书的Docker映像。 您可以使用ENV变量指定允许的域和...

    node实现简单的反向代理服务器

    每当提起反向代理器,人们通常一想到的就是 Nginx,但是今天我们暂时忽略大名鼎鼎的 Nginx,采用同样也是使用单线程、事件循环的服务端小弟——Node 来达成 跨域问题是前端开发很常见的问题 解决方案有很多种 ...

    最全面的门户网站架构设计方案.doc

    b) 对于动态页面 因为服务器解析动态页面必须在其传输到客户端前就通过服务器来进行解释, 这样就会给应用服务器添加额外的性能消耗,如果进一步要访问数据库,则会增 加数据库服务器的性能消耗,则动态页面还有...

    arlb:实验性反向代理和负载均衡器

    任务列表[x]具有预配置后端的基本工作LB []针对NGINX和其他LB的基准性能[x]可以动态添加注册新后端的功能[] stats命令,以每个后端服务器输出统计信息。 统计信息可能包括请求数量,平均花费的时间等。[]用于负载...

    kahlua:kahlua.choclab.net的网站模型

    卡鲁阿 有关狗的网站的w主题。 关于 该站点可以用作简历和博客,并且具有(相对)响应式布局和全屏背景图像。 配色方案主要是巧克力(RGB:78,46,40)和沙子(RGB:205,180,140),所有图像均...NGINX设置代理到开发服

    毕设之springboot微信小程序的点餐系统(源码+部署说明+SpringBoot+微信小程序+vue).zip

    前端:将前端代码部署到服务器上,配置好Nginx反向代理,启动项目即可 移动端:将小程序代码上传到微信小程序平台,进行审核发布即可 总之,这个资源提供了一个完整的点餐系统解决方案,涵盖了前后端和移动端的开发...

    performanceci-core:绩效核心

    PerformanceCI 这是一个将应用程序性能测试添加到您的连续... 我们的docker-compose设置包括一个NGiNX代理,该代理在端口80上接受请求。您可以这样调用ngrok: luser@lolcathost: ~ $ ngrok http $( docker-machine i

    基于SSM架构实现的大型分布式购物网站-B2C项目源码+项目说明.zip

    2、当并发增加后,就添加服务器,做tomcat集群。使用负载均衡服务器来决定存放到哪个服务器的image中。当图片传到tomcat1中到tomcat2中查找图片,我们可以将tomcat1和tomcat映射到另一台服务器上,然后做共享。或者...

    大天使之剑java源码-dev_ci_docker:在Docker上运行的Jenkins/GitlabCI环境

    大天使之剑 ...我对这个解决方案不满意,我真的更喜欢使用不同的端口而不是强制使用预定义的主机名。 如果你有溶质,不要犹豫。 首先,您需要将以下重定向添加到您的主机文件中: 127.0.0.1 imperi

    基于ssm+mysql的分布式电商系统(前后台+订单管理+门户)源码+项目说明.zip

    maven(构建项目),svn(版本控制工具),myeclipse(集成开发环境),nginx(反向代理), FastDFS (图片服务器),tomcat(web服务器),zookeeper(集群管理),mysql(数据库) Junit(测试) ### 技术栈: spring,...

    KODExplorer 芒果云-资源管理器

    - 修复添加收藏夹问题(已打开设置窗口再添加失败问题) - 修复树目录中文文件名bug - 优化pic图片幻灯片播放 - 优化新建文件&文件夹 清空选择状态 - 对话框组件 ie8 优化;tips不显示任务栏; - 优化地址栏过...

    一个适合新手学习的电商项目

    maven(构建项目),svn(版本控制工具),myeclipse(集成开发环境),nginx(反向代理), FastDFS (图片服务器),tomcat(web服务器),zookeeper(集群管理),mysql(数据库) Junit(测试) ### 技术栈: spring,...

    DSShop单用户TP5框架B2C开源商城源码-PHP

    DSShop商城系统是一套完善的B2C(单用户商城)解决方案。系统pc端后台使用国内优秀开源框架THinkPHP,基于PHP+MySQL开发,采用B/S架构,wap端使用vue.js构造,依据6年电商经验打造出的一套开源的B2C电子商务系统。 ...

Global site tag (gtag.js) - Google Analytics