你知道 HTTP 代理/代理缓存吗?

如何理解 HTTP 代理?

我们知道在 HTTP 是基于请求-响应模型的协议,一般由客户端发请求,服务器来进行响应。

当然,也有特殊情况,就是代理服务器的情况。引入代理之后,作为代理的服务器相当于一个中间人的角色,对于客户端而言,表现为服务器进行响应;而对于源服务器,表现为客户端发起请求,具有双重身份

代理服务器的功能

  1. 负载均衡。客户端的请求只会先到达代理服务器,后面到底有多少源服务器,IP 都是多少,客户端是不知道的。因此,这个代理服务器可以拿到这个请求之后,可以通过特定的算法分发给不同的源服务器,让各台源服务器的负载尽量平均。当然,这样的算法有很多,包括随机算法轮询一致性hashLRU(最近最少使用)等等。
  2. 保障安全。利用心跳机制监控后台的服务器,一旦发现故障机就将其踢出集群。并且对于上下行的数据进行过滤,对非法 IP 限流,这些都是代理服务器的工作。
  3. 缓存代理。将内容缓存到代理服务器,使得客户端可以直接从代理服务器获得而不用到源服务器那里。

相关头部字段

  • Via

代理服务器需要标明自己的身份,在 HTTP 传输中留下自己的痕迹,怎么办呢? 通过Via字段来记录。

举个例子

现在中间有两台代理服务器,在客户端发送请求后会经历这样一个过程:

1
客户端 -> 代理1 -> 代理2 -> 源服务器

在源服务器收到请求后,会在请求头拿到这个字段:

1
Via: proxy_server1, proxy_server2

而源服务器响应时,最终在客户端会拿到这样的响应头:

1
Via: proxy_server2, proxy_server1

可以看到,Via中代理的顺序即为在 HTTP 传输中报文传达的顺序。

  • X-Forwarded-For

字面意思就是为谁转发, 它记录的是请求方的IP**地址 ** (注意,和Via区分开,X-Forwarded-For记录的是请求方这一个IP)。

  • X-Real-IP : 是一种获取用户真实 IP 的字段,不管中间经过多少代理,这个字段始终记录最初的客户端的IP

  • X-Forwarded-Host,记录客户端 (不包括代理) 的域名

  • X-Forwarded-Proto : 记录客户端(不包括代理)的协议名

X-Forwarded-For产生的问题

前面可以看到,X-Forwarded-For这个字段记录的是请求方的 IP,这意味着每经过一个不同的代理,这个字段的名字都要变,从客户端到代理1,这个字段是客户端的 IP,从代理1到代理2,这个字段就变为了代理1的 IP。

但是这会产生两个问题:

  • 意味着代理必须解析 HTTP 请求头,然后修改,比直接转发数据性能下降。

  • 在 HTTPS 通信加密的过程中,原始报文是不允许修改的。

由此产生了代理协议,一般使用明文版本,只需要在 HTTP 请求行上面加上这样格式的文本即可:

1
2
3
4
// PROXY + TCP4/TCP6 + 请求方地址 + 接收方地址 + 请求端口 + 接收端口
PROXY TCP4 0.0.0.1 0.0.0.2 1111 2222
GET / HTTP/1.1
...

这样就可以解决X-Forwarded-For带来的问题了。

代理缓存是什么?

我们先来回顾一下强缓存和协商缓存

首先通过 Cache-Control 验证强缓存是否可用

  • 如果强缓存可用,直接使用
  • 否则进入协商缓存,即发送 HTTP 请求,服务器通过请求头中的If-Modified-Since或者If-None-Match这些条件请求字段检查资源是否更新
    • 若资源更新,返回资源和200状态码
    • 否则,返回304,告诉浏览器直接从缓存获取资源

我们说说另外一种缓存方式: 代理缓存。

为什么产生代理缓存?

对于源服务器来说,它也是有缓存的,比如Redis, Memcache,但对于 HTTP 缓存来说,如果每次客户端缓存失效都要到源服务器获取,那给源服务器的压力是很大的。

由此引入了缓存代理的机制。让代理服务器接管一部分的服务端HTTP缓存,客户端缓存过期后就近到代理缓存中获取,代理缓存过期了才请求源服务器,这样流量巨大的时候能明显降低源服务器的压力。

那缓存代理究竟是如何做到的呢?

总的来说,缓存代理的控制分为两部分,一部分是源服务器端的控制,一部分是客户端的控制

源服务器的缓存控制

  • privatepublic

在源服务器的响应头中,会加上Cache-Control这个字段进行缓存控制字段,那么它的值当中可以加入private或者public表示是否允许代理服务器缓存,前者禁止,后者为允许。

比如对于一些非常私密的数据,如果缓存到代理服务器,别人直接访问代理就可以拿到这些数据,是非常危险的,因此对于这些数据一般是不会允许代理服务器进行缓存的,将响应头部的Cache-Control设为private,而不是public

  • proxy-revalidate

must-revalidate的意思是客户端缓存过期就去源服务器获取

proxy-revalidate则表示代理服务器的缓存过期后到源服务器获取

  • s-maxage

限定了缓存在代理服务器中可以存放多久,和限制客户端缓存时间的max-age并不冲突。

举例 : 源服务器在响应头中加入这样一个字段:

1
Cache-Control: public, max-age=1000, s-maxage=2000

相当于源服务器说: 我这个响应是允许代理服务器缓存的,客户端缓存过期了到代理中拿,并且在客户端的缓存时间为 1000 秒,在代理服务器中的缓存时间为 2000 s。

客户端的缓存控制

  • max-stalemin-fresh :在客户端的请求头中,可以加入这两个字段,来对代理服务器上的缓存进行宽容和限制操作。
1
max-stale: 5

表示客户端到代理服务器上拿缓存的时候,即使代理缓存过期了也不要紧,只要过期时间在5秒之内,还是可以从代理中获取的。

1
min-fresh: 5

表示代理缓存需要一定的新鲜度,不要等到缓存刚好到期再拿,一定要在到期前 5 秒之前的时间拿,否则拿不到。

  • only-if-cached

这个字段加上后表示客户端只会接受代理缓存,而不会接受源服务器的响应。如果代理缓存无效,则直接返回504(Gateway Timeout)。


你知道 HTTP 代理/代理缓存吗?
http://example.com/2023/11/13/浏览器代理缓存/
作者
weirdo
发布于
2023年11月13日
更新于
2023年11月13日
许可协议