服务注册中心:Eureka

一、Eureka介绍

1.1、什么是Eureka

Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 Spring Cloud Netflix 中,实现 Spring Cloud 的服务注册与发现,同时还提供了负载均衡、故障转移等能力。

1.2、Eureka的架构图

## 1.3、常见的注册中心

  • Eureka Server:通过 Register、Get、Renew 等接口提供服务的注册和发现。
  • Service Provider:服务提供方,把自身的服务实例注册到 Eureka Server 中。
  • Service Consumer:服务调用方,通过 Eureka Server 获取服务列表,消费服务。

1.3、Eureka的运行流程

在这里插入图片描述

二、Eureka入门案例

2.1、创建注册中心

(1)在spring-cloud2020下创建一个子模块,名称叫:eureka-server7001

(2)在eureka-server7001的pom.xml中,添加以下依赖信息

	<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</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>
            <version>1.18.16</version>
        </dependency>
    </dependencies>

(3)创建一个配置文件application.yml,编写配置

server:
  port: 7001

spring:
  application:
    #该名称在集群模式下应该保持一致
    name: eureka-server

eureka:
  instance:
    #服务注册中心实例的主机名
    hostname: localhost
  client:
    #是否将自己注册到注册中心,默认为 true,单实例模式下需要设置为 false
    register-with-eureka: false
    #是否从注册中心获取服务注册信息,默认为 true,单实例模式下需要设置为 false
    fetch-registry: false
    #注册中心对外暴露的注册地址
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

(4)创建主启动类EurekaMain7001,在启动类上标注开启EurekaServer服务

@EnableEurekaServer
@SpringBootApplication
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class);
    }
}

(5)启动服务,在浏览器地址栏中输入:http://localhost:7001/
在这里插入图片描述

2.2、创建服务提供者

(1)在spring-cloud-study下创建一个子模块,名称叫:cloud-provider-payment8001
(2)在cloud-provider-payment8001的pom.xml中,添加以下依赖信息

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</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>
    <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>
        <version>1.18.16</version>
    </dependency>
</dependencies>

(3)创建配置文件application.yml

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

eureka:
  instance:
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址
    service-url:
      defaultZone: http://localhost:7001/eureka/

(4)创建主启动类,在主启动类上开启EurekaClient

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

}

(5)启动当前服务提供者,然后打开浏览器,在浏览器地址栏中输入:http://localhost:7001/
在这里插入图片描述

2.3、创建服务消费者

(1)在spring-cloud2020下创建一个子模块,名称叫:cloud-consumer-order80

(2)在cloud-consumer-order80的pom.xml中,添加以下依赖信息

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</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>
    <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>
        <version>1.18.16</version>
    </dependency>
</dependencies>

(3)创建配置文件application.yaml

server:
  port: 60

spring:
  application:
    name: cloud-consumer-order80

eureka:
  client:
    #是否将自己注册到注册中心,默认为 true
    register-with-eureka: false
    #表示 Eureka Client 间隔多久去服务器拉取注册信息,默认为 30 秒
    registry-fetch-interval-seconds: 10
    #设置服务注册中心地址
    service-url:
      defaultZone: http://localhost:7001/eureka/

(4)创建主启动类,在主启动类上开启EurekaClient

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

(5)启动当前服务消费者,然后用restTemplate远程调用测试打开浏览器,在浏览器地址栏中输入:http://localhost:60/consumer/payment/get/1测试
在这里插入图片描述

注意:我们现在虽然可以正常调用成功,但聪明的你一定要知道,我们现在只是学习了注册中心的搭建和服务的注册,并没有学习服务的远程调用,这个restTemplate本身就是spring提供的一种访问restful风格的模板类,他不是spring cloud的知识点,spring cloud下服务的调用我们会在后续章节或技术中进行介绍,这里特别说明,是怕大家混淆,我们这一章节重点学习注册中心。

三、Eureka集群配置

3.1、配置集群环境

1)在刚才的父工程下再创建一个 cloud-eureka-server7002注册中心的项目,如果是多机器部署不用修改端口,通过 IP 区分服务,如果在一台机器上演示需要修改端口区分服务。

(2)添加域名映射,eureka会把hostname相同的url移除掉,如果我们配置的都是localhost,所以虽然你启动了两个eureka,但是它们不会把自己当成集群

C:\Windows\System32\drivers\etc\hosts

127.0.0.1 eureka-server7001.com
127.0.0.1 eureka-server7002.com

(3)修改cloud-eureka-server7001的application.yml

server:
  port: 7001

spring:
  application:
    #该名称在集群模式下应该保持一致
    name: eureka-server

eureka:
  instance:
    #服务注册中心实例的主机名
    hostname: eureka-server7001.com
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址,指向另一个注册中心(除自己之外所有,多个使用逗号隔开)
    service-url:
      defaultZone: http://localhost:7002/eureka/

(4)修改cloud-eureka-server7002的application.yml

server:
  port: 7002

spring:
  application:
    #该名称在集群模式下应该保持一致
    name: eureka-server

eureka:
  instance:
    #服务注册中心实例的主机名
    hostname: eureka-server7002.com
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址,指向另一个注册中心(除自己之外所有,多个使用逗号隔开)
    service-url:
      defaultZone: http://localhost:7001/eureka/

(5)启动两个注册中心,他们会自动构成一个集群环境,我们先启动7001的,然后再启动7002的,先启动哪一个无所谓,但是会报错,这属于正常现象,因为你另一台还没有启动,没有办法注册,等两台都启动了,过一会清除一下控制台,就发现不报错了。
在这里插入图片描述

3.2、修改服务提供者

application.yml

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

eureka:
  instance:
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址
    service-url:
#      defaultZone: http://localhost:7001/eureka/
       defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/

http://localhost:7001/ , http://localhost:7002/
在这里插入图片描述
在这里插入图片描述

3.3、修改服务消费者

application.yml

server:
  port: 60

spring:
  application:
    name: cloud-consumer-order80

eureka:
  client:
    #是否将自己注册到注册中心,默认为 true
    register-with-eureka: false
    #表示 Eureka Client 间隔多久去服务器拉取注册信息,默认为 30 秒
    registry-fetch-interval-seconds: 10
    #设置服务注册中心地址
    service-url:
      #defaultZone: http://localhost:7001/eureka/
       defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/


然后依次启动服务,进行测试。
在这里插入图片描述

3.4、支付微服务集群配置

(1)新创建一个支付模块cloud-provider-payment8002
(2)将cloud-provider-payment8001的代码和依赖复制过来
(3)修改cloud-provider-payment8001的application.yml的配置文件

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

eureka:
  instance:
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址
    service-url:
#      defaultZone: http://localhost:7001/eureka/
       defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/

(4)修改cloud-provider-payment8002的application.yml的配置文件

server:
  port: 8002
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

eureka:
  instance:
    #是否使用 ip 地址注册
    prefer-ip-address: true
    #该实例注册到服务中心的唯一ID
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    #设置服务注册中心地址
    service-url:
#      defaultZone: http://localhost:7001/eureka/
       defaultZone: http://eureka-server7001.com:7001/eureka/,http://eureka-server7002.com:7002/eureka/

(5)修改cloud-consumer-order80服务消费者的ApplicationContextConfig,@LoadBalanced开启RestTemplate的负载均衡功能

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

(6)因为cloud-consumer-order80服务消费者的远程调用地址是写死的,我们可以更改为服务名,动态获取。
在这里插入图片描述

@RestController
@Slf4j
public class OrderController {
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }

    @GetMapping("/consumer/payment/create")
    public CommonResult create(Payment payment)
    {
        return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
    }
}

(7)修改cloud-provider-payment8001和cloud-provider-payment8002的controller的代码,因为搭建了服务提供者的集群,我们可以添加以下代码显示端口来查看负载均衡的效果。

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

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

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

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查询成功,端口号:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: ");
        }
    }
}

测试结果如下
在这里插入图片描述
在这里插入图片描述

3.5、服务发现Discovery

我们可以通过以下操作获取到服务注册中心的服务实例信息。
(1)编写cloud-provider-payment8001的Controller,注入DiscoveryClient然后编写getDiscovery()方法获取服务注册信息。

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Resource
    private DiscoveryClient discoveryClient;

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

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

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

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查询成功,端口号:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: ");
        }
    }

    /**
     * 获取注册中心的服务信息
     * @return
     */
    @GetMapping(value = "/payment/getDiscovery")
    public Object getDiscovery(){
        //获取所有的Service
        List<String> services = discoveryClient.getServices();
        for (String service : services) {
            log.info(service);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance : instances) {
            log.info(instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return this.discoveryClient;
    }
}

(2)在主启动类上添加@EnableDiscoveryClient注解

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

}

测试效果如下:
在这里插入图片描述

在这里插入图片描述

四、Eureka架构原理

在这里插入图片描述

  • Register(服务注册):把自己的 IP 和端口注册给 Eureka。
  • Renew(服务续约):发送心跳包,每 30 秒发送一次,告诉 Eureka 自己还活着。如果 90 秒还未发送心跳,宕机。
  • Cancel(服务下线):当 Provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除,防止 Consumer 调用到不存在的服务。
  • Get Registry(获取服务注册列表):获取其他服务列表。
  • Replicate(集群中数据同步):Eureka 集群中的数据复制与同步。
  • Make Remote Call(远程调用):完成服务的远程调用。

五、Eureka自我保护机制

5.1、什么是自我保护

一般情况下,服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来判断服务是否健康,同时会定期删除超过 90 秒没有发送心跳的服务。Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,同时提示一个警告,这种算法叫做 Eureka Server 的自我保护模式,这种自我保护模式默认开启。
在这里插入图片描述

有两种情况会导致 Eureka Server 收不到微服务的心跳:

()1微服务自身的原因
(2)微服务与 Eureka 之间的网络故障

5.2、为什么要使用自我保护机制

  • 因为同时保留"好数据"与"坏数据"总比丢掉任何数据要更好,当网络故障恢复后,这个 Eureka 节点会退出"自我保护模式"。
  • Eureka 还有客户端缓存功能(也就是微服务的缓存功能),即使 Eureka 集群中所有节点都宕机失效,微服务的 Provider 和 Consumer 都能正常通信。
  • 微服务的负载均衡策略会自动剔除死亡的微服务节点。

5.3、如何关自我保护

我们可以在注册中心配置文件中进行关闭

eureka:
  server:
    #true:开启自我保护模式,false:关闭自我保护模式
    enable-self-preservation: false
    #清理间隔(单位:毫秒,默认是 60*1000)
    eviction-interval-timer-in-ms: 60000

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值