3_搭建Eureka集群

👉 前面搭建了单机版的 Eureka,比如此时Eureka 服务故障关闭或者宕机,那就会引起 单 点 故 障 。 \color{red}{单点故障。} 导致整个微服务不可用!!


3_搭建Eureka集群

Eureka集群

没有集群带来的高可用,会带来单点故障

Eureka工作原理

  • 服务注册:将服务信息注册进注册中心
  • 服务发现:从注册中心上获取服务信息
  • 实质:存key服务命名,取value调用地址
  1. 先启动eureka注册中心
  2. 启动服务提供者payment支付服务
  3. 支付服务启动后,会把自身信息(比如 服务地址以别名方式注册进eureka)
  4. 消费者order服务在调用接口时候使用服务别名去注册中心获取实际的RPC远程调用地址
  5. 消费者获得调用地址后,底层实际是利用HttpClient技术实现远程调用
  6. 消费者获取服务地址后会缓存在本地JVM内存中,默认每隔30秒更新一次服务调用地址

在这里插入图片描述

微服务RPC远程调用最核心的就是: 高 可 用 。 \color{green}{高可用。}

因为假设注册中心只有一个,如果出现了故障,那么将会导致整个微服务不可用,所以需要搭建Eureka注册中心集群,实现 负 载 均 衡 + 故 障 容 错 \color{ForestGreen}{负载均衡 + 故障容错} +

Eureka集群原理

一句话: 互 相 注 册 , 相 互 守 望 \color{LightCoral}{互相注册,相互守望}

在这里插入图片描述

搭建Eureak集群

原来单机版本时,我们的注册中心的配置文件为

server:
  port: 7001
eureka:
  instance:
    hostname: localhost #eureka服务端实例名称
  client:
    register-with-eureka: false #表示不向注册中心注册自己
    fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

但是如果使用了集群后,我们的eureka就需要相互注册了,也就是 7001的需要注册到7002, 而7002注册7001

同时 hostname也不能重复,需要有两个主机的 ip

eureka 7001
# 端口
server:
  port: 7001
eureka:
  instance:
    # 单机版【hostname: 127.0.0.1 # Eureka 服务端的实例名称】
    # 集群
    hostname: eureka7001.com

  client:
    register-with-eureka: false # 表示不向注册中心注册自己
    fetch-registry: false # 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      # 设置与eureka server 交互的地址查询和注册服务都需要依赖这个地址
      # 单机版【defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/】
      # 集群:需要去守望另外一台注册中心
      defaultZone: http://eureka7002.com:7002/eureka/
eureka 7002
1、建 Module项目:cloud-eureka-server7002
2、改pom

与 7001 保持一致

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_yang</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7002</artifactId>
    <dependencies>
        <!--eureka-server:注意引入的是带前缀的那个,并且是 server 端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--自定义api通用包-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</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>
    </dependencies>

</project>
3、建 YML
# 端口
server:
  port: 7002
eureka:
  instance:
    # 单机版【hostname: 127.0.0.1 # Eureka 服务端的实例名称】
    # 集群
    hostname: eureka7002.com

  client:
    register-with-eureka: false # 表示不向注册中心注册自己
    fetch-registry: false # 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      # 设置与eureka server 交互的地址查询和注册服务都需要依赖这个地址
      # 单机版【defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/】
      # 集群:需要去守望另外一台注册中心
      defaultZone: http://eureka7001.com:7001/eureka/
4、主启动类:
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7002.class,args);
    }
}
5、修改映射配置

找到C:\Windows\System32\drivers\etc路径下的hosts文件

添加如下内容:

# 集群的域名配置
127.0.0.1  eureka7001.com
127.0.0.1  eureka7002.com
127.0.0.1  eureka7003.com

启动7001和7002,在eureka7001上,能看到7002注册上去了

在这里插入图片描述

同时在eureka7002上,能看到7001,这个时候说明我们的eureka集群已经搭建完毕
在这里插入图片描述


👉此时注册中心倒是解决了单点故障的问题,实现了高可用,但是如果此时服务提供者出现宕机或者丢失,那整个微服务也可能面临风险!!!


搭建服务提供者集群

cloud-provider-payment8001
# 端口
server:
  port: 8001

# 名字
spring:
  application:
    name: cloud-payment-service # 代表的就是我以什么样的名字入驻进的注册中心
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动类
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password:

mybatis:
  mapper-locations: classpath:mapper/*.xml # mapper文件映射地址
  type-aliases-package: com.xj0927.springcloud.entities #所有Entity别名类所在的包

eureka:
  client:
    register-with-eureka: true # 表示将自己注册到 eureka server ,默认为 true
    fetch-registry: true # 表示是否从eureka server 抓取已有的注册信息,默认为true。单节点为所谓,集群必须为 true,才能配合ribbon使用负载均衡
    service-url:
       # 单机版:只用注册进一个服务中心【defaultZone: http://127.0.0.1:7001/eureka/】
       # 集群版:需要同时注册进每个注册中心
       defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com/eureka/

  # 显示的服务主机名称
  instance:
    instance-id: payment8001
    prefer-ip-address: true # 访问路径显示 ip【统一:方便调试】
cloud-provider-payment8002
1、建 Module项目:cloud-provider-payment8002
2、改 pom

与 8001 保持一致

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_yang</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8002</artifactId>
    <dependencies>
        <!-- web 与监控写一起-->
        <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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--eureka client:注意引入的是客户端 client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <!--如果没写版本,从父层面找,找到了就直接用,全局统一-->
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--引用自定义的 entities -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

</project>
3、建 YML
# 端口【从 8001 直接拷贝即可】
server:
  port: 8002

# 名字
spring:
  application:
    name: cloud-payment-service # 代表的就是我以什么样的名字入驻进的注册中心
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动类
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password:

mybatis:
  mapper-locations: classpath:mapper/*.xml # mapper文件映射地址
  type-aliases-package: com.xj0927.springcloud.entities #所有Entity别名类所在的包

eureka:
  client:
    register-with-eureka: true # 表示将自己注册到 eureka server ,默认为 true
    fetch-registry: true # 表示是否从eureka server 抓取已有的注册信息,默认为true。单节点为所谓,集群必须为 true,才能配合ribbon使用负载均衡
    service-url:
       # 单机版:只用注册进一个服务中心【】
       defaultZone: http://127.0.0.1:7001/eureka/
       # 集群版:需要同时注册进每个注册中心
       # defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com/eureka/

  # 显示的主机名称
  instance:
    instance-id: payment8002
    prefer-ip-address: true
    # 心跳续约服务时间【默认客户端向服务端30s续约一次】
    lease-renewal-interval-in-seconds: 1
    # 服务端在收到最后一次心跳后等待时间上限,超时就剔除服务【默认客户端最后一次续约90s后,若还未再进行续约,就删除服务】
    lease-expiration-duration-in-seconds: 2

这里需要注意的就是,为了保证这两个服务,对外暴露的都是同一个服务提供者,我们的 服 务 名 需 要 保 持 一 致 \color{red}{服务名需要保持一致}

spring:
  application:
    name: cloud-payment-service #服务名称
4、修改映射配置

在mapper文件夹下,新建 PaymentMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xj0927.springcloud.dao.PaymentDao">
    <insert id="create" parameterType="com.xj0927.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="com.xj0927.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"></id>
        <result column="serial" property="serial" jdbcType="VARCHAR"></result>
    </resultMap>
    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select * from payment where id = #{id};
    </select>

</mapper>
5、启动测试

访问注册中心任意一个端口号地址,启动后,我们发现CLOUD-PAYMENT_SERVICE上有两个服务提供者了,分别为:8001和8002

在这里插入图片描述

6、改 Controller层

同时我们需要服务名进行调用

http://CLOUD-PAYMENT_SERVICE

完整版:

@RestController
@Slf4j
public class OrderController {

    //单机版:写死了访问的具体服务地址【public static final String PAYMENT_URL = "http://127.0.0.1:8001";】
    //集群版:访问服务名称即可
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create") //浏览器只能发送get请求:但实质服务器端还是发送的post请求
    public CommonResult<Payment> create(Payment payment){
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> get(@PathVariable Long id){
        //getForObject 只是返回json串
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id,CommonResult.class);
    }
    
}
7、改 Config 配置类

通知在 RestTemplate 需要设置负载均衡策略,即 @ L o a d B a l a n c e d \color{green}{@LoadBalanced} @LoadBalanced 注解,不然它不知道调用哪个微服务地址

@Configuration  //1.添加第1处
public class ApplicationConfiguration {

    @Bean //2.添加第2处
    @LoadBalanced //赋予其负载均衡的能力,不再关注端口号!【默认使用轮询:交替使用】
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

这个就是Ribbon的负载均衡功能,默认是轮询

Ribbon 和 Eureka 整合后,Consumer可以直接调用服务而不再关心地址和端口号,且该服务还有负载均衡的功能【这也是为什么后面我们在练习Ribbon负载均衡时,没有引入相关的依赖,这是因为Eureka已经集成了Ribbon】

actuator微服务信息完善

要做图形化的展示这块,这两个依赖都需要导入

    <!--web启动器-->
    <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>

actuator:主要用于IP信息完善

actuator 查看健康状态

http://192.168.80.1:8002/actuator/health

服务名称修改

修改后,对外暴露的就是服务名称

Erueka
  #服务名称
  instance:
	 instance-id: payment8001

设置服务的IP显示

Erueka
  #服务名称
  instance:
    #访问路径显示IP地址
    prefer-ip-address: true

服务发现Discovery

Eureka的新的注解标签 @ E n a b l e D i s c o v e r y C l i e n t \color{green}{@EnableDiscoveryClient} @EnableDiscoveryClient

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

@Resource
private DiscoveryClient discoveryClient;

获得服务列表

# 获取列表【即所有注册到注册中心的服务】
List<String> services = discoveryClient.getServices();

# 获取实例【按指定名字获取服务】
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

# 获取ServiceId
instances.get(0).getServiceId();

# 获取主机名
instances.get(0).getHost();

# 获取端口号
instances.get(0).getPort();

# 获取URL
instances.get(0).getUrl();

Eureka自我保护机制

概念

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

如果在Eureka Server的首页看到以下这段提示,说明Eureka进入了保护模式

在这里插入图片描述

通俗的话来说: 某 时 刻 某 一 个 微 服 务 不 可 用 了 , E u r e k a 不 会 立 刻 清 理 , 依 旧 会 对 该 微 服 务 的 信 息 进 行 保 存 , 属 于 C A P 里 面 的 A P 分 支 \color{red}{某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存,属于CAP里面的AP分支} EurekaCAPAP

导致原因

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例,默认 90 秒。但是当网络分区故障发生(延时,卡顿,拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了- 因为微服务本身其实是健康的,此时不应该注销这个微服务,Eureka通过 自我保护模式 来解决这个问题,当EurekaServer节点在短时间丢失过多客户端,那么这个节点就会进入自我保护模式。

在这里插入图片描述

这是一种高可用的机制

在这里插入图片描述

在自我保护模式下,Eureka Server会保护服务注册表中的信息,不在注销任何服务实例。

它的设计哲学就是: 宁 可 保 留 错 误 的 服 务 注 册 信 息 , 也 不 盲 目 注 销 任 何 可 能 健 康 的 服 务 实 例 。 \color{Gold}{宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。}

综上,自我保护模式是一种应对网络异常的安全保护措施,它的架构哲学是宁可保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加健壮,稳定。

禁止自我保护

Eureka默认开启自我保护

  # 注册中心的自我保护机制【默认是开启的】
  server:
    enable-self-preservation: false # 关闭自我保护机制,保证不可用的服务及时删除
    eviction-interval-timer-in-ms: 2000 # 改变时间间隔

同时在客户端进行设置

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

设置完成后,只要服务宕机,会马上从服务注册列表中清除 !!!

关于Eureka停更

Eureka停更后,出现了其它的替代者

  • Zookeeper
  • Consul
  • Nacos(推荐)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值