对Service Discovery with router tier的思考
前言
最近,在思考Load Balancer(LB)
和Configuration Controller
的区别,最后根据《DDIA》中提到的关键词搜到的资料总算是明确了两者的一些优缺点(大概),也验证了我的一些想法,它们都属于service discovery
问题的一种解决方案。
Service discovery
所谓服务发现,根据我的理解,它就是指一个客户端接入整个集群,它怎么找到应该提供给它服务的服务器,即哪个节点它应该连接以至于服务内容是正确的或是对应的。
比如,对于kv服务,如果采用集群形式提供服务,那么可能采取哈希键的方式获取到具体的节点,然后之后对于该键的操作都应该连接该节点(具体实现会更复杂)。
这个问题的解决影响整个集群的可用性,因此设计集群估计是避不开的。
以下我想讨论的主要是服务发现中的一种方案:带有路由层(router-tier)的集群更细化的方案思考。
Load Balancer
根据nginx文档可以了解到均衡负载器(以下可能称呼为LB)的作用主要是将客户端的请求转发根据均衡负载算法转发给后端的(提供服务的)服务器,从而保证所有服务器并不会工作过载(overwork),否则性能可能会下降(因为资源紧张),并且由于有多个服务器并行处理请求,因此性能相较于单服务器可能有一定的提升(我想如果IO负载比计算负载要更大,那么可能性能不会提升那么明显或者下降)。
在实现上,均衡负载器类似于反向代理
(reverse proxy):收集所有请求并转发给服务器,收集服务器的响应转发给对应的客户端,但是不会做caching等额外的操作。
我对均衡负载器最不理解的一点在于,在客户端和服务端之间加设了一个“路由器”(router),为什么很多横向扩展(horizontal scaling)的方案采用它,因为这不多了一层网络IO的开销,稍微学过OS的都知道各种IO设备之间存在间隙(gap),而网络IO是所有IO设备中目前最差的(尽管gap在缩小)。
但是实际上作为代价也带来了一些好处,见下。
Configuration Controller
首先解释一下,Configuration是指集群节点的元数据,每次节点发送变化会生成新的配置。比如分片服务器,那么维护的可能是分片到节点的映射等元数据。
配置管理器和LB类似,也是在服务器和客户端之间的一个中介,但是它并不是转发中介,而只是个管理集群元数据的服务器,客户端要获取具体的服务内容最终还是得与提供服务的节点进行连接。
配置的存储方式容易想到的有两种:
- 内存(自定义数据结构或者内存数据库)
- 硬盘(一般使用面向硬盘的数据库)
既然配置管理器不转发请求,那么客户端首先必须通过它获取服务器的位置,然后才能向提供服务的节点发送请求。
那么每次获取节点位置都需要请求两次?这样性能想必不会好到哪去。
一种避免方式是将配置缓存到客户端。但是又有新的问题:在客户端的缓存如何更新?
- 客户端同步更新:如果配置管理器每次配置更新的时候同时发送给所有客户端配置的话,IO开销太大了,因为客户端的数目可能很大,甚至远大于集群大小。
- 节点同步更新:然后节点接受到请求后解析请求判断请求是否该送到该节点,如果不是的话,说明客户端的配置已经过期了,需要节点将新的配置发给客户端。
- 向配置管理器索取新配置:节点如果可以不依赖配置判断该请求是否由自己负责的话,可以返回错误码让客户端向配置管理器请求新配置。
6.824
的ShardedKv采用的是这种方式,redis
其实也类似,只是没有配置管理器(它真正采用的方法其实还是有区别的,感兴趣的可以翻redis文档)。
优缺点对比
对于服务发现而言,LB是server-side
的实现方案,而配置管理器是client-side
的实现方案。
Load Balancer
优点:
- 分散节点负载,并行处理多个请求,提升整个系统的吞吐量和性能。
- 不与服务端和客户端的具体实现耦合。换言之,不需要客户端和服务端的代码增加对此的逻辑处理。更重要的对于某些特定协议,可以复用原有的服务端或客户端,比如web服务器可以复用apache,nginx,lighthttpd等。
- 即使节点故障了,LB没故障仍可以提供服务(我认为这个不能算是很好的做法,因为更好的做法是节点自己提供容错手段)。
- 隐藏提供服务的节点,对于某些服务可能有用。
缺点:
- IO负载都集中在LB上,某种意义上系统瓶颈可能在于单个LB(尽管可以构筑LB集群)。
- LB作为路由器多了一跳,换言之,增加额外的网络IO开销。如果请求基本都是IO负载,那么性能不会得到很大的提升。
Configuration Controller
优点:
- 客户端与节点直接连接,避免了中介转发的IO开销。
- 请求不通过中介直接分发给了集群中的所有节点,很大程度上将热点(hop spot)从中介上转移了。
缺点:
- 请求到达的节点并不是负责该请求的节点时,需要客户端获取新的配置。如果节点有新配置缓存,可以直接返回新配置,不然需要向配置管理器索取新配置。所以可能会有3次请求。
- 实现复杂,需要客户端,服务节点都针对其进行定制化,并且对于不同语言的客户端也不友好(都需要针对该逻辑进行特殊处理)。