很多情况下,当我们新增一个特性,往往会局限在某个服务,某个系统中。事实上很多系统功能上的问题是可以通过架构设计或者策略上的方式解决的。
这里我举个索引 shard replications 机制的例子
先介绍下我们索引服务系统。
说到索引的shard replications,ES是靠自己实现的,然而造成的一个弊端是,ES的复杂度其实也是因为类似这种功能而累加起来。 在我们内部对应的系统中(CS),因为实现了分层: 查询策略层 & 索引层。他们之间通过 RPC/HTTP 通讯,以zookeeper进行 信息协调(比如shard,mapping信息)。索引层的功能非常简化,只是提供了shard,mapping功能,然后通过http/thrift 对外暴露接口。
比较特殊的一点是索引构建的内容来源于消息队列,当然也提供http的post接口,可以保证某节点挂掉后再次恢复时,对应节点上的数据不会有因为关掉期间新增/更新/删除动作丢失的情况。
我们在系统建立一个索引A后,会自动构建三份索引,比如 A-1,A-2,A-3,这其实已经实现了shard replications =3了。
对外呈现的名称为A,这是通过查询策略层实现索引命名映射的。索引层老老实实是什么就是什么。
正常情况下,用户访问A,会被映射到索引层的A-1。当A-1的数据发生问题,比如索引损坏了,或者节点当机了,这个时候查询策略层会自动调整映射,将A映射到A-2,服务此时正常。
人工接受到报警后,我们会暂停A-1,A-3从MQ获取新增数据,flush完A-3的数据之后,我们会把数据拷贝替换到A-1上。
我们看到,在拷贝的过程中,会有新的新增/更新/删除 动作。这些动作其实都被缓存在消息队列中。
拷贝完成后我们会开启A-3,A-1,这个时候他们会去消费消息队列中的数据。数据稳定后再将A的映射切换回A-1. 完成整个fail over过程。
这里面我们没有让索引层实现这些复杂的功能,而是通过上层的查询策略层完成,而查询策略层做的工作也只是简单的映射关系的调整。当然这里面依赖于MQ,我们才能保证在实时增加数据的情况下,做各种切换仍然不会丢失新增,更新和删除动作。
目前二次排序,缓存,多索引查询等都是在查询策略层完成。索引层功能变得单一,基本我们现在更新或者重启索引服务的概率非常低。而查询策略层多台服务只是负载均衡,所以可以很方便做到rolling upgrade。