Eureka介绍与使用
- 一、Eureka介绍
- 1.Eureka的诞生
- 2.Eureka的优势
- 2.1 提供完成的服务注册和服务发现实现
- 2.2 与spirngcloud无缝集成
- 2.3 采用AP而非CP
- 2.4 Eureka开源
- 3.Eureka和zk作为注册中心比较
- 4.Eureka的架构设计
- 4.1 设计理念
- 4.1.1 AP由于CP
- 4.1.2 Peer to Peer设计
- 4.1.3 Zone和region的设计
- 4.1.4 self preservation设计
- 4.2 组件调用关系设计
- 4.2.1 服务提供者
- 4.2.2 服务消费者
- 4.2.3 服务注册中心
- 4.3 数据存储结构
- 4.3.1 数据存储层
- 4.3.2 二级缓存层
- 5.Eureka高可用原理分析
- 5.1 客户端高可用原理
- 5.2 服务端高可用原理
- 二、Eureka使用
- 1.环境准备
- 2.创建Eureka Server
- 2.1 创建Spring Boot项目
- 2.2 配置Eureka Server
- 3.创建Eureka Client
- 3.1 添加Eureka Client依赖
- 3.2 配置Eureka Client
- 3.3 注册服务
- 4.使用Eureka进行服务发现
- 4.1在另一个Eureka Client中发现服务
- 4.2使用@FeignClient进行服务调用
- 5.总结
一、Eureka介绍
1.Eureka的诞生
Eureka的诞生要说一下它的公司netflix的故事——netflix是世界上最大的流媒体视频网站,其公司的架构师基于AWS云开发的。在AWS中使用的负载均衡器是ELB(Elastic Load Balancing),即弹性负载均衡通过流量分发扩展应用系统对外的服务能力(类似阿里云SLB服务)。理论上是可以通过ELB对内部进行负载均衡的,但是如果这样就会暴露到外网,存在安全性问题;另外ELB是基于传统的代理的负载均衡解决方案,无法直接基于服务元数据信息定义负载均衡算法。也就是说使用ELB有一定的限制,无法根据复杂的生产环境提供更为复杂的负载均衡方案,且存在一定的安全隐患。netflix鉴于自己的生产环境,设计出了eureka,一方面给内部服务做服务发现,另一方面可以结合ribbon组件提供各种个性化的负载均衡算法。ELB亦是传统的基于代理实现的负载均衡解决方案而Eureka则与之不同,Eureka属于客户端发现模式,客户端负责决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端从一个服务注册服务中查询所有可用服务实例的库,并缓存到本地。在生产中如果是使用AWS等云服务,可以结合eureka一起使用(以AWS为例),ELB用来对客户端或者终端设备进行请求负载均衡,而eureka用来对中间层的服务做服务发现,配合其他组件提供负载均衡的能力。使用eureka作为中间负载均衡和服务发现。
以上的事情,简单的说就是
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
2.Eureka的优势
2.1 提供完成的服务注册和服务发现实现
首先是提供了完整的服务注册和服务发现实现,并且也经受住了Netflix自己的生产环境考验,相对使用起来会比较省心。
2.2 与spirngcloud无缝集成
我们的项目本身就使用了Spring Cloud和Spring Boot,同时Spring Cloud还有一套非常完善的开源代码来整合Eureka,所以使用起来非常方便。另外,Eureka还支持在我们应用自身的容器中启动,也就是说我们的应用启动完之后,既充当了Eureka的角色,同时也是服务的提供者。这样就极大的提高了服务的可用性
2.3 采用AP而非CP
eureka是在部署AWS的背景下面设计的,其设计认为,在云端,特别是大规模部署情况下面,失败是不可以避免的,可能是因为eureka自身部署失败或者网络分区等情况导致服务不可用,这些问题是不可以避免的,要解决这个问题就需要eureka在网络分区的时候,还能够正常提供服务,因此eureka选择满足availability这个特性。
在生产实践中,服务注册及发现中保留可用以及过期的数据总比丢失可用的数据好。—peter kelly
eureka选择了A也就必须放弃C,也就是说在eureka中采用最终一致性的方式来保证数据的一致性问题,因此实例的注册信息在集群的所有节点之间的数据都不是强一性的,需要客户端能支持负载均衡算法及失败重试等机制。
2.4 Eureka开源
Eureka代码是开源的,所以非常便于我们了解它的实现原理和排查问题。需要的话还可以在上面进行二次开发。
3.Eureka和zk作为注册中心比较
4.Eureka的架构设计
4.1 设计理念
4.1.1 AP由于CP
4.1.2 Peer to Peer设计
一般而言在分布式系统的数据有多个副本之间的复制方式,可以分为主从复制和对等复制
主从复制:Master-Slave模式,一个主副本和多个从副本,所有数据的写操作都是提交到主副本,最后由主副本更新到其他的从副本(常采用异步更新),通常写是整个系统的瓶颈所在。对等复制:副本之间不分主从,任何的副本都可以接受写数据,然后副本之间进行数据更新。在对等复制中,由于每一个副本都可以进行写操作,各个副本之间的数据同步及冲突处理是一个比较难解决的问题。
eureka中的对等复制机制
- 客户端:在客户端中配置相应的服务端的多个peer节点,在客户端实际操作中有如下几点规律;(1)有多个分区时候,优先选择与应用实例所在分区一样的其他服务实例,如果没有的话选择默认是defaultZone(2)客户端会维护一个可用的server列表,请求的时候优先从可用的列表中进行选择,如果请求失败切换到下一个server进行重试,重试次数为3次(3)为了防止客户端请求服务端的节点不均匀现象,客户端有一个定时任务来刷新并随机化eureka server的列表。
- 服务端:server本身依赖于客户端,也就是每一个server是作为其他server的客户端存在。在一个server启动的时候,有一个synvUp操作,通过客户端请求其他的server节点中的一个节点获取注册的应用实例信息,然后复制到其他的peer节点。eureka中采用版本号(lastDirtyTimestamp)和心跳机制(renewLease从新租约方式)的方式来解决数据复制过程中的冲突问题。
4.1.3 Zone和region的设计
使用region来代表一个独立的地理区域,比如us-east-1、us-east-2,、us-west-1等。在每一个region下面还分为多个AvailabilityZone,一个region对应多个AvailabilityZone,不同的region之间相互隔离。默认情况下面资源只是在单个region之间的AvailabilityZone之间进行复制,跨region之间不会进行资源的复制。
AvailabilityZone看成是region下面的一个一个机房,各个机房相对独立,主要是为了region的高可用考虑的,一个region下面的机房挂了,还有其他的机房可以使用。
一个AvailabilityZone可以设置多个server实例,他们之间构成peer节点,然后采用peer to peer的复制模式进行数据复制。
4.1.4 self preservation设计
在分布式系统设计中,通常需要对应用实例的存活进行健康检验,这里比较难处理的就是网络偶尔抖动或者短暂不可用而造成的误判。因此eureka设计了self preservation机制。server和client之间有一个租约,client定期发送心跳来维护这个租约,表示心跳还活着,eureka通过当前注册的实例数量,去计算每分钟应用从应用实例接受到的心跳数量,如果近一分钟接受到的租约的次数小于等于指定的阈值,则关闭租约失效剔除,禁止定时任务剔除失效的实例,从而保护注册信息。
4.2 组件调用关系设计
4.2.1 服务提供者
- 启动后,向注册中心发起 register 请求,注册服务
- 在运行过程中,定时向注册中心发送 renew 心跳,证明“我还活着”。
- 停止服务提供者,向注册中心发起 cancel 请求,清空当前服务注册信息。
4.2.2 服务消费者
- 启动后,从注册中心拉取服务注册信息
- 在运行过程中,定时更新服务注册信息
- 服务消费者发起远程调用:
a> 服务消费者(北京)会从服务注册信息中选择同机房的服务提供者(北京),发起远程调用。只有同机房的服务提供者挂了才会选择其他机房的服务提供者(青岛)。
b> 服务消费者(天津)因为同机房内没有服务提供者,则会按负载均衡算法选择北京或青岛的服务提供者,发起远程调用。
4.2.3 服务注册中心
- 启动后,从其他节点拉取服务注册信息。
- 运行过程中,定时运行 evict 任务,剔除没有按时 renew 的服务(包括非正常停止和网络故障的服务)。
- 运行过程中,接收到的 register、renew、cancel 请求,都会同步至其他注册中心节点
4.3 数据存储结构
Eureka数据存储结构图
Eureka 的数据存储分了两层:数据存储层和缓存层。
Eureka Client 在拉取服务信息时,先从缓存层获取(相当于 Redis),如果获取不到,先把数据存储层的数据加载到缓存中(相当于 Mysql),再从缓存中获取。值得注意的是,数据存储层的数据结构是服务信息,而缓存中保存的是经过处理加工过的、可以直接传输到 Eureka Client 的数据结构。
4.3.1 数据存储层
第一层的 key 是spring.application.name,value 是第二层 ConcurrentHashMap;第二层 ConcurrentHashMap 的 key 是服务的 InstanceId,value 是 Lease 对象;Lease 对象包含了服务详情和服务治理相关的属性。
更新一级缓存:
Eureka Server 内置了一个 TimerTask,定时将二级缓存中的数据同步到一级缓存(这个动作包括了删除和加载)。
4.3.2 二级缓存层
一级缓存:ConcurrentHashMap<Key,Value> readOnlyCacheMap,本质上是 HashMap,无过期时间,保存服务信息的对外输出数据结构。二级缓存:Loading<Key,Value> readWriteCacheMap,本质上是 guava 的缓存,包含失效机制,保存服务信息的对外输出数据结构。
二级缓存的更新机制:
eureka二级缓存的更新机制
删除二级缓存:
- Eureka Client 发送 register、renew 和 cancel 请求并更新 registry 注册表之后,删除二级缓存;
- Eureka Server 自身的 Evict Task 剔除服务后,删除二级缓存;
- 二级缓存本身设置了 guava 的失效机制,隔一段时间后自己自动失效;
加载二级缓存:
- Eureka Client 发送 getRegistry 请求后,如果二级缓存中没有,就触发 guava 的 load,即从 registry 中获取原始服务信息后进行处理加工,再加载到二级缓存中。
- Eureka Server 更新一级缓存的时候,如果二级缓存没有数据,也会触发 guava 的 load。
重点关注:ResponseCacheImpl类
5.Eureka高可用原理分析
前面讲了Eureka是基于AWS开发出来的框架,因此eureka天生的支持region和availabilityZone的概念。
1、默认情况下面不同region是相互隔离的,region之间的数据是不会复制的,但是eureka client提供了fetch-remote-regions-registry配置,作用是拉取远程的注册信息到本地2、availabilityZone,eureka中默认eureka-client-prefer-zone-eureka配置为true,也就是拉取serverUrl时候,默认选取和应用实例同一个zone的eureka server列表。
5.1 客户端高可用原理
- 在client启动之前,如果没有eureka server,则通过配置eureka.client.back-registry-impl从备份的registry读取关键服务的信息。
- 在client启动后,如果运行时候server全部挂掉了,本地内存有localRegion之前获取的数据。
- 如果是server部分挂了。如果预计恢复时间比较长,可以人工介入,通过配置中心人工摘除服务(但是基本不用这样做)。在client中会维护一份不可用的server列表,一旦心跳时候失败,当该列表的大小超过指定的阈值时候就会进行重新清空,重新清空后,client会进行重试(默认3次)
5.2 服务端高可用原理
服务端采用peer to peer的架构模式,原则上就是高可用的。同时服务端还可以通过配置remoteRegionUrlsWithName来支持拉取远程的region实例,如果当前的region挂了,会自动fallback到远程的region获取数据
同时服务端采用renew租约和定时心跳的方式保护注册信息(self preservation机制)
二、Eureka使用
如何使用Eureka进行服务注册与发现?
以下我们将通过示例代码展示如何在Spring Boot应用中集成Eureka。
1.环境准备
首先确保您已经安装了以下环境
JDK 1.8 或更高版本
Maven 3.0 或更高版本
Spring Boot 2.x
2.创建Eureka Server
首先我们创建一个Eureka Server。这将作为服务的注册中心。
2.1 创建Spring Boot项目
创建一个新的Spring Boot项目并在pom.xml中添加Eureka Server的依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.2 配置Eureka Server
在application.yml中配置Eureka Server
server:port: 8761
eureka:instance:hostname: localhost #此设置用于配置Eureka实例的主机名。您可以将其设置为本地主机名或指定的主机名。client:registerWithEureka: false # 这个配置用来决定Eureka客户端是否向Eureka Server注册自己。如果设置为true该客户端会向Eureka Server注册自己的信息这样其他服务就可以通过Eureka Server找到它。如果设置为false则该客户端不会注册到Eureka Server。通常情况下Eureka Server会将此值设置为false因为Eureka Server不需要向自己注册。而一般的Eureka Client应将其设置为true以完成服务注册。fetchRegistry: falseserviceUrl: # 此设置用于配置Eureka Server的地址。需要指定Eureka Server的主机名和端口。defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/```---### 2.3 启动Eureka Server
在主类上添加@EnableEurekaServer注解并启动应用```Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}
3.创建Eureka Client
接下来我们创建一个Eureka Client这将是我们要注册的服务。
3.1 添加Eureka Client依赖
在另一个Spring Boot项目的pom.xml中添加Eureka Client的依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2 配置Eureka Client
在application.yml中配置Eureka Client
server:port: 8080
spring:application:name: my-service # 此设置用于配置在Eureka服务中注册的应用名称。
eureka:client:serviceUrl: # 此设置用于配置Eureka Server的地址。需要指定Eureka Server的主机名和端口。defaultZone: http://localhost:8761/eureka/
3.3 注册服务
在主类上添加@EnableEurekaClient注解并启动应用
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaClientApplication {public static void main(String[] args) {SpringApplication.run(EurekaClientApplication.class, args);}@GetMapping("/hello")public String hello() {return "Hello from Eureka Client!";}
}
现在您已经成功创建了一个Eureka Server和一个Eureka Client。启动Eureka Server然后启动Eureka Client您将看到Client成功注册到Server上。通过访问Eureka Server的管理界面默认地址http://localhost:8761/您将看到已注册的服务列表。同时其他服务也可以通过Eureka Server发现这个Client服务并进行调用。
4.使用Eureka进行服务发现
在微服务架构中服务之间的调用经常是动态的我们不知道具体要调用哪个实例这时候就需要Eureka的服务发现功能。
4.1在另一个Eureka Client中发现服务
假设我们有一个名为my-service-consumer的另一个Eureka Client它需要调用上面创建的my-service。
首先确保my-service-consumer也添加了Eureka Client的依赖并进行了相应的配置。
4.2使用@FeignClient进行服务调用
Spring Cloud提供了Feign作为HTTP客户端进行服务间的调用它基于Ribbon和Hystrix并整合了Eureka简化了服务调用的过程。
在my-service-consumer中创建一个接口并使用@FeignClient注解
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;@FeignClient(value = "my-service")
public interface MyServiceClient {@GetMapping("/hello")String hello();
}
这里value = "my-service"表示要调用的服务的名称它应该与在Eureka中注册的服务名称相匹配。
接着你可以在my-service-consumer的任何类中注入并使用这个接口
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ConsumerController {private final MyServiceClient myServiceClient;@Autowiredpublic ConsumerController(MyServiceClient myServiceClient) {this.myServiceClient = myServiceClient;}@GetMapping("/call-my-service")public String callMyService() {return myServiceClient.hello(); // 这里将调用my-service的/hello接口}
}
5.总结
通过上面的示例我们展示了如何在Spring Boot应用中集成Eureka并进行服务注册与发现。在实际应用中您可能会有多个Eureka Server实例和多个Eureka Client实例。确保在生产环境中适当配置这些实例以保证高可用性。同时使用Feign可以简化服务间的调用过程。
请注意本文提供的示例代码仅用于演示目的。在生产环境中使用时请根据实际需求进行适当的调整和优化。