SpringCloud Netflix复习之Eureka

写作背景

SpringCloud Netflix作为SpringCloud第一代产品很经典,而且公司的老项目还在用SpringCloud Netflix的技术栈,有必要对SpringCloud Netflix的各种核心组件回归复习一下了。
本次复习的主角是微服务注册中心Eureka,本文的书写思路是五个方面

  1. Eureka是用来干什么的,为什么会有Eureka
  2. Eureka的核心功能有哪些
  3. 上手搭建Eureka的服务端和客户端实战一下
  4. 从源码的角度验证一下Eureka的核心功能
  5. Eureka常见问题FAQ

Eureka是用来干什么的?

首先来想一个问题,在微服务的架构下,服务之间的调用关系如何维护?
服务A调用服务B,那我服务A需要在代码里记录服务B的访问的IP和端口,现在一个请求经过API网关然后调用多个下游服务,然后聚合各个下游服务返回请求的响应是很常见的,如果都是把下游服务的IP和端口硬编码在代码里,很不优雅。
还是拿服务A调用服务B举例,服务A想知道服务B的访问IP和端口,有没有一个组件可以告诉我服务A,我给你服务B的名字,你返回我服务B的访问IP和端口等信息,这样我服务A就不用自己在代码里去维护服务B的请求IP和端口了。Eureka的一个核心功能之一就是服务发现,服务A可以通过Eureka来发现服务B的访问IP和端口,在实现服务发现的基础上需要Eureka的客户端先进行服务注册,也就是Eureka的客户端程序也就是你的服务,比如服务B先将自己注册到Eureka的服务端,然后服务A就可以通过Eureka拉取到服务注册表信息找到服务B的访问IP和端口。

Eureka的核心功能

1、服务注册(register)
Eureka Client在服务启动时会发送Rest请求的方式向Eureka Server注册自己的服务,注册的时候会提供服务自身的一些元数据,比如IP和端口。Eureka Server在接收到注册请求后,会将这些元数据信息存储在一个双层的Map中,这个Map其实就是服务注册表。
2、服务续约(renew)
服务续约是Eureka Client在服务注册后,会定时(默认每30s)向Eureka Server发送心跳通知Eureka Server 我还活着,还是处于可用的状态,防止被Eureka Server剔除。
3、服务下线(cancel)
Eureka Client在服务关闭或者重启时,会主动向Eureka Server发送Rest请求,告诉Eureka Server自己要下线了,Eureka Server在收到下线请求后,会把该服务的状态设置为DOWN
4、服务同步
在生产环境下,为了防止单点问题,Eureka往往会搭建HA架构,Eureka Server之间会互相进行注册,构建一个Eureka Server集群,不同的Eureka Server之间会进行服务同步,来保证服务信息的一致性。
5、服务剔除(evict)
服务剔除是Eureka Server在启动时会启动一个定时任务,默认每60s扫描一次服务注册表,如果发现服务超过90s没有续约,那么就把这个服务实例剔除掉,后续在这个服务恢复之前,这个服务实例将不再对外提供服务。
6、自我保护
因为有服务剔除机制,那么就有可能因为是网络故障等原因,导致服务续约没有成功,而实际上服务还是可用的情况,但是Eureka Server把所有服务都剔除下线了,这样显然不太合理。为了防止因短期网络波动引起的服务续约失败,导致Eureka Server剔除所有服务的情况,就有了自我保护机制。具体的做法其实就是在短期内,统计服务续约失败的比例,如果达到了一个阈值,那么就触发自我保护Eureka Server不再提出任务服务,直到比例恢复正常后,才退出自我保护。
7、获取服务
Eureka Client在启动的时候,会发送一个REST请求给Eureka Server,获取服务注册表,并且缓存在Eureka Client本地,默认缓存30秒更新缓存一次。同时,为了性能考虑,Eureka Server也会维护一份只读缓存readOnlyCacheMap,该缓存每隔30秒更新一次

上手搭建Eureka的服务端和客户端实战一下

说明一下基于SpringBoot 2.x搭建的Eureka Server

1、pom.xml引入Eureka的依赖坐标

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

2、启动了开启服务注册

/**
 * @author zhangyu
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
   

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

}

3、配置文件application.yml里增加Eureka 的配置信息

#端口
server:
  port: 8761

spring:
  application:
    name: eureka-server

#eureka相关配置
eureka:
  client:
    #表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,故而设为false
    register-with-eureka: false
    # 表示是否从Eureka Server获取注册信息,默认为true,因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8761/eureka/
  #Eureka 服务端配置,其实都是默认配置,这里写出来增加记忆
  server:
    #是否开启自我保护机制,默认是true也就是开启
    enable-self-preservation: true
    #开启自我保护后,期望心跳次数的阈值
    renewal-percent-threshold: 0.85
    #是否开启只读缓存,默认开启
    use-read-only-response-cache: true
    #将readWriteCache读写缓存数据定时刷入readOnlyCache只读缓存的时间,默认30秒
    response-cache-update-interval-ms: 30000
    #readWriteCache读写缓存被动过期时间,默认180秒更新一次读写缓存
    response-cache-auto-expiration-in-seconds: 180

启动eureka-server服务,然后访问http://localhost:8761/ 就会看到Eureka Server的UI界面
在这里插入图片描述
生产环境下一般访问Eureka界面都是要账户密码的,所以需要和Security整合

Eureka与Security整合安全访问

1、pom.xml引入Security的依赖坐标

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、开启Web的Security保护

/**
 * @author zhangyu
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
   
    @Override
    protected void configure(HttpSecurity http) throws Exception {
   
        //关闭csrf
        http.csrf().disable();
        super.configure(http);
    }
}

3、配置文件application.yml配置访问Security的账户和密码

spring:
  application:
    name: eureka-server
  security:
    user:
      name: root
      password: 123456

4、修改暴露给其他Eureka Client注册的地址

eureka:
  client:
    service-url:
      #改动的在这里 
      defaultZone: http://${
   spring.security.user.name}:${
   spring.security.user.password}@localhost:8761/eureka/

重启eureka-server服务,然后再次访问http://localhost:8761/ 你会发现页面被转发到登录页面http://localhost:8761/login
在这里插入图片描述
输入root和123456才会进入Eureka Server的UI界面。

快速用SpringBoot搭建一个服务然后注册到Eureka Server

1、pom.xml引入Eureka Client的依赖坐标

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

2、启动类开启Eureka Client

启动类增加@EnableEurekaClient注解

@EnableEurekaClient
@SpringBootApplication
public class ServiceScreenApplication {
   

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

3、配置文件applicatiion.yml配置Eureka信息

#端口
server:
  port: 8003

spring:
  application:
    name: fc-service-screen
#eureka相关配置
eureka:
  client:
    service-url:
      defaultZone: http://root:123456@localhost:8761/eureka/
  instance:
    #显示的微服务名称
    instance-id: ms-service-screen-8003
    #eureka客户端向服务端发送心跳时间默认30s
    lease-renewal-interval-in-seconds: 10
    #Eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
    lease-expiration-duration-in-seconds: 30

启动fc-service-screen服务,然后去刷新http://localhost:8761/ 页面看看服务注册上去了没
在这里插入图片描述

搭建Eureka HA架构

再新起一个Eureka Server服务,端口设置为8762,然后最关键的地方是application.yml里关于Eureka的两个配置要结合起来使用。
新搭建的eureka-server8762服务的配置文件

#端口
server:
  port: 8762

spring:
  application:
    name: eureka-server8762
  security:
    user:
      name: root
      password: 123456
#eureka相关配置
eureka:
  client:
    # HA架构的关键配置就是下面两个配置联合使用
    # 将自己注册到Eureka Server,默认为true
    register-with-eureka: true
    # 表示是否从Eureka Server获取注册信息,默认为true,因为本身就是Eureka Server不需要
    fetch-registry: false
    service-url:
      defaultZone: http://${
   spring.security.user.name}:${
   spring.security.user.password}@localhost:8761/eureka/,http://${
   spring.security.user.name}:${
   spring.security.user.password}@localhost:8762/eureka/
  #Eureka 服务端配置,其实都是默认配置,这里写出来增加记忆
  server:
    #是否开启自我保护机制,默认是true也就是开启
    enable-self-preservation: true
    #开启自我保护后,期望心跳次数的阈值
    renewal-percent-threshold: 0.85
    #是否开启只读缓存,默认开启
    use-read-only-response-cache: true
    #将readWriteCache读写缓存数据定时刷入readOnlyCache只读缓存的时间,默认30秒
    response-cache-update-interval-ms: 30000
    #readWriteCache读写缓存被动过期时间,默认180秒更新一次读写缓存
    response-cache-auto-expiration-in-seconds: 180

关键的地方是register-with-eureka 设置为true,相当于本身也是Eureka Client,然后就是fetch-registry设置为false,因为eureka-server8762本身也是Eureka Server它在服务启动时会从相邻的Eureka Server节点拉取注册表数据,然后服务注册时也会往其他Eureka Server节点转发注册保持数据一致性。然后就是暴露给Eureka Client注册的地址变成两个,用逗号隔开。
我们启动eureka-server和eureka-server8762两个服务,然后再启动fc-service-screen服务,然后访问两个Eureka Server的UI界面看看
eureka-server的
在这里插入图片描述
eureka-server8762的

在这里插入图片描述

你会发现两个Eureka Server的服务注册信息是一样,但是我的fc-service-sreen的Eureka注册地址只配置了http://root:123456@localhost:8761/eureka/也就是eureka-server这一台的。后面源码会分析,这个就是Eureka的服务同步功能。

从源码的角度验证一下Eureka的核心功能

先说明一下,我用的SpringBoot版本是2.2.2.RELEASE,小于2.7也就是说SpringBoot自动装配等的配置还是在META-INF/spring.factories文件里。

EurekaServer自动装配源码

我们知道在Eureka Server的启动类有加@EnableEurekaServer这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
   

}

这个注解导入了EurekaServerMarkerConfiguration,我们看下这个Marker配置类有啥

@Configuration(proxyBeanMethods = false)
public class EurekaServerMarkerConfiguration {
   

	@Bean
	public Marker eurekaServerMarkerBean() {
   
		return new Marker();
	}

	class Marker {
   

	}

}

我们发现这个EurekaServerMarkerConfiguration里就是声明了一个叫Marker的Bean。我们想想SpringBoot的自动装配的原理,一般都是XXXAutoConfiguration,大胆猜测一下是不是有个EurekaServerAutoConfiguration,果然在spring-cloud-netflix-eureka-server-2.2.1.RELEASE.jar的META-INF/spring.factories文件里找到了。
在这里插入图片描述

@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({
    EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
   
...
}

@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)看这个,有EurekaServerMarkerConfiguration.Marker这个Bean,EurekaServerAutoConfiguration才会生效,所以上面说到的EurekaServerMarkerConfiguration里其实就是开启一个Marker的开关用于控制EurekaServer自动装配的开关。

然后再看看@Import(EurekaServerInitializerConfiguration.class) 一般这种导入的类肯定是很关键的,我们进去看一下

@Configuration(proxyBeanMethods = false)
public class EurekaServerInitializerConfiguration
		implements ServletContextAware, SmartLifecycle, Ordered {
   
}

EurekaServerInitializerConfiguration实现了SmartLifecycle接口,这个SmartLifecycle是spring-context包里的东西,它的作用是在Spring容器的refresh()方法里的finishRefresh()方法里会去调用SmartLifecycle的start()方法,我们看下EurekaServerInitializerConfiguration的start()方法

@Override
	public void start() {
   
		new Thread(() -> {
   
			try {
   
				// 初始化EurekaServer
				eurekaServerBootstrap.contextInitialized(
						EurekaServerInitializerConfiguration.this.servletContext);
				log.info("Started Eureka Server");

				publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
				EurekaServerInitializerConfiguration.this.running = true;
				publish(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值