为什么我们在负载均衡器前面放置了 CDN(以及为什么 cookies 才是真正的问题)
Source: Dev.to


上下文
我们正处于两个并行的变更之中:一次网站的全面重新设计和一次服务器迁移。这两项都有明确的计划范围,进展顺利……直到我们注意到在迁移后,一部分用户开始出现故障。并非全部用户,也不是始终如一。但已经足以构成真实的问题。
罪魁祸首:cookies。
问题
之前的服务器随着时间累积了 cookies。新服务器引入了自己的 cookies。当用户在迁移过程中访问——仍在加载旧 cookies 并收到重新设计站点的新 cookies——时,合并后的负载超过了负载均衡器接受的最大大小:16 KB。
此限制并非随意。它是大多数负载均衡器和 CDN 在 HTTP 层强制执行的硬性约束。一旦超出该限制,请求会悄然失败或被拒绝。
更复杂的是:域名并不在我们直接控制之下。它通过外部 DNS 提供商的 CNAME 指向负载均衡器。这意味着我们无法在不与第三方协调的情况下更改路由,也不能直接修改负载均衡器的配置。
为什么很困难
复杂的并不是技术修复,而是主要的限制:在更改期间我们不能对用户产生任何影响。
错误 too_many_redirects 只会在拥有大 cookie 负载的用户出现——特定会话、特定浏览器、旧系统与新系统之间的特定路径——。一次完整的切换会让所有人面临风险。回滚也不是干净的选项,因为迁移已经部分上线。
我们需要一个解决方案,使其能够:
- 在请求到达负载均衡器之前进行拦截。
- 基于白名单清理 cookie(仅保留必要的)。
- 只修改请求,不触发重定向。
- 能够在不更改域名注册商和负载均衡器的情况下部署。
解决方案
我们在 DNS 与负载均衡器之间引入了 CDN 层,并将 CNAME 更改为指向 CDN,而不是直接指向负载均衡器。
在 CDN 的边缘,我们附加了一个 Lambda@Edge 函数,它会在每个传入请求上执行。它的工作很简单:读取 Cookie,应用白名单,删除其他所有内容,然后将干净的请求向下转发。
没有重定向。白名单中的 Cookie 不会丢失会话。无需更改应用代码或负载均衡器的配置。
最终架构如下:
[DNS → Firewall] → CDN → (Lambda@Edge) → Balanceador → ClusterCDN 为我们提供了所需的边缘执行层。Lambda@Edge 函数让我们能够在请求层面进行精确控制。而且我们只修改请求的 header ——从不修改响应——,因此 too_many_redirects 循环从未触发。
我们学到的
- 了解哪些是你能控制的,哪些不是。 域名不在我们的注册范围内。负载均衡器有固定的限制。用户已有的 cookie 不是可协商的。 在这些限制内工作塑造了每个决策。
- CDN 不是目标,而是使能器。 我们添加它不是为了性能或缓存。我们添加它是因为它在我们需要的地方提供了可编程层:用户和负载均衡器之间。
- 白名单比黑名单更安全。 与其尝试识别导致溢出的 cookie 并只删除那些,不如反转思路:定义哪些应该保留,删除其余的。更易维护,部署更安全。
- 无停机的更改是架构决策,而不是部署技巧。 我们能够在不影响用户的情况下完成此更改并非运气:而是因为架构允许我们在不修改底层任何内容的情况下插入新层。
结论
好的架构并不总是关于最优雅的设计。有时是要了解你的系统之间如何相互作用,识别你真正拥有的杠杆点,并根据你当前的限制——而不是你希望拥有的限制——选择正确的资源。
一个 CDN 和一个小的 Lambda 函数解决了看似是 cookie 的问题。但它们真正解决的是控制问题。