一、 为什么需要服务注册与发现机制
当我们在尝试使用微服务架构时,我们会将一个大的单应用拆解成多个独立自治的小服务,如果在没有服务发现的机制下,我们想要在服务之间进行通信,我们只能使用 hard code 的方式,将需要通信的服务的网络信息写在服务中。这样会导致一系列的问题:
- 使用场景有限:由于每个服务属于"微"服务,每个服务生命周期不长,每个服务可能随时被关闭、重启、替换,如果服务提供者的网络地址发生了变化,将会影响服务消费者。
- 无法动态收缩:在生产环境中,每个服务一般都会部署多个实例,从而实现容灾和负载均衡,硬编码则无法适应这种需求。
二、 服务发现简介
那么要解决这些问题,服务的消费者就需要有一个强大的服务发现机制,服务消费者使用这个机制获取服务提供者的网络信息。即使服务提供者的信息发生变化,服务消费者也无须修改配置文件。服务发现组件便提供了这种能力,在微服务架构中,服务发现组件是一个非常重要的组件。
- 在各个服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
- 服务消费者可从服务发现组件中查询提供者到网络地址,并使用该地址调用服务提供者的接口。
- 各个服务与服务发现组件使用一定机制(例如心跳机制)通信。服务发现组件如果长时间无法与某微服务实例通信,就会注销该实例。
- 微服务网络地址发生变更时,会重新注册到微服务发现组件。
三、 服务发现技术选型比较:Consul vs Zookeeper vs Etcd vs Eureka
这里就平时经常用到的服务发现的产品进行下特性的对比,首先看下结论:
Feature |
Consul |
zookeeper |
etcd |
euerka |
服务健康检查 |
服务状态,内存,硬盘等 |
(弱)长连接,keepalive |
连接心跳 |
可配支持 |
多数据中心 |
支持 |
— |
— |
— |
kv存储服务 |
支持 |
支持 |
支持 |
— |
一致性 |
raft |
paxos |
raft |
— |
cap |
cp |
cp |
cp |
ap |
使用接口(多语言能力) |
支持http和dns |
客户端 |
http/grpc |
http(sidecar) |
watch支持 |
全量/支持long polling |
支持 |
支持 long polling |
支持 long polling/大部分增量 |
自身监控 |
metrics |
— |
metrics |
metrics |
安全 |
acl /https |
acl |
https支持(弱) |
— |
spring cloud集成 |
已支持 |
已支持 |
已支持 |
已支持 |
CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得 [1] 。
● 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
● 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
● 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
Eureka是一个服务发现工具。该体系结构主要是客户端/服务器,每个数据中心有一组Eureka服务器,通常每个可用区域一个。通常Eureka的客户使用嵌入式SDK来注册和发现服务。对于非本地集成的客户,使用功能区边框等透过Eureka透明地发现服务。
Eureka提供了一个弱一致的服务视图,使用尽力而为复制。当客户端向服务器注册时,该服务器将尝试复制到其他服务器,但不提供保证。服务注册的生存时间(TTL)较短,要求客户端对服务器心存感激。不健康的服务或节点将停止心跳,导致它们超时并从注册表中删除。发现请求可以路由到任何服务,由于尽力而为的复制,这些服务可能会导致陈旧或丢失数据。这个简化的模型允许简单的群集管理和高可扩展性。
在CAP中,Consul使用CP体系结构,有利于实现可用性的一致性。
最大的区别是Eureka保证AP, Consul为CP。
Consul强一致性(C)带来的是:
- 服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功
- Leader挂掉时,重新选举期间整个consul不可用。保证了强一致性但牺牲了可用性。
Eureka保证高可用(A)和最终一致性:
- 服务注册相对要快,因为不需要等注册信息replicate到其他节点,也不保证注册信息是否replicate成功
- 当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。
其他方面,eureka就是个servlet程序,跑在servlet容器中; Consul则是go编写而成。
这里就平时经常用到的服务发现的产品进行下特性的对比,首先看下结论:
- Euraka 使用时需要显式配置健康检查支持;Zookeeper,Etcd 则在失去了和服务进程的连接情况下任务不健康,而 Consul 相对更为详细点,比如内存是否已使用了90%,文件系统的空间是不是快不足了。服务的健康检查
- 多数据中心支持
Consul 通过 WAN 的 Gossip 协议,完成跨数据中心的同步;而且其他的产品则需要额外的开发工作来实现;
- KV 存储服务
除了 Eureka ,其他几款都能够对外支持 k-v 的存储服务,所以后面会讲到这几款产品追求高一致性的重要原因。而提供存储服务,也能够较好的转化为动态配置服务哦。
- 产品设计中 CAP 理论的取舍
Eureka 典型的 AP,作为分布式场景下的服务发现的产品较为合适,服务发现场景的可用性优先级较高,一致性并不是特别致命。其次 CP 类型的场景 Consul,也能提供较高的可用性,并能 k-v store 服务保证一致性。 而Zookeeper,Etcd则是CP类型 牺牲可用性,在服务发现场景并没太大优势;
- 多语言能力与对外提供服务的接入协议
Zookeeper的跨语言支持较弱,其他几款支持 http11 提供接入的可能。Euraka 一般通过 sidecar的方式提供多语言客户端的接入支持。Etcd 还提供了Grpc的支持。 Consul除了标准的Rest服务api,还提供了DNS的支持。
- Watch的支持(客户端观察到服务提供者变化)
Zookeeper 支持服务器端推送变化,Eureka 2.0(正在开发中)也计划支持。 Eureka 1,Consul,Etcd则都通过长轮询的方式来实现变化的感知;
- 自身集群的监控
除了 Zookeeper ,其他几款都默认支持 metrics,运维者可以搜集并报警这些度量信息达到监控目的;
- 安全
Consul,Zookeeper 支持ACL,另外 Consul,Etcd 支持安全通道https.
- Spring Cloud的集成
目前都有相对应的 boot starter,提供了集成能力。
总的来看,目前Consul 自身功能,和 spring cloud 对其集成的支持都相对较为完善,而且运维的复杂度较为简单(没有详细列出讨论),Eureka 设计上比较符合场景,但还需持续的完善。
四、 Eureka 简介
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
五、 Eureka 原理
Eureka 包含两个组件:Eureka Server 和 Eureka Client
- Eureka Server 提供服务发现的能力,各个微服务启动时,会向 Eureka Server 注册自己的信息,Eureka Server 会存储这些信息
- Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互
- 微服务启动后会周期性(默认30秒)的向 Eureka Server 发送心跳以续约自己的“租期“
- 如果 Eureka Server 在一定时间内没有接收到某个服务实例到心跳,那么会注销该实例
- 默认情况下,Eureka Server 同时也是 Eureka Client,多个 Eureka Server 实例之间是通过复制的方式来实现服务注册表中数据的同步
- Eureka Client 会缓存注册表中的信息。无须微服务每次请求都查询 Eureka Server
六、 编写 Eureka Server
- 添加 eureka 依赖
- 在启动类上添加 @EnableEurekaServer注解,声明这是一个 Eureka Server
- 在配置文件application.properties中添加
- 访问http://localhost:8000/
五、 将微服务注册到 Eureka Server 上
- 在微服务应用中添加依赖
- 在配置文件中添加
- 启动类上添加 @EnableDiscoveryCilent 或者@EnableEurekaClient注解,声明这个是一个 Eureka Client。
- 启动 服务发现组件APP 和 微服务客户端,访问 http://localhost:8000
六、 高可用
Eureka Server 可以通过运行多个实例并相互这次的方式实现高可用部署,Eureka Server 会彼此增量的同步信息,从而确保所有节点数据一致。
- 在 hosts 中配置以下信息
- 将 application.yml 配置中添加
3 将应用注册到 Eureka Server 上
在 application.yml
中添加
七、 备注
Eureka参数配置如下:
|
Name |
Default |
Description |
1 |
eureka.client.enabled |
true |
表明尤里卡客户端启用 |
2 |
eureka.client.eureka-connection-idle-timeout-seconds |
30 |
显示HTTP连接尤里卡服务器处于闲置状态多少时间(以秒为单位)才可以关闭。 在AWS环境中,建议值是30秒或更少,因为在不稳定状态防火墙清除连接信息几分钟后断开连接 |
3 |
eureka.client.eureka-server-connect-timeout-seconds |
5 |
表示要等多长时间(以秒为单位)之前到尤里卡的连接服务器超时。 注意,连接在客户端被org.apache.http.client汇集。HttpClient和创造这个设置会影响实际的连接以及等待时间从池中获取连接。 |
4 |
eureka.client.register-with-eureka |
true |
表明该实例能否被注册到尤里卡服务的其他客户端发现。 在某些情况下,您不希望您的实例被发现而你想发现其他实例。 |
5 |
eureka.client.fetch-registry |
true |
表明这个客户能否应该从尤里卡服务端获取尤里卡注册表信息 |
6 |
eureka.client.g-zip-content |
true |
显示只要是支持的服务从尤里卡服务端获取的内容是否被压缩。 表明从尤里卡服务端获取的信息是压缩过的用来优化网络流量。 |
7 |
eureka.client.heartbeat-executor-thread-pool-size |
2 |
心跳执行器的线程池初始值。 |
8 |
eureka.client.initial-instance-info-replication-interval-seconds |
40 |
表明最初多长时间(以秒为单位)复制实例信息到尤里卡服务。 |
9 |
eureka.client.instance-info-replication-interval-seconds |
30 |
表明多长时间(以秒为单位)复制实例更改到尤里卡服务。 |
10 |
eureka.client.registry-fetch-interval-seconds |
30 |
表明多长时间(以秒为单位)从尤里卡服务获取注册表信息。 |
11 |
eureka.client.serviceUrl.defaultZone |
|
尤里卡客户端和服务端通信的地址,多个地址用逗号隔开,e.g. http://localhost:8761/eureka/ |
12 |
eureka.dashboard.enabled |
true |
表明是否启用尤里卡服务控制台,默认为启用 |
13 |
eureka.dashboard.path |
/ |
尤里卡服务控制台的相对路径,默认为“/” |
14 |
eureka.instance.health-check-url |
|
尤里卡实例健康检查绝对路径URL |
15 |
eureka.instance.home-page-url |
|
尤里卡实例home页面绝对路径URL |
16 |
eureka.instance.hostname |
|
尤里卡实例主机名;如果不配置,尤里卡会直接使用操作系统的主机名 |
17 |
eureka.instance.lease-expiration-duration-in-seconds |
90 |
表明尤里卡服务等待的时间间隔,以秒为单位自收到最后的心跳才能把这个实例从它的视图中删除。 这个值被设置为至少高于leaseRenewalIntervalInSeconds中指定的值。 |
18 |
eureka.instance.lease-renewal-interval-in-seconds |
30 |
表明尤里卡客户端间隔多长时间(以秒为单位)向尤里卡服务发送心跳信息用来证明客户端是活着的; 在leaseExpirationDurationInSeconds参数的指定值期间没有收到来自客户端的心跳,这个实力会被尤里卡服务删除 |
19 |
eureka.instance.namespace |
eureka |
通过配置文件找到namespace,忽略springcloud的配置 |
20 |
eureka.instance.status-page-url |
|
尤里卡实例状态页面绝对路径URL |