美文网首页
第10章 Nginx反向代理

第10章 Nginx反向代理

作者: hubery_zhao | 来源:发表于2023-03-05 09:14 被阅读0次

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

    相关文章

      网友评论

          本文标题:第10章 Nginx反向代理

          本文链接:https://www.haomeiwen.com/subject/uqqgkdtx.html