当您键入 things.news 时会发生什么?

robertu
·
(修改过)
·
IPFS
·
本文首次发表在Matters Engineering Wiki上。

在之前的文章中,我们先睹了 Matters.news 的架构、我们用于构建网站和 API 的技术堆栈,以及我们如何设计组件并使它们顺利协同工作的详细信息。

在本文中,我们从另一个角度出发,从网络请求开始。首先,我们简要概述了服务器如何响应用户请求,然后,我们更深入地研究了性能优化。

网络请求概述

网络请求的生命周期

客户

客户端(通常是 Web 浏览器)通过 HTTP 请求与服务器通信。

当用户在地址栏中输入matters.news时,浏览器首先会联系 DNS 解析器并获取该域后面的 IP 地址,然后向该 IP 地址发送请求,等待响应,并将结果呈现在浏览器。

边缘

在网络边缘,我们使用 Cloudflare 来加速网络请求并保护我们的源服务器,所有 DNS 查找和请求首先由 Cloudflare 处理。

DNS 查找将解析到最近的 Cloudflare 反向代理,而不是我们的源服务器。反向代理可以过滤掉恶意请求,并通过缓存和智能路由提供性能优势。

源服务器

SSR(服务器端渲染)服务器将预渲染的 HTML 发送到客户端。在内部,许多 API 调用代表客户端发送到 API 服务器。

一旦 API 请求 ( server.matters.news ) 到达源 API 服务器,如果之前缓存了相同的请求,则由缓存服务器提供服务,否则由数据库提供服务。

端到端

让我们把它们包起来。

  1. 用户打开一个以things.news域开头的 URL;
  2. 浏览器发出 HTTP 请求,该请求通过 Cloudflare 边缘网络发送到我们的SSR 服务器
  3. SSR 服务器发送 HTML 并由浏览器呈现;
  4. 用户开始浏览其他页面或与网站交互;
  5. 浏览器进行后续 API 调用,通过 Cloudflare 边缘网络到我们的API 服务器

网络分析

网络性能是网站性能的核心。要进行优化,我们首先需要了解网络流量。发送了多少请求?服务器的响应速度有多快?...

我们将介绍一些随着时间的推移不断监控和改进的关键指标。

总请求

一段时间内的总请求是了解网络状态的最基本和最直观的指标。

Cloudflare Analytics 显示有多少请求发送到边缘,而 AWS Elastic Beanstalk 和 Apollo Studio 显示有多少请求到达源服务器。

响应时间

响应时间是服务器为 API 处理请求并响应客户端所花费的时间。

我们可以知道所有请求或单个请求类型(GraphQL 操作)的响应时间百分比(例如 P50、P95)。这对于识别哪个 GraphQL 操作是响应时间慢的“受害者”非常有帮助。

缓存命中率

Cache Hit Ratio 告诉我们有多少百分比的请求在没有命中原始 API 服务器或数据库的情况下得到了服务。越高,响应越快。

资源利用率

我们的原始服务器和数据库不是无限自动扩展的,因此 CPU 和内存利用率对性能和可靠性很重要。

性能优化

许多工具和服务可以帮助我们提高网络性能,尤其是在应用层,以下是我们采取的一些行动。

防火墙和智能路由

在网络边缘,我们使用Cloudflare WAF过滤恶意流量并保护我们的源站服务器。防火墙规则可以通过基于地理位置、IP 地址、HTTP 标头过滤请求来控制传入流量。和速率限制以限制最大频率。

由于 API 响应是动态实时数据,很难被 Cloudflare CDN 缓存,因此我们启用Cloudflare Argo根据网络条件路由流量,选择最快和最可靠的网络路径到我们的源服务器。

内容分发网络

与 API 不同,许多用户生成的内容 (UGC) 是静态的,例如图像,这些内容一旦创建就不会更改。我们的静态内容分发到 AWS CloudFront 的全球网络,缓存并根据地理位置提供给用户。

内存缓存

API 请求命中源服务器,响应数据将被缓存到缓存(Redis)服务器,然后再发送回客户端。

我们使用AWS ElastiCache for Redis作为内存缓存,以实现最佳 IOPS 和吞吐量性能。

在 Apollo 的 GraphQL 插件之上,我们还构建了一组工具来控制缓存和失效。我们在计算机科学中两件困难的事情之一上做出了努力。

公共/私人拆分

API 请求(GraphQL 操作)由访问者或登录用户发送。来自访问者的请求被公开缓存,而登录用户被私下缓存。由于私有数据只能由单个登录用户访问,因此我们通过带有用户 ID 的自定义缓存键来缓存 API 响应。

此外,由于matters.news是一个公共平台,大部分用户登陆的都是公共页面,比如首页和文章页面,这些页面由公共数据和少量私人数据(例如我是否关注作者)组成。我们将 API 请求一分为二,一是查询公共数据,二是查询私有数据。

现在,提高了公共查询的缓存命中率,减少了源站处理私有查询的负载。

深度限制

根据复杂性,不同的请求需要不同的处理时间。我们通过深度限制 GraphQL 查询的复杂性,它还保护了我们的 API 服务器。

索引

最后,如果一个请求是缓存未命中,它需要从数据库中查询数据。我们会持续监控最高负载查询并添加索引以提高性能并减轻负载。

概括

优化网络性能的方法还有很多,但我们不会在这里介绍它们。最终,这一切都归结为要实现的目标:最小化请求数量和复杂性,最大化缓存命中率,以及最小化处理时间。

CC BY-NC-ND 2.0 授权

喜欢我的作品吗?别忘了给予支持与赞赏,让我知道在创作的路上有你陪伴,一起延续这份热忱!