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

nginx模块开发入门(七)-3.2 Anatomy of an Upstream (a.k.a Proxy) Handler

 
阅读更多
3.2. 剖析Upstream(又称 Proxy) Handler
Anatomy of an Upstream (a.k.a Proxy) Handler



    我已经帮你了解了如何让你的handler来产生响应。有些时候你可以用一小段C代码就可以得到响应,但是通常情况下你需要同另外一台server打交道(比如你正在写一个用来实现某种网络协议的模块)。你当然可以自己实现一套网络编程的东东,但是如果你只收到部分的响应,需要等待余下的响应数据,你会怎么办?你不会想阻塞整个事件处理循环吧?这样会毁掉Nginx的良好性能!幸运的是,Nginx允许你在它处理后端服务器(叫做"upstreams")的机制上加入你的回调函数,因此你的模块将可以和其他的server通信,同时还不会妨碍其他的请求。这一节将介绍模块如何和一个upstream(如 Memcached, FastCGI,或者其它HTTP server)通信。

3.2.1. Upstream 回调函数概要

    与其他模块的回调处理函数不一样,upstream模块的处理函数几乎不做“实事”。它压根不调用ngx_http_output_filter。它仅仅是告诉回调函数什么时候可以向upstream server写数据了,以及什么时候能从upstream server读数据了。实际上它有6个可用的钩子:
    create_request 生成发送到upstream server的request buffer(或者一条缓冲链)
    reinit_request 在与后端服务器连接被重置的情况下(在create_request 被第二次调用之前)被调用
    process_header 处理upstream 响应的第一个bit,通常是保存一个指向upstream "payload"的指针
    abort_request 在客户端放弃请求时被调用
    finalize_request 在Nginx完成从upstream读取数据后调用
    input_filter 这是一个消息体的filter,用来处理响应消息体(例如把尾部删除)

    这些钩子是怎么勾上去的呢?下面是一个例子,简单版本的代理模块处理函数:
static ngx_int_t
ngx_http_proxy_handler(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_upstream_t        *u;
    ngx_http_proxy_loc_conf_t  *plcf;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);

/* set up our upstream struct */
    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
    if (u == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    u->peer.log = r->connection->log;
    u->peer.log_error = NGX_ERROR_ERR;

    u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;

    u->conf = &plcf->upstream;

/* attach the callback functions */
    u->create_request = ngx_http_proxy_create_request;
    u->reinit_request = ngx_http_proxy_reinit_request;
    u->process_header = ngx_http_proxy_process_status_line;
    u->abort_request = ngx_http_proxy_abort_request;
    u->finalize_request = ngx_http_proxy_finalize_request;

    r->upstream = u;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}

    看上去都是些例行事务,不过重要的是那些回调函数。同时还要注意的是ngx_http_read_client_request_body,它又设置了一个回调函数,在Nginx完成从客户端读数据后会被调用。

    这些个回调函数都要做些什么工作呢?通常情况下,reinit_request, abort_request, 和 finalize_request用来设置或重置一些内部状态,但这些都是几行代码的事情。真正做苦力的是create_requestprocess_header

3.2.2. create_request 回调函数

    简单起见,假设我有一个upstream server,它读入一个字符打印出两个字符。那么函数应该如何来写呢?

    create_request需要申请一个buffer来存放“一个字符”的请求,为buffer申请一个链表,并且把链表挂到upstream结构体上。看起来就像这样:
static ngx_int_t
ngx_http_character_server_create_request(ngx_http_request_t *r)
{
/* make a buffer and chain */
    ngx_buf_t *b;
    ngx_chain_t *cl;

    b = ngx_create_temp_buf(r->pool, sizeof("a") - 1);
    if (b == NULL)
        return NGX_ERROR;

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL)
        return NGX_ERROR;

/* hook the buffer to the chain */
    cl->buf = b;
/* chain to the upstream */
    r->upstream->request_bufs = cl;

/* now write to the buffer */
    b->pos = "a";
    b->last = b->pos + sizeof("a") - 1;

    return NGX_OK;
}

    不是很难,对吧?当然实际应用中你很可能还会用到请求里面的URI。r->uri作为一个 ngx_str_t类型也是有效的,GET的参数在r->args中,最后别忘了你还能访问请求的header和 cookie信息。

3.2.3. process_header 回调函数

    现在轮到process_header了,就像create_request把链表指针挂到请求结构体上去一样,process_header把响应指针移到客户端可以接收到的部分。同时它还会从upstream 读入header信息,并且相应的设置发往客户端的响应headers。

    这里有个小例子,读进两个字符的响应。我们假设第一个字符代表“状态”字符。如果它是问号,我们将返回一个404错误并丢弃剩下的那个字符。如果它是空格,我们将以 200 OK的响应把另一个字符返回给客户端。好吧,这不是什么多有用的协议,不过可以作为一个不错的例子。那么我们如何来实现这个process_header 函数呢?
static ngx_int_t
ngx_http_character_server_process_header(ngx_http_request_t *r)
{
    ngx_http_upstream_t       *u;
    u = r->upstream;

    /* read the first character */
    switch(u->buffer.pos[0]) {
        case '?':
            r->header_only; /* suppress this buffer from the client */
            u->headers_in.status_n = 404;
            break;
        case ' ':
            u->buffer.pos++; /* move the buffer to point to the next character */
            u->headers_in.status_n = 200;
            break;
    }

    return NGX_OK;
}

     就是这样。操作头部,改变指针,搞定!注意headers_in实际上就是我们之前提到过的头部结构体( http/ngx_http_request.h),但是它位于来自upstream的头中。一个真正的代理模块会在头信息的处理上做很多文章,不光是错误处理,做什么完全取决于你的想法。

    但是……如果一个buffer没有能够装下全部的从upstream来的头信息,该怎么办呢?

3.2.4. 状态保持 (Keeping state)

    好了,还记得我说过abort_request, reinit_requestfinalize_request 可以用来重置内部状态吗?这是因为许多upstream模块都有其内部状态。模块需要定义一个 “自定义上下文结构” ,来标记目前为止从upstream读到了什么。这跟之前说的“模块上下文”不是一个概念。“模块上下文”是预定义类型,而“自定义上下文结构”可以包含任何你需要的数据和字段(这可是你自己定义的结构体)。这个结构体在create_request函数中被实例化,大概像这样:
    ngx_http_character_server_ctx_t   *p;   /* my custom context struct */

    p = ngx_pcalloc(r->pool, sizeof(ngx_http_character_server_ctx_t));
    if (p == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_http_set_ctx(r, p, ngx_http_character_server_module);


    最后一行实际上将“自定义上下文结构体”注册到了特定的请求和模块名上,以便在稍后取用。当你需要这个结构体时(可能所有的回调函数中都需要它),只需要:
    ngx_http_proxy_ctx_t  *p;
    p = ngx_http_get_module_ctx(r, ngx_http_proxy_module);


    指针 p 可以得到当前的状态. 设置、重置、增加、减少、往里填数据……你可以随心所欲的操作它。当upstream服务器返回一块一块的响应时,读取这些响应的过程中使用持久状态机是个很nx的办法,它不用阻塞主事件循环。很好很强大!
分享到:
评论

相关推荐

    Redis-x64-win-3.2.100+nginx-win

    标题 "Redis-x64-win-3.2.100+nginx-win" 暗示了这是一个针对 Windows 平台的 Redis 和 Nginx 的安装包组合,其中 Redis 的版本为 3.2.100,Nginx 的版本可能为 1.14.2。这个组合通常用于在 Windows 环境下搭建一个...

    nginx-1.22.1-1.sles12.ngx.x86-64.rpm文件(分享给需要的同学)

    nginx-1.22.1-1.sles12.ngx.x86_64.rpm文件 nginx-1.22.1-1.sles12.ngx.x86_64.rpm文件 nginx-1.22.1-1.sles12.ngx.x86_64.rpm文件 nginx-1.22.1-1.sles12.ngx.x86_64.rpm文件 nginx-1.22.1-1.sles12.ngx.x86_64.rpm...

    nginx-1.18.0-2.el7.ngx.x86-64.rpm安装包(含有部署手册)

    nginx-1.18.0-2.el7.ngx.x86_64.rpm安装包(含有部署手册) nginx-1.18.0-2.el7.ngx.x86_64.rpm安装包(含有部署手册) nginx-1.18.0-2.el7.ngx.x86_64.rpm安装包(含有部署手册) nginx-1.18.0-2.el7.ngx.x86_64.rpm...

    nginx1.8-tomcat7-redis3.2-session配置和jar包

    本文件包经过本人亲测能用,支持nginx1.8.1和tomcat7.0.63和Redis-x64-3.2.100版, 包含tomcat集群redis会话共享的依赖jar包,包含有2.1.0和2.7.2两个版本,配置方式稍微有点差别,两个版本不兼容。 2.1.0版包含 ...

    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-...

    nginx-rtmp-module-1.2.1.tar.gz

    在这个例子中,我们使用的是 Nginx 1.2.1 版本和 RTMP 模块的 1.2.1 版本,即 "nginx-rtmp-module-1.2.1.tar.gz"。 2. 解压 Nginx 和 RTMP 模块的源码包: ``` tar -zxvf nginx-1.2.1.tar.gz tar -zxvf nginx-...

    fastdfs-nginx-module_v1.16.tar.gz

    《深入解析fastdfs-nginx-module_v1.16.tar.gz:构建高效文件服务器的利器》 在现代互联网应用中,文件存储与传输是不可或缺的一部分。FastDFS作为一个轻量级、高性能的分布式文件系统,因其简单易用、稳定可靠的...

    nginx-1.8.0-1.el6.ngx.x86_64.cpio

    nginx-1.8.0-1.el6.ngx.x86_64.rpm libpcre.so.0()(64bit) 被 nginx-1.18.0-1.el6.ngx.x86_64 需要

    nginx-upstream-fair-master.zip

    "nginx-upstream-fair-master.zip"是一个包含Nginx公平负载均衡(fair)第三方模块的压缩包,该模块使得Nginx可以根据服务器的实际响应时间来分配请求,从而实现更公平的负载均衡策略。 公平负载均衡(fair)模块...

    nginx-1.21.6-1.el7-rtmp.ngx.x86_64.tar.gz

    本资源提供的 "nginx-1.21.6-1.el7-rtmp.ngx.x86_64.tar.gz" 是 Nginx 的一个特定版本,包含了 RTMP (Real-Time Messaging Protocol) 模块,适用于实时流媒体传输,如直播服务。 **RTMP 模块介绍** RTMP 是 Adobe ...

    nginx-upstream-jvm-route-1.15

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

    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-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-goodies-nginx-sticky-module-ng-08a395c66e42.zip

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

    ty-Redis-x64-3.2.100.rar

    "nginx-1.14.0.zip" 文件则与Redis无关,它是Nginx web服务器的一个版本。Nginx是一款高性能的HTTP和反向代理服务器,以其高并发处理能力而闻名。通常,Nginx与Redis结合使用可以实现动态内容和静态内容的分离,提高...

    nginx-http-flv-win32-master.rar

    【Nginx-http-flv-win32-master.rar】是一个针对Windows平台的Nginx服务器配置,主要用于处理RTSP流并将其转换为FLV格式,以便在网页上通过video.js插件进行播放。这个压缩包包含了运行RTSP到FLV流转换服务所需的...

    nginx-1.6.2-1.el7.centos.ngx.x86_64.rpm

    很难找的 centos7 nginx-1.6.2-1.el7.centos.ngx.x86_64.rpm 包

    nginx-upstream-jvm-route-0.1.tar.gz

    "nginx-upstream-jvm-route-0.1.tar.gz"正是为了解决这个问题而设计的一个解决方案。 首先,让我们了解一下Nginx的Upstream模块。Upstream模块允许Nginx将接收到的请求转发到一组后端服务器,可以根据配置的策略...

    nginx-1.24.0-1.el6.ngx.x86-64.rpm

    nginx官网不再更新centos6版,此rpm构建于nginx最新稳定版1.24.0

Global site tag (gtag.js) - Google Analytics