1、引言
这里服务的发现与注册使用Etcd,Etcd是CoreOS团队于2013年6月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库。Etcd内部采用raft
协议作为一致性算法,etcd基于Go语言实现。
开原地址:Etcd
服务的发现与注册需要解决以下问题:
- 服务IP与端口的确定方式。
- 服务注册与发现。
- 服务下线
- 服务健康监测。
- 节点加入或退出,如何通知订阅者变化。
- 查看应用的订阅列表,发布列表,以及订阅节点。
2、ip and port
在微服务的系统应用中,服务的实例个数是动态变化的,各个实例动态化分配网络地址和端口,为了弹性扩容,快速部署,多机部署,需要自动化识别ip与port。
2.1 ip 确认
- 手动配置:可以使用,但是生产环境下无法做到水平扩容,多机部署,部署一台就需要去手动配置一台,运维成本较大。
- 遍历网卡:可在生产环境中使用,实现自动化适配。推荐这种方法。
1 | func GetLocalIP() (string,error){ |
2.2 port
在微服务中,直接使用配置文件中配置好的端口就可以。
3、服务注册
服务注册就是服务启动后向注册中心(Etcd)登记自己的IP与端口信息,服务的消费方通过查看登记信息,可以直接找到对应的服务,并发起请求。
注册模式有两种:
- 客户端注册
- 服务端注册
3.1、客户端注册
服务启动后,主动去注册。服务停止,主动去注销信息。服务注册应该在服务完全成功启动后在发生注册。通过检测对应端口是否已经处于监控状态了,就可以判断该服务是否成功启动。
3.2、服务端注册
同样,服务成功启动后去注册。但是不是由自己去注册,统一交给一个独立的注册服务去完成。服务启动后,通过一种机制通知注册服务,让其去帮自己完成注册。这个注册服务一定要保证高可用,否则,整个系统的注册服务就无法使用。
3.3、clientv3服务注册
使用Etcd客户端clientv3,操作。通过put方法完成注册。通过get获取对应注册信息,并获取覆盖之前的数据。
文档: clientv3
3.3.1 注册
1 | import ( |
3.3.2 小节
同理,删除可以直接调用Delete方法更多api,可查阅文档。clientv3文档,
这里推荐一个Etcd的UI管理端etcdkeeper
4、服务发现
服务发现就是去注册中心查询指定信息。连接服务,完成对应的请求。
发现模式:
- 客户端发现
- 服务端发现
4.1 客户端发现
客户端发现指客户端直接连接注册中心,获取服务信息,自己实现负载均衡,使用一种负载均衡策略发起请求。优势可以定制化发现策略与负载均衡策略,劣势也很明显,每一个客户端都需要实现对应的服务发现和负载均衡。
4.2 服务端发现
使用reverse proxy,在proxy层实现服务发现与负载均衡。后面利用Envoy设置一个反向代理。优势是服务发现与负载均衡对客户端来说完全透明。缺陷是这里需要维护一个高可用的反向代理,并且支持负载均衡。
4.3、clientv3服务发现
获取服务数据
1 | func GetInfo() { |
5、 服务下线
基本上注册中心都是有健康检查的功能的,应用停止服务后,会自动检测剔除服务,但是不应该依靠健康检测。所以我们应该主动去注销。需要注意不应该直接注销,先将权重修改为0,不会再有服务请求,调用注册中心注销接口,进行注销。
5.1、clientv3服务注销
1 | func Logout() { |
6、健康监测
基本上健康检查分为两种,客户端心跳检测,服务端主动检测。
6.1、客户端心跳
- 维持长链接,定期发送心跳包,心跳可以是TCP,也可以是HTTP的形式。
客户端心跳检测的更偏向于链路是否通,所以服务端的服务台可能会异常。
6.2、服务端检测
- 注册中心调用服务发布者接口,通过返回结果直接判断健康。
- 通过脚本定时检测。
服务端检测会比较准确,但是很难做到通用性,毕竟接口不同。而且还需要保证注册中心所在网络与接口发布者所在网络是互通的。
6.3、小结
具体使用哪种方式,可以根据自己的业务场景而定。
6.4、Etcd健康监测
用户可以在etcd中注册服务,并且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。需要维护一个TTL(V3 使用 lease实现),类似于心跳。
申请lease租约,设置服务生存周期TTL,让key支持自动过期,在服务正常的状态下,通过KeepAlive定期去续租,避免过期。这里还需要考虑一个异常场景,极有可能在Put或者Keepalive时Lease已经过期,所以需要进行容错处理,分配新的Lease进行重试。
官方demo:
1 | func test() { |
同样也可以使用KeepAliveOnce续租一次,在一个for循环中定时续租。
7、变化通知
7.1 Etcd watch
watch的作用就是在新服务改变(重新注册,下线等)后, 告知各个服务执行相应的逻辑(重新连接新服务,报警等).
比如我们注册一个key,并且设置TTL为10s,10s后key自动删除。
1 | resp, _:= cli.Grant(context.TODO(), 10) |
设置watch,/services/book/下发生变化会发出通知。根据Evevt执行对应逻辑,报警,重连,等等。
1 | ch := cli.Watch(context.TODO(), "/services/book/", clientv3.WithPrefix()) |
8、总结
在微服务中,服务发现与注册至关重要,并且很多设计还需要根据业务来确定。这里也是大量用Etcd举例。Etcd是Kubernetes集群中的一个十分重要的组件,用于保存集群所有的网络配置和对象的状态信息.很多公司在生产环境中都在使用Etcd,如果微服务技术栈都是使用go,很推荐Etcd作为基础组件。
8.1、参考文献
1.etcd-io
4.深入学习Etcd