zoukankan      html  css  js  c++  java
  • nginx服务器屏蔽上游错误码

    平时的开发工作中,有时会遇到脚本权限不对导致403,文件被删除导致404,甚至后端业务异常导致5xx等情况,其实我们可以在服务器加上判断,检测当后端服务出现异常的时候前端返回一个指定的静态文件(也可以是一个动态资源)。

    这样可以为一些关键业务(html或者动态资源,js等)配置此功能,当后端关键业务出现错误时,也会把一个指定的正确资源返回给用户。

    想法自然是在nginx向客户端输出响应头时捕获信息,查看是不是异常,异常则跳转到指定文件

    看了下nginx的第三方模块都没有实现这个功能,那还是自己来造轮子吧,最直接的开发方法是用openresty或者nginx+lua,nginx-lua提供了header_filter_by_lua挂载入口,可惜尝试下来却不行,仔细查看官方文档才发现这个入口里面禁用了一些关键IO操作的api

    Uses Lua code specified in <lua-script-str> to define an output header filter.
    
    Note that the following API functions are currently disabled within this context:
    
    Output API functions (e.g., ngx.say and ngx.send_headers)
    Control API functions (e.g., ngx.exit and ngx.exec)
    Subrequest API functions (e.g., ngx.location.capture and ngx.location.capture_multi)
    Cosocket API functions (e.g., ngx.socket.tcp and ngx.req.socket).
    Here is an example of overriding a response header (or adding one if absent) in

    只能做nginx模块开发来实现了。折腾了3个晚上,艰苦的调试过程就不提了,直接贴代码吧。

    nginx配置文件如下:

    location ~ .php($|/) {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
            up_error_code   403 404 500 503 504 505;
            up_error_go     /ok.html;
    
    }
    
    location / {
            root   html;
            index  index.html index.htm;
            up_error_code   502;
            up_error_go     /ok.html;
    }

    其中:

    up_error_code是需要屏蔽的后段错误码类型,支持多个;

    up_error_go是需要跳转到的资源位置

    (502错误需要在location /里面才能捕获到,这个非常奇怪,原因还不清楚,所以除了要在proxy的location里面设置其他错误过滤外也要在根目录下过滤502错误才行。)

    代码(ngx_http_upstream_error_go_module.c)实现较为简单,具体如下:

    /*
     * Copyright (C) Ciaos
     */
    
    #include <ngx_config.h>
    #include <ngx_core.h>
    #include <ngx_http.h>
    
    typedef struct {
        ngx_array_t     *error_codes;
        ngx_str_t       file_path;
    } ngx_http_upstream_error_go_conf_t;
    
    static void *ngx_http_upstream_error_go_create_conf(ngx_conf_t *cf);
    static char *ngx_http_upstream_error_go_merge_conf(ngx_conf_t *cf, void *parent,void *child);
    
    static char *ngx_http_upstream_error_go_set_code(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    static char *ngx_http_upstream_error_go_set_file_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    
    static ngx_int_t ngx_http_upstream_error_go_init(ngx_conf_t *cf);
    
    static ngx_command_t  ngx_http_upstream_error_go_commands[] = {
        { ngx_string("up_error_code"),
            NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
            ngx_http_upstream_error_go_set_code,
            NGX_HTTP_LOC_CONF_OFFSET,
            0,
            NULL },
    
        { ngx_string("up_error_go"),
            NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
            ngx_http_upstream_error_go_set_file_path,
            NGX_HTTP_LOC_CONF_OFFSET,
            0,
            NULL },
    
        ngx_null_command
    };
    
    static ngx_http_module_t  ngx_http_upstream_error_go_module_ctx = {
        NULL,                                  /* preconfiguration */
        ngx_http_upstream_error_go_init,              /* postconfiguration */
    
        NULL,                                  /* create main configuration */
        NULL,                                  /* init main configuration */
    
        NULL,                                  /* create server configuration */
        NULL,                                  /* merge server configuration */
    
        ngx_http_upstream_error_go_create_conf,       /* create location configration */
        ngx_http_upstream_error_go_merge_conf         /* merge location configration */
    };
    
    ngx_module_t  ngx_http_upstream_error_go_module = {
        NGX_MODULE_V1,
        &ngx_http_upstream_error_go_module_ctx,       /* module context */
        ngx_http_upstream_error_go_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 ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
    static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
    
    static ngx_int_t
    ngx_http_upstream_error_go_header_filter(ngx_http_request_t *r)
    {
        ngx_uint_t      e;
        ngx_uint_t      *ecode;
        ngx_http_upstream_error_go_conf_t *clcf;
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_upstream_error_go_module);
        if(clcf == NULL) {
            return NGX_ERROR;
        }
    
        if(clcf->error_codes){
            ecode = clcf->error_codes->elts;
            for(e = 0; e < clcf->error_codes->nelts; e ++){
                if(r->headers_out.status == *(ecode+e)){
                    r->err_status = 0;
                    ngx_str_set(&r->headers_out.status_line, "200 OK");
                    ngx_http_internal_redirect(r, &clcf->file_path, &r->args);
    
                    return NGX_ERROR;
                }
            }
        }
    
        return ngx_http_next_header_filter(r);
    }
    
    static ngx_int_t
    ngx_http_upstream_error_go_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        return ngx_http_next_body_filter(r, in);
    }
    
    static char *
    ngx_http_upstream_error_go_set_code(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
        ngx_http_upstream_error_go_conf_t *clcf = conf;
    
        ngx_str_t       *value;
        ngx_uint_t      i;
        ngx_uint_t      *ecode;
    
        if(clcf->error_codes == NGX_CONF_UNSET_PTR) {
            clcf->error_codes = ngx_array_create(cf->pool, 4, sizeof(ngx_uint_t));
            if(clcf->error_codes == NULL)
            {
                return NGX_CONF_ERROR;
            }
        }
        value = cf->args->elts;
        for(i=1; i< cf->args->nelts; i++){
            ecode = ngx_array_push(clcf->error_codes);
            if(ecode == NULL) {
                return NGX_CONF_ERROR;
            }
            *ecode = ngx_atoi(value[i].data, value[i].len);
        }
        return NGX_CONF_OK;
    }
    
    static char *
    ngx_http_upstream_error_go_set_file_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
        ngx_http_upstream_error_go_conf_t *clcf = conf;
        ngx_str_t       *value;
        value = cf->args->elts;
    
        clcf->file_path = value[1];
    
        return NGX_CONF_OK;
    }
    
    static void *
    ngx_http_upstream_error_go_create_conf(ngx_conf_t *cf)
    {
        ngx_http_upstream_error_go_conf_t  *clcf;
    
        clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_error_go_conf_t));
        if (clcf == NULL) {
            return NULL;
        }
    
        clcf->error_codes = NGX_CONF_UNSET_PTR;
    
        return clcf;
    }
    
    static char *
    ngx_http_upstream_error_go_merge_conf(ngx_conf_t *cf, void *parent, void *child)
    {
        ngx_http_upstream_error_go_conf_t *prev = parent;
        ngx_http_upstream_error_go_conf_t *clcf = child;
    
        ngx_conf_merge_str_value(clcf->file_path, prev->file_path, "");
        ngx_conf_merge_ptr_value(clcf->error_codes, prev->error_codes, NULL);
    
        return NGX_CONF_OK;
    }
    
    static ngx_int_t
    ngx_http_upstream_error_go_init(ngx_conf_t *cf)
    {
        ngx_http_next_header_filter = ngx_http_top_header_filter;
        ngx_http_top_header_filter = ngx_http_upstream_error_go_header_filter;
        ngx_http_next_body_filter = ngx_http_top_body_filter;
        ngx_http_top_body_filter = ngx_http_upstream_error_go_body_filter;
    
        return NGX_OK;
    }

     配置如下(config):

    ngx_addon_name=ngx_http_upstream_error_go_module
    HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_upstream_error_go_module"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_error_go_module.c"

    将源代码和配置文件放到某个目录下,编译nginx(版本1.7.4测试通过)带上此模块即可,测试工具可用Test::Nginx(perl -t t/test.t或prove -r t),测试代码如下

    use Test::Nginx::Socket;
    repeat_each(1);
    plan tests => 1 * repeat_each() * blocks();
    
    our $config = <<"_EOC_";
            location /foo {
                fastcgi_pass   127.0.0.1;
                up_error_code   403 404 502 500 503 504 505;
                up_error_go     /index.html;
            }
    _EOC_
    
    run_tests();
    
    __DATA__
    
    === TEST 1: upstream 403
    --- http_config eval
    "
        server{
            listen  127.0.0.1:80;
            location /foo {
                return 403;
            }
        }
    "
    --- config eval: $::config
    --- request
    GET /foo
    --- error_code: 200
    
    === TEST 2: upstream 404
    --- http_config eval
    "
        server{
            listen  127.0.0.1:80;
            location /foo {
                return 404;
            }
        }
    "
    --- config eval: $::config
    --- request
    GET /foo
    --- error_code: 200
    
    === TEST 3: upstream 502
    --- http_config
    --- config eval: $::config
    --- request
    GET /foo
    --- error_code: 200
  • 相关阅读:
    [C语言] 时间操作,把1970年开始秒数计算的时间,转换为字符串格式输出;
    [C语言] 文件操作,解压华为官方固件UNDATE.APP工具(源代码);
    [Linux] 批量转换整个目录下的文件编码为UTF8;
    [C语言] 文件操作,解压SZB格式中的一段函数片段;
    [Android] patchrom的随笔(个人运行记录..);
    App Store 申请经验 完整的IDP申请直到软件上架
    timer
    终于弄明白iPad UIPopoverController弹出窗口的位置和坐标了
    UIPopoverController的使用
    iPhone开发的一些小技巧
  • 原文地址:https://www.cnblogs.com/ciaos/p/3935950.html
Copyright © 2011-2022 走看看