单体架构的系统下,系统由不同的功能模块组成,在一个进程内,模块之间调用,函数直接调用即可。如果模块拆分为独立的进程,就引入了进程间通讯,进程都在一台主机上时,通讯方式可以是管道,网络,消息;如果是分布式,可以是HTTP请求、也可以是RPC,HTTP协议报文太长,报文序列化效率比较低,我们选择RPC。
RPC调用首先要知道调用的接口在哪个服务上,服务启动在哪台机器上地址是什么,服务的端口是什么,这样client才能与server建立网络连接通讯,和DNS服务一样这就是NameSpace服务,可以通过服务名字和调用的方法名字来找到Server的IP和PORT,这就是服务发现组件的意义和功能。
服务发现设计包括几部分,存储设计,Client/Server,API服务健康检查,高可用,数据一致性,高性能,API服务节点的选择。
存储的内容有:服务名字,IP:PORT,典型的KV结构,K(服务名字):V(IP:PORT), 还要存储每个服务的健康状态,存储介质有:内存,外部缓存,文件系统;内存有易失性,Server宕机服务注册信息丢失,服务发现功能不可用;可以写到外部缓存如Redis同时同步到磁盘文件,Redis缓存不是实时同步到磁盘,快照的间隙期的注册信息会丢失;直接存储在文件系统。一个存储系统的设计,存储系统的拓扑是使用单机、集群还是分布式,单机系统单点故障服务不可用,集群的节点之间需要数据同步,数据同步延迟会导致每个节点数据不是实时一致,一个节点故障可以切换到另一个节点来服务,分布式存储系统,使用分布式通讯协议来保证各个节点之间的数据同步,保证了数据的实时一致性。
我们常见的服务发现组件consul,zk,etcd,核心都是一个强一致性的KV存储系统,使用Raft协议去实现节点的数据强一致性,分布式系统符合CAP特性,网络分区是始终存在的,我们保证了强一致性,那么就会牺牲一些可用性,参与选取的节点越多某一个节点故障对系统影响会越小,可用性越高然后节点选举和数据同步耗时就会越多。此外通过节点故障检测,数据的缓存也是提高系统可用性的方法。
服务发现要能及时发现API服务健康状态,健康检查可以放到服务发现的Server端,可以在业务代码里添加心跳机制监控所连接Server的健康状态,zk使用这种方式,consul采用独立的client节点通过goosip协议加入到分布式网络中,然后同步健康状态给server节点来记录。业务代码通过HTTP接口来更新可用服务列表。
为了提高服务的高可用和高性能,同一个API Server我们会部署多个实例,同时服务用户,这就需要负载均衡策略, 选择API Server节点不属于服务发现的工作,我们放到系统的BFF层去做,可以采用随机选择,顺序选择,权重选择的负载均衡策略。