写点什么

虎牙在全球 DNS 秒级生效上的实践

  • 2019-05-31
  • 本文字数:6354 字

    阅读完需:约 21 分钟

虎牙在全球 DNS 秒级生效上的实践

这次分享的是全球 DNS 秒级生效在虎牙的实践,以及由此产生的一些思考,整体上,分为以下 5 各部分:


  • 背景介绍;

  • 方案设计和对比;

  • 高可用;

  • 具体实践和落地;

  • 规划;

背景介绍

虎牙用到的基础技术很多,DNS 是其中比较重要的一个环节。



DNS 的解析过程很关键,例如上图中的 DNS 解析器通过一个定位解析追踪到我们的 DNS,再到本地域名服务器迭代解析,经过根域再到.com 名,最后到 huya.com 的根域名,获取最终的解析结果。


在这个过程中, DNS 解析是天然的分布式架构,每一层都会有缓存,上一层出现问题挂掉,下一层都会有缓存进行容灾。另外,整个 DNS 协议支持面广,包括手机和 PC,我们用的编程框架里也有 DNS 解析器,服务器也会配 DNS 解析引擎,因此,DNS 在虎牙的基础设施中是很重要的部分。

虎牙的 DNS 的应用现状

虎牙当前主要是依赖于公共的 DNS,相信在座的小伙伴们或多或少都会遇到过下面这些问题:


  • 依赖公共 localDNS,解析不稳定,延迟大。

  • 记录变更生效时间长,无法及时屏蔽线路和节点异常对业务的影响。例如,权威 DNS 全球各节点数据同步时间不可控,全局生效时间超过 10 分钟;localDNS 缓存过期时间不可控,部分 localDNS 不遵循 TTL 时间,缓存时间超过 48 小时。

  • 内部 DNS 功能缺失,无法解决内部服务调用面临挑战。例如,时延大、解析不准、支持多种调度策略。

  • 无法满足国外业务的快速发展,虽然一些海外云厂商提供了基于 DNS 的快速扩容方案,以及基于 DNS 的数据库切换方案。

方案设计和对比

基于以上的问题,我们开始重新规划 DNS 的设计。

名字服务架构


整个规划会分三个方面,核心是我们做了「名字服务」的中心点,基于此,可以满足我们的需求。


一方面通过 Nacos Sync,将现有多个注册中心的服务, 同步到「名字服务」中, 通过 DNS 实现不同框架之间的 Rest 服务方式的调用, 实现例如 Eureka,Consul,Taf 等框架之间的服务调用。


另一方面,在全球负载均衡的场景下,由于虎牙是以音视频业务为主,而音视频业务对节点的延迟是非常敏感的,所以我们希望一旦出现节点延迟的情况,能立马做切换。


第三个是传统 DNS 的场景, 可以满足容器和物理机的 DNS 需求, 提供本机 Agent 和集群两种方案, 通过缓存和 prefect 大大提高 DNS 解析的可用性和加快生效时间。



对于名字服务的总体设计主要分 3 部分,接入层需要提供 API,消息通知和 DNS 接入的能力。核心功能需要能在基于现有网络数据,CMDB 和 IP 库的数据基础上,提供灵活的负载均衡能力,全球数据的秒级同步,多个数据源的同步,能对全网服务的健康状态进行监控,及时感知到大范围的节点异常,并且能够及时将节点的屏蔽的信息推送到端上。


最终,我们选择 Nacos 作为名字服务的核心,提供统一的 API ,包括名字注册、变化推送、负载均衡等;Nacos Sync 作为全球集群间数据同步的组件;DNS - F 是客户端组件,用于拦截 DNS 请求,实现基于 DNS 的名字服务。

改造前后 DNS 变更生效流程的不同

接下来,我们通过对比看下改造前后 DNS 变更生效流程的差异。



原有 DNS 变更生效流程中,对 DNS 生效时间有影响的是:


Auth DNS


  • 跨区域、跨国数据同步慢,不稳定。

  • bind 在数据量比较大的时候,同步比较慢。


Local DNS:


  • 根据 TTL 缓存,过期后才会刷新数据。

  • 部分厂商不遵循 TTL 时间缓存,超过 24 小时的缓存时间。


服务器:


服务器开启 nscd 做 DNS 缓存。


业务进程:


应用的 DNS 缓存,比如 Java 虚拟机、框架层的 DNS 缓存。


以上四种情况会比较影响 DNS 的变更生效流程,下图是我们现有的 DNS 变更生效流程:



整体上相对简单,只要业务进程这边将自己内部的 DNS 缓存关掉, 通过 DNS-F 进行查询的时候, 会直接到最近的 Nacos 集群拉取最新的服务节点信息, 而且后续节点的变化也会推送到 DNS-F 中, 后续可以直接在缓存中获取最新信息。


国内 Nacos 集群


集群内通过 raft 协议同步数据,毫秒级别完成同步。


Nacos Sync:


  • Nacos 推送变化到 Nacos Sync,跨区域、跨国网络差的情况下可能会导致推送结果丢失,或者延迟加大。

  • Nacos Sync 会主动拉取实例变更,拉取周期和监听的服务数量会影响到变更时效。


DNS - F:


  • Nacos 会将变更推送到 DNS - F,网络差的情况可能会导致推送结果丢失,或者延迟加大。

  • DNS - F 会主动拉取实例变更,拉取周期和监听的服务数量会影响到变更时效。


业务进程:


通过应用禁用 DNS 缓存来解决。

核心设计 Nacos

Nacos 有两套推送机制。



一种是通过客户端来选择一个可获节点,比如它第一次拉取的是一个正常节点,这个正常节点就会跟它维护一个订阅关系,后面有变化就会有一个相应的实地变化推送给我。如果当前节点挂掉, 他会通过重连, 在节点列表上,连上一个正常的节点。这时候会有新的 DNS 关系出现。


另一种是通过 SDK 的方式,在服务端寻找可获节点。服务端每个节点之间, 会进行一个可活的探测, 选择其中一个可活节点用户维护这个订阅关系。 当这个节点出现问题, 连接断开后, SDK 重新发送订阅请求,服务端会再次选择另外一个可活的节点来维护这个订阅关系。这就保证整了推送过程不会因为某个节点挂掉而没有推送。


推送的效率方面,主要是用 UDP 的方式,这个效率不像 TCP 消耗那么高。


以上两个方案都比较适合我们目前的场景。

核心组件设计 Nacos Sync

我们选择 Nacos Sync 作为多集群数据同步的组件,主要是从以下 4 方面进行考虑的。


  • 同步粒度:


Nacos Sync 同步数据的时候是以服务为维度, 比较容易做最终一致性处理, 同时可以提供保活的机制,满足节点维持的场景。 数据库通过 Binlog 同步的方式只能局限于事务粒度, 而文件同步只能通过单个文件的粒度, 在服务同步这个维度并不是很合适。


  • 可用性方面:


Nacos Sync 作为一个中间件,是以集群方式进行的,传统的数据库和文件方式基本是单进程进行的,可用性方面可能不太满足要求。


  • 同步方式方面:


Nacos Sync 通过在服务粒度的全量写入,满足服务注册和 DNS 这两种场景, 不需要额外的事务消耗, 能保证最终一致即可。


  • 环形同步:


我们国内有多个可获的节点,希望它们之间的数据可以进行环形同步,每个节点之间是相互备份的,这时候用 Nacos Sync 的话,是支持的。虽然数据库方面,比较经典的是主主同步,但如果同时对一个主件进行更新的话,每一个点进行协助是会有问题的,而且文件方面是不支持的。

Nacos Sync 和开源版本的不同

我们对 Nacos Sync 开源方案上做了几处修改,以更好的适用于现在的场景:


第一,通过配置方式对任务进行分拆。因为在实际应用场景里面,因为 Nacos Sync 的任务达一两万,单机很容易到达瓶颈,所以我们通过配置的方式将这些分片到多台 Nacos Sync 机器上。


第二,通过事件合并和队列控制的方式控制 Nacos 集群的写入量,以保证后端的稳定性。虽然下发事件一秒钟只有一个,但在很多场景中,例如需要 K8s 或者 Taf 进行数据同步的时候,变化的频率是非常高的,这时候通过事件合并,每个服务单独进行一个写入进程。这样通过队列控制的方式可以控制整个 Nacos 集群的写入量。


第三,添加了能支持从 K8s 和 Taf 同步数据的功能。后期我们会将这个特性提交给 Nacos,让更多的开发者使用。

核心组件设计 DNS - F

DNS - F 是基于 CoreDNS 上开发的,我们扩展了以下 4 个组件:


Nacos 插件:查询 Nacos 服务信息,监听 Nacos 服务变化,并将服务转化为域名,实现以 DNS 协议为基础的服务发现;


Cache 插件:提供域名缓存服务;


Log 插件:将 DNS 解析日志上报到日志服务;


Proxy 插件:代理解析外部域名;

DNS - F 和开源版本的不同

第一,在日志组件里面将日志上传到自己的日志服务。


第二,对缓存功能做了一个增强。一般的缓存功能可能根据 TTL 时间会过期,我们把这个过期时间给去掉了,直接令到缓存永远不会过期,然后通过异步将这个缓存进行刷新。比如 TTL 可能快到到时间了,我们就会主动做一个查询或者推送查询,这样,服务端或者公共 DNS 出现问题的时候,就不会影响到整体服务。


第三,增强了高可用的保障能力。包括进程监控、内部运营和外部运营的探测。另外,原来的开源版本用的是本机部署的方式,我们做成了集群化的部署,解决了服务推送、服务负载均衡方面的问题。

高可用

接下来由我们团队的李志鹏,分享一下虎牙在高可用方面的实践。


周健同学跟大家介绍了项目的背景跟方案设计,我来和大家介绍一下具体的实践和落地,实践过程中的主要关注点是高可用。

全球化部署方案


这是虎牙的一个全球化的部署方案,我们在全球部署了两个大区,分别是国内和国外。这两个大区是指定服务同步的,走的是专线,这样可以保障同步的稳定性。在一个大区内我们又部署了多个接入点,例如在国内大区,我们部署了深圳和无锡两个接入点,这两个节点的数据是互相同步、互为备份,保证在一个集群挂掉下可以切换到另外一个集群。


多个接入点的情况下,我们通过 HttpDNS 实现客户端的就近接入。客户端定期请求 HttpDNS,HttpDNS 能根据地域寻找就近接入点。如果接入点出现故障,我们就直接在 HttpDNS 把这个节点给摘除,这样客户端就能快速地切换到另外一个接入点。


接下来讲一下单个集群下的部署方案。



单个集群部署了多个 Nacos 节点,并通过 7 层负载均衡的方式暴露给外面使用,并且提供了多个 VIP,满足不同线路和区域的接入要求。同时,Nacos Sync 做了分片处理,将同步压力分散到各个分片上,一个分片下我们又部署了多个 Nacos Sync 的节点,以保障多活和高可用。

线上演练

演练的场景是模拟一个单个集群挂了和两个集群都挂了。



从图中可以看到,把深圳的流量切走之后,无锡的流量就涨上去了,然后再把无锡的流量切走,再关闭服务,这样就可以看到两边的流量已经没了。之后,再去恢复两个集群的流量,看一下整个切换过程中对服务的影响。



首先看一下对写入的影响,在单个集群挂了的情况下,是没有任何影响的。如果是两个集群都挂了,写入就会失败。可以看到,这个图有一个波峰,这个波峰就是我们两个集群都挂了的情况下,写入失败延迟加大。


但是切换的整个过程对 DNS-F 是没有任何影响的,延迟保持平稳。此外,在集群重新上线前,我们需要做数据校验,保证集群之间元数据和实例数据的最终一致。


可用性级别方面,我们可以保障:


  • 单集群挂掉后不会有影响;

  • 双集群挂掉后只会影响域名变更,不影响域名解析;

线上演练数据校验机制

运行过程中,我们也要保证集群间数据的一致性。我们通过全量校验和增量校验两种手段去保证,全量校验方式如下:


  • 大区内部做 10 分钟的全量校验,保证大区内各个集群数据的一致;

  • 大区之间做 2 分钟做一次全量校验,保证大区之间被同步的服务的数据一致性。


增量校验方式如下:


  • 从其他数据源同步的数据,通过数据源的时间戳,做增量校验;

  • 基于 API 的写入日志,定期校验写入的内容是否已经全部同步。

DNF - S 高可用

关于 DNS - F 的高可用,我们主要做了以下 5 个点:


  • Agent 的健康状态监测,包括进程存活和是否能正常解析;

  • 缓存内部域名,并做持久化处理,保证 Nacos 集群出现问题时不会影响内部域名的解析;

  • 提供备用节点,保证在 DNS-F 挂了,或者是 DNS-F 需要升级的情况下,也不会影响到内部域名解析;

  • resolv.conf 配置检查,发现 127.0.0.1 不在配置中会自动添加;

  • 限制 Agent 的 CPU 的使用,避免对业务进程造成影响。

具体的实践和落地

实践一:数据库域名改造

之前的数据库是用 IP 方式接入的,在数据库切换的时候,需要通知每个业务方修改配置,重启服务,这样就带来一个问题:整个过程是不可控的,取决于业务方的响应速度,生效时间通常超过十分钟。



提升数据库切换的关键点,第一个就是切换时不需要业务方参与,能在业务方无感知的情况下进行切换;第二个是实例变化能秒级推送到我们的应用,将应用快速切换到一个新的实例上。



大家可以看一下???这个图,这是我们现在做的一个改造,图中的 DMX 是虎牙内部的一个数据库管理系统,思路就是把 DMX 和名字服务打通。DMX 会把数据库实例信息以服务的形式注册到名字服务,服务名就是域名。


实际应用过程中,通过这个域名去访问数据库,应用在访问前首先会经过 DNS - F 去做域名的解析,解析的时候是从名字服务查询实例信息,然后把实例的 IP 返回给应用。这样,应用就能通过 IP 和我们的数据库实例进行连接。


切换的时候,在 DMX 平台修改域名对应的实例信息,并把变更推送到名字服务,名字服务再推送给 DNS-F,应用在下一次解析的时候就能拿到新的实例 IP,达到切换数据库实例的目的。


这套方案落地后,虎牙的数据库切换基本上在 10 秒钟之内能够完成。

实践二:内部调用使用内部域名

虎牙部分内部系统之间调用是通过 7 层负载均衡,但是由于没有内部 DNS,需要通过的公共的 LocalDNS 来解析,这就带来一些问题:


问题一:扩缩容的时候要去修改 DNS 记录,整个过程生效时间可能会超过 10 分钟,故障的节点会影响业务较长的时间。


问题二:公共的 LocalDNS 智能解析不准确,比如无锡的机器可能会解析到深圳的一个接入点,影响接入质量。


问题三:不支持定制化的负载均衡策略,例如同机房、同大区优先的策略,通过公共 LocalDNS 是实现不了的。


如果想要提升内部服务调用质量,一是 DNS 记录变更绕过 LocalDNS,把 DNS 的记录变更直接推到 DNS-F。二是与内部系统打通,从 CMDB 等内部系统获取机器信息,支持多种负载均衡策略。



大家可以看一下上面???的图,这个改造和数据库域名的改造思路是一样的,最右上角有一个 7 层负载管理系统,我们把这个系统和名字服务打通,7 层负载管理系统会把域名信息以服务形式注册到名字服务,变更域名记录时直接从 7 层负载管理系统推送到名字服务,名字服务再推送到 DNS-F,达到快速切换的目的。


如果域名配置了负载均衡策略,名字服务会从 CMDB 获取机器、机房等信息,打标到域名的实例信息。然后,DNS-F 查询名字服务时,会携带 ClientIp,名字服务根据 ClientIp 的 CMDB 信息过滤实例列表,返回同机房的实例给 DNS-F,达到同机房优先的目的。


由此带来的效果是:


第一,服务扩缩容能够秒级完成,减少了故障时间。


第二,扩展了 DNS 的负载均衡策略,例如有些业务是需要在不同区域有不同的接入点的,而且不能跨区域调用,之前的 DNS 负载均衡策略是不能满足这个需求的,但在改造之后,我们能根据 CMDB 信息去做同区域调度的负载均衡策略。



第三,业务在接入内部域名之后,延迟会有明显的下降。上图显示的就是某个服务在接入到内部域名之后,延迟出现明显的下降。


另一个落地的效果就是我们对主机上的域名解析的优化。因为我们的 DNS - F 是部署在每台主机上的,然后提供一个缓存的功能。带来的效果就是:


  • 平均解析延迟会从之前的 200 毫秒下降到现在的 1 毫秒;

  • 缓存命中率会从之前的 90%上升到 99.8%,90%是用 CoreDNS 原生的那个 Cache,99.8%是在这个 Cache 的组件下做了优化之后的效果;

  • 解析失败率是从之前的 0.1%下降到 0%;


这里再总结一下项目落地的技术价值:


第一,提供了基于 DNS 服务发现的能力,消除异构系统之间互相调用的障碍。


第二,填补了没有内部域名解析能力的空白。


第三,解决我们上面说的内部服务调用面临的挑战:延时大、解析不准、不支持多种负载均衡策略、故障牵引慢。


第四,优化外部域名的解析,屏蔽 LocalDNS 的故障。


落地规模是:DNS - F 覆盖率 100%,完成 Taf 和 Eureka 注册中心的数据同步。

后续规划

LocalDNS:


  • 解决公共 DNS 节点位置影响域名解析准确性的问题;

  • 解决内部使用公共 DNS 不稳定的问题;

  • 优化内外网解析;


精准调度:


解决全球 DNS 节点生效慢的问题。

本文作者

周健:GitHub ID @nanamikon,虎牙中间件团队成员,2012 年毕业于中山大学,主要负责名字和配置服务,以及虎牙 DNS 和微服务相关的工作。


李志鹏:GitHub ID @lzp0412,虎牙中间件团队成员,主要负责 DNS,以及服务注册与发现、服务治理、Service Mesh 等相关工作。


本文转载自公众号阿里巴巴中间件(ID:Aliware_2018)


原文链接


https://mp.weixin.qq.com/s/9bEiE4QFBpukAfNOYhmusw


2019-05-31 08:0011357

评论

发布
暂无评论
发现更多内容

Android 面试官:这些经验要记录好,面试途中会遇到,做了6年的Android

android 程序员 移动开发

android 快速开发(三)巧用公共标题栏,音视频编解码开发

android 程序员 移动开发

Android 性能监控系列一(原理篇),闭关60天学懂NDK+Flutter

android 程序员 移动开发

Android 约束布局(ConstraintLayout)1,最详细的解释小白也能听懂

android 程序员 移动开发

Android 组件通信中有哪些不为人知的细节?,面试题分享

android 程序员 移动开发

Android 获取子 View 的位置及坐标的方式(1),Android入门视频教程

android 程序员 移动开发

Android 黑科技保活实现原理揭秘,精心整理

android 程序员 移动开发

Android 开发必备知识点整理,34岁程序员年薪50w

android 程序员 移动开发

Android 开发经验分享:挺重要的网络基础,android实现选择题模式

android 程序员 移动开发

Android 架构组件的最新进展 (上篇),flutter登录界面设计

android 程序员 移动开发

Android---用力过猛!为了组件化改造学习十几家大厂的技术博客

android 程序员 移动开发

Android 开发必备知识点及面试题汇总(Android+Java,斩获offer

android 程序员 移动开发

Android 开发市场是盛是衰?你应该知晓,android实战项目

android 程序员 移动开发

Android 技术的下半场(1),android开发书籍下载

android 程序员 移动开发

Android 技术的下半场,kotlin开源

android 程序员 移动开发

Android 开发之深入浅出 NavigationUI,2020-2021阿里巴巴安卓面试真题解析

android 程序员 移动开发

Android 自定义 View 之 LeavesLoading,移动开发工程师考试

android 程序员 移动开发

Android 开发一比一年难做!面试题都这么难了,android插件化

android 程序员 移动开发

Android 进阶之 MVP,高级安卓工程师面试题

android 程序员 移动开发

Android 面试主题集合整理,kotlin协程原理

android 程序员 移动开发

Android 高频面试必问之Java基础,安卓framework开发

android 程序员 移动开发

Android&Java面试题大全—金九银十面试必备【上,大厂Android面试真题精选

android 程序员 移动开发

Android 开发行业真的不行了嘛?,深入讲解Android

android 程序员 移动开发

Android 无缝换肤深入了解与使用,android快速开发

android 程序员 移动开发

Android 组件化实战,kotlin协程实现原理

android 程序员 移动开发

Android 网络框架之Retrofit源码解析,android嵌入式开发及实训答案

android 程序员 移动开发

Android 开发面试心得:BAT大厂Android面试题整理,面试8家大厂后终于拿到Offer

android 程序员 移动开发

Android 插桩入门,腾讯T2大牛手把手教你

android 程序员 移动开发

Android 高工面试必考题(二),android开发艺术探索pdf百度云

android 程序员 移动开发

Android---Fragment-的过去、现状与未来,android网络层框架设计实战

android 程序员 移动开发

android 快速开发(三)巧用公共标题栏(1),android开发从入门到精通

android 程序员 移动开发

虎牙在全球 DNS 秒级生效上的实践_文化 & 方法_周健_InfoQ精选文章