在构建需要高吞吐量和最小响应时间的系统的API时,缓存几乎是不可避免的。每个在分布式系统上工作的开发人员都曾在某个时候使用过某种缓存机制。在本文中,我们将探讨如何使用CDN构建读取缓存设计,不仅可以优化您的API,还可以降低基础架构成本。 了解一些关于缓存和CDN的知识将有助于理解本文。如果您对此一无所知,建议您先了解一些相关知识,然后再回到这里。 背景 作为后端开发人员,我们始终在努力构建高度优化的API,以为用户提供良好的体验。故事从这里开始,我们如何面对一个特定的问题,然后如何解决它。我希望您在后能够从中学到一些关于大规模系统设计的东西。 问题 我们需要开发一些API,这些API具有以下特征: 1.数据不会经常更改。2.对所有用户来说,响应是相同的,没有意外的查询参数,只是简单的GET API。3.响应数据量最多为约600 KB。4.我们预计API的吞吐量非常高(最终约为每秒5-6万次查询)。 当您第一次看到这个问题时,你的第一反应是什么?对我来说,首先想到的是,只需在节点上添加内存缓存(例如Google Guava),使用Kafka发送失效消息(因为我喜欢Kafka,它很可靠),设置服务实例的自动缩放(因为流量在一天中不均匀)。类似于下面的示意图: 1*F60S9SCN5JVmgutDCOwPKg.jpeg 嘭!问题解决了!很容易对吧?嗯,事实并非如此,像任何其他设计一样,这个设计也带来了一些缺陷。例如,对于一个简单的用例来说,这个设计略微复杂,基础架构成本将会增加,因为现在我们必须生成一个Kafka + Zookeeper集群,而且为了处理每秒5-6万次请求,我们需要水平扩展服务实例(对于我们来说是Kubernetes Pod),这意味着需要增加更多的物理节点或虚拟机。 因此,我们寻找了一种更简单和经济有效的方法,这就是我们最终开发了一种具有“使用CDN构建读取缓存”的解决方案。不久之后,我将讨论架构的细节以及权衡。 但在进一步探讨之前,让我们先了解设计的构建块。 读取缓存 标准的缓存更新策略有: 1.旁路缓存(Cache-Aside)2.读取通过缓存(Read-Through)3.写入通过缓存(Write-Through)4.写入后缓存(Write-Behind)5.提前刷新(Refresh-Ahead) 我将不详细讨论其他策略,而只关注读取缓存,因为本文只涉及此内容。让我们深入研究并了解它的工作原理。 1*CZ3W153osigEQh1u09NFNQ.png 上图很容易理解,但简要总结一下: 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作为用户和实际后端服务之间的缓存层。如下图所示: 1*fn-zmPouY7r3XoWS5c-mzQ.jpeg 正如您所看到的,CDN位于客户端和我们的后端服务之间,并成为缓存。数据流顺序如下: 1*4oGxf26V7E7MYAGKl4MtnA.png 让我们深入探讨一下,因为这是设计的精髓。 用于缩写的缩写 ?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是根据您能够提供过期数据多长时间以及是否接受它而选择的。如果这引发了一个问题,为什么不能在数据更改时使缓存失效,那么请稍等,我马上在缺点部分回答。 实施 1*vrlRYFpBKKy5IqDSbrUidA.jpeg 请求合并 但还有一个问题,CDN承担了所有负载,我们不必进行扩展。但我们的吞吐量达到了每秒60,000次查询,这意味着在缓存未命中的情况下,会有60,000个请求同时命中我们的源服务(假设需要1秒来填充CDN缓存),这可能会使服务不堪重负。 这就是请求合并的工作方式: 1*ze0WtYQVhFRtClZq0GEoVQ.jpeg 顾名思义,它基本上将具有相同查询参数的多个请求合并在一起,并将很少的请求发送到源服务器。 我们设计的美妙之处在于,我们不必自己执行请求合并,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上设置日志记录和监控,但这通常会带来一定的成本。 最后 在分布式世界中的任何设计都具有一定程度的主观性,并且总会有一些权衡。作为开发人员/架构师,我们的职责是权衡各种权衡,并选择适合我们的设计。说到这里,没有哪种设计足够具体以继续下去,因此鉴于约束条件,我们选择了一种设计,根据它的运作方式,我们可能会进一步演化它。
ZooKeeper是一个开源的分布式协调服务,它为分布式系统提供了一种可靠的配置管理、命名服务、同步和组服务,ZooKeeper的设计目标是减轻构建复杂同步服务的难度,它使用简单的接口和强大的功能来实现这一目标。,ZooKeeper的特性, ,1、 简单性:ZooKeeper的API设计简单,使得开发者能够快速地构建分布式应用。,2、 可靠性:ZooKeeper通过复制数据存储在多个节点上,确保了数据的高可用性和容错性。,3、 高性能:ZooKeeper的性能优化使其能够处理大量的读写请求,适用于大规模的分布式系统。,4、 顺序一致性:ZooKeeper保证客户端的更新操作是有序的,这有助于实现复杂的同步原语。,5、 可扩展性:ZooKeeper可以通过添加更多的服务器节点来水平扩展。,ZooKeeper的数据模型,ZooKeeper的数据模型类似于文件系统,它由一系列的节点组成,每个节点被称为一个znode,每个znode可以存储数据,并且可以有子节点,ZooKeeper中的节点有两种类型:,1、临时节点:这种类型的节点在创建它的会话结束时会被自动删除。,2、持久节点:这种类型的节点不会因为会话的结束而消失,它会一直存在于ZooKeeper中,直到显式地被删除。,ZooKeeper的应用场景, ,1、 分布式锁:ZooKeeper可以用来实现跨多个进程或线程的同步锁。,2、 配置管理:ZooKeeper可以作为集中式的配置存储,供分布式系统中的各个组件读取配置信息。,3、 命名服务:ZooKeeper可以作为服务发现的基础设施,帮助服务之间相互发现和通信。,4、 集群管理:ZooKeeper可以用于管理和监控集群中的状态,例如Hadoop或Kafka集群。,ZooKeeper的工作原理,ZooKeeper的核心是原子广播,它是一个客户端用来更新服务器状态的协议,当一个客户端想要改变ZooKeeper中的状态时,它会发送一个请求到一组ZooKeeper服务器中的一个,这个服务器称为领导者(leader),其他的服务器称为跟随者(followers),领导者负责处理所有的写请求,并将更新以事务的形式广播给所有的跟随者,一旦大多数跟随者确认了更新,领导者就会提交这个事务,并通知客户端操作成功。,ZooKeeper的安装与运行,ZooKeeper的安装相对简单,可以从Apache ZooKeeper的官方网站下载预编译的二进制文件或者源代码,安装完成后,可以通过配置文件设置服务器的参数,如服务器列表、数据目录等,启动ZooKeeper服务后,可以使用命令行工具或者编写客户端程序来进行操作。,相关问题与解答,1、 ZooKeeper是如何保证高可用性的?, ,答:ZooKeeper通过数据复制和故障切换机制来保证高可用性,它将所有数据复制到多个服务器节点上,如果领导者节点发生故障,其他节点中的一个会自动成为新的领导者。,2、 ZooKeeper如何处理网络分区问题?,答:ZooKeeper使用观察者(watcher)机制来检测网络分区,当网络分区发生时,ZooKeeper会触发观察者事件,允许客户端采取相应的措施。,3、 ZooKeeper的领导者选举是如何工作的?,答:ZooKeeper使用一种称为“Zab”的协议来进行领导者选举,当领导者发生故障时,剩余的服务器节点会通过投票的方式选出一个新的领导者。,4、 ZooKeeper能否限制并发访问?,答:是的,ZooKeeper可以通过设置ACL(访问控制列表)来限制并发访问,ACL可以定义哪些用户可以执行哪些操作,从而提供一定程度的访问控制。,
ZooKeeper是一个开源的分布式协调服务,它主要用于维护配置信息、命名注册、分布式同步和提供组内选举等功能,在分布式系统中,ZooKeeper通过提供一种可靠的共享存储来帮助解决数据一致性、系统配置管理、分布式锁以及分布式应用中的其他协调问题。,主要用途, ,1、 配置管理:ZooKeeper可以作为一个集中式的配置文件存储和管理服务,允许应用程序在运行时获取配置信息,当配置发生更改时,ZooKeeper可以通知所有相关的服务。,2、 命名服务(Naming Service):在分布式环境中,服务实例经常需要相互引用,ZooKeeper可以作为命名服务,为每个服务实例分配唯一的名称或ID,并允许其他服务通过这些名称或ID来查找和引用它们。,3、 分布式锁:ZooKeeper提供了一种机制,用于在多个竞争进程之间实施互斥锁,这在确保资源的串行访问时非常有用,特别是在分布式计算环境中。,4、 集群管理:ZooKeeper可用于检测节点故障,并通过领导者选举算法自动选择新的领导者,以确保分布式系统的高可用性。,5、 分布式队列:使用ZooKeeper可以实现分布式队列,以实现跨多个服务器的作业调度和消息传递。,6、 状态共享:分布式应用中的不同服务可能需要共享状态信息,ZooKeeper提供了一个可靠的共享存储,可以用来存储和更新这些状态信息。,7、 同步服务:在分布式系统中进行精确的时间同步是一个挑战,ZooKeeper可以帮助实现简单的时间同步服务,尽管它不是专为这个目的设计的。, ,技术细节,ZooKeeper使用了一个称为ZAB(ZooKeeper Atomic Broadcast)的协议来保证事务的顺序性和可靠性,它的核心是一组服务器节点组成的集合,这些节点被称为“ensemble”,它们共同维护一个层次化的命名空间,类似于文件系统的结构,每个节点都可以存储数据,并且有一个版本号与之关联,这样可以跟踪数据的变更历史。,ZooKeeper的数据模型非常简洁,主要由znodes(zookeeper nodes)组成,每个znode可以包含数据和子znode的列表,Znodes被组织成树形结构,可以通过路径来访问,每个znode都有一个访问控制列表(ACL),用于控制谁可以执行读、写和创建子节点的操作。,ZooKeeper客户端可以通过各种API与ZooKeeper服务器交互,包括创建、检索、更新和删除znodes,客户端还可以设置观察者(watchers),以便在znode的状态发生变化时接收通知。,相关问题与解答, Q1: ZooKeeper是如何保证数据的一致性的?,A1: ZooKeeper通过ZAB协议确保数据的一致性,ZAB协议类似于传统的两阶段提交(2PC),但它设计用来处理网络分区和节点故障,它确保了即使在不稳定的网络中,所有的更新也会按照一定的顺序被应用到所有的节点上。, , Q2: 在ZooKeeper中如何实现分布式锁?,A2: 分布式锁可以通过创建一个特定的znode来实现,当一个进程想要锁定资源时,它会尝试创建一个znode,如果创建成功,它就拥有了锁,其他进程如果试图创建同一个znode会失败,因为它们会收到已经存在的通知,释放锁只需删除该znode。, Q3: ZooKeeper如何处理节点故障?,A3: 当ZooKeeper检测到一个节点故障时,它会进入领导者选举过程,剩下的活动节点将选择一个领导者继续提供服务,这个过程是自动的,并且对客户端透明。, Q4: ZooKeeper是否支持多租户?,A4: ZooKeeper本身不直接支持多租户,可以通过在ZooKeeper的命名空间中划分不同的区域给不同的租户使用,或者通过在应用层实现逻辑隔离来达到类似多租户的效果。,
在分布式系统中,服务注册与发现、配置管理、负载均衡、熔断降级等功能是必不可少的,为了实现这些功能,我们需要使用一些中间件来帮助我们完成,Zookeeper和Dubbo就是其中的两个非常优秀的中间件,本文将详细介绍Zookeeper和Dubbo在服务器中的应用与优势。,Zookeeper是一个分布式协调服务,主要用于解决分布式系统中的一些数据一致性问题,它提供了一些基本服务,包括:配置管理、域名服务、分布式同步、组服务等,Zookeeper的核心是原子广播,即所有的更新都是全局顺序的,每个更新都有一个唯一的时间戳,这个时间戳反映了这个更新在所有更新中的顺序。, ,Dubbo是一个高性能、轻量级的Java RPC框架,主要用于构建分布式服务系统,它提供了三个核心功能:面向接口的远程方法调用、容错和负载均衡、自动服务注册与发现,Dubbo通过SPI机制支持多种序列化协议,如Hessian、Kryo、FST等,Dubbo还提供了丰富的监控和管理功能,如:访问控制、动态配置、路由规则、集群容错等。,1、配置管理,在分布式系统中,配置信息通常需要集中管理,Zookeeper可以将这些配置信息存储在Znode节点上,客户端可以通过监听Znode的变化来实时获取最新的配置信息,这样可以避免手动更新配置信息,提高系统的可维护性。,2、域名服务,在分布式系统中,服务之间可能需要通过域名进行访问,Zookeeper可以将域名和服务之间的映射关系存储在Znode节点上,客户端可以通过查询Znode来获取服务的地址信息,这样可以避免硬编码服务地址,提高系统的灵活性。,3、分布式同步,在分布式系统中,多个节点之间可能需要进行数据同步,Zookeeper可以提供分布式锁和队列等同步原语,帮助实现数据的一致性,这样可以避免并发问题,提高系统的稳定性。,4、组服务,在分布式系统中,多个节点可能需要组成一个组来进行协同工作,Zookeeper可以提供组服务,帮助实现节点的分组管理和组内通信,这样可以避免重复开发,提高系统的可复用性。, ,1、面向接口的远程方法调用,Dubbo支持面向接口的远程方法调用,这样可以降低系统的耦合度,提高代码的可维护性,Dubbo还支持多种调用方式,如:同步调用、异步调用、单向调用等,满足不同的业务需求。,2、容错和负载均衡,Dubbo内置了多种容错策略和负载均衡策略,如:失败重试、快速失败、随机调用、一致性哈希等,这样可以避免单点故障,提高系统的稳定性和可用性。,3、自动服务注册与发现,Dubbo支持自动服务注册与发现,当一个服务启动时,会自动向注册中心注册自己的信息;当一个服务需要调用另一个服务时,会自动从注册中心获取目标服务的地址信息,这样可以避免手动配置服务地址,提高系统的灵活性。,4、丰富的监控和管理功能,Dubbo提供了丰富的监控和管理功能,如:访问控制、动态配置、路由规则、集群容错等,这样可以帮助开发者更好地了解系统的运行状况,提高系统的可运维性。,1、Zookeeper和Dubbo有什么区别?, ,答:Zookeeper主要用于解决分布式系统中的数据一致性问题,提供配置管理、域名服务、分布式同步、组服务等功能;而Dubbo主要用于构建分布式服务系统,提供面向接口的远程方法调用、容错和负载均衡、自动服务注册与发现等功能。,2、Dubbo支持哪些序列化协议?,答:Dubbo支持多种序列化协议,如:Hessian、Kryo、FST等,Dubbo还支持自定义序列化协议。,3、Zookeeper如何实现配置管理?,答:Zookeeper将配置信息存储在Znode节点上,客户端可以通过监听Znode的变化来实时获取最新的配置信息,这样可以避免手动更新配置信息,提高系统的可维护性。,4、Dubbo如何实现自动服务注册与发现?,答:Dubbo支持自动服务注册与发现,当一个服务启动时,会自动向注册中心注册自己的信息;当一个服务需要调用另一个服务时,会自动从注册中心获取目标服务的地址信息,这样可以避免手动配置服务地址,提高系统的灵活性。,