nginx实现subrequest子请求

1.ngx_http_mytest_module.c


/*
 * Copyright (C) Maxim Dounin
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>


typedef struct {
  ngx_str_t     response_json;   //注意这里尽量定义成nginx支持的类型,否则赋值时可能出现问题
} ngx_http_mytest_ctx_t;


static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t mytest_subrequest_post_handler(ngx_http_request_t *r, void *data, ngx_int_t rc);
static void mytest_post_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);

static ngx_command_t  ngx_http_mytest_commands[] = {

    { ngx_string("mytest"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_mytest,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};


static ngx_http_module_t  ngx_http_mytest_module_ctx = {
    NULL,              /* preconfiguration */
    NULL,              /* postconfiguration */

    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_mytest_module = {
    NGX_MODULE_V1,
    &ngx_http_mytest_module_ctx,     /* module context */
    ngx_http_mytest_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
};


static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_mytest_handler;

    return NGX_CONF_OK;
}

/*子请求结束时的处理方法*/
static ngx_int_t mytest_subrequest_post_handler(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
    ngx_http_request_t *pr = r->parent;     //当前请求的r是子请求,parent指向父请求
    ngx_http_mytest_ctx_t* myctx = ngx_http_get_module_ctx(pr,ngx_http_mytest_module);  //获取父请求的上下文

    pr->headers_out.status = r->headers_out.status;


    if(r->headers_out.status == NGX_HTTP_OK)    //返回ok意味着子请求访问服务器成功,开始解析返回的HTTP包体
    {
        ngx_buf_t* pRecvBuf = &r->upstream->buffer;

        myctx->response_json.data = pRecvBuf->pos;
        myctx->response_json.len  = pRecvBuf->last - pRecvBuf->pos; //接收返回数据

    }

    pr->write_event_handler = mytest_post_handler;          //注册父请求回调函数

    return NGX_OK;
}

//父请求处理方法
static void mytest_post_handler(ngx_http_request_t *r)
{
    if(r->headers_out.status != NGX_HTTP_OK)    //如果没有返回200,则直接把错误码发回用户
    {
        ngx_http_finalize_request(r, r->headers_out.status);
        return;
    }

    ngx_http_mytest_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);  //取上下文

    ngx_str_t output_format = ngx_string("大家好我是%V"); //定义发送给用户的HTTP包体内容

    int bodylen = output_format.len + myctx->response_json.len - 2; //计算包体长度

    //在内存池上分配内存,保存要发送的包体
    ngx_buf_t* b = ngx_create_temp_buf(r->pool, bodylen);
    ngx_snprintf(b->pos, bodylen, (char*)output_format.data, &myctx->response_json);
    b->last = b->pos + bodylen;
    b->last_buf = 1;

    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;

    static ngx_str_t type = ngx_string("text/plain");   //设置返回字体
    r->headers_out.content_type = type;
    r->headers_out.status       = NGX_HTTP_OK;

    r->connection->buffered |= NGX_HTTP_WRITE_BUFFERED;
    ngx_int_t ret = ngx_http_send_header(r);
    ret = ngx_http_output_filter(r, &out);

    ngx_http_finalize_request(r, ret);  //注意这里必须手动调用函数结束请求,因为这时HTTP框架不会再帮助调用它

}


//启动subrequest
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
    //创建HTTP上下文
    ngx_http_mytest_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);
    if(myctx == NULL)
    {
        myctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mytest_ctx_t));
        if(myctx == NULL)
        {
            return NGX_ERROR;
        }

        //将上下文设置到原始请求r中
        ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);
    }

    //创建子请求结构体
    ngx_http_post_subrequest_t *psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if(psr == NULL)
    {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    psr->handler = mytest_subrequest_post_handler;  //设置子请求的回调函数为mytest_subrequest_post_handler

    psr->data = myctx;  //将子请求中的参数data设置为myctx,这样在回调mytest_subrequest_post_handler函数时传入的data参数就是myctx


    /*获取nginx的conf文件中location为list的子请求要转到的ip*/
    ngx_str_t sub_prefix = ngx_string("/list=");
    ngx_str_t sub_location;
    sub_location.len  = sub_prefix.len + r->args.len;
    sub_location.data = ngx_palloc(r->pool, sub_location.len);
    ngx_snprintf(sub_location.data, sub_location.len, "%V%V", &sub_prefix, &r->args);

    //sr就是子请求
    ngx_http_request_t *sr;

    ngx_int_t rc = ngx_http_subrequest(r, &sub_location, NULL, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY);    //调用函数传入设置的参数,注册子请求,等待HTTP框架处理
    if(rc != NGX_OK)
    {
        return NGX_ERROR;
    }

    return NGX_DONE;    //这里必须返回NGX_DONE

}

2.nginx.conf

.............

server{

        listen 2222;

       location /mytest {

                mytest;

        }

        location /list {

                proxy_pass http://127.0.0.1:1111/response;

                proxy_set_header Accept-Encoding "";        //不压缩返回

        }

}

3.测试方法:

1)将此nginx服务复制,并修改配置文件如下:

server{

        listen 1111;

       location /response {

              return 200 'caixukun';

        }

}

2)将两个服务都启动

3)使用postman访问http://ip:2222/mytest即可得到返回        大家好我是caixukun

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值