内容来源: 2016年12月16日,郑然在“GIAC 全球互联网架构大会”进行《支撑百度搜索引擎99.995%可靠名字服务架构设计》演讲分享。IT大咖说做为独家视频合做方,经主办方和讲者审阅受权发布。
阅读字数:2783 | 4分钟阅读
百度搜索引擎是全球最大的中文搜索引擎,致力于向人们提供"简单,可依赖"的信息获取方式。百度网页搜索部架构师郑然为咱们分享支撑百度搜索引擎的可靠名字服务架构设计。html
机器数量多,服务数量大:咱们有数万台服务器,数十万个服务,分布在多个IDC。
web
服务变动多,变动数据大:天天几十万次变动,每周10P量级的文件更新,千余人并行开发上百个模块。
小程序
检索流量大,稳定性要高:每秒数万次请求,知足99.995%的可用性,极短期的故障均可能引起大量的拒绝。缓存
全部服务下游自行向服务注册表中进行注册,同时服务上游集成注册表的客户端,查询注册表以获取服务下游列表。服务上游集成负载均衡器,实施负载均衡。服务器
服务端服务发现和客户端服务发现的区别就在于,服务端服务发现全部服务上游的请求都是经过网关去查询。网络
服务发现主要由服务注册表、注册表客户端和负载均衡组成。session
服务注册表是分布式的存储,持久化服务地址和自定义属性,服务名字全局惟一。架构
注册表客户端支持对注册表的增删改查,支持高并发高吞吐,对延迟的要求不过高,读多写少。并发
负载均衡对于整个服务系统来讲是一个不可或缺的组件。它根据负载选择某个服务,剔除和探活故障服务。负载均衡
Eden是百度网页搜索部自行研发的基于百度matrix集群操做系统的PaaS平台。它主要负责服务的部署、扩缩容、故障恢复等一系列服务治理的相关工做。底层是基础设施层,包括监控、故障检测以及matrix集群操做系统。
Eden由三部分组成,第一部分是总控服务,分为InstanceMgr和NamingService。底层有多个agent来执行总控下发的命令。全部的源数据保存在zookeeper上,并提供了命令行、web之类的接口来使用。还有一个机器维修仲裁的组件,根据故障类型、服务状态来决策机器的维修,同时提供了日志的收集分析,和一个仿真的测试平台。
在这之上是job engine层,提供了封装平常服务变动的操做,包括升级、数据的变动、服务扩容、故障实例的迁移等,在这层上作了抽象。
最上面是一些托管的服务,有网页图片搜索服务、度秘服务、和信息流。
对于一个IDC来讲,咱们会部署一套NamingService,agent复用的是Eden的agent。
服务发现不可避免的是要支持跨机房的服务发现机制,必需要有跨机房查询的功能,咱们就作了一套远程的跨机房查询,上层提供了SDK接口把这些过程封装起来。
为了支持跨机房必定要APP的ID或者名字必需要全局惟一。
我以为一个真正可以在线上稳定运行的服务发现系统必需要解决如下六个问题:
调用时机:谁来向服务注册表注册和注销服务?
健康检查:上游如何感知下游的健康状况?
无损升级:如何无损的进行服务升级?
变动分级:链接关系变动如何分级?
感知变化:上游服务如何感知下游服务列表的变化?
避免单点:如何避免服务注册表局部故障?
第一种方式是服务本身,在启动或中止的时候注册或注销本身。这种方式服务的启停对注册表有很强的依赖,服务须要植入SDK,会产生植入成本,容易干扰运维的可预期性,影响过载保护策略。
第二种方式就是采用第三方组件,有一个代理模块去实施服务的注册和注销。咱们使用这种方法的时候是利用agent经过SDK去操做。它的优势就是只需在服务添加删除时修改注册表,不用植入SDK,对注册表的依赖很弱,更容易进行运维效果监控,下降注册表的负载。
健康检查有服务端健康检查和客户端健康检查两种作法。
服务端健康检查是服务本身作健康检查,把健康结果反馈给服务注册表。但这种方式对注册表的依赖性很强,并且它本身的健康不必定是上游看到的健康,因此结果未必准确,感知周期也很长。
客户端健康检查则是客户端和服务端创建心跳和探活机制。它的优势是对注册表的依赖性弱,感知周期短,准确性更高了。
升级就意味着同时重启的数量要比平时更多。对于上游来讲,不可访问的服务也比平常要多,这就意味着失败几率会变大。
重试虽然能够在必定程度上解决问题,但重试的反作用大,一般重试的次数会被严格限制。
而健康检查虽然能够探测到不可用的下游服务,可是健康检测存在周期性。
后来咱们又有了一个更好的作法::咱们采起的方法是下游服务退出过程当中,先不会关闭服务读写端口,而仅仅关闭心跳端口,使服务处于"跛脚鸭"状态,等上游检测到下游心跳异常以后,将流量调度到其余服务实例,而后下游服务实例再关闭读写端口退出,从而实现彻底可控的无损服务升级。
基于分布式锁的个数,控制上游变动的服务,但上游分级方式具备随机性,出错状况损失偏大。
给下游实例打tag,标记是否被上游可见。
其实这种分级方式并非很好,由于变动链接关系高危变动,一旦错误,损失很大。更好的方法是经过权重来控制下游服务的流量比例。
咱们在实践中发现zookeeper的通知机制不可靠,对注册表依赖太重,发生局部故障,影响服务可用性。
而轮询是一种比较可靠的机制。由agent周期性轮询服务注册表,引入版本节点,只有版本变化时,才获取全量数据,加强了运维的可预期性。
对于IDC来讲,它不但愿因为服务发现系统局部故障而影响服务。
历史上咱们发生过屡次zookeeper的局部故障,好比网络抖动致使大量session超时所引发的通知机制。
把这些“不可靠”做为设计思路,咱们把上游持久化缓存下游服务列表,做为容灾手段。采用的是轮询机制。
健康检查是经过上游服务探测下游服务健康状态。
目前的服务发现系统应用到了万级的服务数量,支持了十万级的服务实例数量,覆盖了百度搜索引擎规模最大的indexer服务,数千个实例扩缩容的索引分布调整,分钟级完成链接变动。
原有方案不管是ssdb、proxy仍是master,都大量应用了对于zk通知的机制,同时还依赖zk的session机制作探活。
这个方案在实际应用中很容易出现网络抖动session超时的故障,zk通知机制也容易丢消息,zk故障会致使服务总体不可用,平均1~2个月就会发生故障。
因此咱们把它迁移到了NamingService,以解决上述问题。
使用第三方组件进行注册和注销;
上游探测下游服务健康状态;
经过服务分组实现无损升级;
链接关系变动必定要有分级机制;
使用轮询而不使用通知;
以服务注册不可靠做为假设条件。
咱们打算引入相似k8s的endpoint机制;经过控制流量比例更好的实现分级;提高易用性,成为通用的中间件。
以上就是我今天的分享,谢谢你们!