写点什么

Eureka 架构原理及其源码分析

用户头像
程序员Fox
关注
发布于: 2020 年 12 月 18 日
Eureka 架构原理及其源码分析

1. RestTemplate实现服务间调用

Spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。

在SpringBoot中可以通过RestTemplate 调用方式来进行服务调用,比如user服务(服务消费者)调用order服务(服务提供者)

String url = "http://localhost:8010/order/findOrderByUserId/"+id;
ResponseEntity<List> responseEntity = restTemplate.getForEntity(url,
List.class);
List<Order> orderList = responseEntity.getBody();



2. 注册中心演进

https://www.processon.com/view/link/5e71cc85e4b011fcce9d604d



3. Eureka快速使用

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务注册与发现的功能。 jersey(restful框架)

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通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

3.1)搭建eureka服务端

引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

父pom中引入spring-cloud-dependencies

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

在主启动类上 添加@EnableEurekaServer注解

@SpringBootApplication
@EnableEurekaServer
public class ServiceEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceEurekaApplication.class, args);
}
}

编写配置文件application.properties

#eureka服务端应用的端口默认是8761
server.port=8761
#表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,
#故而设为false
eureka.client.register-with-eureka=false
# 表示是否从Eureka Server获取注册信息,默认为true,因为这是一个单点
#的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false
eureka.client.fetch-registry=false
#暴露给其他eureka client 的注册地址
eureka.client.service-url.defaultZone =
http://www.eureka8761.com:8761/eureka/

测试:http://www.eureka8761.com:8761/

3.2)搭建eureka客户端

引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

在主启动类上 添加@EnableDiscoveryClient (可以不加)

@SpringBootApplication
//@EnableDiscoveryClient
public class ServiceOrderApplication {

public static void main(String[] args) {
SpringApplication.run(ServiceOrderApplication.class, args);
}

}

编写配置文件application.properties

server.port=8010
#注册到eureka服务端的微服务名称
spring.application.name=service-order
#注册到eureka服务端的地址
eureka.client.service-url.defaultZone=http://www.eureka8761.com:8761/eureka/
#将ip注册到Eureka Server上
eureka.instance.prefer-ip-address=true
#显示微服务的服务实例id
eureka.instance.instance-id=service-order-${server.port}

测试:

使用RestTemplate进行服务调用,可以使用微服务名称 (spring.application.name)

String url = "http://service-order/order/findOrderByUserId/"+id;
ResponseEntity<List> responseEntity = restTemplate.getForEntity(url,
List.class);
List<Order> orderList = responseEntity.getBody();

注意:需要添加@LoadBalanced注解

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}



3.3) Eureka集群搭建

环境: www.eureka8761.com:8761 ,www.eureka8762.com:8762

8761端口配置

#eureka服务端应用的端口默认是8761
server.port=8761
#表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,故而设为false
eureka.client.register-with-eureka=false
# 表示是否从Eureka Server获取注册信息,默认为true,因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false
eureka.client.fetch-registry=false
#暴露给其他eureka client 的注册地址
eureka.client.service-url.defaultZone =
http://www.eureka8762.com:8762/eureka/

8762端口配置

#eureka服务端应用的端口默认是8761
server.port=8762
#表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,
#故而设为false
eureka.client.register-with-eureka=false
# 表示是否从Eureka Server获取注册信息,默认为true,因为这是一个单点的
# Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false
eureka.client.fetch-registry=false
#暴露给其他eureka client 的注册地址
eureka.client.service-url.defaultZone =
http://www.eureka8761.com:8761/eureka/



eureka client需向两个注册中心注册

#注册到eureka服务端的地址
eureka.client.service-url.defaultZone=
http://www.eureka8761.com:8761/eureka/,
http://www.eureka8762.com:8762/eureka/

3.5)安全配置

eureka server端引入依赖

<!--安全配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

修改application.properties

# 注册的时候需要带入用户名和密码 基本格式为 http:用户名:密码@ip:port/eureka/
eureka.client.service-url.defaultZone= http://${spring.security.user.name}
:${spring.security.user.password}@www.eureka8762.com:8762/eureka/

spring.security.basic.enable=true
spring.security.user.name=root
spring.security.user.password=root

需要禁用csrf(跨站请求伪造)保护,否则eureka client 会注册失败

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable();
}
}

eureka client 注册 :

修改application.properties

eureka.client.service-url.defaultZone=http://${security.login.username}
:${security.login.pass}@www.eureka8761.com:8761/eureka/
security.login.username=root
security.login.pass=root

3.5)服务上下线监控

在某些特定的需求下,我们需要对服务的上下线进行监控,上线或下线都进行邮件通知,Eureka 中提供了事件监听的方式来扩展。

目前支持的事件如下:

  • EurekaInstanceCanceledEvent 服务下线事件。

  • EurekaInstanceRegisteredEvent 服务注册事件。

  • EurekaInstanceRenewedEvent 服务续约事件。

  • EurekaRegistryAvailableEvent Eureka 注册中心启动事件。

  • EurekaServerStartedEvent Eureka Server 启动事件。

基于 Eureka 提供的事件机制,可以监控服务的上下线过程,在过程发生中可以发送邮件来进行通知。

@Component
public class EurekaStateChangeListener {
@EventListener
public void listen(EurekaInstanceCanceledEvent event) {
System.err.println(event.getServerId() + "\t" + event.getAppName()
+ " 服务下线 ");
}
@EventListener
public void listen(EurekaInstanceRegisteredEvent event) {
InstanceInfo instanceInfo = event.getInstanceInfo();
System.err.println(instanceInfo.getAppName() + " 进行注册 ");
}
@EventListener
public void listen(EurekaInstanceRenewedEvent event) {
System.err.println(event.getServerId() + "\t" + event.getAppName()
+ " 服务进行续约 ");
}
@EventListener
public void listen(EurekaRegistryAvailableEvent event) {
System.err.println(" 注册中心启动 ");
}
@EventListener
public void listen(EurekaServerStartedEvent event) {
System.err.println("Eureka Server启动 ");
}
}



4. Eureka源码分析

4.1) Eureka架构图



4.2) Eureka的核心功能点

服务注册(register):Eureka Client会通过发送REST请求的方式向Eureka Server注册自己的服务,提供自身的元数据,比如ip地址、端口、运行状况指标的url、主页地址等信息。Eureka Server接收到注册请求后,就会把这些元数据信息存储在一个双层的Map中。

服务续约(renew):在服务注册后,Eureka Client会维护一个心跳来持续通知Eureka Server,说明服务一直处于可用状态,防止被剔除。Eureka Client在默认的情况下会每隔30秒

(eureka.instance.leaseRenewallIntervalInSeconds)发送一次心跳来进行服务续约。

eureka.instance.lease-renewal-interval-in-seconds=30

服务同步(replicate):Eureka Server之间会互相进行注册,构建Eureka Server集群,不同Eureka Server之间会进行服务同步,用来保证服务信息的一致性。

获取服务(get registry):服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给Eureka Server,获取上面注册的服务清单,并且缓存在Eureka Client本地,默认缓存30秒

(eureka.client.registryFetchIntervalSeconds)。同时,为了性能考虑,Eureka Server也会维护一份只读的服务清单缓存,该缓存每隔30秒更新一次。

前提:eureka.client.fetch-registry=true (默认)
eureka.client.registry-fetch-interval-seconds=30

服务调用:服务消费者在获取到服务清单后,就可以根据清单中的服务列表信息,查找到其他服务的地址,从而进行远程调用。Eureka有Region和Zone的概念,一个Region可以包含多个Zone,在进行服务调用时,优先访问处于同一个Zone中的服务提供者。

服务下线(cancel):当Eureka Client需要关闭或重启时,就不希望在这个时间段内再有请求进来,所以,就需要提前先发送REST请求给Eureka Server,告诉Eureka Server自己要下线了,Eureka Server在收到请求后,就会把该服务状态置为下线(DOWN),并把该下线事件传播出去。

服务剔除(evict):有时候,服务实例可能会因为网络故障等原因导致不能提供服务,而此时该实例也没有发送请求给Eureka Server来进行服务下线,所以,还需要有服务剔除的机制。Eureka Server在启动的时候会创建一个定时任务,每隔一段时间(默认60秒),从当前服务清单中把超时没有续约(默认90秒,

eureka.instance.leaseExpirationDurationInSeconds)的服务剔除。

eureka.instance.lease-expiration-duration-in-seconds=90

自我保护:既然Eureka Server会定时剔除超时没有续约的服务,那就有可能出现一种场景,网络一段时间内发生了异常,所有的服务都没能够进行续约,Eureka Server就把所有的服务都剔除了,这样显然不太合理。所以,就有了自我保护机制,当短时间内(15min是否低于85%),统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。自我保护开关(eureka.server.enableself-preservation: false)



Eureka注册中心常用http rest接口:

1.查看所有服务的注册列表:
GET http://www.eureka8761.com:8761/eureka/apps
2.查看某一服务的注册列表:
GET http://www.eureka8761.com:8761/eureka/apps/SERVICE-NAME
3. 暂时剔除某服务(隔几秒会重新注册上来)
DELETE http://www.eureka8761.com:8761/eureka/apps/SERVICE-NAME/
INSTANCE-NAME
4. 下线某一服务:
PUT http://www.eureka8761.com:8761/eureka/apps/SERVICE-NAME/INSTANCE-NAME/
status?value=OUT_OF_SERVICE
5. 下线后再恢复服务:
PUT http://www.eureka8761.com:8761/eureka/apps/SERVICE-NAME/INSTANCE-NAME/
status?value=UP


官方文档: https://github.com/Netflix/eureka/wiki/Eureka-REST-operations



4.3) Eureka Sever源码分析

https://www.processon.com/view/link/5e5fa095e4b0a967bb35b667



4.4) Eureka Client源码分析

https://www.processon.com/view/link/5e60a8f6e4b097b727525e07



发布于: 2020 年 12 月 18 日阅读数: 16
用户头像

程序员Fox

关注

思想比技术更重要,有术无道止于术 2019.03.12 加入

多年中间件开发经验,擅长分布式,微服务架构技术,精通各大源码框架,源码控,喜欢分享技术

评论

发布
暂无评论
Eureka 架构原理及其源码分析