您当前的位置: 首页 >  eureka

星夜孤帆

暂无认证

  • 0浏览

    0关注

    626博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring Cloud Eureka源码解析

星夜孤帆 发布时间:2021-07-23 12:22:51 ,浏览量:0

一、Eureka源码环境搭建

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

1.1 搭建Eureka服务端 1.1.1 pom
    
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-server
        

        
            org.projectlombok
            lombok
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
1.1.2 yaml
# eureka服务端应用的端口默认是8761
server:
  port: 2222

spring:
  application:
    name: server

eureka:
  instance:
    hostname: 127.0.0.1  #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:
      # 暴露给其他eureka client的注册地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
1.1.3 主启动
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

http://localhost:2222/

1.2 搭建客户端-生产者 1.2.1 pom
    
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

        
            org.projectlombok
            lombok
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
1.2.2 yaml
server:
  port: 3333

spring:
  application:
    name: provider

eureka:
  instance:
    # 将ip注册到eureka server上
    prefer-ip-address: true
    # 显示微服务的服务实例id
    instance-id: provider-${server.port}
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://127.0.0.1:2222/eureka

1.2.3 主启动
@SpringBootApplication
@EnableEurekaClient
public class EurekaProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaProviderApplication.class, args);
    }
}
1.2.4 业务类
@RestController
@RequestMapping("/provider")
public class ProviderController {

    @Value("${server.port}")
    String port;

    @GetMapping("/find/instance")
    public String service(@RequestHeader("token") String token) {

        String applicationName = "provider";
        String result = applicationName + "-" +  port;

        System.out.println("**********" + result);

        System.out.println("token: " + token);

        return result;
    }

}
1.3 搭建客户端-消费者 1.3.1 pom
    
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

        
            org.projectlombok
            lombok
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
1.3.2 yaml
server:
  port: 5555

# 注册到eureka服务端的微服务名称
spring:
  application:
    name: consumer

eureka:
  instance:
    # 将ip注册到eureka server上,ip替代hostname
    prefer-ip-address: true
    # 显示微服务的服务实例id
    instance-id: consumer-${server.port}
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      # 注册到eureka服务端的地址
      defaultZone: http://127.0.0.1:2222/eureka

# 设置Feign接口的日志级别 (前提)
logging:
  level:
    org.springframework.web: trace
    com.best.nacos.client: debug

feign:
  client:
    config:
      # 设置要调用微服务的日志级别:none, basic, headers, full
      provider.loggerLevel: full

#provider:
#  ribbon:
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#
#    NFLoadBalancerPingClassName: com.netflix.loadbalancer.PingUrl
1.3.3 主启动
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class EurekaConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaConsumerApplication.class, args);
    }
}
 1.3.4 拦截器
public class MyInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        System.out.println("===============执行拦截器apply==============");
        template.header("token", "jak");
    }
}
1.3.5 配置类
@Configuration
public class FeignConfiguration {

    @Bean
    Logger.Level feignLoggerLevel() {
        // 设置日志级别: None,Basic,Headers,Full
        return Logger.Level.FULL;
    }

    @Bean
    public RequestInterceptor interceptor() {
        return new MyInterceptor();
    }

}
@Configuration
public class AppConfig {

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

//    @Bean
//    public IRule myRule() {
//        return new RandomRule();
//    }
}
 1.3.6 Feign Client
// name: 微服务的名称 path: 服务生成者的调用前缀
@FeignClient(name = "provider", path = "/provider", configuration = FeignConfiguration.class)
public interface ProviderClient {

    @GetMapping("/find/instance")
    String service();

}
1.3.7 RestTemplate
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/find/instance")
    public String queryOrderInstanceId() {
        // provider: 生产者服务名->替换-> 127.0.0.1:3333
        String url = "http://provider/order/find/instance";
        return restTemplate.getForObject(url, String.class);
    }

}
1.3.8 业务类
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Value("${server.port}")
    String port;

    // 动态代理对象,内部远程调用服务生产者
    @Autowired
    ProviderClient providerClient;

    @GetMapping("/find/instance")
    public String service() {

        // 远程调用
        String providerResult = providerClient.service();
        String result = "consumer invoke: " + port + " | " + providerResult;

        System.out.println("****************" + result);

        return result;
    }
}
1.4  服务上下线监控

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

目前支持的事件如下:

EurekalnstanceCanceledEvent服务下线事件。

EurekalnstanceRegisteredEvent服务注册事件。

EurekalnstanceRenewedEvent服务续约事件。

EurekaRegistryAvailableEvent Eureka 注册中心启动事件。

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

1.4.1 服务端配置监听器

@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启动 ");
    }
}
 1.5 启动测试

分别,依次启动服务端,生产者,消费者

Eureka: http://localhost:2222/

 http://localhost:5555/consumer/find/instance

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

官方文档

http://localhost:2222/eureka/apps

 

 http://localhost:2222/eureka/apps/provider

 

二、Eureka原理介绍 2.1 注册中心的演进

注册中心的演进

2.1.1 阶段一

 2.1.2 阶段二

 2.1.3 阶段三

 2.1.4 阶段四

 2.1.5 阶段五

2.2 Eureka架构图

 2.3 Eureka核心功能点

服务注册(register):Eureka Client会通过发送REST请求的方式向Eureka Server注册自己的服务,提供自身的元数据,

比如ip地址、端口、运行状况指标的url、主页地址等信息。Eureka Server接收到注册请求后,就会把这些元数据信息存储在一个双层的Map中。

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

Eureka Client在默认的情况下会每隔30秒(eureka.instance.leaseRenewalllntervallnSeconds)发送一次心跳来进行服务续约。

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 Server源码解析

Nacos源码ProcessOn

Eureka Server源码ProcessOn

3.1 源码解析

ProcessOn

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四、Eureka Client源码解析 

ProcessOn流程图

 4.1 配置入口

 

 

 

此处一直调父类的方法

 

 

 

@Inject注解用法 

 

 

 

 

 

4.2 服务发现源码

CacheRefreshThread

4.2.1 客户端源码

 

 

1.全量拉取

 

 

 

2.增量拉取 

 

 

 

 4.2.2 服务端源码

 

 

 

 

 

 

4.3 服务续约源码  4.3.1 客户端源码

 

 

 4.3.2 服务端源码

 

 

 

4.4 服务注册源码 4.4.1 客户端源码

 

 

 

 

 

 

 

 

 

 

 

 

Eureka Client源码ProcessOn

视频教程、gitee源码、参考博客

关注
打赏
1636984416
查看更多评论
立即登录/注册

微信扫码登录

0.0396s