06 nginx 处理转发其他域的处理 以及 proxy_redirect

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了06 nginx 处理转发其他域的处理 以及 proxy_redirect相关的知识,希望对你有一定的参考价值。

前言

容器端口映射导致 302 存在问题 以及 nginx 对于 302 的 Location 的重写

中描述了如果 上游服务 sendRedirect 到所在域的其他服务之后, 来到 nginx 这一层, nginx 会将这个转发的服务更新为 nginx 所在的域 

那么 假设上游服务 sendRedirect 到其他域的服务呢 ? 

另外 就是在 探究这个问题的时候, 使用了一下 proxy_redirect, 这个 proxy_redirect 又是如何处理的呢?

以下截图, 调试基于 nginx-1.18.0

测试用例

        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/;
        

sendRedirect 的服务, 是部署在 8080 端口上面 

通过 nginx 访问发现出现 302, 跳转的域是 给定的目标域, nginx 没有重写 

nginx 对于其他非上游域的服务的302的处理 

location 的重写模块这里有一个对于域的判断, 如果 不是当前域直接 DECLINED, 不走后面的 rewrite 的处理 

另外还有一个细节是 这个流程的 location 还是存放在 request 的 header 列表, 没有写到 request.headers_out.location 里面 

响应的 location 和 proxy_pass 的 域不一致, 直接走 DECLINED 

Breakpoint 1, ngx_http_proxy_rewrite_complex_handler (r=0x7f98e2000a50, 
    h=0x7f98e2001000, prefix=0, len=54, pr=0x7f98e0806ac0)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) print pattern
$1 = len = 22, data = 0x7f98e0803b5b "http://localhost:8080/"
(gdb) print h->value
$2 = len = 54, 
  data = 0x7f98e2002859 "http://localhost:8083/HelloWorld/listFormWithoutHeader"
(gdb) list
2625	
2626	    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) 
2627	        return NGX_ERROR;
2628	    
2629	
2630	    if (pattern.len > len
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
2632	                        pattern.len) != 0)
2633	    
2634	        return NGX_DECLINED;
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2630	    if (pattern.len > len
(gdb) next
2634	        return NGX_DECLINED;

proxy_redirect

增加 proxy_redirect 的配置如下 

        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/;
	       proxy_redirect 80 83;
	       proxy_redirect http://localhost http://127.0.0.1;
        

来到 ngx_http_proxy_rewrite_redirect 的循环处理 

可以看到 这里配置到的两个 proxy_redirect 分别为 80 -> 83, http://localhost -> http://127.0.0.1 

响应的 location 为 http://localhost:8083/HelloWorld/listFormWithoutHeader 

使用 80 -> 83 的配置的时候, 发现 location 不以 80 开头, 放弃处理  

使用 http://localhost -> http://127.0.0.1 配置的时候, 执行了替换处理, 更新之后的 location 为 http://127.0.0.1:8083/HelloWorld/listFormWithoutHeader

Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) bt
#0  ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
    at src/http/modules/ngx_http_proxy_module.c:2630
#1  0x000000010aacb893 in ngx_http_proxy_rewrite_redirect (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0)
    at src/http/modules/ngx_http_proxy_module.c:2524
#2  0x000000010aa894ed in ngx_http_upstream_rewrite_location (
    r=0x7fd48a814050, h=0x7fd48a82ee70, offset=0)
    at src/http/ngx_http_upstream.c:5100
#3  0x000000010aa8d9bd in ngx_http_upstream_process_headers (r=0x7fd48a814050, 
    u=0x7fd48a82e5e0) at src/http/ngx_http_upstream.c:2806
#4  0x000000010aa8ede7 in ngx_http_upstream_process_header (r=0x7fd48a814050, 
    u=0x7fd48a82e5e0) at src/http/ngx_http_upstream.c:2432
#5  0x000000010aa8e7e5 in ngx_http_upstream_handler (ev=0x10ac1f208)
    at src/http/ngx_http_upstream.c:1286
#6  0x000000010aa57c10 in ngx_kqueue_process_events (cycle=0x7fd48a80c050, 
    timer=13648, flags=1) at src/event/modules/ngx_kqueue_module.c:669
#7  0x000000010aa479f6 in ngx_process_events_and_timers (cycle=0x7fd48a80c050)
    at src/event/ngx_event.c:247
#8  0x000000010aa55ac5 in ngx_worker_process_cycle (cycle=0x7fd48a80c050, 
    data=0x0) at src/os/unix/ngx_process_cycle.c:750
#9  0x000000010aa52cfa in ngx_spawn_process (cycle=0x7fd48a80c050, 
    proc=0x10aa55a10 <ngx_worker_process_cycle>, data=0x0, 
    name=0x10ab1da1e "worker process", respawn=-3)
    at src/os/unix/ngx_process.c:199
#10 0x000000010aa54be7 in ngx_start_worker_processes (cycle=0x7fd48a80c050, 
    n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
#11 0x000000010aa54558 in ngx_master_process_cycle (cycle=0x7fd48a80c050)
    at src/os/unix/ngx_process_cycle.c:131
#12 0x000000010aa0c8ba in main (argc=3, argv=0x7ffee51f4548)
    at src/core/nginx.c:382
(gdb) frame 1
#1  0x000000010aacb893 in ngx_http_proxy_rewrite_redirect (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0)
    at src/http/modules/ngx_http_proxy_module.c:2524
2524	        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
(gdb) print pr[0]
$3 = handler = 0x10aac8a80 <ngx_http_proxy_rewrite_complex_handler>, 
  pattern = complex = value = len = 2, data = 0x7fd48a808fef "80", 
      flushes = 0x0, lengths = 0x0, values = 0x0, u = size = 0, 
    regex = 0x2, replacement = value = len = 2, 
      data = 0x7fd48a808ff2 "83", flushes = 0x0, lengths = 0x0, values = 0x0, 
    u = size = 0
(gdb) print pr[1]
$4 = handler = 0x10aac8a80 <ngx_http_proxy_rewrite_complex_handler>, 
  pattern = complex = value = len = 16, 
        data = 0x7fd48a809097 "http://localhost", flushes = 0x0, 
      lengths = 0x0, values = 0x0, u = size = 0, regex = 0x10, 
  replacement = value = len = 16, data = 0x7fd48a8090a8 "http://127.0.0.1", 
    flushes = 0x0, lengths = 0x0, values = 0x0, u = size = 0
(gdb) print plcf->redirects->nelts
$5 = 2
(gdb) print h->value
$6 = len = 54, 
  data = 0x7fd48a82f059 "http://localhost:8083/HelloWorld/listFormWithoutHeader"
(gdb) frame0
Undefined command: "frame0".  Try "help".
(gdb) frame 0
#0  ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2630	    if (pattern.len > len
(gdb) next
2634	        return NGX_DECLINED;
(gdb) next
2642	
(gdb) print pattern
$7 = len = 2, data = 0x7fd48a808fef "80"
(gdb) c
Continuing.

Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050, 
    h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a809128)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) print pattern
$8 = len = 16, data = 0x7fd48a809097 "http://localhost"
(gdb) print h->value 
$9 = len = 54, 
  data = 0x7fd48a82f059 "http://localhost:8083/HelloWorld/listFormWithoutHeader"
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2631	        || ngx_rstrncmp(h->value.data + prefix, pattern.data,
(gdb) next
2632	                        pattern.len) != 0)
(gdb) next
2630	    if (pattern.len > len
(gdb) next
2637	    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) 
(gdb) next
2641	    return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
(gdb) next
2642	
(gdb) print h->value 
$10 = len = 54, 
  data = 0x7fd48a82f059 "http://127.0.0.1:8083/HelloWorld/listFormWithoutHeader"

如果 proxy_pass 携带的有 uri, 并且没有配置 proxy_redirect

可以参见 容器端口映射导致 302 存在问题 以及 nginx 对于 302 的 Location 的重写

的场景

是可以看到 nginx 默认增加了一个 "http://localhost:8080/" -> "/api/" 的配置来处理, nginx 拿到 Location 响应头之后重写为相对路径 

我们可以看一下 

更新配置为如下 

因为携带有 uri, 如果是上游本域的 302 需要将 上游本域信息更新为当前域, 因此有一个 "http://localhost:8080/" -> "/api/" 的替换操作

        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/;
        

可以看到 确实是存在一条  "http://localhost:8080/" -> "/api/" 的替换操作

Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fec8a800450, 
    h=0x7fec8a800a00, prefix=0, len=54, pr=0x7fec89001ec0)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) frame 1
#1  0x0000000100d2d893 in ngx_http_proxy_rewrite_redirect (r=0x7fec8a800450, 
    h=0x7fec8a800a00, prefix=0)
    at src/http/modules/ngx_http_proxy_module.c:2524
2524	        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
(gdb) print pr[0]
$11 = handler = 0x100d2aa80 <ngx_http_proxy_rewrite_complex_handler>, 
  pattern = complex = value = len = 22, 
        data = 0x7fec88008f5b "http://localhost:8080/", flushes = 0x0, 
      lengths = 0x0, values = 0x0, u = size = 0, regex = 0x16, 
  replacement = value = len = 5, data = 0x7fec88007a94 "/api/", 
    flushes = 0x0, lengths = 0x0, values = 0x0, u = size = 0
(gdb) print plcf->redirects->nelts
$12 = 1

这个替换的配置来自于

    proxy_pass 带 uri, "/api/" -> "http://10.60.50.16:8081/"
    proxy_pass 不带 uri, "/" -> "http://10.60.50.16:8081/"

如果手动添加了一条 proxy_redirect 那么默认的这个替换还存在吗?

配置文件如下, 增加了一条无用的 proxy_redirect 

        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/;
	       proxy_redirect 80 83;
        

可以发现 上游服务响应的是上游服务所在的域的地址, 然后 nginx 拿到该地址之后 没有更新域为当前域 

因为 已经存在 proxy_redirect, 没有添加 "http://localhost:8080/" -> "/api/" 的替换操作 

拿到运行时的数据信息, 可以看到没有 "http://localhost:8080/" -> "/api/" 的替换操作 

Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7ffd25000a50, 
    h=0x7ffd25001000, prefix=0, len=54, pr=0x7ffd2400f820)
    at src/http/modules/ngx_http_proxy_module.c:2630
2630	    if (pattern.len > len
(gdb) frame 1
#1  0x0000000107375893 in ngx_http_proxy_rewrite_redirect (r=0x7ffd25000a50, 
    h=0x7ffd25001000, prefix=0)
    at src/http/modules/ngx_http_proxy_module.c:2524
2524	        rc = pr[i].handler(r, h, prefix, len, &pr[i]);
(gdb) print plcf->redirects->nelts
$13 = 1
(gdb) print pr[0]
$14 = handler = 0x107372a80 <ngx_http_proxy_rewrite_complex_handler>, 
  pattern = complex = value = len = 2, data = 0x7ffd2400f7ef "80", 
      flushes = 0x0, lengths = 0x0, values = 0x0, u = size = 0, 
    regex = 0x2, replacement = value = len = 2, 
      data = 0x7ffd2400f7f2 "83", flushes = 0x0, lengths = 0x0, values = 0x0, 
    u = size = 0
(gdb) 

关于 proxy_direct default

配置如下 proxy_redirect default;

运行时拿到的 替换配置如下  "/api/" -> "http://localhost:8080/"

这个和 “如果 proxy_pass 携带的有 uri, 并且没有配置 proxy_redirect” 创建的替换配置一样 

    proxy_pass 带 uri, "/api/" -> "http://10.60.50.16:8081/"
    proxy_pass 不带 uri, "/" -> "http://10.60.50.16:8081/"

以上是关于06 nginx 处理转发其他域的处理 以及 proxy_redirect的主要内容,如果未能解决你的问题,请参考以下文章

06 nginx 处理转发其他域的处理 以及 proxy_redirect

什么是跨域以及处理跨域的方案?

nginx负载均衡

使用 nginx 重定向某个域的所有流量

Nginx 反向代理原理以及和LVS的区别

使用nginx处理静态资源请求,其余交给node