Eureka集群

问题:微服务RPC远程服务调用最核心的要求是什么?

高可用,如果注册中心只有一个,那么它一旦出故障,那么所有的服务都会瘫痪;

所以,我们搭建Eureka集群是必须要做的,实现负载均衡 + 故障!

Eureka集群的原理:互相注册,相互守望

本节,我们将搭建如下集群:

16.jpg


一、Eureka Server集群环境的构建

1、参考cloud-eureka-server7001创建(复制)一个一模一样的cloud-eureka-server7002服务

2、因为是本机搭建集群,我们需要修改我们的 hosts 文件(一般在此目录下:C:\Windows\System32\drivers\etc )

增加以下映射配置项:

127.0.0.1  eureka7001.com
127.0.0.1  eureka7002.com

3、分别修改cloud-eureka-server7001和cloud-eureka-server7002的application.yml配置文件:

cloud-eureka-server7001:

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端实例名称
  client:
    register-with-eureka: false #表示不向注册中心注册自己
    fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
    service-url:
      #      defaultZone: http://eureka7001.com:7001/eureka/ # 不搭建集群 单机 指向自己
      defaultZone: http://eureka7002.com:7002/eureka/ # 搭建集群 集群是指向其他eureka

cloud-eureka-server7002:

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端实例名称
  client:
    register-with-eureka: false #表示不向注册中心注册自己
    fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
    service-url:
      #      defaultZone: http://eureka7002.com:7002/eureka/ # 不搭建集群 单机 指向自己
      defaultZone: http://eureka7001.com:7001/eureka/ # 搭建集群 集群是指向其他eureka

4、分别启动cloud-eureka-server7001和cloud-eureka-server7002服务

http://localhost:7001

12.jpg

http://localhost:7002

13.jpg

可见,集群版的EurekaServer已经搭建完成,测试通过!


二、将cloud-provider-payment8001入驻EurekaServer集群

修改application.yml配置文件(其他的不用改动,只需要修改 eureka.service-url.defaultZone配置项即可):

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  #当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 831121

eureka:
  client:
    register-with-eureka: true #表示向注册中心注册自己 默认为true
    fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
#      defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka  #集群版
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.jiguiquan.springcloud.entities  #所有entity别名所在包

三、将cloud-consumer-order80入驻EurekaServer集群

修改application.yml配置文件(其他的不用改动,只需要修改 eureka.service-url.defaultZone配置项即可):

server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    register-with-eureka: true #表示向注册中心注册自己 默认为true
    fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
#      defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka  #集群版

四、分别启动cloud-provider-payment8001和cloud-consumer-order80服务

http://eureka7001.com:7001

14.jpg

http://eureka7002.com:7002

15.jpg

由此可见,cloud-provider-payment8001和cloud-consumer-order80服务也都顺利注册进我们的EurekaServer集群了!


五、支付服务cloud-provider-payment8001服务的高可用集群搭建

1、参考cloud-provider-payment8001服务新建(或复制)一个cloud-provider-payment8002服务

pom.xml、application.yml、主启动类、业务结构;

2、为了后面的负载均衡,知道具体是调的那个payment服务,我们需要对payment8001和payment8002服务的Controller层做下标记:

在对应的返回数据 message 中带上各自的 serverPort端口号;

17.jpg

3、启动cloud-provider-payment8001和cloud-provider-payment8002服务,进行测试:

http://eureka7001.com:7001 和 http://eureka7002.com:7002

18.jpg

4、通过cloud-consumer-order80调用方式访问:

19.jpg

经过多次测试,端口一直都是访问的“8001”这个payment服务,所以,通过restTemplate的方式,并没有实现负载均衡;


六、通过Eureka注册中心,实现服务间调用及负载均衡

1、修改cloud-consumer-order80中调用的url:

20.jpg

2、修改ApplicationContextConfig.java 文件

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced   //赋予RestTemplate一个默认的负载均衡能力
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

3、重启cloud-consumer-order80服务

再次查询的结果如下图:

21.jpg

22.jpg

经过多次尝试,确定实现了负载均衡,且默认的负载均衡方式为轮询(单次轮询)

—— 当然,默认的轮询方式可以修改,可以自定义。


七、actuator微服务信息完善

1、只暴露服务名,不要显示主机名:

修改前:

23.jpg

修改对应的微服务的 application.yml 配置文件,增加一个配置信息 eureka.instance.instance-id :

eureka:
  client:
    register-with-eureka: true #表示向注册中心注册自己 默认为true
    fetch-registry: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
#      defaultZone: http://localhost:7001/eureka # 单机版 入驻的EurekaServer地址
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka  #集群版
  instance:
    instance-id: payment8001

修改后:

24.jpg

2、访问信息有IP信息显示:

修改前:

25.jpg

修改对应的微服务的 application.yml 配置文件,增加一个配置信息 eureka.instance.prefer-ip-address: true:

instance:
  instance-id: payment8002
  prefer-ip-address: true

修改后:

26.jpg

同理,完成对 cloud-consumer-order80 服务的修改!


八、服务发现Discovery

每个微服务要向外提供一个功能 —— 类似于“关于我们”!

对于注册进Eureka里面的微服务,可以通过服务发现来获得该服务的信息。

以 cloud-provider-payment8001 为例:

1、在PaymentController 中引用 DiscoveryClient;

@RestController
@RequestMapping("/payment")
@Slf4j
public class PaymentController {
    @Autowired
    private PaymentService paymentService;

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

    @Autowired
    private DiscoveryClient discoveryClient;

    @PostMapping(value = "/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("****插入结果为:" + result);

        if (result > 0){
            return new CommonResult(200, "插入数据库成功, serverPort:  "+ serverPort, result);
        } else {
            return new CommonResult(444, "插入数据库失败", null);
        }
    }

    @GetMapping(value = "/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("****~~~~查询结果为:"+payment);

        if (payment != null){
            return new CommonResult<>(200, "查询成功, serverPort:  "+ serverPort, payment);
        } else {
            return new CommonResult<>(444, "没有对应记录, 查询id为:"+id, null);
        }
    }

    @GetMapping
    public Object discovery(){
        List<String> services = discoveryClient.getServices();
        for (String element:services){
            log.info("*****element: "+ element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for(ServiceInstance instance: instances){
            log.info(instance.getServiceId() + "\t" + instance.getHost()+
                    "\t"+ instance.getPort()+"\t"+instance.getUri());
        }
        return this.discoveryClient;
    }
}

2、在主启动类上添加 @EnableDiscoveryClient 注解:

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

3、启动测试:

访问 http://localhost:8001/payment/discovery 的结果:

27.jpg

控制台的输出结果:

28.jpg


九、Eureka的自我保护模式

Eureka的自我保护模式,主要用于一组客户端 和 EurekaServer之间存在网络分区场景下的保护;

一旦进入保护模式,EurekaServer 将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务;

如果在Eureka页面看到如下红色提示,则说明Eureka已经进入了保护模式:

29.jpg

简单总结:

某时刻某一个微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存;

属于 CAP理论 中的 AP!

1、什么是自我保护模式?

默认情况下,如果EurekaServer在一定时间内没有收到摸个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。

但是当网络分区故障发生时(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险——因为微服务本身其实是健康的,此时本不应该注销这个微服务

Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端(可能发生了网络分区故障),那么这个节点就会进入自我保护模式!

30.jpg

2、如何禁用Eureka的自我保护?

Eureka默认是开启了自我保护的,但是我们也有方式将其关闭:以cloud-eureka-server7001 和 cloud-provider-payment8001为例:

修改cloud-eureka-server的配置文件application.yml,增加:

  eureka:  
    server:
      #关闭Eureka的自我保护机制,保证不可用服务被立即剔除
      enable-self-preservation: false
      eviction-interval-timer-in-ms: 2000

修改cloud-provider-payment8001的配置文件application.yml:

  eureka:
    instance:
      #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认为30秒)
      lease-renewal-interval-in-seconds: 1
      #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认为90秒)
      lease-expiration-duration-in-seconds: 2

再次测试,Eureka上面的提示信息已经改变,而且,只要8001服务一停,EurekaServer上的服务列表,在2秒后就立即将payment8001服务剔除了!

测试关闭Eureka自我保护模式成功!

个人此项目代码地址(持续更新):

https://github.com/jiguiquan/cloud2020

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐