第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;
        }
    }
}
...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容