title: SpringCloud学习笔记一:Eureka
date: 2020-04-30 16:07:48
tags:

  • SpringCloud
  • Eureka
    categories: SpringCloud
    toc_number: true

Eureka Server主要作为服务注册中心使用。

搭建过程:

添加Eureka Server依赖

  1. <!--父依赖-->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.2.6.RELEASE</version>
  6. <relativePath/> <!-- lookup parent from repository -->
  7. </parent>
  8. <!--Eureka Server-->
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  12. </dependency>

配置文件修改

  1. server:
  2. port: 7001 # 端口号
  3. eureka:
  4. instance:
  5. hostname: eurekaserver01.com # 服务地址,将eurekaserver01.com添加到host,后面有介绍
  6. client:
  7. register-with-eureka: false #是否向注册中心注册
  8. fetch-registry: false # 是否去用户中心获取其他已注册服务信息

启动类添加注解

  1. @EnableEurekaServer // 将本服务配置为注册中心
  2. @SpringBootApplication
  3. public class EurekaServerApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaServerApplication.class, args);
  6. }
  7. }

启动服务后,访问http://localhost:7001/即可访问Eureka监控页面

Eureka Client

引入依赖

  1. <!--父依赖-->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.2.6.RELEASE</version>
  6. <relativePath/> <!-- lookup parent from repository -->
  7. </parent>
  8. <!--Eureka Client-->
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  12. </dependency>

配置文件修改

  1. server:
  2. port: 8081
  3. spring:
  4. application:
  5. name: micro-client
  6. eureka:
  7. client:
  8. service-url:
  9. defaultZone: http://eurekaserver01.com:7001/eureka # 注册中心地址
  10. instance:
  11. instance-id: micro-client:8081 # 本服务实例信息
  12. prefer-ip-address: true #暴露IP地址

启动类添加注解

  1. @EnableEurekaClient
  2. @SpringBootApplication
  3. public class EurekaClientApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaClientApplication.class, args);
  6. }
  7. }

注解@EnableEurekaClient@EnableDiscoveryClient用法及功能类似,主要是暴露服务被注册中心发现,前者主要针对Eureka使用。Spring Cloud Edgware版本之后该注解省略也可注册服务到注册中心(只需要引入client依赖就行了)。

服务发现与远程调用

通过SpringCloud提供的DiscoveryClient可以发现其他服务的元数据。

服务发现

复制一个Eureka Client项目,将其修改为eureka-consumer,用来执行调用其他服务的操作(消费者)。

在消费者的Controller中尝试获取其他服务的信息:

  1. // eureka-consumer
  2. import org.springframework.cloud.client.discovery.DiscoveryClient;
  3. @RestController
  4. @RequestMapping(value = "/api/consumer")
  5. public class IndexController {
  6. @Autowired
  7. private DiscoveryClient discoveryClient;
  8. @GetMapping(value = "/getInstances/{serviceId}")
  9. public String getInstancesInfo(@PathVariable String serviceId) {
  10. List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceId);
  11. return JSON.toJSONString(instanceList);
  12. }
  13. }

启动后访问localhost:8003/api/consumer/getInstances/MICRO-CLIENT 即可查看其他服务的信息。应当注意这个输入的serviceId为服务生产者的application.name值

服务间的远程调用

当服务向同一个注册中心注册后可以相互发现并远程调用。本次使用RestTemplate

在启动类中添加RestTemplate的Bean初始化

  1. // eureka-consumer
  2. @EnableEurekaClient
  3. @SpringBootApplication
  4. public class EurekaClientApplication2 {
  5. @Bean(value = "restTemplate")
  6. public RestTemplate restTemplate() {
  7. return new RestTemplate();
  8. }
  9. public static void main(String[] args) {
  10. SpringApplication.run(EurekaClientApplication2.class, args);
  11. }
  12. }

在原项目eureka-client中创建Controller模拟一个接口用以被调用(生产者)。

  1. // eureka-client
  2. @RestController
  3. @RequestMapping(value = "/api/index")
  4. public class IndexController {
  5. @Value("${eureka.instance.instance-id}")
  6. private String ipAdress;
  7. @Value("${server.port}")
  8. private String port;
  9. @GetMapping(value = "/getMsg")
  10. public String getMsg() {
  11. return "Hello," + ipAdress + ":" + port;
  12. }
  13. }

在eureka-consumer项目中也创建一个Controller用以触发远程调用(消费者)。

  1. // eureka-consumer
  2. @RestController
  3. @RequestMapping(value = "/api/consumer")
  4. public class IndexController {
  5. @Autowired
  6. private RestTemplate restTemplate;
  7. // 方式1
  8. @GetMapping(value = "/getMsg")
  9. public String getMsg() {
  10. return restTemplate.getForObject("http://localhost:8001/api/index/getMsg", String.class);
  11. }
  12. // 方式2
  13. @GetMapping(value = "/getMsg/{serviceId}")
  14. public String getInstanceMsg(@PathVariable String serviceId) {
  15. List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceId);
  16. if (instanceList.isEmpty()) {
  17. logger.info("");
  18. // throw
  19. }
  20. ServiceInstance serviceInstance = instanceList.get(0);
  21. return restTemplate.getForObject(
  22. "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/api/index/getMsg",
  23. String.class);
  24. }
  25. }

如上代码,使用getForObject接收一个远程服务的接口地址即可。方式2借助了前面的服务发现,只需要提供生产者的application.name的值即可(生产者暴露的application名称)

构建Eureka Server集群

复制上方Eureka Server项目,组成Eureka-serverEureka-server1Eureka-server2三个服务,其配置文件如下:

  1. # Eureka-server
  2. server:
  3. port: 7001 # 端口号
  4. eureka:
  5. instance:
  6. hostname: eurekaserver01.com # 服务地址
  7. client:
  8. register-with-eureka: false #是否向注册中心注册
  9. fetch-registry: false # 是否去用户中心获取其他已注册服务信息
  10. service-url:
  11. defaultZone: http://eurekaserver02.com:7002/eureka/,http://eurekaserver03.com:7003/eureka/
  1. # Eureka-server1
  2. server:
  3. port: 7002 # 端口号
  4. eureka:
  5. instance:
  6. hostname: eurekaserver02.com # 服务地址
  7. client:
  8. register-with-eureka: false #是否向注册中心注册
  9. fetch-registry: false # 是否去用户中心获取其他已注册服务信息
  10. service-url:
  11. defaultZone: http://eurekaserver01.com:7001/eureka/,http://eurekaserver03.com:7003/eureka/
  1. # Eureka-serve2
  2. server:
  3. port: 7003 # 端口号
  4. eureka:
  5. instance:
  6. hostname: eurekaserver03.com # 服务地址
  7. client:
  8. register-with-eureka: false #是否向注册中心注册
  9. fetch-registry: false # 是否去用户中心获取其他已注册服务信息
  10. service-url:
  11. defaultZone: http://eurekaserver02.com:7002/eureka/,http://eurekaserver01.com:7001/eureka/

同时将三个Eureka Server的hostname加入到系统的HOST文件中(相同域名下的不同端口号发布的多个服务不会被Client识别):

  1. 127.0.0.1 eurekaserver01.com
  2. 127.0.0.1 eurekaserver02.com
  3. 127.0.0.1 eurekaserver03.com

复制Eureka Client项目,组成Eureka-clientEureka-client1两个项目以备后续调用,其配置文件如下:

  1. server:
  2. port: 8001
  3. spring:
  4. application:
  5. name: micro-client:8001 #暴露到注册中心的服务名称
  6. eureka:
  7. client:
  8. fetch-registry: true # 是否去注册中心获取其他注册模块信息
  9. service-url:
  10. defaultZone: http://eurekaserver01.com:7001/eureka,http://eurekaserver02.com:7002/eureka,http://eurekaserver03.com:7003/eureka # 注册中心地址
  11. instance:
  12. instance-id: micro-client:8001 # 本服务实例信息
  13. prefer-ip-address: true # 将服务IP注册到注册中心
  1. server:
  2. port: 8002
  3. spring:
  4. application:
  5. name: micro-client:8002
  6. eureka:
  7. client:
  8. fetch-registry: true # 是否去注册中心获取其他注册模块信息
  9. service-url:
  10. defaultZone: http://eurekaserver01.com:7001/eureka,http://eurekaserver02.com:7002/eureka,http://eurekaserver03.com:7003/eureka # 注册中心地址
  11. instance:
  12. instance-id: micro-client:8002 # 本服务实例信息
  13. prefer-ip-address: true

访问eurekaserver01.com:7001,显示如下:

相关原理及配置

CAP:

  • 一致性(Consistency):同一个数据在集群中的所有节点,同一时刻是否都是同样的值。
  • 可用性(Availability):集群中一部分节点故障后,集群整体是否还能处理客户端的请求。
  • 分区容忍性(Partition tolerance):是否允许集群中的节点之间无法通信。

Eureka:AP原则,Zookeeper:CP原则

  1. 服务注册(Register)
    注册服务列表是以嵌套HashMap格式存在的,Eurek客户端自检时,如果不可用,将会向注册中心更新状态,同时用replicateToPeers()向其他Eureka服务器节点做状态同步
    eureka.client.register-with-eureka控制是否向注册中心注册
  2. 服务续约(Renew)
    客户端每隔一段时间向服务器发送一次心跳(即续约请求,默认为30s,eureka.instance.lease-renewal-interval-in-seconds控制心跳时间)
    若服务器在一段时间内(默认为90s,可通过eureka.instance.lease-expiration-duration-in-seconds修改),没有收到客户端的续约请求,则剔除该服务。
    若服务器开启了保护模式,则不会剔除。
  3. 获取服务
    客户端每隔30s会获取服务器注册列表并缓存到本地
  4. Region&Zone
    概念来自于AWS(Amazon Web Services),客户端优先访问速度最快的服务器。
    Region:可以理解为地理上的分区,比如华南区、华北区、北京、深圳等
    zone:可以理解为机房,比如深圳的两个机房

Eureka自我保护

Eureka服务器每分钟接收心跳续约的次数少于一个阈值就会触发自我保护,防止已注册的服务被剔除(AP原则)。