SpringCloud02-Eureka

SpringCloud02-Eureka

1.Eureka注册中心

  1. 传统的RPC框架,管理服务与服务之间依赖关系比较复杂,所以需要使用注册中心,管理服务于服务之间依赖关系。
  2. Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
  3. Eureka Server提供服务注册服务。各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
  4. Eureka Client通过注册中心获取服务地址。

2.搭建通用模块的cloud-api-commons

  1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.13</version>
    </dependency>
</dependencies>
  1. 实体类Payment
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {

    private Long id;
    private String serial;
}
  1. 响应统一结果CommonResult
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

3.创建Eureka Server微服务cloud-eureka-server7001

  1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
  1. 单机时Eureka Server的yml配置
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    # false表示不向注册中心注册自己,单机时不需要向注册中心注册自己。集群时需要相互注册。
    register-with-eureka: false
    # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 设置与Eureka server交互的地址,Eureka Client查询服务和注册服务都需要依赖这个地址。
      # 单机服务指向自己
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 关闭 Eureka的自我保护。默认true,开启。
    enable-self-preservation: false
    # 设置清理无效服务实例的时间。默认60秒清理一次。
    eviction-interval-timer-in-ms: 60000
  1. Eureka Server启动类。
@EnableEurekaServer // Eureke Server端
@SpringBootApplication
public class CloudEurekaServer7001Main {

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

4.创建Eureka Client微服务cloud-eureka-provider-payment8001

  1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- 后续需要 下面的依赖时使用 同SpringCloud02-Eureka-4.1 代替 -->
    <!-- 通用模块 -->
    <dependency>
        <groupId>com.my.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  1. yml配置
server:
  port: 8001

spring:
  application:
    name: cloud-provider-payment # 微服务名称,显示在左边
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.my.springcloud.entity

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    # 是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      # 单机注册
      defaultZone: http://127.0.0.1:7001/eureka
    # Eureka Client从Eureka Server拉取配置信息的频率,默认30秒客户端从服务端拉取一下配置信息。
    registry-fetch-interval-seconds: 30
  instance:
    # 修改Eureka界面微服务实例名显示为payment8001,即右边显示。
    instance-id: payment8001
    # 显示ip地址,当鼠标悬停时在右边微服务实例上时,左下角是否显示ip
    prefer-ip-address: true
    # eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,
    # 在这个时间内若没收到下一次心跳,则将移除该instance,默认90秒。
    lease-expiration-duration-in-seconds: 90
    # Eureka Client发送心跳给Eureka Server的频率。默认30秒。
    lease-renewal-interval-in-seconds: 30
  1. 启动类
@EnableEurekaClient
@SpringBootApplication
public class CloudEurekaProviderPaymentMain8001 {

    public static void main(String[] args) {
        SpringApplication.run(CloudEurekaProviderPaymentMain8001.class, args);
    }
}
  1. controller、service、dao、mapper......省略。
  2. 启动cloud-eureka-provider-payment8001,将其注册到cloud-eureka-server7001

5.创建Euerka Client微服务cloud-eureka-consumer-order80

  1. pom.xml、yml、Main相同。
  2. controller
@Slf4j
@RestController
public class OrderController {
    
    public static final String PROVIDER_PAYMENT = "http://CLOUD-PROVIDER-PAYMENT";
    
    @Resource
    private RestTemplate restTemplate;
    
    @PostMapping("/consumer/payment/create")
    public CommonResult<?> create(@RequestBody Payment payment) {
        CommonResult<?> commonResult = restTemplate.postForObject(PROVIDER_PAYMENT + "/payment/create",
                payment, CommonResult.class);

        if (commonResult == null || commonResult.getCode() == 500) {
            log.info("创建失败,{}", commonResult);
        }
        return commonResult;
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<?> getById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PROVIDER_PAYMENT + "/payment/get/" + id, CommonResult.class);
    }
}
  1. RestTemplate配置类
@Configuration
public class RestTemplateConfig {

    /**
     * 负载均衡,通过微服务名称cloud-provider-payment调用cloud-eureka-provider-payment8001。
     * 需要引入org.springframework.cloud.client.loadbalancer.LoadBalanced。
     * 这里调用没有问题是因为
     * spring-cloud-starter-netflix-eureka-client中spring-cloud-starter-loadbalancer。
     * 在使用其他的注册中心时,需要检查是否引入spring-cloud-starter-loadbalancer。
     * @return
     */
    @LoadBalanced
    @Bean("restTemplate")
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

6.Eureka Server集群原理

  1. Eureka Server 集群相互之间通过 Replicate 来同步数据,相互之间不区分主节点和从节点,所有的节点都是平等的。
  2. 节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。
  3. 如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点。当宕机的服务器重新恢复后,Eureka Server集群会再次将其纳入到服务器集群管理之中。
  4. Eureka Server 集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。
  5. 相互注册,相互守望。

7.创建Eureka Server集群,创建cloud-eureka-server7002

  1. pom.xml、Main同上
  2. yml
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
  	# 集群时,Eureka Server也是一个Client,需要向其他Eureka Server注册
    register-with-eureka: true
    fetch-registry: true
    service-url:
      # Eureka集群时指向其他的注册中心
      defaultZone: http://eureka7001.com:7001/eureka
  1. 修改本地hosts文件。
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com

8.创建微服务提供者集群,创建cloud-eureka-provider-payment8002

  1. pom.xml、Main、业务代码省略。
  2. yml不同点,cloud-eureka-provider-payment8002需要注册到Eureka Server集群中。
spring:
  application:
  	# 和cloud-eureka-provider-payment8001 的name保持一致,
  	# 这时Eureka界面cloud-provider-payment的右边有两个微服务实例,8001和8002
    name: cloud-provider-payment
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      # 集群注册
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  1. cloud-eureka-consumer-order80通过 RestTemplate+LoadBalance 调用8001和8002的服务。

9.获取微服务信息的代码

@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class CloudEurekaProviderPaymentMain8001 {

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

@Resource
private DiscoveryClient discoveryClient;

@GetMapping("/payment/discovery")
public Object discovery() {
    // 获取注册中心注册的服务列表,有两个服务 "cloud-consumer-order","cloud-provider-payment"
    // 返回的数据
    // {"services":["cloud-consumer-order","cloud-provider-payment"],"order":0}
    List<String> services = discoveryClient.getServices();
    for (String service : services) {
        log.info("服务 ===== {}", service);
    }

    // CLOUD-PROVIDER-PAYMENT服务有两个实例,8001和8002
    // 服务的实例名=CLOUD-PROVIDER-PAYMENT, 地址=192.168.2.103, 端口8001, uri=http://192.168.2.103:8001, instance_id=payment8001
    // 服务的实例名=CLOUD-PROVIDER-PAYMENT, 地址=192.168.2.103, 端口8002, uri=http://192.168.2.103:8002, instance_id=payment8002
    List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PROVIDER-PAYMENT");
    for (ServiceInstance instance : serviceInstances) {
        log.info("服务的实例名={}, 地址={}, 端口{}, uri={}, instance_id={}",
                 instance.getServiceId(),
                 instance.getHost(),
                 instance.getPort(),
                 instance.getUri(),
                 instance.getInstanceId());
    }

    return discoveryClient;
}

10.Eureka自我保护机制

  1. 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

  2. 如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制。

  3. 在Eureka进入自我保护时,Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务;Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用;当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

  4. 关闭Eureka自我保护机制。

eureka:
  server:
    # 关闭 Eureka的自我保护。默认true,开启。
    enable-self-preservation: false
    # 设置清理无效服务实例的时间。默认60秒清理一次。
    eviction-interval-timer-in-ms: 60000
  1. 关闭自我保护模式之后,第一次访问Eureka提示。THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

11.Eureka总结

  1. Eureka Client默认30秒向Eureka Server发送心跳。
  2. Eureka Server发现Eureka Client90秒都没有发送心跳,下次清理时,直接剔除该Eureka Client。
  3. 默认60秒Eureka Server清理一次无效的Eureka Server。
  4. Eureka Client每30秒从Eureka Server拉取一次配置信息。
  5. Eureka Server自我保护模式默认开启,15分钟内超过85%的Eureka Clent都没有正常的心跳,Eureka Server自动进入自我保护模式。
  6. Eureka Client默认将自己注册到Eureka Server,并从Eureka Server拉取配置信息。
# Eureka Client配置
eureka:
  client:
  	# 注册到Eureka Server
    register-with-eureka: true
    # 从Eureka Server拉取配置信息
    fetch-registry: true
  	# 每30秒拉取配置信息
    registry-fetch-interval-seconds: 30
  instance:
    # eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,
    # 在这个时间内若没收到下一次心跳,则将移除该instance,默认90秒。
    lease-expiration-duration-in-seconds: 90
    # Eureka Client发送心跳给Eureka Server的频率。默认30秒。
    lease-renewal-interval-in-seconds: 30
    
# Eureka Server配置
eureka:
  server:
    # 关闭 Eureka的自我保护。默认true,开启。
    enable-self-preservation: true
    # 设置清理无效服务实例的时间。默认60秒清理一次。
    eviction-interval-timer-in-ms: 60000
posted @ 2021-10-07 19:48  行稳致远方  阅读(23)  评论(0)    收藏  举报