从优化性能到应对峰值流量:微博缓存服务化的设计与实践
《从优化性能到应对峰值流量:微博缓存服务化的设计与实践》要点: 导读:高可用架构 8 月 20 日在深圳举办了『互联网架构:从 1 到 100』为主题的闭门私董会研讨及技术沙龙,本文是陈波分享的微博缓存服务的演进历程. 陈波,08 年加入新浪,参与 IM 系统的后端研发.09 年之后从事新浪微博的系统研发及架构工作,在海量数据存储、峰值访问、规模化缓存服务及开放平台等方面参与技术架构改进,当前主要负责微博平台的基础设施、中间件的研发及架构优化工作,经历新浪微博从起步到成为数亿用户的大型互联网系统的技术演进过程. 在所有介绍微博架构演进的使用场景都离不开缓存,今天上午腾讯分享的 CKV 也同样提到了缓存服务在腾讯社交产品的重要性.缓存的设计为什么重要,我们先介绍其使用场景. 1、微博的缓存业务场景微博几乎所有的接口都是实时组装的,用户请求最终转化到资源后端可能会存在 1 – 2 个数量级的读放大,即一个用户请求可能需要获取几十上百个以上的资源数据进行动态组装. 比如大家刷微博的时候,会触发一个 friends_timeline 的接口请求,然后服务端会聚合并组装最新若干条(比如 15 条)微博给用户,那这个过程后端服务需要到资源层拿哪些数据来组装?
从以上过程可以看到,用户的一个首页请求,最终后端 server 可能需要从资源层获取几百甚至几千个数据进行组装才能得到返回的数据. 微博线上业务的很多核心接口响应需要在毫秒级,可用性要求达到 4 个 9.因此,为了保证资源数据的获取性能和可用性,微博内部大量使用缓存,而且对缓存是重度依赖的,不少核心业务的单端口缓存访问 QPS 已经达到了百万级以上. 微博使用的缓存主要是 Memcache 和 Redis,因为 Memcache 的使用场景、容量更大,而且目前推的缓存服务化也是优先基于 Memcache,然后再扩展到 Redis、 ssdcache 等其他缓存,所以今天的缓存服务讨论也是以 Memcache 为存储标的来展开的.我们最早使用的缓存架构就是直接利用开源版本的 Memcache 运行在物理机上,我们称之为裸资源. 2、缓存的裸资源架构演进首先看一下微博 Memcache 缓存的裸资源架构的演进过程.微博上线之初,我们就对核心业务数据进行分池、分端口的,把 size 接近的数据放在相同的池子里面.业务方通过 Hash 算法访问缓存池里的节点.同时,每个 IDC 部署使用独立的缓存资源,为了加速,业务前端也会在本地启用 local-Cache. 上线几个月之后,随着业务量和用户量的急聚增加,缓存节点数很快增加到数百个.这段时间,时常会因为网络异常、机器故障等,导致一些缓存节点不可用,从而导致缓存 miss.这些 miss 的请求最终会穿透到 DB 中. 如果某个时间点,核心业务的多个缓存节点不可用,大量请求穿透会给 DB 带来巨大的压力,极端情况会导致雪崩场景.于是我们引入 Main-HA 双层架构. 对后端的缓存访问时,会先访问 Main 层,如果 miss 继续访问 HA 层,从而在获得更高的命中率的同时,即便部分 Main 节点不可用,也可以保证缓存的命中率,并减少 DB 压力. 这一阶段我们对业务资源进一步的分拆,每一种核心数据都分拆到独立的端口.同时,根据不同的访问频率、容量进行缓存搭配部署,对 Memcache 资源的端口进行统一规划,确保缓存层的性能和可用性.同时我们发现,在各种海量业务数据的冲刷下,前端使用 local-Cache,命中率不高,性能提升不明显,所以我们把 local Cache 层去掉了. 随着业务访问量进一步增加,特别是一些突发事件爆发式的出现并传播,Main-HA 结构也出现了一些问题,主要是很多缓存节点的带宽被打满,Memcache 的 CPU 比较高,Memcache 响应变慢. 通过分析,我们发现主要是大量热数据的集中访问导致的服务过载,单个端口不能承载热数据的访问(比如明星发的微博所在的端口),于是我们引入了 L1 结构. 通过部署 3 – 4 组以上的小容量 L1 缓存,每个 L1 组等价存储热数据,来满足业务要求. 总结一下微博的缓存架构演进过程:
在以上 3 阶段的演进过程中,我们较好的解决了访问性能与访问峰值的压力,不过在服务的可管理性方面依然存在可管理空间.不同业务之间只有经验可以复用,在缓存的实现方面经常需要各种重复的劳动.我们需要把缓存的使用服务化才能把可管理性带到一个新的阶段. 3、缓存服务的设计与实践直接使用裸缓存资源也存在一系列问题:
于是我们开始考虑缓存的服务化,主要的过程及策略如下:
通过 cacheProxy,简化了业务前端的配置,简化了开发,业务方只需要知道 cacheProxy 的 IP 和端口,即可实现对后端各种业务的多层缓存进行访问. 我们对缓存服务的服务治理也做了不少工作. 接入配置中心首先,把 Cache 层接入了配置中心 configServer(内部叫 vintage).实现了 Memcache 缓存、 cacheProxy 的动态注册和订阅,运维把 Memcache 资源的 IP 端口、 Memcache 访问的 hash 方式、分布式策略等也以配置的形式注册在配置中心,cacheProxy 启动后通过到配置中心订阅这些资源 IP 及访问方式,从而正确连接并访问后端 Memcache 缓存资源.而且 cacheProxy 在启动后,也动态的注册到配置中心,client 端即可到配置中心订阅这些 cacheProxy 列表,然后选择最佳的 cacheProxy 节点访问 Memcache 资源.同时,运维也可以在线管理 Memcache 资源,在网络中断、 Memcache 宕机,或业务需要进行扩容时,运维启动新的 Memcache 节点,同时通知配置中心修改资源配置,就可以使新资源快速生效,实现缓存资源管理的 API 化、脚本化. 监控体系其次,把 cacheProxy、后端 Memcache 资源也纳入到了 Graphite 体系,通过 logtailer 工具将缓存的访问日志、内部状态推送到 Graphite 系统,用 dashboard 直接展现或者按需聚合后展现. Web 化管理同时,我们也开发了缓存层管理组件 clusterManager(内部也叫 captain),把之前的 API 化、脚本化管理进一步的升级为界面化管理.运维可以通过 clusterManager,界面化管理缓存的整个生命周期,包括业务缓存的申请、审核,缓存资源的变更、扩缩容、上下线等. 监控与告警ClusterManager 同时对缓存资源、 cacheProxy 等进行状态探测及聚合分析,监控缓存资源的 SLA,必要时进行监控报警. 我们也准备将 clusterManager 整合公司内部的 jpool(编排发布系统)、 DSP(混合云管理平台) 等系统,实现了对 cacheProxy、 Memcache 节点的一键部署和升级. 开发工具对于 client 端,我们基于 Motan(微博已开源的 RPC 框架)扩展了 Memcache 协议,使 client 的配置、获取服务列表、访问策略更加简洁.方便开发者实现面向服务编程,比如开发者在和运维确定好缓存的 SLA 之后,通过 spring 配置 <weibo:cs namespace=“ unread-feed” registry=”vintage” />,即可访问 unread-feed 业务对应的 Memcache 资源,后续的扩容、节点切换等都不需要开发者介入,也不需要重启. 部署方式对于 cacheProxy 的部署,目前有两种方式,一种是本地化部署,就是跟业务前端部署在一起的,在对 cacheProxy 构建 Docker 镜像后,然后利用 jpool 管理系统进行动态部署.另外一种是集中化部署,即 cacheProxy 在独立的机器上部署,由相同的业务数据获取方进行共享访问. Cache 服务化后的业务处理流程如图. 首先运维通过 captain 把 Memcache 资源的相关配置注册到 configServer,cacheProxy 启动后通过 configServer 获取 Memcache 资源配置并预建连接; cacheProxy 在启动准备完毕后将自己也注册到 configServer,业务方 client 通过到 configServer 获取 cacheProxy 列表,并选择最佳的 cacheProxy 发送请求指令,cacheProxy 收到请求后,根据 namespace 选择缓存的 cluster,并按照配置中的 hash 及分布策略进行请求的路由、穿透、回写. Captain 同时主动探测 cacheProxy、 Memcache 缓存资源,同时到 Graphite 获取历史数据进行展现和分析,发现异常后进行报警. (编辑:ASP站长网) |