微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

SpringCloudH版以及Alibaba版本学习笔记一

本笔记学习自B站尚硅谷Springcloud时所记录

点击直达

  • 【SpringCloud(H版以及Alibaba版本)学习笔记(二)】
  • 【SpringCloud(H版以及Alibaba版本)学习笔记(三)】

目录

一、SpringCloud的理论学习

SpringCloud+SpringCloud Alibaba

1、分布式架构会遇到的四个核心问题是什么?

  • 这么多的服务,客户端该如何去访问?
  • 这么多服务,服务之间该怎么去通信?
  • 这么多服务,如何治理呢?
  • 服务挂了之后该怎么办?

2、分布式架构问题的解决方

SpringCloud是一套生态,就是用来解决以上分布式架构的四个问题
想使用SpringCloud就必须要掌握SpringBoot,因为SpringCloud是基于SpringBoot的;

  • 1、一站式解决方案:
    SpringCloud NetFlix出来了一套解决方案,一站式解决方案:

    • API网关,zuul组件
    • Feign —> HttpClient —> Http的通信方式,同步并阻塞
    • 服务的注册与发现,Eureka
    • 熔断机制,Hystrix

    2018年低,NetFlix宣布无限期停止维护,生态不再维护就会脱节。

  • 2、第二套半自动解决方
    Apache dubbo Zookeeper,第二套解决方案,半自动

    • API:没有,要么找第三方组件,要么自己实现
    • dubbo是一个性能的基于JAVA实现的RPC通信框架 2.6.x
    • 服务的注册与发现:Zookeeper(动物管理员)(Hadoop,Hive)
    • 熔断机制:没有,借助了Hystrix

    不完善,dubbo停止更新太久。

  • 3、SpringCloud Alibaba ,一站式解决方

  • 4、未来方案:

    • 服务网格:下一代微服务标准,Server ,Mesh
    • 代表解决方案:istio
  • 5、总结
    万变不离其宗,一通百通,主要的还是解决以下问题:

    • API网关,服务路由
    • HTTP,RPC框架,异步调用
    • 服务注册与发现,高可用
    • 熔断机制,服务降级

基于以上这四个问题,开发一套解决方案,也叫SpringCloud

3、微服务架构理论

  • 微服务架构是一种架构模式,它提倡将单一应用程序划分为一组小的服务,服务之间相互协调、相互配合,为用户提供最终价值
  • 每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTfukl API)
  • 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等等。值得注意的是:应当尽量避免同一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

4、微服务技术栈有哪些?

在这里插入图片描述

  • 服务开发: SpringBoot、Spring、SpringMVC

  • 服务配置与管理: Netflix公司的Archaius、阿里的Diamond等

  • 服务注册与发现: Euraka、Consul、Zookeeper等

  • 服务调用: Rest、RPC、gRPC

  • 服务熔断器: HyStrix、Envoy等

  • 负载均衡: Ribbon、Nginx

  • 服务接口调用: Feign等 (客户端调用服务的简化工具)

  • 消息队列: Kafka、RabbirMQ、ActiveMQ等

  • 服务配置中心管理:SpringCloudConfig、Chef等

  • 服务路由(API网关):Zuul等

  • 服务监控: Zabbix、Nagios、Metrics、Specatator等

  • 全链路追踪: Zipkin、Brave、Dapper等

  • 服务部署: Doker、OpenStack、Kubernetes等

  • 数据流操作开发包:SprinfCloud Stream(封装了与Redis。Rabbit、Kafka等发送接收消息)

  • 时间消息总线: SpringCloud Bus

    在这里插入图片描述

    在这里插入图片描述

5、SpringBoot和SpringCloud版本选型

在这里插入图片描述

  • Spring Boot 与 Spring Cloud 兼容性查看

  • 此次开发用到的组件版本

    • Cloud - Hoxton.SR1
    • Boot - 2.2.2.RELEASE
    • Cloud Alibaba - 2.1.0.RELEASE
    • Java - Java 8
    • Maven - 3.5及以上
    • MysqL - 5.7及以上

特别注意:如果boot和cloud的版本不一致,可能会导致环境问题,所以推荐使用官网建议的boot和cloud的版本。

6、SpringCloud组件停更/升级/技术替代

(1)停更引发的“升级惨案”问题

  • 停更不停用
  • 被动修复bugs
  • 不再接受合并请求
  • 不再发布新版本

(2)Cloud升级

在这里插入图片描述

  • 服务注册中心

    • × Eureka
    • √ Zookeeper
    • √ Consul
    • √ Nacos(重点)
  • 服务调用

    • √ Ribbon
    • √ LoadBalancer
  • 服务调用2

    • × Feign
    • √ OpenFeign
  • 服务降级

    • × Hystrix
    • √ resilience4j
    • √ sentienl(推荐)
  • 服务网关

    • × Zuul
    • ! Zuul2
    • √ gateway(推荐)
  • 服务配置

    • × Config
    • √ Nacos (推荐)
  • 服务总线

    • × Bus
    • √ Nacos(推荐)

二、微服务架构编码构建

  • 目标:使用SpringCloud的不同组件,搭建订单-支付模块微服务。
  • 要求:约定 > 配置 > 编码
  • Rest微服务工程构建

1、微服务父工程的构建

  • (1)New Project

    在这里插入图片描述

  • (2)聚合总父工程名字

    在这里插入图片描述

  • (3)Maven选版本

    在这里插入图片描述

  • (4)工程名字

    在这里插入图片描述


    在这里插入图片描述


    接下来整环境。

  • (5)字符编码
    打开Setting----->Editor ------>File Encoding----->设置为UTF-8

    在这里插入图片描述

  • (6)注解生效激活

    在这里插入图片描述

  • (7)Java编译版本选8

    在这里插入图片描述

  • (8)File Type过滤

    在这里插入图片描述

2、父工程的POM文件

<?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">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.oldou.springcloud</groupId>
  <artifactId>cloud2021</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!-- 由于这是父工程,这里添加pom,注意不是jar或war -->
  <packaging>pom</packaging>

  <!-- 统一管理jar包版本 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <MysqL.version>5.1.47</MysqL.version>
    <druid.version>1.1.16</druid.version>
    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
  </properties>

  <!-- 子模块继承之后,提供作用:
      锁定版本+子modlue不用写groupId和version -->
  <dependencyManagement>
    <dependencies>
      <!--spring boot 2.2.2-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud Hoxton.SR1-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud alibaba 2.1.0.RELEASE-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>MysqL</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${MysqL.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.spring.boot.version}</version>
      </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <optional>true</optional>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.3.5.RELEASE</version>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

小知识

3、Maven的DependencyManagement和Dependencies

  • Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。
  • 通常会在一个组织或者项目的最顶层的父POM中看到dependencyManagement元素。
  • 使用pom.xml中的dependencyManagement元素能让所有在子项目中引用个依赖而不用显式的列出版本量。
  • Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用这个dependencyManagement元素中指定的版本号。

例如:在父工程中

<dependencyManagement>
    <dependencies>
        <dependency>
        <groupId>mysq1</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.2</version>
        </dependency>
    <dependencies>
</dependencyManagement>

然后在子项目里就可以添加mysql-connector时可以不指定版本号,例如:

<dependencies>
    <dependency>
    <groupId>mysq1</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

这样做的好处就是

  • 如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改

  • 另外如果某个子项目需要另外的一个版本,只需要声明version就可以了

  • dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖

  • 如果不在子项目中声明依赖,是不会从父项目中继承下来的只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom

  • 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

注意

  • 父工程创建完成执行mvn : install将父工程发布到仓库方便子工程继承。

    在这里插入图片描述

  • IDEA右侧旁的Maven插件有Toggle ’ Skip Tests’ Mode按钮,这样maven可以跳过单元测试

    在这里插入图片描述

4、支付模块的构建

  • 步骤:

    • (1)建Module
      选中父工程,new一个module,maven,选中JDK1.8,然后下一步。

      在这里插入图片描述

    • (2)改子工程的POM文件
    <dependencies>
            <!--包含了sleuth+zipkin-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
            <!--eureka-client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
                <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <!--
            <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.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </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>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    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/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: root
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.oldou.springcloud.entities    # 所有Entity别名类所在包
    
    • (4)主启动

      在这里插入图片描述

    • (5)建表
    CREATE TABLE `payment` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `serial` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '流水号',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    

在这里插入图片描述

  • (6)Entities

       /**
        * @auther oldou
        */
       @Data
       @AllArgsConstructor
       @NoArgsConstructor
       public class Payment implements Serializable {
       
           private Long id;
       
           private String serial;
       
       }
    
    • JSON封装体CommonResult
    /**
     * @auther oldo
     * JSON封装体
     */
    @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);
        }
    }
    
  • (7)Dao层

    • 接口PaymentDao:
    /**
     * @auther oldou
     */
    @Mapper
    public interface PaymentDao {
    
        public int create(Payment payment);
    
        public Payment getPaymentById(@Param("id") Long id);
    
    }
    
    • 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.oldou.springcloud.dao.PaymentDao">
    
        <insert id="create" parameterType="com.oldou.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
            insert into payment(serial)  values(#{serial});
        </insert>
    
        <resultMap id="BaseResultMap" type="com.oldou.springcloud.entities.Payment">
            <id column="id" property="id" jdbcType="BIGINT"/>
            <id column="serial" property="serial" jdbcType="VARCHAR"/>
        </resultMap>
    
        <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
            select * from payment where id=#{id};
        </select>
    
    </mapper>
    
    
  • (8)Service层

    • 接口PaymentService:
    public interface PaymentService {
    
        public int create(Payment payment);
    
        public Payment getPaymentById(@Param("id") Long id);
    
    }
    
    • 接口PaymentServiceImpl:
    @Service
    public class PaymentServiceImpl implements PaymentService {
        @Resource
        private PaymentDao paymentDao;
    
        public int create(Payment payment) {
            return paymentDao.create(payment);
        }
    
        public Payment getPaymentById(Long id) {
            return paymentDao.getPaymentById(id);
        }
    }
    
  • (9)Controller层

    • PaymentController:
@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

	@PostMapping(value = "/pay/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("**************插入结果:"+result);
        if(result > 0){
            return new CommonResult(200,"操作成功");
        }
        return new CommonResult(500,"操作失败");
    }


    @GetMapping(value = "/pay/get/{id}")
    public CommonResult getById(@PathVariable("id") Long id){
        Payment result = paymentService.getPaymentById(id);
        log.info("**************查询结果:"+result);
        if(result != null){
            return new CommonResult(200,"查询成功",result);
        }
        return new CommonResult(404,"找不到ID为:"+id+"的记录",null);
    }
}

5、支付模块的测试

  • (1)测试
    我们首先在数据库表中手动插入一条测试数据,
    然后找到支付模块的启动类PaymentMain8001,启动项目,
    在浏览器中输入:http://localhost:8001/pay/get/1

    在这里插入图片描述


    然后使用POSTMAN工具发送新增请求:

    在这里插入图片描述

6、热部署Devtools

我们每次改动项目中的代码时,都需要将项目重启,有没有什么办法可以改善呢,那么热部署可以帮助我们。

  • 第一步:添加devtools的pom依赖到项目中
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<build>
   <plugins>
     <plugin>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-maven-plugin</artifactId>
       <version>2.3.5.RELEASE</version>
       <configuration>
         <fork>true</fork>
         <addResources>true</addResources>
       </configuration>
     </plugin>
   </plugins>
 </build>
  • 第三步:打开以下界面,配置

    在这里插入图片描述

  • 第四步:在IDEA中 按下【 Ctrl+Shift + Alt + /】,选择第一个

    在这里插入图片描述

  • 第五步:重启IDEA就可以了

7、消费者订单模块

在这里插入图片描述

(1)建Module
名称为:cloud-consumer-order80 的模块

(2)改POM

<?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>cloud2021</artifactId>
        <groupId>com.oldou.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order80</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <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>

</project>

(3)写YML

 server:
  port: 80

(4)主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


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

(5)业务类

  • 实体类
    和上面两个一样,Payment 和 CommonResult

  • 控制层Controller

import com.oldou.springcloud.entities.CommonResult;
import com.oldou.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/consumer/pay/create")
    public CommonResult<Payment> create(Payment payment){

        return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/pay/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
    }
}
  • 配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

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

RestTemplate的说明

RestTemplate提供了多种便捷访问远程HTTP服务的方法
是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集。

官网地址

使用:

  • 使用restTemplate访问restful接口非常的简单粗暴无脑。
  • (url, requestMap, ResponseBean.class)这三个参数分别代表。
  • REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。

(6)测试
首先启动消费者订单服务,然后再启动支付服务

  • 再浏览器中先输入:http://localhost:8001/pay/get/1

    在这里插入图片描述

  • 然后再输入:http://localhost/consumer/pay/get/4

    在这里插入图片描述

8、如何开启Run DashBoard【课外】

<option name="configurationTypes">
	<set>
		<option value="SpringBootApplicationConfigurationType"/>
    </set>
</option>
  • 第三步:重启IDEA
  • 可能名称有不一样的,有的命名是Services

    在这里插入图片描述

9、工程重构

(1)问题
我们对比订单服务和支付服务,发现两个服务的entities中的内容都是一致的,这样会造成代码的冗余,为了减少这样的情况,我们将工程中相同的代码提取出来,成为一个公共的工程,以便于其他服务调用

(2)新建一个新的工程【cloud-api-commons】

(3)改POM文件

<?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>cloud2021</artifactId>
        <groupId>com.oldou.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-commons</artifactId>
    <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.1.0</version>
        </dependency>
    </dependencies>
</project>

(4)代码移动

  • 第一步:在cloud-api-commons中新建com.oldou.springcloud.entities包;
  • 第二步:将cloud-consumer-order80或者是cloud-provider-payment8001的entities中两个实体类复制到cloud-api-commons新建的com.oldou.springcloud.entities包中;
  • 第三步:将cloud-api-commons项目进行maven install打包;
  • 第四步删除cloud-consumer-order80和cloud-provider-payment8001两个项目中的entities;
  • 第五步:分别在cloud-consumer-order80和cloud-provider-payment8001两个项目的pom文件中引入cloud-api-commons的依赖。
<dependency>
   <groupId>com.lun.springcloud</groupId>
   <artifactId>cloud-api-commons</artifactId>
   <version>${project.version}</version>
</dependency>

(5)测试
启动两个项目,重新调用接口测试一下,发现是OK的。

三、微服务组件之服务注册与发现

1、Eureka服务注册与发现

1.1、Eureka基础知识

什么是服务治理?

  • Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理。
  • 在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用负载均衡、容错等,实现服务发现与注册

什么是服务注册与发现?

  • Eureka采用了C-S(客户端-服务端)的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
  • 在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息(接口地址)
  • dubbo架构对比:

    在这里插入图片描述

什么是Eureka?

  • Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用配置文件了,功能类似于dubbo的注册中心,比如Zookeeper。
  • Netflix 在设计 Eureka 时,遵循的就是AP原则(CAP文章下面有介绍)。

Eureka的两个组件介绍

  • Eureka包含两个组件: Eureka Server和Eureka Client。

  • Eureka Server提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server中的服务注册表将会注册所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到;

  • Eureka Client是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器在应用启动后,将会向EurekaServer发送心跳(认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉(认周期为90秒)。

1.2、单机Eureka的构建

(1)IDEA生成eurekaServer端服务注册中心,类似物业公司

  • 第一步:创建名为cloud-eureka-server7001的Maven工程

  • 第二步修改pom.xml文件

    <?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>cloud2021</artifactId>
            <groupId>com.oldou.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-eureka-server7001</artifactId>
        <dependencies>
            <!--eureka-server-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <dependency>
                <groupId>com.oldou.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <!--boot web actuator-->
            <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>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  • 第三步添加application.yml

    server:
      port: 7001
    
    eureka:
      instance:
        hostname: locathost #eureka服务端的实例名称
      client:
        register-with-eureka: false  #false表示不向注册中心注册自己。
        fetch-registry: false  #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
  • 第四步:主启动类,这里需要开启EurekaServer

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @EnableEurekaServer //开启EurekaServer
    public class EurekaMain7001 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaMain7001.class,args);
        }
    }
    
  • 第五步:启动测试:localhost:7001

    在这里插入图片描述

1.3、支付服务8001入驻EurekaServer

我们访问Eureka的界面时发现现在是没有任何服务入驻的,接下来我们将支付服务8001入驻Eureka。

在这里插入图片描述


目的我们将EurekaClient端cloud-provider-payment8001注册进EurekaServer 7001成为服务提供者provider。

在这里插入图片描述

  • 第一步修改cloud-provider-payment8001服务的pom文件添加以下依赖:
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  • 第二步修改cloud-provider-payment8001服务的YML,添加以下配置:
    eureka:
      client:
        #表示是否将自己注册进Eurekaserver认为true。
        register-with-eureka: true
        #是否从EurekaServer抓取已有的注册信息,认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    
  • 第三步修改cloud-provider-payment8001服务的主启动类,添加以下注解:
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient      //添加此注解,EurekaClient
    public class PaymentMain001 {
    
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain001.class, args);
        }
    }
    
  • 第四步:启动cloud-provider-payment8001服务,在Eureka网页中刷新发现服务注册进来了。

    在这里插入图片描述


    而上图中,我们可以发现注册进来的Application显示名称正是cloud-provider-payment8001服务的配置文件application.yml设置的应用名cloud-payment-service。

    在这里插入图片描述

1.4、Eureka的自我保护机制

  • 出现的问题:

    在这里插入图片描述


    我们在Eureke服务的界面中看到以上红色提示,这就是Eureka的自我保护机制,那么这是什么意思呢?

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

一句话总结:某时刻某一个微服务不可以用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存!

  • 认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可能变得非常危险了,因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过自我保护机制来解决这个问题当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。

  • 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数(5s检测一次)重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话:好死不如赖活着

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

怎么禁止Eureka的自我保护机制?

在SpringCloud中,可以在yml文件中使用eureka.server.enable-self-preservation = false禁用自我保护模式【不推荐关闭自我保护机制】

认:
eureka.instance.lease-renewal-interval-in-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90

# 举例说明
eureka:
  ...
  instance:
    instance-id: payment8001
    prefer-ip-address: true
    #心跳检测与续约时间
    #开发时没置小些,保证服务关闭注册中心能即使剔除服务
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(认是30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(认是90秒),超时将剔除服务
    lease-expiration-duration-in-seconds: 2

1.5、订单服务80入驻EurekaServer

目的我们将EurekaClient端cloud-consumer-order80注册进EurekaServer 7001成为服务消费者consumer。

  • 第一步修改cloud-consumer-order80服务的pom文件添加以下依赖:
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  • 第二步修改cloud-consumer-order80服务的YML,添加以下配置:
    server:
      port: 80
    
    spring:
      application:
        name: cloud-order-service
    
    eureka:
      client:
        #表示是否将自己注册进Eurekaserver认为true。
        register-with-eureka: true
        #是否从EurekaServer抓取已有的注册信息,认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    
  • 第三步修改cloud-consumer-order80服务的主启动类,添加以下注解:
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    
    @SpringBootApplication
    @EnableEurekaClient
    public class OrderMain80 {
        public static void main( String[] args ){
            SpringApplication.run(OrderMain80.class, args);
        }
    }
    
  • 第四步:启动cloud-consumer-order80服务,在Eureka网页中刷新发现服务注册进来了。

    在这里插入图片描述

1.6、Eureka集群的构建

Eureka集群原理说明:

在这里插入图片描述


问题:微服务RPC远程服务调用最核心的问题是什么?
答:高可用,如果注册中心只有一个,当它出问题的时候就会导致整个微服务环境不可用,也就是常见的单点故障,因此,要想解决这样的问题,就需要搭建Eureka注册中心集群,实现负载均衡+故障容错。

Eureka集群的搭建:

在这里插入图片描述

  • 第一步: 创建cloud-eureka-server7002工程,将7001的全部内容包括pom、yml、启动类)复制到7002,yml文件中的端口需要修改成7002。

  • 第二步: 找到C:\Windows\System32\drivers\etc路径下的hosts文件修改映射配置添加进hosts文件

    127.0.0.1  eureka7001.com
    127.0.0.1  eureka7002.com
    

由于注册中心需要相互注册、相互守望,因此第三、四步需要修改配置文件

  • 第三步: 修改cloud-eureka-server7001配置文件

    server:
      port: 7001
    
    eureka:
      instance:
        hostname: eureka7001.com#eureka服务端的实例名称
      client:
        register-with-eureka: false  #false表示不向注册中心注册自己。
        fetch-registry: false  #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          #集群时需要指向其它eureka地址
          defaultZone: http://eureka7002.com:7002/eureka/
    
          #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。 单机时就是自己
          # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
  • 第四步: 修改cloud-eureka-server7002配置文件

    server:
      port: 7002
    
    eureka:
      instance:
        hostname: eureka7002.com #eureka服务端的实例名称
      client:
        register-with-eureka: false  #false表示不向注册中心注册自己。
        fetch-registry: false  #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          #集群时需要指向其它eureka地址
          defaultZone: http://eureka7001.com:7001/eureka/
    
          #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。 单机时就是自己
          # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
  • 第五步: 访问测试,访问 http://eureka7001.com:7001 或者 localhost:7001

    在这里插入图片描述


    发现两个注册中心能够相互守望了,测试成功。

1.7、配置订单支付两个服务注册进Eureka集群

  • 目的: 将支付服务8001微服务,订单服务80微服务发布到上面2台Eureka集群配置中。

  • 修改 将支付服务8001,订单服务80的yml文件中的eureka.client.service-url.defaultZone进行修改

  # Eureka的配置
eureka:
  client:
    #表示是否将自己注册进Eurekaserver认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # 单机版
      #defaultZone: http://localhost:7001/eureka
      #集群版
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
  • 测试: 启动两个注册中心的同时,将8001服务以及80服务启动,分别访问两个注册中心,可以看到以下:

    在这里插入图片描述


    在这里插入图片描述

1.8、支付服务集群的配置

  • 准备: 新建一个Module名为:cloud-provicer-payment8002,将8001中所有的内容(pom依赖、yml文件、启动类、业务类)都复制一份到8002中,配置文件中的端口改成8002,启动类记得改名。

  • 修改 cloud-provicer-payment8001和cloud-provicer-payment8002的controller中的代码做以下修改添加serverPort:

import com.oldou.springcloud.entities.CommonResult;
import com.oldou.springcloud.entities.Payment;
import com.oldou.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

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

    @PostMapping(value = "/pay/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("**************插入结果:"+result);
        if(result > 0){
            return new CommonResult(200,"操作成功,serverPort:"+serverPort);
        }
        return new CommonResult(500,"操作失败,serverPort:"+serverPort);
    }


    @GetMapping(value = "/pay/get/{id}")
    public CommonResult getById(@PathVariable("id") Long id){
        Payment result = paymentService.getPaymentById(id);
        log.info("**************查询结果:"+result);
        if(result != null){
            return new CommonResult(200,"查询成功,serverPort:"+serverPort,result);
        }
        return new CommonResult(404,"找不到ID为:"+id+"的记录,serverPort:"+serverPort,null);
    }
}
  • 访问测试: 访问 http://eureka7001.com:7001/得到以下界面:

    在这里插入图片描述


    我们可以发现,同名称的有两个服务(8001和8002),这是因为8001和8002服务在yml文件中设置的名称是一致的。
    • 我们访问:http://localhost:8002/pay/get/1或者 http://localhost:8001/pay/get/1 都能看到对应的端口号。

      在这里插入图片描述

负载均衡的配置:

  • 第一步: 我们还需要去cloud-consumer-order80订单服务中去修改访问地址,因为我们现在的支付服务有两个,因此访问地址不能写死,需要将访问地址改成服务的名称

@Slf4j
@RestController
public class OrderController {
 	// 单机版是配置的固定访问地址
    // public static final String PAYMENT_URL = "http://localhost:8001";

    // 集群就需要配置成服务名称,去Eureka上通过注册的服务名去查找
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
    
    ...
}
  • 第二步: 修改完以上之后,我们还需要去ApplicationContextConfig配置类中为RestTemplate添加@LoadBalanced注解开启负载均衡,让服务知道同一名称下的多个服务,到底是分配哪一个
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced //使用此注解赋予RestTemplate负载均衡的能力
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  • 第三步: 测试,我们访问 http://localhost/consumer/pay/get/1 ,进行测试,每刷新一次端口号都会改变,这是负载均衡中的轮询策略,因此就会出现8001/8002交替出现。

1.9、actuator的修改

我们在Eureka服务的界面中发现,注册的服务的ip地址都暴露出来了,本部分主要是关于如何修改主机名称以及隐藏ip。

在这里插入图片描述


主机名称:服务名称修改

  • 第一步: 找到8001以及8002的yml文件打开
  • 第二步: 作以下修改
eureka:
 client:
   #表示是否将自己注册进Eurekaserver认为true。
   register-with-eureka: true
   #是否从EurekaServer抓取已有的注册信息,认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
   fetchRegistry: true
   service-url:
     # 单机版
     #defaultZone: http://localhost:7001/eureka
     #集群版
     defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
 instance:
   instance-id: payment8001 #添加这个配置,8002就该成8002
  • 第三步: 刷新Eureka服务的界面

    在这里插入图片描述


    访问8001服务的健康检查:http://localhost:8001/actuator/health

    在这里插入图片描述

显示IP的修改
我们访问信息有IP信息提示(就是将鼠标指针移至payment8001,payment8002名下,左下角会有IP地址提示
将8001和8002的yml文件添加以下配置:

eureka:
...
  instance:
    instance-id: payment8001
    prefer-ip-address: true   #添加此配置

在这里插入图片描述

1.10、Eureka的服务发现discovery

注册进eureka里面的微服务,我们可以通过其他服务发现并获得该服务的信息(主机名称、端口号等等)。

  • 第一步: 修改cloud-provider-payment8001/8002中的PaymentController
    @RestController
    @Slf4j
    public class PaymentController {
    	...
        // 注意这里导包是:org.springframework.cloud.client.discovery.discoveryClient;
        @Resource
        private discoveryClient discoveryClient; // 服务发现
    
       ...
    
        @GetMapping(value = "/pay/discovery")
        public Object discovery(){
            // 这是获取全部注册的服务
            List<String> services = discoveryClient.getServices();
            services.forEach(
                    e->{
                        log.info("******element--->"+ e);
                    }
            );
    
            //这里是通过已经暴露的服务名称进行获取
            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;
        }
    }
    
  • 第二步: 在cloud-provider-payment8001/8002的主启动类上添加@EnablediscoveryClient注解
    /**
     * @auther oldou
     */
    @SpringBootApplication
    @EnableEurekaClient
    @EnablediscoveryClient  //添加此注解
    public class PaymentMain8001 {
        public static void main( String[] args ) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    
  • 第三步: 访问测试,访问http://localhost:8001/pay/discovery同时查看控制台。

    在这里插入图片描述


    控制台输出

    在这里插入图片描述


    我们可以发现,通过此方法我们可以拿到注册到Eureka中的服务信息。

1.11、Eureka的停更说明

官网地址:https://github.com/Netflix/eureka/wiki

Eureka 2.0 (discontinued)
The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.
Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.
现有的eureka 2.0的开源工作已经停止。代码库和工件作为2上现有工作存储库的一部分发布。X分支机构的使用风险由您自行承担。
Eureka 。x是Netflix服务发现系统的核心部分,目前仍是一个活跃的项目。

那么接下来,我们尝试使用zookeeper代替Eureka实现服务注册与发现。

2、使用Zookeeper实现服务注册与发现

  • zookeeper是一个分布式协调工具,可以实现注册中心功能
  • 关闭Linux服务器防火墙后启动zookeeper功能
  • zookeeper服务器取代Eureka服务器,zk作为服务的注册中心

因此,这里需要准备一个Linux服务器。

2.1、支付服务注册进zookeeper

前期准备:

  • 一台Linux服务器,我这里是centos 7
  • 查看以下服务器的ip:192.168.15.131
  • 关闭服务器的防火墙 systemctl stop firewalld
  • 安装一个zookeeper,教程在这里
  • 安装的位置:/usr/local/zookeeper
  • 启动zk的命令:
    启动zookeeper:
    ./zkServer.sh start
    
    关闭zookeeper:
    ./zkServer.sh stop
    
    查看zookeeper状态:
    ./zkServer.sh status
    
    链接zookeeper
    ./zkCli.sh -server localhost:2181
    

先启动zookeeper,然后再链接zk。

在这里插入图片描述


zookeeper服务器取代Eureka服务器,zk作为服务注册中心

开始构建

  • 第一步:新建一个Module,名为cloud-provider-payment8004的Maven工程

  • 第二步:改pom文件zk的版本要一致

    <?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>cloud2021</artifactId>
            <groupId>com.oldou.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-provider-payment8004</artifactId>
    
        <dependencies>
            <!-- SpringBoot整合Web组件 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
                <groupId>com.oldou.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <!-- SpringBoot整合zookeeper客户端 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <!--先排除自带的zookeeper3.5.3 防止与3.6.0起冲突-->
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!--添加zookeeper3.6.0版本-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.6.0</version>
            </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>
    
    </project>
    
  • 第三步:yml文件

    #8004表示注册到zookeeper服务器的支付服务提供者端口号
    server:
      port: 8004
    
    #服务别名----注册zookeeper到注册中心名称
    spring:
      application:
        name: cloud-provider-payment-zk
      cloud:
        zookeeper:
          connect-string: 192.168.15.131:2181 # zookeeper安装的Linux ip地址
    
  • 第四步:主启动类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnablediscoveryClient;
    
    @SpringBootApplication
    @EnablediscoveryClient//该注解用于向使用consul或者zookeeper作为注册中心时注册服务
    public class PaymentMain8004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8004.class, args);
        }
    }
    
  • 第五步:controller

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.UUID;
    
    @RestController
    @Slf4j
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        @RequestMapping(value = "/pay/zk")
        public String payzk(){
            return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
        }
    }
    
  • 第六步:启动测试,我们启动8004,然后访问:http://localhost:8004/paym/zk

    在这里插入图片描述


    接下来我们去zookeeper中查看发现,多出了一个东西:

    在这里插入图片描述


    说明我们的8004服务已经成功注册到zookeeper服务了,我们继续深挖:

    在这里插入图片描述


    我们发现,注册到zookeeper服务的是一串JSON字符串,我们复制到网页JSON解析工具查看一下:

    {
      "name": "cloud-provider-payment-zk",
      "id": "df6f19ab-d2a6-4ed6-b5ff-0e966c936dbf",
      "address": "192.168.56.1",
      "port": 8004,
      "sslPort": null,
      "payload": {
        "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
        "id": "application-1",
        "name": "cloud-provider-payment-zk",
        "Metadata": {}
      },
      "registrationTimeUTC": 1626084216494,
      "serviceType": "DYNAMIC",
      "uriSpec": {
        "parts": [
          {
            "value": "scheme",
            "variable": true
          },
          {
            "value": "://",
            "variable": false
          },
          {
            "value": "address",
            "variable": true
          },
          {
            "value": ":",
            "variable": false
          },
          {
            "value": "port",
            "variable": true
          }
        ]
      }
    }
    

这就是服务注册的信息。

提出问题: 服务注册到zookeeper中,是作为临时节点还是持久性节点存在呢?
答:临时节点。当8004服务停掉以后,zookeeper会有一个心跳监测时间,一段时间后就会将停掉的服务节点进行清除。

2.2、订单服务注册进zookeeper

  • 新建Module: 新建一个名为cloud-consumerzk-order80 的maven项目。
  • 改pom文件
    <?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>cloud2021</artifactId>
            <groupId>com.oldou.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerzk-order80</artifactId>
        <dependencies>
            <!-- SpringBoot整合Web组件 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- SpringBoot整合zookeeper客户端 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <!--先排除自带的zookeeper-->
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!--添加zookeeper3.6.0版本-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.6.0</version>
            </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>
    </project>
    
  • 新建application.yml文件
    server:
      port: 80
    
    #服务别名----注册zookeeper到注册中心名称
    spring:
      application:
        name: cloud-consumer-order-zk
      cloud:
        zookeeper:
          connect-string: 192.168.15.131:2181  # zookeeper安装的Linux ip地址
    
  • 启动类:
    @SpringBootApplication
    @EnablediscoveryClient
    public class OrderZKMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderZKMain80.class, args);
        }
    }
    
  • 业务类:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderZKController {
    public static final String INVOKE_URL = "http://cloud-provider-payment-zk";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/pay/zk")
    public String paymentInfo() {
        String result = restTemplate.getForObject(INVOKE_URL+"/pay/zk",String.class);
        return result;
    }
}
  • 启动测试:
    运行ZooKeeper服务端,启动服务cloud-consumerzk-order80和cloud-provider-payment8004。

打开ZooKeeper客户端去查看:

在这里插入图片描述


浏览器访问:http://localhost/consumer/pay/zk

在这里插入图片描述

3、Consul实现服务注册与发现

3.1、Consul的简介

Consul是什么?

官网解释: Consul是一个服务网格解决方案,它提供了一个功能齐全的控制平面,具有服务发现、配置和分段功能。这些特性中的每一个都可以根据需要单独使用,也可以一起用于构建全服务网格。Consul需要一个数据平面,并支持代理和本机集成模型。Consul船与一个简单的内置代理,使一切工作的开箱即用,但也支持第三方代理集成,如Envoy。

  • Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp 公司用Go语言开发。
  • 它提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
  • 它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查,同时支持HTTP和DNS协议支持跨数据中心的WAN集群提供图形界面跨平台,支持Linux、Mac、Windows。

Consul能够干什么?

  • 提供服务发现 - 提供HTTP和DNS两种发现方式。
  • 健康监测 - 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
  • KV存储 - Key、Value的存储方式
  • 多数据中心 - Consul支持多数据中心
  • 可视化Web界面

3.2、Consul的安装与运行

这里我为了方便,下载了一个Windows版本的Consul,windows版解压缩后,得consul.exe,按住WIN+R打开cmd,切换到Consul所在位置,或者是如图所示:

  • 查看版本:consul -v

    在这里插入图片描述

  • 开发模式启动:consul agent -dev
  • 浏览器输入 - http://localhost:8500/- 打开Consul控制首页

    在这里插入图片描述

3.3、服务提供者注册进Consul

  • 新建一个Module,名称为:cloud-provider-payment8006

  • 修改pom文件

    <?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>cloud2021</artifactId>
            <groupId>com.oldou.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-provider-payment8006</artifactId>
        <dependencies>
            <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <dependency>
                <groupId>com.oldou.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <!--SpringCloud consul-server -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <!-- SpringBoot整合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>
            <!--日常通用jar包配置-->
            <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>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>RELEASE</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>RELEASE</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    </project>
    
  • 新增并修改application.yml文件

    ###consul服务端口号
    server:
      port: 8006
    
    spring:
      application:
        name: consul-provider-payment
      ####consul注册中心地址
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            #hostname: 127.0.0.1
            service-name: ${spring.application.name}
    
  • 主启动类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnablediscoveryClient;
    
    @SpringBootApplication
    @EnablediscoveryClient
    public class PaymentMain8006 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8006.class, args);
        }
    }
    
  • Controller

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.UUID;
    
    @RestController
    @Slf4j
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        @RequestMapping(value = "/pay/consul")
        public String paymentConsul() {
            return "springcloud with consul: "+serverPort+"\t   "+ UUID.randomUUID().toString();
        }
    }
    
  • 启动服务,访问测试
    访问:http://localhost:8006/pay/consul

    在这里插入图片描述


    访问:http://localhost:8500 - 会显示provider8006的服务名称

    在这里插入图片描述


    说明服务已经注册到Consul服务中了。

3.4、服务消费者注册进Consul

  • 新建一个Module,名称为:cloud-consumerconsul-order80

  • 修改pom文件

    <?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>cloud2021</artifactId>
            <groupId>com.oldou.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerconsul-order80</artifactId>
    
        <dependencies>
            <!--SpringCloud consul-server -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <!-- SpringBoot整合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>
            <!--日常通用jar包配置-->
            <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>
    
    </project>
    
  • 新增并修改application.yml文件

###consul服务端口号
server:
  port: 80

spring:
  application:
    name: consul-consumer-order
  ####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}
  • 启动类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnablediscoveryClient;
    
    @SpringBootApplication
    @EnablediscoveryClient  //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
    public class OrderConsulMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderConsulMain80.class,args);
        }
    }
    
  • 配置类

    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    
  • Controller

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.annotation.Resource;
    
    @RestController
    @Slf4j
    public class OrderConsulController {
    
       public static final String INVOKE_URL = "http://consul-provider-payment";
    
       @Resource
       private RestTemplate restTemplate;
    
       @GetMapping(value = "/consumer/pay/consul")
       public String paymentInfo() {
           String result = restTemplate.getForObject(INVOKE_URL+"/pay/consul",String.class);
           return result;
       }
    }
    
  • 访问测试
    保证consul运行正常,启动cloud-providerconsul-payment8006,cloud-consumerconsul-order80两个服务。

访问:http://localhost/consumer/pay/consul

在这里插入图片描述

  • 访问:http://localhost:8500/ui/dc1/services 可以看见consul和consul-consumer-order、consul-provider-payment三个服务

    在这里插入图片描述

4、Eureka、ZK、Consul三者的异同

组件名语言CAP服务健康检查对外暴露接口Spring Cloud集成
EurekaJavaAP可配支持HTTP
ConsulGoCP支持HTTP/DNS
ZookeeperJavaCP支持客户端已集成

4.1、CAP的介绍

什么是CAP?

  • CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

  • 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

  • CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。

RDBMS(MysqL、Oracle、sqlServer)关系型数据库-------->ACID
Nosql(Redis、Mongodb)非关系型数据库---------->CAP

因此,根据CAP原理将Nosql数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:

  • CA - 单点集群,满足—致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

CAP理论关注粒度的数据,而不是整体系统设计的策略。

4.2、三种对应的CAP


AP架构(Eureka)

  • 当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。

结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

在这里插入图片描述


CP架构(ZooKeeper/Consul)

  • 当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性。

结论:违背了可用性A的要求,只满足一致性和分区容错,即CP。

在这里插入图片描述


CP 与 AP 对立同一的矛盾关系。

4.3、Eureka和ZK的对比

4.3.1、作为注册中心,Eureka比Zookeeper好在哪里?

  • 由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。
  • Zookeeper保证的是CP;
  • Eureka保证的是AP。

4.3.2、Zookeeper保证的是CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接宕机不可用,也就是说,服务注册功能对可用性的要求要高于一致性。但是Zookeeper会出现这样一种情况,当Master节点因为网络故障与其他节点失去联系的时候,剩余节点就会重新进行leader的选举,问题在于选举leader的时间太长(30-120s),且选举期间整合Zookeeper是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络原因使得ZK集群失去Master节点是较大概念会发生的事件,虽然服务最终能够回复,但是漫长的选举事件导致注册长期不可用是不能容忍的。

4.3.3、Eureka保证的是AP

Eureka看明白了这个问题,因此在设计的时候就优先保证可用性。Eureka的各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册查询服务,而Eureka的客户端在向某个Eureka注册的时候,如果发现连接失败,则会自动切换到其他节点,只要由一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的数据,除此之外,Eureka还有一种自我保护机制,如果15分钟内超过85%的节点都没有正常的心跳(每隔5秒检测一下),那么Eureka就会认为客户端与注册中心出现了网络故障,此时就会出现以下几种情况:
(1)Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务;
(2)Eureka仍然能够接受新服务的注册查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用);
(3)当网络稳定时,当前实例新的注册信息会被同步到其他节点中。

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个注册服务瘫痪。

4.3.4、Zookeeper与Eureka区别总结【重点】:

  • zookeeper保证cp(一致性);
  • eureka保证ap(可用性);
  • zookeeper在选举leader的期间注册服务瘫痪,期间不可用;
  • eureka的各个节点平等关系,只要有一台就可保证服务可用,而查询到的数据可能不是最新的,可以很好应对网络故障导致部分节点失联情况;
  • zookeeper有leader、follower、observer三种角色,而eureka各个节点平等;
  • zookeeper采用半数存活原则(避免脑裂),eureka采用自我保护机制来解决分区问题;
  • eureka本质是个工程,zookeeper只是一个进程 ZooKeeper基于CP,不保证高可用,如果zookeeper正在选主,或者Zookeeper集群中半数以上机器不可用,那么将无法获得数据。Eureka基于AP,能保证高可用,即使所有机器都挂了,也能拿到本地缓存的数据。作为注册中心,其实配置是不经常变动的,只有发版(发布新的版本)和机器出故障时会变。对于不经常变动的配置来说,CP是不合适的,而AP在遇到问题时可以用牺牲一致性来保证可用性,既返回旧数据,缓存数据。所以理论上Eureka是更适合做注册中心。而现实环境中大部分项目可能会使用ZooKeeper,那是因为集群不够大,并且基本不会遇到用做注册中心的机器一半以上都挂了的情况。所以实际上也没什么大问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐