出于这样或者那样的原因,Web 开发中总是需要代理,即客户端的请求不直接到服务器而是要经过某种途径访问,比如VPN那样的代理。
对于 Web 开发中的代理主要有两种:一种是正向的,另外一种是反向的。
# 正向代理 ↵
这个最为直观,比如翻墙就是一种正向代理:境内的电脑需要用 SS 这种代理来访问不存在的网站,即正向代理夹在服务端和客户端之间,客户端访问代理,让代理服务器去请求服务端。
正向代理源于客户端的网络需求,会隐藏真实的客户端。
# 反向代理 ↵
虽说反向代理做的事情跟正向的很像,但还是有所不同的,比如负载均衡就用到了反向代理:单台服务器撑不住,所以搞了很多个服务器,然后让其中一台作为对外显示的服务器,这个服务器吧全部客户端的请求分发给不同内网的服务器来达到负载均衡的目的,对于客户端来说它访问 test.cc 跟原来没有什么不同,而实际上处理客户端请求的服务器在不同时间点上很可能是不同的。
反向代理源于服务端的网络需求,会隐藏真实的服务端。
# 区别 ↵
主要还是两句话:
正向代理源于客户端的网络需求,会隐藏真实的客户端。
反向代理源于服务端的网络需求,会隐藏真实的服务端。
再来一张图

代理靠左点就是正向的,靠右点就是反向的
其实无须过度区分这些,不论正反,请求都会经过中间的代理层,只要配通了、达到目的了就好。
# 工程应用 ↵
实际项目中反向代理用得最多,因为反向代理针对的是服务端的真实需求,我在实际使用的时候是利用 Nginx 来完成反向代理的,以下是一些用到代理的例子。
# vhost 虚拟站点
一个服务器有时候需要挂不止一个站点,也就是说有多个域名指向了这个站点,这时候需要用到 Nginx 的虚拟站点配置,别紧张这也是一种反向代理过程,因为这是服务端的需求,而且隐藏了服务器 (多个域名看起来好像不同的服务器提供的网站、而实际上却是同一个服务器提供的)
具体的而言,需要编写类似如下配置到你的 nginx 配置中:
00server { 01 server_name test.cc; 02 listen 80; 03 root /xxx/xxx/xx; 04 index index.html index.htm; 05}
我配置服务器的时候,通常使用 Nginx 作为网关层,让 Nginx 建立不同 VHOST,然后具体的对应 VHOST 上不同路径代理到不同端口上。
# 跨域的问题
如果静态文件在 test.cc 上,而接口在 test.cc:3000 上,需要通过反向代理的方式来规避跨域,其实只是:
将 test.cc/api/
代理到 test.cc:3000/api/
这里需要修改 VHOST 的配置:
00server { 01 server_name test.cc; 02 listen 80; 03 root /xxx/xxx/xx; 04 index index.html index.htm; 05 06 location /api { 07 proxy_pass http://127.0.0.1:3000; 08 proxy_http_version 1.1; 09 proxy_set_header Upgrade $http_upgrade; 10 proxy_set_header Host $host; 11 proxy_cache_bypass $http_upgrade; 12 } 13}
重启后,访问 test.cc/api 的时候,会返回访问 test.cc:3000/api 的结果,对于前端来说,访问的还是 test.cc/api 但实际上,服务器上的 Nginx 接到请求后将其代理到 3000 端口上了,对于客户端来说,隐藏了真实的服务器(端口)。
# HTTPS
续接上面,3000 端口无须设置 SSL 只需要 test.cc 是 https 的,那么访问 https://test.cc/api/* 的时候依然是 HTTPS 的,即使实际在服务器内部是一次简单的端口转发。
# 负载均衡
虽然没有在项目中应用过负载均衡,但是对它的原理比较清楚:
- 对于一个服务器
- 上面起了很多个 Node 实例在不同端口 3000 3001 3002 3003 等
- 客户端访问它的 80 端口
- 将 80 端口接到的请求转发到 3000 3001 3002 3003 等端口上完成负载均衡
上述过程就是一次简单的负载均衡 (针对一台服务器、提高多核处理器利用率),不过看过很多文章,一般的负载均衡是用在集群里的,整个集群有一个网关,这个网关就是负载均衡器,用来分发请求。 (详见腾讯云提供的负载均衡服务)
# DNS 和 CDN
CDN 中文名是内容分发网络,它可以极大的提高静态资源的分发速度,基本上我还没见过大型网站不用这个的 。。。
基于如下的考虑:
- 服务器离客户端的地理距离越近,加载速度越快
- 可以在不同地理位置建立静态资源服务器,在广州建一个、深圳建一个、华北建一个等等
- 不同地理位置的客户端的 DNS 通常是当地的 DNS
- 联系当地的 DNS 让它们吧 img.test.cc 解析到当地的静态资源站
可以看到深圳地区访问 img.test.cc DNS 解析到位于深圳的静态资源服务器,广州地区访问 img.test.cc DNS 解析到位于广州的静态资源服务器这样。
这是一个典型的反向代理:用户访问 img.test.cc 但是不必知道到底是访问了深圳的那个还是广州的那个,也就是说 DNS 隐藏了真实的服务器,DNS 充当了负载均衡器。
DNS 在 CDN 中是主角,一旦 DNS 有问题,CDN 的速度可能会比原来直接访问还要慢。
CDN 还涉及到很多分布式问题,比如不同地域的静态服务器的增量同步问题,如何通知 DNS 解析中心刷新dns记录等等。。
# HTTP 接口代理
反向代理除了可以代理到本地的某个端口,甚至可以代理到其他的服务器上。利用这个可以完成如下的需求:
访问 http://test.cc/baidu-api/
代理到 http://api.baidu.com/
去,进而解决前端直接访问 api.baidu.com 出现跨域的问题。可以用 nginx 做也可以用 express 的 http-proxy-middleware 来做:
00const express = require('express') 01 , proxy = require('http-proxy-middleware') 02 , app = express() 03 04app.use('/baidu-api', proxy({ 05 target: 'http://api.baidu.com', 06 changeOrigin: true, 07 pathRewrite: { 08 '^/baidu-api': '' 09 } 10}))
这样会隐藏真实的 baidu 服务器,属于反向代理,不是正向代理。
但是,对于 baidu 服务器来说,客户端被node中间价真实地隐藏了,并不是反向代理。
这种情况下其实都对:反向代理客户端的请求,将其正向代理到另外一个服务器上。