导读:本文由作者在2020年云原生微服务大会上分享的《爱奇艺在Dubbo生态下的微服务架构实践》整理而成,重点讲述了爱奇艺使用Dubbo、Sentinel等开发框架的经验,以及微服务生态的构建经验。
作者|周晓军爱奇艺中间件团队负责人

导读:本文由作者在2020年云原生微服务大会上分享的《爱奇艺在Dubbo生态下的微服务架构实践》整理而成,重点讲述了爱奇艺使用Dubbo、Sentinel等开发框架的经验以及构建微服务生态的心得。
本文将关注以下主题:
Apache Dubbo及其在爱奇艺的发展历史介绍,爱奇艺内部Dubbo SDK的扩展以及围绕Dubbo 1的微服务生态建设后续规划。Apache Dubbo简介
Apache Dubbo是由阿里开源的高性能RPC框架。除了通信,Dubbo框架本身内置了很多微服务治理的功能。
自2017年重启维护以来,Dubbo社区一直保持着较高的活跃度。从周边生态来看,也是比较完善的。例如,Nacos和Sentinel等开源框架为其提供了支持。在语言支持方面,除了Java,目前Dubbo-go社区也非常活跃,有一些python、nodejs等主流开发语言Dubbo的开源实现。基于以上因素,我们决定引入Dubbo框架来替代原有的自研RPC框架。
爱奇艺于2019年6月正式开始引入Dubbo框架。我们将其与对接公司的内部基础设施如注册中心、监控系统等连接起来,并于2019年8月正式发布了第一个内部版本。
这里值得一提的是,我们没有维护自己的Dubbo分支,而是使用Dubbo强大的扩展机制来开发我们的新功能,这使得我们可以无障碍地跟进新版本的Dubbo社区。在这个内部版本发布后不久,第一个生产应用程序于同年9月上线。后续,在进一步拓展Dubbo SDK功能的同时,也在周边生态建设方面做了大量工作,包括2020年3月上线的Nacos注册中心。
2.爱奇艺Apache Dubbo的发展史
优秀的微服务开发框架是业务服务的基石。然而,由于微服务应用的复杂性,需要一个相对完整的微服务生态系统来帮助业务团队更好地实践微服务架构。
微服务生态系统
下图是爱奇艺目前微服务生态圈的全景。
这分为几个层次:
首先是开发框架层面。Dubbo SDK集成了注册发现、通信和负载均衡的能力,但类似的熔丝和限流功能仍然需要Sentinel和其他框架的支持。在基础设施层面,注册中心/配置中心都是微服务生态中的重要组成部分;另外,为了保证应用的可用性,一个完整的监控系统也是必不可少的,比如索引监控、日志监控、链接跟踪等。最后,为了方便运维人员管理微服务应用,需要一套功能完善的管理平台,包括服务管理、配置分发、监控告警,以及对开发者的一些支持功能。如你所见,微服务的整个生态系统还是很大的,由于篇幅的限制,下面的讲座将主要集中在以下几个方面:
Dubbo SDK的扩展,生态系统的构建,注册中心的演进,监控系统的构建,熔断限流1的支持。Dubbo SDK的扩展
根据爱奇艺内部的实际情况和各业务团队的需求,我们主要在以下几个方面对Dubbo SDK进行了扩展:
基础设施的适配:包括注册中心、监控系统、内部容器平台等。可用性增强:包括不健康实例隔离和区域最近路由机制;安全性增强:支持服务间调用的认证机制;序列化:增加了对protobuf序列化的支持。1)不健康实例的隔离机制
首先,介绍了不健康实例隔离机制。
Dubbo SDK默认采用随机负载均衡策略,通过失败重试的策略保证调用成功率。通过实践发现,有时候在生产环境中,虽然有少数提供者节点处于不健康状态,但仍然可以与注册中心正常通信,使得消费者仍然可以找到这些实例,导致一些请求被分发。由于实例本身的问题,这些请求的响应时间可能会变慢或者出错率增加,从而导致整体服务质量的下降。
为了解决这样的问题,我们的思路是引入客户端健康检查机制,即消费者会统计每个提供者实例的请求成功率来判断是否健康;对于不健康的提供者实例,使用者将在一段时间内隔离它们,并且后续请求将不会通过负载平衡策略发送到这些实例。此外,消费者端将维护每个提供者实例的隔离时间,如果一个实例被隔离多次,则每个隔离时间将相应地更长。
我们的扩展机制提供了默认的健康检查策略,包括检查服务器端异常是否发生在最后一次调用中,或者是否有大量请求在一段时间内没有返回。用户也可以通过扩展我们提供的接口来实现自己的检查策略。
为了避免网络抖动带来的意外影响,我们还设计了一套底层机制。即当提供者实例的不健康比例超过一定阈值时,消费者会忽略实例隔离的策略,以防止集中流量碾压剩余实例。
2)区域最近路由机制
接下来,介绍本地最近路由机制。
爱奇艺在很多地方都建了机房。为了保证单个机房出现故障时,所有业务系统仍能正常工作,核心业务一般会部署在两地三中心的框架内。在这种场景下,如果系统产生跨区域的访问请求,由于网络延迟,请求延迟必然会增加。因此,所有服务一般都要求客户端访问附近的服务器实例。
我们通过扩展Dubbo的路由机制实现了这一策略。的一般实现原理是,当提供者和消费者实例启动时,它们将首先从一个公共区域服务中获取实例的当前区域信息。提供商在注册其服务时,会将上述区域信息作为URL的一部分注册在注册中心,这样消费者实例在发现服务时就可以知道每个提供商实例的区域信息,并与自己的区域信息进行比较,选择附近的实例先访问。
此外,消费者实例还将通过上面提到的健康检查机制来检查服务器实例。如果发现本地健康的提供者实例低于设定的比例,就会忽略最近路由的策略,改为负载均衡所有的提供者实例,从而实现自动故障转移机制。
3)认证机制
一些内部服务具有与安全认证相关的需求,并且它们不希望被未授权的应用程序访问。为了解决这个问题,我们开发了一个基于数字签名和AK/SK的认证系统。
其基本原则是:
服务提供者可以通过配置对服务开放认证;需要访问敏感服务的消费类应用需要在微服务平台上申请。批准后,将在这个授权关系上生成一对AK/SK,并同步到认证服务。提供者/消费者与认证服务通信以获得相关的AK/SK。该过程使用HTTPS进行通信;当消费者发起调用时,会为请求参数生成一个数字签名,连同时间戳、AK等信息一起发送给提供者;当提供者收到请求时,它将检查其数字签名和其他信息,以确认所请求信息的来源和数据的完整性。以上是我们对Dubbo SDK的扩展,接下来主要介绍我们的微服务生态圈建设。

2.生态系统建设
注册中心是微服务应用中最重要的基础设施之一。Dubbo SDK推出之初,为了快速落地,我们使用ZooKeeper作为注册中心。其实ZooKeeper并不是微服务注册表的最佳选择。它的主要缺点包括:
无法向外扩展;作为一个一致的系统,它将在网络分区中不可用。1)注册中心的演变
在考察了行业内的各种方案后,我们选择了Nacos作为我们的下一代微服务注册中心。右下角是Nacos的整体介绍。选择Nacos的主要原因是:
高性能,可横向扩展;既适用于传统服务架构,也适用于云原生环境,包括支持与Istio控制平面的接口;提供了Nacos-Sync组件,可以以较低的成本迁移注册表。
在部署Nacos服务时,我们充分考虑了服务部署架构的高可用性。目前,我们的Nacos服务是一个大型集群,实例分布在许多不同的可用区域。在每个可用的区域内,我们会申请不同的VIP,最终的内网域名都是和这些VIP绑定的。此外,底层使用的MySQL也采用多机房部署。这种架构可以避免由于单个Nacos实例或单个房间的故障而导致的整个Nacos服务的不可用性。
以下是一些可能的故障场景模拟:
单个Nacos实例的故障:通过使用负载平衡器集群提供的健康检查功能,自动从VIP中删除;一个VIP集群故障:通过客户端重试机制解决;单AZ故障:通过客户端重试机制解决;MySQL集群故障:MySQL与注册发现过程无关,不受影响;整个Nacos服务失败:客户端机制,比如服务实例缓存等。接下来简单介绍一下如何使用Nacos-Sync平滑迁移注册表。
首先,要部署Nacos-Sync服务,需要将旧注册表中的数据同步到Nacos。Nacos-Sync支持集群部署。在部署多个实例时,它对新注册表的写入时间是幂等的,并且它原生支持Dubbo的注册数据格式。检查数据是否正确后,首先升级消费者端,改为从Nacos注册表进行发现。此时,我们服务发现的数据都是由Nacos-Sync从旧注册表中同步的;升级提供者端,改为向Nacos注册服务;当Nacos-Sync服务和旧注册表离线时,整个迁移过程就结束了。2)监控系统的建设
接下来主要介绍我们内部微服务监控系统的建设。一个完整的微服务监控系统一般由以下三个方面组成:
监控指标:包括QPS/响应延迟/错误率等黄金指标,业务自定义指标,JAVA应用的JVM指标,此外还有采集和基础环境的相关指标,包括CPU/内存利用率等。日志监控:比如错误日志的数量;还可以利用AI技术对日志模式进行统计分析等。链路监控:由于微服务调用关系的复杂性,调用链跟踪也是非常必要的。它可以帮助业务人员更好地分析应用程序之间的依赖关系,并监控每个调用关系的核心指标。
在指标上,我们围绕普罗米修斯构建了一套比较完善的监测和报警方案。有几个问题需要解决:
首先,指数计算的问题。为了减少入侵,我们在skywalking agent的基础上进行了二次开发,可以自动拦截Dubbo的呼叫,统计其呼叫次数、处理时间、错误等。
其次,指标收集的问题。普罗米修斯使用拉模式收集指标。对于微服务场景,一般使用普罗米修斯的服务发现机制。Prometheus默认集成了consul、K8s等服务发现方式,但不直接支持Nacos注册表。我们修改了开源Nacos适配器,使Prometheus能够发现要从Nacos收集的应用程序实例信息。
Grafana主要用于查看指标。我们提供一套通用的配置模板,业务也可以根据需要进行扩展。
在报警方面,我们在Prometheus中设置了报警策略,具体的报警会由alert-manager通过适配器发送到内部监控报警平台。
监控仪表盘查看、报警策略设置和订阅的访问点都设置在我们内部的全链路监控平台上,用户可以在这里查看和执行相应的操作。
下图显示了服务监控界面:
链接追踪的基本原理也和谷歌关于Dapper的论文一致。应用程序通过嵌入式代理生成调用链数据,然后通过日志收集或网络直报收集到kafka,通过我们的实时分析程序进行分析。
分析结果大致可以分为三类:
我们将使用ES+Hbase来存储原始的调用链数据;调用关系上的实时监控数据。我们使用时间序列数据库druid来存储它。拓扑关系由图形数据存储。
3)保险丝电流限制
最后简要介绍了利用sentinel框架进行熔断和限流的相关内容。
由于微服务架构的特点,存在很多上下游的依赖和网络通信,这些因素会给应用本身带来一定的风险,比如上游系统的突发流量或热参数;下游服务不可用、延迟增加、错误率增加等等。如果缺乏自身系统的保护,可能会出现雪崩效应。为了应对这些场景,我们主要引入Sentinel框架来解决。
Sentinel的核心原理是用户可以定义各种资源,并在其上设置各种规则。当访问资源时,Sentinel组件将检查是否满足这些规则,如果不满足,它将抛出一个特定的异常。用户可以捕捉这些异常来实现快速失败或降级等业务逻辑。Sentinel还提供了一个控制台,可以用来管理规则的参数设置和查看实时监控。
为了满足内部业务团队的需求,我们还对sentinel框架做了一些扩展。下面的例子是我们实现的复参数限流功能。Sentinel框架本身有热点参数限流的功能,但是只支持一些简单类型的参数。在某些情况下,限流场景可能很复杂。例如,在下图中,可能根据第一个参数的id属性来执行电流限制,这是本场景原生的sentinel所不支持的。针对这种情况,我们提供了一个抽象接口,允许用户通过自己的实现,从参数中提取需要限流的资源。
为了实现规则参数的动态分配,我们将sentinel适配到内部配置中心。在sentinel dashboard上进行的参数更改将最终保存到配置中心。通过引入配置中心的SDK,业务系统可以在不重启应用的情况下动态调整参数。
在我们的微服务管理平台上,我们还提供了sentinel dashboard的托管功能。
发展状况和开源贡献

爱奇艺引入Dubbo的时间不长,但由于线上表现稳定,各业务团队整体接受度较高,线上规模也在快速增长。短短一年时间,上线百余项服务,实例数超过5000个。
此外,我们还在使用过程中积极回馈社区。到目前为止,已经提交了大约三十个补丁,上面提到的认证机制也为社区做出了贡献,成为Dubbo 2 . 7 . 6版本的新特性之一。在这个过程中,团队中诞生了一个社区委员。
未来规划
对于未来的规划,大概有以下几个方面:
云原生和服务网格已经成为微服务技术演进的一个趋势,并在一些公司得到了大规模实践。如何将dubbo与service mesh结合起来,如何提供平滑过渡的解决方案,将是我们今后工作的一个重点。在服务治理方面,我们希望建立一个同时适用于服务网格和传统微服务的控制平面。在开发人员支持方面,我们计划推出项目脚手架和在线调试等服务,这将使开发人员更容易开发项目和解决在线问题。


