引言
Diamond是淘宝内部广泛使用的配置中心,
提供持久化管理和动态配置推送服务。配置的持久化管理,
是Diamond与淘宝内部另外一个软负载配置产品ConfigServer的主要区别。应用方发布的配置会通过持久化存储保存,与发布者的生命周期无关。动态配置推送,
则是Diamond的核心功能,在淘宝内部有很多应用场景,如数据库动态切换和扩容,业务系统开关配置运行时变更等。Diamond服务于淘宝内部几乎所有应用,因此,“可靠、易用、高效”便成为了Diamond的产品目标。 为了实现这个目标,我们定下了
“make it simple”
的原则,并在设计、实现以及运维各个阶段中尽力遵循着它。 本文会较详细的介绍Diamond的设计思路和实现原理,以此说明“可靠、易用、高效”的产品目标是如何实现的。
可靠的Diamond
简单的架构
Diamond在架构设计时就遵循简单原则。通过简单的结构,从源头上为提高系统的稳定性。
如图1所示,整个Diamond服务由三个组件构成,分别是:地址服务器组件,diamond-server组件,Mysql存储。
- 地址服务器组件, 提供Diamond服务发现功能, 实现了集群扩容、下线等运维工作对于应用方的透明。应用方通过jmenv.tbsite.net域名,使用http协议,请求Diamond地址服务器组件的服务。
- diamond-server组件,是Diamond服务的核心。 借助diamond-client,通过Http协议向应用方提供配置的持久化管理、动态配置推送服务。 实现上,diamond-server组件是一个Web服务集群,集群节点间通过 特定机制实现数据变更的增量同步,是无中心化的结构。
- Mysql存储,是Diamond服务持久化配置管理的基础。在淘宝生产环境中, 使用一主两备的部署方式 保障数据的安全。
总结来说,Diamond的设计,使用集群代替中心,做到无单点,以此提高系统的可靠性;使用Http无状态协议暴露服务,使系统实现逻辑简单,不易出错。
完善的容灾机制
除了通过简化架构的方式来间接保证可靠性之外,完备的容灾机制是Diamond可靠性的直接保障。 应用方的配置被存储在mysql、服务端磁盘、客户端缓存目录和可以手动干预的容灾目录这4个地方。 应用方调用客户端API获取配置时,会按照优先级依次尝试从容灾目录、服务端磁盘、客户端缓存获取数据。
具体来说,Diamond的容灾机制,可以在如下场景还能保证持久化配置获取的可用性。
- mysql主库不可用时,启用备库后,继续提供配置读、写服务。
- mysql主备库都不可用时,通过服务端缓存,继续提供配置读服务。
- mysql主备库、diamond-server集群整体不可用时,客户端可以借助缓存目录继续使用配置读服务,借助容灾目录使用配置写服务。
因此,只有mysql主备库不可用,diamond-server集群整体不可用,客户端缓存目录数据丢失这几种情况同时发生时,Diamond服务才不可用。这在生产环境中可以认为是小概率事件,不可能发生。
总结上面的介绍可知,通过简单的架构和完善的容灾机制,实现了Diamond的高可靠性。
/**
* 按照本地容灾 -> server -> 本地缓存的优先级获取配置。超时单位是毫秒。
*/
static public String getConfig(String dataId, String group, long timeoutMs) throws IOException {
return defaultEnv.getConfig(dataId, group, timeoutMs);
}
----
易用的Diamond
Diamond在易用性上也下了一番功夫,目标是使用户易于使用,运维人员易于维护。
客户端API
它提供了精简的客户端API,有持久配置管理和动态配置订阅、获取两种语义的API。持久配置管理主要包括配置的发布、删除;动态配置订阅、获取,对于应用方来说是异步、同步两种获取配置在服务端最新值的方法。直观、易于理解的客户端API,可以使开发人员快速的上手。
控制台
另外,Diamond还提供了控制台,方便用户执行动态配置变更。应用方不用客户端API编码,也可以触发动态配置推送。在淘宝内部,几乎所有业务系统的开关配置变更都是依靠它来实现。
地址服务器
在运维方面,Diamond在架构上引入了地址服务器组件的角色。它提供了Diamond服务发现功能,实现了运维人员在执行集群扩容、下线工作对于应用方的透明,降低了运维难度。
---
高效的Diamond
Diamond的高效,主要体现在它提供的高吞吐率的配置获取,以及实时的动态配置推送能力上。
引入cache,借助zero-copy
为了提高数据读取吞吐率,引入cache是一个普遍的思路。Diamond的持久化配置保存在mysql中,额外的服务端每个节点都以本地磁盘文件的形式保存了全量的配置数据作为cache。当客户端请求读取配置数据时,服务端直接读取本地磁盘文件即可。 这样不但减少了对数据库单点的访问压力,也可以方便的实现配置获取服务的水平扩展。 引入服务端配置数据的缓存文件后,一次配置数据的读取请求,其实就是一次静态文件的Http请求。借助0拷贝的优化,减少了配置数据在应用态和内核态之间无用的内存拷贝,大大提升了数据传输效率。 借助这上述两点优化,diamond-server单节点(4核,8G虚拟机)的配置(1KB)读取能力在qps 6000+。
CAP的抉择
引入cache机制后,Diamond在实现上可以被定为一个分布式的缓存系统。图-2描述了配置的读、写流程。 对于一个分布式的缓存系统来说,CAP的取舍就是必须要面对的问题。本着“可靠、易用、高效”的目标,Diamond选择牺牲数据的强一致,提供了最终一致性;保证了系统的可用性和分区容忍性。 通过实践也证明了,提供最终一致性的配置并发读写,可以满足几乎所有的配置中心应用场景。
图-2
基于Http的服务端"推"
实时的动态配置推送是Diamond的核心,支持了许多淘宝中间件产品的重要功能,
如Tddl的数据源动态切换,HSF流控规则动态调整。 实时的动态配置推送,本质上是如何把服务端的更新状态,实时发送给客户端的问题。
直觉上,如果有一种机制可以使服务端源源不断的“推送”信息给客户端,这个问题就解决了。 TCP长连接是最自然的一种解决方案,但是由于设计上,Diamond选择了Http协议提供服务。 如何在Http协议的Request-Response通信模型下解决这个问题,是Diamond面临的难题。
在Diamond产品发展过程中,先后使用了2种思路。
- 基于拉模型的客户端轮询的方案,如图-3
- 客户端通过轮询方式发现服务端的配置变更事件。轮询的频率决定了动态配置获取的实时性。
- 这种方案最大的好处就是简单、可靠。 但是,当用户量增加时,较高的轮询频率给整个Diamond服务带来的压力也不断增加。另外,从配置中心的应用场景上来看,是一种写少读多的系统,客户端大多数轮询请求都是没有意义的,因此这种方案不够高效。
- 基于推模型的客户端长轮询的方案,如图-4
- 当前淘宝生产环境中的Diamond服务,采用了这种方案来实现动态配置实时推送。
- 基于Http长轮询模型,实现了让客户端在没有发生动态配置变更的时候减少轮询。这样减少了无意义的轮询请求量,提高了轮询的效率;也降低了系统负载,提升了整个系统的资源利用率。
- 另外,这种推拉结合的策略,做到了 在长连接和短连接之间的平衡, 实现上让服务端不用太关注连接的管理,效果上又获得了 类似TCP长连接的信息推送的实时性。
上图是第一种,轮训间隔无法改变,会发生配置变化后感知有延时的情况
上图是第二种,如果配置有变化会实时感知,然后轮询间隔会变短,平时会进行长轮询,减少客户端不必要的轮询
基于后一种方案,在2013年双11购物狂欢节中,面对巨大的消费者请求,Diamond集群高峰期间TPS达到18000,数据变更15,000次,动态配置推送客户端感知的最大延时小于2s。
双11当天部分应用的数据库做了动态切换,业务上完全没有影响。