使用CDN构建读取缓存设计
在构建需要高吞吐量和最小响应时间的系统的API时,缓存几乎是不可避免的。每个在分布式系统上工作的开发人员都曾在某个时候使用过某种缓存机制。在本文中,我们将探讨如何使用CDN构建读取缓存设计,不仅可以优化您的API,还可以降低基础架构成本。
了解一些关于缓存和CDN的知识将有助于理解本文。如果您对此一无所知,建议您先了解一些相关知识,然后再回到这里。
背景
作为后端开发人员,我们始终在努力构建高度优化的API,以为用户提供良好的体验。故事从这里开始,我们如何面对一个特定的问题,然后如何解决它。我希望您在阅读本文后能够从中学到一些关于大规模系统设计的东西。
问题
我们需要开发一些API,这些API具有以下特征:
1.数据不会经常更改。2.对所有用户来说,响应是相同的,没有意外的查询参数,只是简单的GET API。3.响应数据量最多为约600 KB。4.我们预计API的吞吐量非常高(最终约为每秒5-6万次查询)。
当您第一次看到这个问题时,你的第一反应是什么?对我来说,首先想到的是,只需在节点上添加内存缓存(例如Google Guava),使用Kafka发送失效消息(因为我喜欢Kafka,它很可靠),设置服务实例的自动缩放(因为流量在一天中不均匀)。类似于下面的示意图:

嘭!问题解决了!很容易对吧?嗯,事实并非如此,像任何其他设计一样,这个设计也带来了一些缺陷。例如,对于一个简单的用例来说,这个设计略微复杂,基础架构成本将会增加,因为现在我们必须生成一个Kafka + Zookeeper集群,而且为了处理每秒5-6万次请求,我们需要水平扩展服务实例(对于我们来说是Kubernetes Pod),这意味着需要增加更多的物理节点或虚拟机。
因此,我们寻找了一种更简单和经济有效的方法,这就是我们最终开发了一种具有“使用CDN构建读取缓存”的解决方案。不久之后,我将讨论架构的细节以及权衡。
但在进一步探讨之前,让我们先了解设计的构建块。
读取缓存
标准的缓存更新策略有:
1.旁路缓存(Cache-Aside)2.读取通过缓存(Read-Through)3.写入通过缓存(Write-Through)4.写入后缓存(Write-Behind)5.提前刷新(Refresh-Ahead)
我将不详细讨论其他策略,而只关注读取缓存,因为本文只涉及此内容。让我们深入研究并了解它的工作原理。

上图很容易理解,但简要总结一下:
1.应用程序永远不直接与数据库交互,而始终通过缓存进行。2.在缓存未命中时,缓存将从数据库中读取数据并丰富缓存存储。3.在缓存命中时,数据将从缓存中提供。
您可以看到,数据库很少被频繁访问,响应速度很快,因为缓存主要是内存中的(如Redis或Memcached)。已经解决了很多问题。
CDN
互联网上关于CDN的定义是:“内容传递网络(CDN)是一种全球分布的代理服务器网络,用于在离用户更近的位置提供内容,并用于提供静态文件,如图像、视频、HTML、CSS文件”。但我们将违反潮流,使用CDN提供动态内容(JSON响应,而不是JSON文件)。
此外,从概念上说,通常有两种CDN:
1.推送CDN(Push CDN):您负责将数据上传到CDN服务器。2.拉取CDN(Pull CDN):CDN将从您的服务器(源服务器)拉取数据。
我们将使用拉取CDN,因为使用推送方法,我必须处理重试、幂等性和其他内容,这对我来说是一个额外的麻烦,而且对于这个用例并没有真
正添加任何价值。
将CDN作为读取缓存
这个想法很简单,我们将CDN作为用户和实际后端服务之间的缓存层。如下图所示:

正如您所看到的,CDN位于客户端和我们的后端服务之间,并成为缓存。数据流顺序如下:

让我们深入探讨一下,因为这是设计的精髓。
用于缩写的缩写
•T1 -> 时间实例1 + 毫秒数•T2 -> 时间实例1 + 1分钟+某些毫秒数•TTL -> 存活时间•源服务器 -> 您实际的后端服务
1.T1:客户端请求获取user1。2.T1:请求着陆在CDN上。3.T1:CDN发现在其缓存存储中没有user1相关的键。4.T1:CDN到上游,即实际的后端服务,以获取user1。5.T1:后端服务返回user1作为标准的JSON响应。6.T1:CDN接收到JSON,现在它需要存储它。7.所以现在需要决定这个数据的TTL,它是如何做到的?8.通常有两种设置TTL的方式,要么源服务器指定数据应该被缓存多长时间,要么在CDN配置中设置了一个恒定值,它使用该时间来设置TTL。9.最好让源服务器控制TTL,这样我们有能力根据需要控制TTL或具有条件的TTL。10.那么问题就产生了,源服务器如何指定TTL。缓存控制头(Cache-Control headers)来拯救。来自源服务器的响应可以包含像 cache-control: public, max-age: 180
这样的缓存控制头。这将转化为该数据可以被公开缓存,有效期为180秒。11.T1:现在CDN看到这一点并使用180秒的TTL缓存了数据。12.T1:CDN向调用者响应user1 JSON。13.T2:另一个客户端请求user1。14.T2:请求着陆在CDN上。15.T2:CDN看到它的缓存中有user1键,因此不会到源服务器,而是返回缓存的JSON响应。16.T3:CDN在180秒后缓存失效。17.T4:某个客户端请求user1,但由于缓存为空,流程再次从第3步开始。这种情况一直重复。
不一定要将TTL设置为180秒。选择TTL是根据您能够提供过期数据多长时间以及是否接受它而选择的。如果这引发了一个问题,为什么不能在数据更改时使缓存失效,那么请稍等,我马上在缺点部分回答。
实施

请求合并
但还有一个问题,CDN承担了所有负载,我们不必进行扩展。但我们的吞吐量达到了每秒60,000次查询,这意味着在缓存未命中的情况下,会有60,000个请求同时命中我们的源服务(假设需要1秒来填充CDN缓存),这可能会使服务不堪重负。
这就是请求合并的工作方式:

顾名思义,它基本上将具有相同查询参数的多个请求合并在一起,并将很少的请求发送到源服务器。
我们设计的美妙之处在于,我们不必自己执行请求合并,CDN将帮助我们执行。正如我已经提到的,我们使用的是Google Cloud CDN,它有请求合并的概念,这只是请求合并的另一种名称。因此,当在同一时间进行大量的缓存填充请求时,CDN会识别出这一点,每个CDN节点只发送一个请求到源服务器,然后从该响应中响应所有这些请求。这就是如何保护我们的源服务器免受高流量的影响。
好的,我们现在接近结束了,任何设计在没有经过利弊分析之前都是不完整的。因此,让我们稍微分析一下这个设计,看看它如何有所帮助,以及它的不足之处。
设计的优点
1.简单性: 这个设计非常简单,易于实现和维护。2.响应时间: 您已经知道CDN服务器的地理位置优化了数据传输,因此我们的响应时间也变得非常快。例如,忽略TCP连接建立时间,60毫秒听起来如何?3.减少负载: 由于实际的后端服务器现在只收到约每180秒1个请求,负载非常低。
设计的缺点
1.缓存失效: 缓存失效是计算机科学中最难正确执行的事情之一,而且由于CDN成为缓存,它变得更加困难。在CDN上的任意即兴的缓存失效是一个昂贵的过程,通常不会实时发生。如果数据发生更改,由于我们无法使CDN上的缓存失效,您的客户端可能会在一段时间内获得旧数据。但这又取决于您设置的TTL,如果TTL为几小时,那么您也可以在CDN上调用缓存失效。但如果TTL以秒/分钟为单位,这可能会有问题。此外,请记住,并非所有CDN提供商都提供API以使CDN缓存失效。2.控制较少: 由于请求现在不会着陆在我们的服务器上,因此会有这样一种感觉,即作为开发人员,您对系统没有足够的控制。可观察性可能会受到轻微影响,您可以在CDN上设置日志记录和监控,但这通常会带来一定的成本。
最后
在分布式世界中的任何设计都具有一定程度的主观性,并且总会有一些权衡。作为开发人员/架构师,我们的职责是权衡各种权衡,并选择适合我们的设计。说到这里,没有哪种设计足够具体以继续下去,因此鉴于约束条件,我们选择了一种设计,根据它的运作方式,我们可能会进一步演化它。
相关文章:

使用CDN构建读取缓存设计
在构建需要高吞吐量和最小响应时间的系统的API时,缓存几乎是不可避免的。每个在分布式系统上工作的开发人员都曾在某个时候使用过某种缓存机制。在本文中,我们将探讨如何使用CDN构建读取缓存设计,不仅可以优化您的API,还可以降低基…...

windows上下载github上的linux内核项目遇到的问题
问题一:clone的时候报错 Cloning into G:\github\linux... POST git-upload-pack (gzip 27925 to 14032 bytes) remote: Counting objects: 6012062, done. remote: Compressing objects: 100% (1031/1031), done. remote: Total 6012062 (delta 893), reused 342 (…...
Leetcode 15:三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 解题思…...

npm常用命令与操作篇
npm简介 npm是什么 npm 的英文是,node package manager,是 node 的包管理工具 为什么需要npm 类比建造汽车一样,如果发动机、车身、轮胎、玻璃等等都自己做的话,几十年也做不完。但是如果有不同的厂商,已经帮我们把…...
Go 语言的垃圾回收机制:自动化内存管理
在编程的世界中,内存管理一直是一个重要的问题。不正确的内存管理可能导致内存泄漏和程序崩溃。Go 语言以其高效的垃圾回收机制而闻名,使开发者从手动内存管理的烦恼中解脱出来。本文将深入探讨Go语言的垃圾回收机制,介绍它的工作原理以及如何…...

java-各种成员变量初始化过程-待完善
前置条件 一、本文章讨论的成员变量 public static final String aa "aa";public static final Integer bb 1;public static final Students cc new Students();public static String aa1 "aa";public static Integer bb1 1;public static String bb2…...

059:mapboxGL监听键盘事件,通过eastTo控制左右旋转
第059个 点击查看专栏目录 本示例是介绍演示如何在vue+mapbox中监听键盘事件,通过eastTo控制左右旋转。 本例通过easeTo方法来加减一定数值的bearing角度,通过.addEventListener的方法来监听键盘的按键动作。这里一定要设置interactive: false, 否则展现不出来旋转效果。 直…...
jdk对linux cgroup v2容器化环境识别情况
Linux各发行版将cgroups v2作为默认的情况如下: Container-Optimized OS(从 M97 开始)Ubuntu(从 21.10 开始,推荐 22.04)Debian GNU/Linux(从 Debian 11 Bullseye 开始)Fedora&…...

vue3后台管理系统之顶部tabbar组件搭建
1.1静态页面搭建 <template><div class"tabbar"><div class"tabbar_left"><!-- 面包屑 --><Breadcrumb /></div><div class"tabbar_right"><!-- 设置 --><Setting /></div></di…...

安装Apache2.4
二、安装配置Apache: 中文官网:Apache 中文网 官网 (p2hp.com) 我下的是图中那个版本,最新的64位 下载下后解压缩。如解压到D:\tool\Apache24 PS:特别要注意使用的场景和64位还是32位版本 2、修改Apcahe配置文件 2.1配置Apache…...
KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(9)
接前一篇文章:KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(8) 上一回讲完了drm_internal_framebuffer_create函数中的framebuffer_check函数中的drm_get_format_info函数,本文继续讲解framebuffer_check函数中的余下步骤。为了便于理解,再次贴出framebuffer_ch…...

Win10使用nginx,注册到服务设置自启与后台运行,解决 Access is denied 问题
安装 nginx 下载 nginx 官网下载页面:https://nginx.org/en/download.html直接选择当前最新 window 版本的或找到自己需要的版本下载即可 安装使用 下载完成后是有一个压缩包文件,直接解压到自己需要的目录下即可。是免安装的,解压即用简…...

短视频矩阵系统源头开发
一、智能剪辑、矩阵分发、无人直播、爆款文案于一体独立应用开发 抖去推----主要针对本地生活的----移动端(小程序软件系统,目前是全国源头独立开发),开发功能大拆解分享,功能大拆解: 7大模型剪辑法(数学阶乘&#x…...

《windows核心编程》第1章 错误处理
一、错误信息的获取 1.1 C库错误信息 1、获取错误信息 #include <stdio.h> #include <stdlib.h> #include <string.h>int main() {fopen("D:\\ASC", "r");printf("%s\n", strerror(errno));getchar();return 0; } 2、设置错…...

解剖—单链表相关OJ练习题
目录 一、移除链表元素 二、找出链表的中间节点 三、合并两个有序链表 四、反转链表 五、求链表中倒数第k个结点 六、链表分割 七、链表的回文结构 八、判断链表是否相交 九、判断链表中是否有环(一) 十、 判断链表中是否有环(二) 注:第六题和第七题牛…...

php对接飞书机器人
有同事接到对接飞书机器人任务,开发中遇到响应错误: {"code": 19021,"msg": "sign match fail or timestamp is not within one hour from current time" } 意思应该就是签名错误或者时间戳不在有效范围内等,…...

中间件安全-CVE复现IISApacheTomcatNginx漏洞复现
目录 中间件安全&CVE复现&IIS&Apache&Tomcat&Nginx漏洞复现中间件-IIS安全问题中间件-Nginx安全问题漏洞复现Nginx 解析漏洞复现Nginx 文件名逻辑漏洞 中间件-Apache-RCE&目录遍历&文件解析等安全问题漏洞复现漏洞复现CVE_2021_42013 RCE代码执行&…...
@ResponseBodyAdvice @RequestBodyAdivce失效
背景 最近项目要有向外部提供服务的能力,但是考虑到数据安全问题,要对接口进行加解密;实现加解密的方案有很多,比如过滤器、拦截器、继承RequestResponseBodyMethodProcessor什么的,不过我最近正在了解ResponseBodyAd…...

【c#】Quartz开源任务调度框架学习及练习Demo
Quartz开源任务调度框架学习及练习Demo 1、定义、作用 2、原理 3、使用步骤 4、使用场景 5、Demo代码参考示例 6、注意事项 7、一些Trigger属性说明 1、定义、作用 Quartz是一个开源的任务调度框架,作用是支持开发人员可以定时处理业务,比如定时…...

spring cloud Eureka集群模式搭建(IDEA中运行)
spring cloud Eureka集群模式搭建(IDEA中运行) 新建springboot 工程工程整体目录配置文件IDEA中部署以jar包形式启动总结 新建springboot 工程 新建一个springboot 工程,命名为:eureka_server。 其中pom.xml文件为: …...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...