订阅以接收新文章的通知:

我们的构建过程:Cloudflare Radar 2.0 背后的技术

2022/11/17

22 分钟阅读时间
How we built it: the technology behind Cloudflare Radar 2.0

Radar 2.0 构建在 Radar 1.0 的基础上,在上个月的 Cloudflare Birthday Week 期间作为完整的产品改进发布。希望用户可以更轻松地发现洞见,浏览数据,在整体上提供更好、更快的用户体验。

我们正在构建 Supercloud。现在,Cloudflare 的产品包括网络、安全、访问控制、计算、存储等方面的数百种功能。

这篇博客将从工程角度解释我们如何构建新的 Radar。我们希望借此证明,以我们的堆栈为基础,任何人都可以构建一个有点复杂、涉及苛刻的要求和多个架构层的网站,整个过程非常简单。

希望这能激励其他开发人员抛弃传统的软件架构,使用更有效的现代技术来构建应用程序。

架构概览

下图是 Radar 2.0 架构的鸟瞰图。如图所示,架构主要分为三层。

  • 核心层保存数据湖、数据探索工具和后端 API。
  • Cloudflare 网络层托管和运行 Radar,并为公共 API 提供服务。
  • 客户端层基本上包括浏览器中运行的其他所有内容。我们称之为 Radar Web 应用程序。

如您所见,到处都使用了 Cloudflare 产品。它们提供了托管和安全地大规模运行代码的基础资源,还提供了端到端运行应用程序所需的其他构建块。

通过让这些功能随时可用,并紧密集成到我们的生态系统和工具中,只需点击一下、输入几行代码,工程团队就不必不断地白费力气做重复工作,可以把时间用在关键之处:应用程序的逻辑。

我们来深入了解一下。

Cloudflare 页面

Radar 2.0 是使用我们以开发人员为中心的网站托管平台——Cloudflare Pages 部署的。在早期阶段,您只能在 Pages 上托管静态资产,这对许多用例(包括与 HugoJekyll Gatsby 等静态网站生成器集成)都很有帮助。但当应用程序需要某种服务器端计算或使用单一部署的高级逻辑时,这并不能解决问题。

幸运的是,Pages 最近增加了对运行自定义 Workers 脚本的支持。您现可利用 Functions 运行服务器端代码,启用通常使用独立 Worker 实施的任何类型的动态功能。

Cloudflare Pages Functions 还支持您使用 Durable ObjectsKVR2D1,就像普通 Worker 一样。我们在开发人员文档中提供了一些出色的文档,介绍如何执行此操作等内容。此外,我们的团队还撰写了一篇关于如何构建全栈应用程序的博客,其中详细介绍了所有步骤。

Radar 2.0 需要服务器端功能,因为以下两个原因:

  • 渲染 Radar,并运行 Remix 的服务器端。
  • 实施前端 API 并为其提供服务。

Remix 和服务器端渲染

我们在 Radar 2.0 上搭配 Cloudflare Pages 使用 Remix。

Remix 遵循服务器/客户端模式,以您无法控制用户的网络为前提,因此 Web 应用程序必须减少通过电线发送 Javascript、CSS 和 JSON 的数量,因此需要将一些逻辑转移到服务器上。

在这种情况下,客户端浏览器将得到预先渲染的 DOM 组件,以及预先抓取的 API 调用结果,其中包含适量的 JSON、Javascript 和 CSS 代码,并根据 UI 需求进行适当调整。请参阅技术说明了解更多详情。

通常,Remix 需要一个 Node.js 服务器来完成所有工作,但您可能不知道:它还可以在 Cloudflare Workers 和 Pages 上运行

使用 Cloudflare Pages 在 Workers 上运行 Remix 服务器的代码如下:

import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";

const handleRequest = createPagesFunctionHandler({
  build: {
    ...build,
    publicPath: "/build/",
    assetsBuildDirectory: "public/build",
  },
  mode: process.env.NODE_ENV,
  getLoadContext: (context) => ({
    ...context.env,
    CF: (context.request as any).cf as IncomingRequestCfProperties | undefined,
  }),
});

const handler: ExportedHandler<Env> = {
  fetch: async (req, env, ctx) => {
    const r = new Request(req);
    return handleRequest({
      env,
      params: {},
      request: r,
      waitUntil: ctx.waitUntil,
      next: () => {
        throw new Error("next() called in Worker");
      },
      functionPath: "",
      data: undefined,
    });
  },
};

在 Remix 中,路由处理用户与应用程序的交互与更改(例如点击菜单选项)。Remix 路由可以有一个加载器、一个操作和一个默认导出。加载器处理用于抓取数据(GET 方法)的 API 调用。操作处理向服务器传输的提交(POST、PUT、PATCH、DELETE 方法)并返回响应。默认导出处理返回给该路由的 React 中的 UI 代码。没有默认导出的路由则仅返回数据。

由于 Remix 同时在服务器和客户端运行,它可以变得很智能,知道哪些可以在服务器端预先抓取和计算,哪些必须通过网络连接,从而一一优化,提升性能和响应。

以下为一个 Radar 路由的示例。为了便于阅读,对中断中心页面进行了简化。

import type { MetaFunction } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { type LoaderArgs } from "@remix-run/server-runtime";

export async function loader(args: LoaderArgs) {
  const ssr = await initialFetch(SSR_CHARTS, args);
  return { ssr, };
}

export default function Outages() {
  const { ssr } = useLoaderData<typeof loader>();

  return (
    <Page
      filters={["timerange"]}
      title={
        <>
          <Svg use="icon-outages" />
          {t("nav.main.outage-center")}
        </>
      }
    >
      <Grid columns={[1, 1, 1, 1]}>
        <Card.Article colspan={[1, 1, 1, 1]} rowspan={[1, 1, 1, 1]}>
          <Card.Section>
            <Components.InternetOutagesChoropleth ssr={ssr} />
          </Card.Section>
          <Divider />
          <Card.Section>
            <Components.InternetOutagesTable ssr={ssr} />
          </Card.Section>
        </Card.Article>
      </Grid>
    </Page>
  );
}

该路由产生的结果如下:

Remix 和 SSR 还可以帮助您提高 Lighthouse 评分和搜索引擎优化 (SEO)。它可以减少抓取次数和从服务器向浏览器传输的信息,对 DOM 进行预渲染,从而显著改善某些指标,例如累积布局偏移首次内容绘制最大内容绘制

另一个将其应用程序移植到 Remix 的项目是 Cloudflare TV。这是更改前后的指标情况。

在性能、可访问性、最佳实践和搜索引擎优化方面,Radar 的桌面 Lighthouse 评分现已接近 100%。

我们在 Radar 2.0 上广泛使用的另一个 Cloudflare 产品是 Speed。我们想特别介绍一下 Early Hints 功能。Early Hints 是一个新的 Web 标准,它定义了一个新的 HTTP 103 标头,服务器可以使用该标头来通知浏览器,在还在请求网页时即告知可能需要哪些资产来渲染网页,从而大大缩短了加载时间。

您可以结合使用 Cloudflare Pages 和 Early Hints。

API

Radar 有两个 API。后端可以直接访问我们的数据源,前端则可以通过互联网访问。

后端 API

后端 API 是使用 PythonPandasFastAPI 编写的,由 Cloudflare AccessJWT 令牌经过身份验证的 Origin Pull (AOP) 配置提供保护。使用 Python 可以让团队中的任何人、工程师或数据科学家轻松协作,帮助改进和扩展 API,这是非常棒的体验。我们的数据科学团队使用 JupyterHubJupyter Notebooks 作为数据探索工作流,让原型代码和重用代码、算法和模型变得特别简单快捷。

然后,它通过一个基于 Strawberry 的 GraphQL 服务器与上游前端 API 对话。使用 GraphQL 可以轻松创建复杂的查询,让内部用户和分析师在利用我们收集的庞大数据构建报告时拥有所需的灵活性。

前端 API

我们在 Cloudflare Workers 的基础上构建了 Radar 的前端 API。这个 Worker 有两个主要功能。

  • 使用 GraphQL 从后端 API 抓取数据,然后进行转换。
  • 它提供了一个所有工具均可使用(包括 Radar)的公共 REST API

在我们的核心 API 前面使用一个 Worker,我们可以轻松地添加和分离微服务,还可以添加一些重要功能,例如:

  • Cloudflare 的 Cache API 可以更精细地控制需要缓存的内容和时间,并支持 POST 请求和可定制的缓存控制标头,这是我们也在使用的。
  • 过期的响应使用 R2。如果由于某种原因,后端 API 不能为请求提供服务,且缓存了过期的响应,则将直接从 R2 提供服务,为最终用户提供更好的体验。
  • CSVJSON 输出格式。CSV 格式很方便,让数据科学家、分析师和其他人可以更轻松地使用 API,并直接从其他工具使用我们的 API 数据。

开放 OpenAPI 3 模式生成器和验证器的源代码

前端 API 的最后一个功能是 OpenAPI 3 支持。我们自动生成一个 OpenAPI 模式,并用它来验证用户输入。这是通过我们在 itty-router 的基础上构建的自定义库完成的,我们也在使用这个库。现在,我们将开放这项成果的源代码。

itty-router-openapi 为 Cloudflare Workers 提供了一个简单紧凑的 OpenAPI 3 模式生成器和验证器。请参阅我们的 GitHub 存储库了解更多信息和使用详情。

开发人员文档

今天,我们还在开发人员文档中推出了 Radar API 页面,其中提供了关于数据许可、基本概念、入门步骤和可用 API 方法的更多信息。Cloudflare Radar 的 API 是免费的,支持学者、数据侦探和其他 Web 爱好者根据我们全球网络的数据,调查全球的互联网使用情况。

为方便使用我们的 API,我们还整合了一个 Colab 笔记本模板,您可以单纯玩一下,也可以复制或扩展到您的用例中。

Radar 应用程序

Radar 应用程序是在浏览器中运行的代码。我们已经介绍了 Remix,但我们还要使用哪些工具?

Radar 依赖于大量数据可视化。图表和地图等工具对我们必不可少。我们决定在其他两个框架的基础上构建可重复使用的可视化组件库:visx(一个“用于 React 的底层表达性可视化基本元素集合”)、D3(一个基于数据操作 DOM 的强大 JavaScript 库)以及 MapLibre(一个开源地图可视化堆栈)。

下图是我们正在运行的一个可视化组件。我们称之为“PewPew 地图”。

如果我们需要在页面中使用它,Remix React 的代码如下:

<Card.Section
    title={t("card.attacks.title")}
    description={t("card.attacks.description")}
  >
    <Flex gap={spacing.medium} align="center" justify="flex-end">
      <SegmentedControl
        label="Sort order:"
        name="attacksDirection"
        value={attacksDirection}
        options={[
          { label: t("common.source"), value: "ORIGIN" },
          { label: t("common.target"), value: "TARGET" },
        ]}
      onChange={({ target }: any) => setAttacksDirection(target.value)}
      />
    </Flex>

    <Components.AttacksCombinedChart
      ssr={ssr}
      height={400}
      direction={attacksDirection}
    />
  </Card.Section>

SVG

我们对 Radar 的另一项更改是将我们的图像和图形资产换成了 可缩放矢量图形 (SVG)。SVG 非常好用,因为它们本质上是一种声明式图形语言。它们是含有矢量信息的 XML 文本文件,因此可以轻松地操作、转换、存储或索引。当然,它们也可以以任何尺寸呈现,在任何设备、任何分辨率的情况下均能得到漂亮清晰的图像。

与位图格式相比,SVG 的尺寸非常小,生成效率高,并且支持国际化,更容易翻译成其他语言(本地化),因而可访问性更高。

下图是一个 Radar 气泡图示例。如果仔细检查,您可以看到 SVG 代码和嵌入的 <text/> 字符串。

Cosmos

React Cosmos 是一个“沙盒,用于开发和测试隔离的 UI 组件”。我们想在 Radar 2.0 中使用 Cosmos,因为它非常适合这个项目。

  1. 它有很多可视化组件;有些很复杂,有很多配置选项和功能。
  2. 这些组件可在多个页面上拥有不同数据的不同上下文中重复使用。
  3. 我们有一支多学科团队;人人都可以发送拉取请求,并在前端添加或更改代码。

Cosmos 作为一个组件库,您可以看到我们的调色板上随时可用的可视化和小工具,从简单的按钮到复杂的图表,您可以实时玩一下各个选项,看看会发生什么。任何人都可以这样做,不仅设计师或工程师,项目的其他利益相关者也可以。这有效改善了团队沟通,可快速做出贡献并迭代。

下图是正在运行的 Cosmos 截图。

持续集成和持续开发 (CI/CD)

对制作现代软件的任何团队来说,持续集成都很重要。Cloudflare Pages 提供了多种选择,可以通过直接上传的方式使用 CI 工具,而且开箱即用。该团队提供了文档和示例,说明如何使用 GitHub Actions、CircleCI 和 Travis,当然您也可以使用其他工具。

在我们的案例中,我们在内部使用 BitBucket 和 TeamCity 来构建和部署我们的版本。我们的工作流程只需几分钟即可在批准的 PR 和后续合并中自动构建、测试和部署 Radar 2.0。

使用 Vitest 进行单元测试,使用 Playwright 进行 E2E 测试。已制定了视觉回归测试计划,Playwright 也可以提供帮助

此外,我们有多个环境,在投入生产之前模拟并测试我们的版本。借助我们的 CI/CD 设置,可轻松地从一个环境切换到另一个环境或快速放弃任何不想要的部署。

Cloudflare Pages 使用 预览部署(即分支版本控制)可以轻松完成任务。普通 Worker 使用 Environments 也可以。

快速预览和通知

我们承认,在 CI/CD 方面,Radar 1.0 的速度并不是特别快。我们都曾有过这样的经历:从提交代码到部署,快速修复可能需要 30 分钟,令我们无比沮丧。

因此,我们投入了大量资源,确保新的 CI 快速高效。

我们的最终成果非常不错,可提供推送到代码库的任何提交的快速预览链接。在构建过程中使用智能缓存,并在提交超出正常版本分支时进行异步测试,我们能够将部署时间缩短到几秒钟。

这就是有人向任何分支推送代码时,我们在聊天中收到的通知。

任何人都可以在聊天中关注特定分支的线程,并在发生新变化时收到通知。

极速构建、预览链接和通知带来了颠覆性变革。利用链接,工程师可以将自己的想法或快速修复的结果展示给产品经理或其他团队成员。任何人都可以迅速点击链接,查看一个正在全面运行的端到端版本 Radar 的变更。

可访问性和本地化

Cloudflare 致力于提高 Web 可访问性。最近,我们公布了对 Cloudflare 的仪表板进行了哪些升级,以遵守行业无障碍标准,这个前提对我们的所有属性都有效力。本地化也是如此。2020 年,我们实现了仪表板国际化,增加了对新的语言和地区的支持。

可访问性和本地化携手并进,两者都很重要,但又各不相同。《Web 内容可访问性指南》提供了许多关于可访问性的最佳实践,包括使用颜色和对比度、标签、SVG、快捷方式、手势等等。A11Y 项目页面提供了丰富的资源,助您了解更多信息。

但本地化也被称为 L10n,在您开始一个新项目时,它更像是一项技术要求,旨在确保您选择正确的库和框架集,助您更轻松地添加新的翻译,而无需依赖工程或重写代码。

我们希望 Radar 在这两方面都表现出色。我们的设计系统认真对待 Cloudflare 的设计和品牌指南,并尽可能添加更多 A11Y 优秀实践,而且该应用程序了解其各页面和 UI 组件中的本地化字符串。

添加新语言与翻译 JSON 文件一样简单。这是一个 en-US.json 文件的片段,字符串默认使用美式英语:

{
  "abbr.asn": "Autonomous System Number",
  "actions.chart.download.csv": "Download chart data in CSV",
  "actions.chart.download.png": "Download chart in PNG Format",
  "actions.chart.download.svg": "Download chart in SVG Format",
  "actions.chart.download": "Download chart",
  "actions.chart.maximize": "Maximize chart",
  "actions.chart.minimize": "Minimize chart",
  "actions.chart.share": "Share chart",
  "actions.download.csv": "Download CSV",
  "actions.download.png": "Download PNG",
  "actions.download.svg": "Download SVG",
  "actions.share": "Share",
  "alert.beta.link": "Radar Classic",
  "alert.beta.message": "Radar 2.0 is currently in Beta. You can still use {link} during the transition period.",
  "card.about.cloudflare.p1": "Cloudflare, Inc. ({website} / {twitter}) is on a mission to help build a better Internet. Cloudflare's suite of products protects and accelerates any Internet application online without adding hardware, installing software, or changing a line of code. Internet properties powered by Cloudflare have all web traffic routed through its intelligent global network, which gets smarter with every request. As a result, they see significant improvement in performance and a decrease in spam and other attacks. Cloudflare was named to Entrepreneur Magazine's Top Company Cultures 2018 list and ranked among the World's Most Innovative Companies by Fast Company in 2019.",
  "card.about.cloudflare.p2": "Headquartered in San Francisco, CA, Cloudflare has offices in Austin, TX, Champaign, IL, New York, NY, San Jose, CA, Seattle, WA, Washington, D.C., Toronto, Dubai, Lisbon, London, Munich, Paris, Beijing, Singapore, Sydney, and Tokyo.",
  "card.about.cloudflare.title": "About Cloudflare",
...

我们即将发布其他语言版本的 Radar,敬请期待。

Radar 报告和 Jupyter 笔记本

Radar 报告利用数据探索和讲故事来深入分析某一特定主题。有些报告往往会不时更新。Radar 报告示例包括我们每季度发布的 DDoS 攻击趋势IPv6 采用情况

这些报告的信源是 Jupyter 笔记本。我们的数据科学团队使用我们内部的 Jupyter Hub 工具与其他利益相关者一起研究一些用例或主题。完成所有迭代和探索,并将成果签核后,就产生了一个笔记本。

Jupyter 笔记本是 JSON 文档,包含文本、源代码、富媒体(例如图片或图表)和其他元数据。它是展示数据科学项目的实际标准,每位数据科学家都在使用。

在 Radar 1.0 版本中,将 Jupyter 笔记本转换为 Radar 页面是一个非常耗时的人工处理过程,涉及许多工程和设计资源,让每个参与者都备感沮丧。哪怕只是将已经发布的笔记本更新,也常常会给我们带来麻烦。

Radar 2.0 解决了以上所有问题。现在整个过程已完全自动化,它可以提取 Jupyter Notebook,而且只要是使用一系列简单的规则和内部指南设计的,就可以自动转换,将得到的 HTML 和资产托管在 R2 存储桶中,并将其发布在 Reports 页面。

转换为 HTML 时考虑到了我们的设计系统和 UI 组件,得到的是一个非常漂亮的文件,且通常为长格式,与 Radar 的外观和感觉完美匹配。

我们最终将开放源代码,让所有人都可以使用这个工具。

多利用 Cloudflare,少一分担忧

我们举例说明了如何使用 Cloudflare 的产品和功能来构建下一代应用程序,而不必过多担心那些对您的业务或逻辑不太重要的非核心元素。但还缺少几点。

一旦启动并运行应用程序,您必须保护它免受不良流量和恶意行为者的影响。Cloudflare 在开箱后只需点击几下即可为您提供 DDoSWAF自动程序管理保护。

我们的一些安全规则示例如下。在我们的应用程序中,我们不必担心这种流量,因为 Cloudflare 可以检测到,并根据我们的规则采取行动。

此外,我们也不需要担心从旧网站重定向到新网站。Cloudflare 有批量重定向功能,您可以直接在仪表板上轻松创建重定向列表。

同样值得一提的是,使用我们的仪表板可以完成的所有事项,事实上也都可以使用 Cloudflare 的 API 完成。因为我们的仪表板完全是在 Cloudflare 的 API 基础上构建的。对于基础设施即代码用户,我们也能满足您的需求;您可以使用 Cloudflare Terraform 提供商

部署和管理 Worker、R2 存储桶或 Pages 站点显然也可以编写脚本。命令行工具 Wrangler 可以完成这项工作以及一些其他任务,而且您还可以在部署之前,在您的计算机上本地运行完整的应用程序,模拟我们的堆栈。

结语

希望您喜欢 Radar 团队的这篇文章,并受到启发,在我们的 Supercloud 上构建您的下一个应用程序。我们将继续改进和创新 Radar 2.0,不断推出新的功能,与您分享我们的发现,开放工具的源代码。

同时,我们的开发人员 Discord 服务器上开设了 Radar 室。欢迎加入,向我们提问;团队渴望收到大家的反馈,与大家讨论 Web 技术问题。

您也可以在 Twitter 上关注我们,了解 Radar 的最新消息。

我们保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的过程

从任何设备访问 1.1.1.1,以开始使用我们的免费应用程序,帮助您更快、更安全地访问互联网。要进一步了解我们帮助构建更美好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位
Developer Week (CN)Cloudflare Radar (CN)Cloudflare Workers (CN)Cloudflare Pages (CN)简体中文

在 X 上关注

Celso Martinho|@celso
Cloudflare|@cloudflare

相关帖子

2024年4月05日 13:01

Browser Rendering API GA,推出 Cloudflare Snippets 、SWR,并向所有用户提供 Workers for Platforms

Browser Rendering API 现已向所有付费 Workers 客户提供,并改进了会话管理功能...

2024年4月04日 13:05

生产安全新工具——渐进式部署、源码映射、速率限制和全新 SDK

我们今天发布了五项更新,旨在为您提供更 多功能——渐进式部署、Tail Workers 中的源映射堆栈跟踪、全新的速率限制 API、全新的 API SDK,以及对 Durable Objects 的更新——每项更新都旨在满足重要的生产服务需求...