服务发现就是程序如何通过一个标志来获取服务列表,并且这个服务列表是能够随着服务的状态而动态变更,最终得以调用到相应的服务。

服务发现是在分布式系统规模越来越大的情况下,服务治理的必然产物,不然服务的配置调用将难以维护。

服务发现

服务发现可以分为注册和解析两个部分。

服务注册

存储的信息是域名和ip的对应映射关系,存储的解析信息至少包括正在运行的服务的主机ip和端口信息。

注册的方式有两种

  • 上报,每个服务启动后主动将自己的域名和ip信息上报存储下来
  • 监听,服务监听集群中服务的创建,并且获取相关信息存储下来,比如coreDNS

解析发现

服务发现主要存在有两种模式,客户端模式与服务端模式,两者的本质区别在于,客户端是否保存服务列表信息。

客户端模式

在客户端模式下,如果要进行微服务调用,首先要进行的是到服务注册中心获取服务列表,然后再根据调用端本地的负载均衡策略,进行服务调用。

客户端(自身有服务注册中心--可以获取服务列表)------>调用服务
   |
   |
   |
服务注册中心--服务列表

我们用图可以看出来

优点

  1. 只需要周期性获取列表,在调用服务时可以直接调用少了一个RT。但需要在每个客户端维护获取列表的逻辑
  2. 可用性高,即使注册中心出现故障也能正常工作
  3. 服务上下线对调用方有影响(会出现短暂调用失败)

大部分服务发现的实现都采取了客户端模式

服务端模式

在服务端模式下,调用方直接向服务注册中心进行请求,服务注册中心再通过自身负载均衡策略,对微服务进行调用。这个模式下,调用方不需要在自身节点维护服务发现逻辑以及服务注册信息,这个模式相对来说比较类似DNS模式。

客户端(自身没有服务注册中心)------>服务注册中心(在服务端)------>调用服务

如图

优点

  1. 简单,不需要在客户端维护获取服务列表的逻辑
  2. 可用性由路由器中间件决定,路由中间件故障则所有服务不可用,同时,由于所有调度以及存储都由中间件服务器完成,中间件服务器可能会面临过高的负载
  3. 服务上下线调用方无感知

框架

目前服务发现框架:

consul---------go语言编写,常用,简单,无依赖,集成了http/DNS library
etcd-----------go语言编写,常用,简单,无依赖,集成了Client Binging/http
Zookeeper------java语言编写,常用,简单,依赖jvm,集成了Client Binging
Eureka---------java
smartstsck-----ruby
nsq------------go
serf-----------go
spotify--------DNS
skydns---------go

前三个都是比较常用和通用的,其他都是自己造轮子,适合特定场景。

Eureka

eureka是netflix用于服务注册和发现的框架。在这个框架中,分为server和client两种角色。server负责保存服务的注册信息,同时server之间也可以彼此相互注册,client则需要向特定的server进行注册。

client/server通过RESTful Api向server进行服务注册,并且定期调用renew接口来更新服务的注册状态,若server在60s内没有收到服务的renew信息,则该服务就会被标志为下线。而如果服务需要主动下线的话,向server调用cancel就可以了。

Consul

Consul是强一致性的数据存储,使用Raft形成动态集群。它提供分级键/值存储方式,不仅可以存储数据,而且可以用于注册器件事各种任务,从发送数据改变通知到运行健康检查和自定义命令,具体如何取决于它们的输出。

与Zookeeper和etcd不一样,Consul内嵌实现了服务发现系统,所以这样就不需要构建自己的系统或使用第三方系统。这一发现系统除了上述提到的特性之外,还包括节点健康检查和运行在其上的服务。

Zookeeper和etcd只提供原始的键/值队存储,要求应用程序开发人员构建他们自己的系统提供服务发现功能。而Consul提供了一个内置的服务发现的框架。客户只需要注册服务并通过DNS或HTTP接口执行服务发现。其他两个工具需要一个亲手制作的解决方案或借助于第三方工具。

Consul为多种数据中心提供了开箱即用的原生支持,其中的gossip系统不仅可以工作在同一集群内部的各个节点,而且还可以跨数据中心工作。

模版Consul-template

confd可以像和etcd搭配一样用于Consul,不过Consul有自己的模板服务,其更适配Consul。

通过从Consul获得的信息,Consul-template是一个非常方便的创建文件的途径,还有一个额外的好处就是在文件更新后可以运行任意命令,正如confd,Consul-template也可以使用Go模板格式。

Zookeeper

Zookeeper是这种类型的项目中历史最悠久的之一,它起源于Hadoop,帮助在Hadoop集群中维护各种组件。它非常成熟、可靠,被许多大公司(YouTube、eBay、雅虎等)使用。其数据存储的格式类似于文件系统,如果运行在一个服务器集群中,Zookeper将跨所有节点共享配置状态,每个集群选举一个领袖,客户端可以连接到任何一台服务器获取数据。

Zookeeper的主要优势是其成熟、健壮以及丰富的特性,然而,它也有自己的缺点,其中采用Java开发以及复杂性是罪魁祸首。尽管Java在许多方面非常伟大,然后对于这种类型的工作还是太沉重了,Zookeeper使用Java以及相当数量的依赖使其对于资源竞争非常饥渴。因为上述的这些问题,Zookeeper变得非常复杂,维护它需要比我们期望从这种类型的应用程序中获得的收益更多的知识。这部分地是由于丰富的特性反而将其从优势转变为累赘。应用程序的特性功能越多,就会有越大的可能性不需要这些特性,因此,我们最终将会为这些不需要的特性付出复杂度方面的代价。

Zookeeper为其他项目相当大的改进铺平了道路,“大数据玩家“在使用它,因为没有更好的选择。今天,Zookeeper已经老态龙钟了,我们有了更好的选择。

etcd

etcd是一个采用HTTP协议的健/值对存储系统,它是一个分布式和功能层次配置系统,可用于构建服务发现系统。其很容易部署、安装和使用,提供了可靠的数据持久化特性。它是安全的并且文档也十分齐全。

etcd比Zookeeper是比更好的选择,因为它很简单,然而,它需要搭配一些第三方工具才可以提供服务发现功能。比如Confd

Confd是一个轻量级的配置管理工具,常见的用法是通过使用存储在etcd、consul和其他一些数据登记处的数据保持配置文件的最新状态,它也可以用来在配置文件改变时重新加载应用程序。换句话说,我们可以用存储在etcd(或者其他注册中心)的信息来重新配置所有服务。

对比

功能\组件 Zookeeper etcd Consul
产生时间
原生语言 JAVA Go Go
算法 Paxos Raft Raft
多数据中心 不支持 不支持 支持
健康检查 支持 不支持 支持
web管理界面 支持 不支持 支持
http协议 较为复杂 支持 支持
DNS协议 较为复杂 不支持 支持

对比的情况下,我们在k8s的集群中可以使用etcd做服务发现,但是在常规情况下,consul更加的全面直接的做服务发现,比如自带服务发现功能,支持多数据中心,直接界面友好等。

高可用

在多注册中心(server)的情况下,单个server在接收到服务的注册/更新信息的时候,它还会将这些信息同步给同样为server的peer(replicate to peer),为了避免广播风暴,这些信息只会传递一次,也就是说,接收到的server,不会再同步给自身的peer。

服务注册完成之后,当client需要进行服务调用的时候,就可以向server获取当前的服务列表,再根据服务列表中的ip地址以及端口号进行调用了。

Consul是目前较为流行的一个服务发现以及配置工具,Consul能够承担包括服务注册与发现、健康检查(health check)以及键值对存储等,同时,Consul还支持多个数据中心。

我们可以通过Consul的Restful Api(curl -request PUT http://consul/v1//agent/service/register) 向Consul Agent注册服务信息,提交服务的端口号,ip地址,以及健康检查的方式。随后,这个Client就按照配置中的周期以及方式执行健康检查,当健康检查失败的时候,就会像Server Agent发送服务不可用的消息,这个服务就会被Consul标记为不可用了。