当前位置: 首页>后端>正文

第10章 Nginx反向代理

Nginx 最强大的地方是在于其 HTTP 请求的反向代理,也即常说的七层反向代理。在这一层代理中,通过 Nginx 框架提供的相关配置,我们能在该层将发送过来的 http 协议转换成各种其他的协议比如 fastcgi 协议、uwsgi 协议、grpc、http(高版本协议)、websocket 协议等。这样使用 Nginx 框架,我们可以支持多种应用服务(java web、python web 等)的反向代理
Nginx 从 1.9.0 开始,新增加了一个 stream 模块,用来实现四层协议( TCP 或UDP 协议)的转发、代理或者负载均衡。这层比较简单,只是单纯将 TCP 或 UDP层的流量转发到上游服务器中

Nginx的四层反向代理

前面我们刚开始使用 Nginx 时只是用了 http 指令块,因为是针对 http 请求进行处理。这进行的是四层反向代理,转发 TCP 或者 UDP 协议的报文。针对该层的处理,Nginx 是使用了 stream 模块进行处理,对应的是 stream 指令块,它和 http 指令块非类似,用法几乎一致。stream 指令块里面可以包含 server 指令块,server 指令块里面也可以包含 listen 指令块指定监听的端口、还可以包含 proxy_pass指令,对该端口监听的 tcp 或者 udp 报文进行转发。总之,和 http 指令块大部分用法一致

...
stream {
    ...
    server {
    listen 1234;
    # 转发四层流量
    proxy_pass  192.168.232.132:1234;
    }
    …
}
...

nginx-1.11.2 版开始, ngx_stream_core_module,也同 http 模块一样支持变量,部分支持变量如下,这和 http 模块也是类似的,甚至连变量名都非常相似

$binary_remote_addr     二进制格式的客户端地址
$baytes_received        从客户端接收的字节数
$bytes_sent             发往客户端的字节数
$hostname               连接域名
$msec                   毫秒精度的当前时间
$nginx_version          nginx版本
$pid                    worker进程号
$protocol               通信协议(udp 、 tcp)
$remote_addr            客户端ip
$remote_port            客户端端口
$server_addr            接受链接的服务器ip,计算此变量需要一次系统调用。所以避免系统调用,在listen指令中指定地址并且使用bind参数。
$server_port            请求到达服务器的端口号
$server_name            服务器名称
$session_time           毫秒精度的回话时间(版本1.11.4开始)
$status                 会话状态(版本1.11.4开始),可以是以下几个值(200成功 ; 400不能正常解析客户端数据 ; 403 禁止访问 ; 500 服务器内部错误 ; 502网关错误 , 比如上游服务器无法链接 ; 503 服务不可用,比如由于限制链接等措施导致 )
$time_iso8601           ISO 8601时间格式
$time_local             普通日志格式时间戳

Nginx七层反向代理

http 协议的反向代理

nginx七层反向代理处理的是http请求,对应的http协议,如果只是转发http请求, 可以简单使用proxy_pass指令 , 与四层方向代理类似

# 在转发 http请求时, url必须以http或者https开头
Syntax: proxy_pass url;
Default: - 
Context: location , if in location , limit_except

在使用proxy_pass指令对http 或者 https 协议进行反向代理时, 需要注意以下问题:
在url不屑道rui 时,会将对应的url 直接转发到上游服务器
在url 携带 uri 时 , 会将location 参数中匹配上的那一段替换为该url
示例配置

...
http {
    server {
        listen 8000;
        location /test {
            proxy_pass http://ip:port/xyz;
        }
    }

    server {
        listen 9000;
        location /test {
            proxy_pass http://ip:port;
        }
    }
}
...

在代理 http 请求 http://主机 ip:8000/test/abc 时,由于 proxy_pass 指令后面的 URL 带了 /xyz 这样的路径,所以转发的请求是 http:// ip:port/xyz/abc,其中匹配的 /test 已经被替换掉了。而对于第二个 http 请求 http://主机ip:9000/test/abc 时,由于 proxy_pass 指令后面直接是上游服务器地址,没有带URI,所以转发的请求为 http://ip:port/test/abc,其中匹配到的/test 并没有被替换

实验

四层反向代理示例

在nginx配置中加入如下内容

…
stream {
        server {
                listen 8080;
                return '8080 server get ip:$remote_addr!\n';
        }

        server {
        listen 8081;
        # 注意 , 只有写iP和  port ,不要加上“http:”等这类前缀,这是四层协议
        proxy_pass  127.0.0.1:8080;
        }
}
…

Curl 一下

[root@localhost data]# curl 192.168.232.13:8080
8080 server get ip:192.168.232.14!
curl: (56) Recv failure: Connection reset by peer
[root@localhost data]# curl 192.168.232.13:8081
8080 server get ip:127.0.0.1!

telnet一下

[root@localhost data]# telnet   192.168.232.13 8080
Trying 192.168.232.13...
Connected to 192.168.232.13.
Escape character is '^]'.
8080 server get ip:192.168.232.14!
Connection closed by foreign host.
[root@localhost data]# telnet   192.168.232.13 8081
Trying 192.168.232.13...
Connected to 192.168.232.13.
Escape character is '^]'.
8080 server get ip:127.0.0.1!
Connection closed by foreign host.

结论: 访问 30 端口时,nginx 帮我们转发 tcp 层流量到 3000 端口,最后
返回了相关响应字符串。

七层反向代理示例

nginx.conf 中加入如下

user  root;
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       8000;
        server_name  localhost;
        return 200 '$uri\n';
        }

    server {
        listen 8001;
        location /test {
            proxy_pass http://127.0.0.1:8000;
        }
    }

    server {
        listen 8002;
        location /test {
            proxy_pass http://127.0.0.1:8000/xyz;
        }
    }
}

启动 nginx 后,通过请求服务器的 8000 端口,我们可以知道请求的 uri 值,然后测试经过两种类型的 proxy_pass 配置后最后的 uri 的值。

[root@localhost data]# curl 192.168.232.13:8000
/
[root@localhost data]# curl 192.168.232.13:8000/abc
/abc
[root@localhost data]# curl 192.168.232.13:8001/test
/test
[root@localhost data]# curl 192.168.232.13:8001/test/abc
/test/abc
[root@localhost data]# curl 192.168.232.13:8002/test
/xyz
[root@localhost data]# curl 192.168.232.13:8002/test/abc
/xyz/abc

Websocket 的反向代理

Websocket 是目前比较成熟的技术了, websocket 协议为创建客户端和服务器端需要实时双向通讯的webapp提供了一个选择, 服务器可以向浏览器推送相关消息,这样在前段实现的某个页面中我么可以及时看到服务器的状态变化,而不是实时刷新去获取后台信息 。 目前大部分浏览器都支持websocket协议,例如chrome firefox iE safari opera等 ,数量越来越多。 更多的服务器框架现在也支持websocket 。 在js 、Java、Python中提供websocket开发库,这也使得websocket 协议的广泛应用于web服务的开发中,当然作为浏览器和后台服务的中间代理nginx也一定支持websocket , 这样才能更好的完成代理的功能, 在nginx中通过 ngx_http_proxy_module模块实现websocket反向代理功能。
简单配置如下:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”

上述配置表示将转发的协议提升至 1.1, 同时在转发的 http 请求的头部中加上如下配置

Upgrade : websocket
Connection : upgrade

这两个字段表示请求服务器升级协议为websocket,上游服务器处理完请求后,相应如下报文

HTTP/1.1 101 Switching Protocoli
Upgrade : websocket
Connection: upgrade

这个响应是告诉客户端已成功切换协议,升级为 Websocket 协议。握手成功之后,服务器端和客户端便角色对等,就像普通的 Socket 一样,能够双向通信。不再进行 HTTP 的交互,而是开始 WebSocket 的数据帧协议实现数据交换。默认情况下,连接将会在无数据传输 60 秒后关闭,proxy_read_timeout 参数可以延长这个时间。源站通过定期发送 ping 帧以保持连接并确认连接是否还在使用。
通过以上简简单单的三行配置,我们就能在 Nginx 中轻松实现 Websocket 的反向代理,这也说明了 Nginx

wsgi 的反向代理

首先,理清楚几个概念:

WSGI:全称是 Web Server Gateway Interface,WSGI 只是一种规范,描述 web server如何与 web application 通信的规范。要实现 WSGI 协议,必须同时实现 web server和 web application,当前运行在 WSGI 协议之上的 web 框架有 Flask, Django,这也是目前最流行的 python web 框架。

uwsgi:与 WSGI 一样是一种通信协议,是 uWSGI 服务器的独占协议,用于定义传输信息的类型(type of information),每一个 uwsgi packet 前 4byte 为传输信息类型的描述。

uWSGI:是一个 web 服务器,实现了 WSGI 协议、uwsgi 协议、http 协议等

WSGI 协议其实是定义了一种 server 与 application 解耦的规范,即可以有多个实现 WSGI server 的服务器,也可以有多个实现 WSGI application 的框架,那么就可以选择任意的 server 和 application 组合实现自己的 web 应用。Django,Flask 框架都有自己实现的简单的 WSGI server,一般用于服务器调试,生产环境下直接使用 WSGI server。
Nginx 中将 http 协议的报文转换成 uwsgi 协议的报文,只需要使用 uwsgi_pass指令即可。和 proxy_pass 指令类似,前者转发为 uwsgi 协议的报文,后者代理转发 http 协议的报文。其余用法一致。

Syntax: uwsgi_pass [protocol://]address;
Default:    —
Context:    location, if in location

官方示例

uwsgi_pass localhost:9000;
uwsgi_pass uwsgi://localhost:9000;
uwsgi_pass suwsgi://[2001:db8::1]:9090;

示例

...
http {
    ...
    server {
        listen 9000;
        
        location / {
        # 包含uwsgi 请求描述文件
        include uwsgi_parame;
        # 配置请求传递, socket地址
        uwsgi_pass 127.0.0.1:9000;
        }
    }
}
...

https://www.xamrdz.com/backend/3p91934362.html

相关文章: