SpringCloud学习笔记

182 阅读15分钟

一、学习前闲聊~

所需知识

  • JavaSE
  • 数据库
  • 前端
  • Servlet
  • Http
  • Mybatis
  • Spring
  • SpringMVC
  • SpringBoot
  • Dubbo\Zookeeper
  • Maven
  • Git
  • Ajax\Json

这个阶段该如何学

  • 三层架构 + MVC
  • 框架:
    • Spring(IOC、AOP)解决开发的复杂性
    • SpringBoot (新一代JavaEE开发标准,Spring的升级,自动装配)
    • 微服务核心:模块化,不同模块存放在不同电脑上
  • 微服务架构四个核心问题:
      1. 客户端如何访问?
      1. 服务之间如何通信?HTTP、RPC
      1. 服务如何治理?服务注册与发现
      1. 服务挂了怎么办?熔断机制
  • 解决方案:

SpringCloud 是解决上述四个问题的一种生态,成熟的解决方案有如下:

  1. Spring Cloud NetFlix (一站式解决方案) 如何访问:api网关、Zuul组件 如何通信:Feign基于HTTP通信,可以进行负载均衡,同步、阻塞 服务注册与发现:Eureka 熔断机制:Hystrix

  2. Apache Dubbo+Zookeeper (半自动,需要整合别人) 如何访问:没有,找第三方组件,或者自己实现 如何通信:Dubbo基于Java的RPC通信框架 服务注册与发现:Zookeeper 熔断机制:没有,找第三方 方案并不完善,就是想单一做RPC通信框架

  3. Spring Cloud Alibaba (最新的一站式解决方案,因为NetFlix停更) 更简单

  • 万变不离其宗(归其本质分布式网络不可靠)

      1. API
      1. HTTP、RPC
      1. 注册和发现
      1. 熔断机制
  • 带着一下问题去学习:

  1. 什么是微服务?
  2. 微服务之间是如何独立通讯的?
  3. SpringCloud和Dubbo有哪些区别?
  4. 请谈谈SpringBoot和SpringCloud的理解
  5. 什么是服务熔断?什么是服务降级?
  6. 微服务的优缺点分别是什么,说一下你在项目中遇到的坑
  7. 你所知道的微服务技术栈?
  8. Eureka和Zookeeper都可以提供服务注册与发现,有什么区别

二、微服务概述

1. 什么是微服务

微服务(Microservice Architecture)是近几年流行的一种架构思想,引用ThoughtWorks公司首席科学家Martin Fowler于2014年提出的一段话:

原文链接

  • 就目前而言,对于微服务,业界没有统一标准的定义
  • 通常而言,微服务架构是一种架构模式,或是一种架构风格,提倡单一的应用程序划分成一组小的服务,每个服务运行在其独立的进程中,服务之间相互协调,相互配置,为用户提供最终价值。服务之间采用轻量级的通信机制,每个服务围绕具体业务进行构建,能够被独立部署在生产环境中。另外,应尽量避免统一集中的服务管理制度,对具体一个服务,应根据业务上下文, 选择合适的语言、工具对其进行构建,可以有一个轻量级集中式管理来协调服务,可以使用不同语言编写,不同数据存储。

从技术维度来理解:

  • 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底的解耦合,每个服务提供单个业务功能,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动和销毁,拥有自己独立的数据库。

2. 微服务与微服务架构

(1)微服务 强调的是服务的大小,他关注的是某一个点,是具体解决一个问题、提供落地对应服务的一个服务应用,狭义的看可以是IDEA中一个个微服务工程,或者Model (2)微服务架构 一种新的架构形式,Martin Fowler于2014年提出,是要处理开头提出的四个问题。

3. 微服务优缺点

(1)优点

  • 单一职责原则
  • 每个服务足够内聚,代码容易理解,聚焦一个指定的业务功能或业务需求
  • 开发简单,一个服务可能就是专一只干一件事
  • 微服务能够被小团队单独开发
  • 微服务是松耦合的,是有功能意义的服务
  • 微服务能使用不同语言开发
  • 易于和第三方集成,容易且灵活的方式继承自动部署,如Jenkins、Hudson、Bamboo
  • 微服务易于被一个开发人员理解,修改和维护
  • 微服务允许你利用融合最新技术
  • 微服务只是业务逻辑代码,不会和HTML、CSS界面混合
  • 每个微服务都有自己的存储能力,有自己的数据库

(2)缺点

  • 开发人员要处理分布式系统的复杂性
  • 多服务运维难度,随着服务的增加,运维压力也增加
  • 系统部署依赖
  • 服务间通信成本
  • 数据一致性
  • 系统集成测试
  • 性能监控

4. 微服务技术栈有哪些

微服务条目落地技术
服务开发SpringBoot、Spring、SpringMVC
服务配置与管理Netflix公司的Archaius、阿里的Diamond等
服务注册与发现Eureka、Consul、Zookeeper
服务调用Rest、RPC、gRPC
服务熔断Hystrix、Envoy等
负载均衡Ribbon、Nginx等
服务接口调用Feign等
消息队列Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理SpringCloudConfig、Chef等
服务路由(API网关)Zuul等
服务监控Zabbix、Nagios、Metrics、Specatator等
全链路追踪Zipkin、Brave、Dapper等
服务部署Docker、OpenStack、K8s等
数据库操作开发包SpringCloud Stream(封装与Redis、Rabbit、Kafka等发送接收消息)
事件消息总线SpringCloud Bus

5. 为什么选择SpringCloud作为微服务架构

(1)选型依据

  • 整体解决方案和框架成熟度
  • 社区热度
  • 可维护性
  • 学习曲线

(2)当前各大IT公司用微服务架构有哪些?

  • 阿里:dubbo+HFS
  • 京东:JSF
  • 新浪:Motan
  • 当当网:DubboX

(3)微服务框架对比

功能点Netflix/SpringCloudMotangRPCThriftDubbo/DubboX
功能定位完成的微服务框架RPC框架,但整合Zookeeper或Consul,实现集群环境基本服务注册发现RPC框架RPC框架服务框架
支持Rest是,Ribbon支持多种可插拔序列化选择
支持RPC是(Hession2)
支持多语言
负载均衡是,服务端Zuul+客户端Ribbon,Eureka针对中间层服务器是(客户端)是(客户端)
配置服务NetfixArchaius,SpringCloudConfigServer是(Zookeeper提供)
服务调用链监控是(Zuul),提供边缘服务,API网关
高可用/容错是(服务端Hystrix+客户端Ribbon)是(客户端)是(客户端)
典型应用案例NetfilxSinaGoogleFacebook

三、SpringCloud入门概述

1. SpringCloud是什么

image.png SpringCloud是基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现、配置中心、全链路监控、服务网关、负载均衡、熔断器等组件,除了基于NetFlix的开源组件做高抽象封装之外,还有一些选型中立的开源组件。
Springcloud利用Springboot的开发便利,巧妙地简化分布式系统基础设施开发,SpringCloud为开发人员提供快速构建分布式系统的工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式回话等,都可以用SpringBoot开发风格做到一键启动和部署。

2. SpringCloud和SpringBoot关系

  • SpringBoot专注于快速方便的开发单个个体微服务
  • SpringCloud是关注全局微服务协调整理框架,将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等

3. Dubbo和SpringCloud技术选型

(1)分布式+服务治理Dubbo 目前成熟的互联网架构:应用服务化拆分+消息中间件 image.png

(2)Dubbo+SpringCloud

Dubbo(组装机)Spring(品牌机)
服务注册中心ZookeeperSpring Cloud Netflix Eureka
服务调用方式RPCREST API
服务调监控Dubbo-monitorSpring Boot Admin
断路器不完善Spring Cloud Netflix Hystrix
服务网关Spring Cloud Netflix Zuul
分布式配置Spring Cloud Config
服务跟踪Spring Cloud Sleuth
消息总线Spring Cloud Bus
数据流Spring Cloud Stream
批量任务Spring Cloud Task

最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用基于HTTP的REST方式。 严格来说,二者各有优劣,后者牺牲了服务调用性能,也避免了原生RPC带来的问题。而且REST比RPC更灵活,服务提供方和调用方依赖只依靠一纸契约,不存在代码级别的强依赖。

四、创建项目

1. 主项目搭建

  • 创建一个普通的Maven项目

image.png

image.png

  • 设置父级POM文件导包
<!--打包方式 pom-->
<packaging>pom</packaging>

<properties>
    <junit.version>4.13.2</junit.version>
    <lombok.version>1.18.20</lombok.version>
    <log4j.version>1.2.17</log4j.version>
    <logback.version>1.2.6</logback.version>
</properties>

<dependencyManagement>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

2. 实体模块--API

image.png

  • 添加依赖
<!-- 当前Model自己要是用的依赖,父依赖已经配置了版本 -->
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
  • 创建实体

image.png

/**
 * @author wangwk-a
 * @date 2021/9/24 17:09
 * @description 在网络传输中,所有实体类务必实现序列化
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class Dept implements Serializable {
    private Long deptno;
    private String dname;
    /**
     * 看这个数据存在哪个数据库,微服务一个服务对应一个数据库
     */
    private String db_source;

    public Dept(String dname) {
        this.dname = dname;
    }
}

3. 生产者模块--springcloud-provider-dept-8001

image.png

  • 添加依赖

首先通过POM引入API模块所提供的实体

<!-- 我们需要拿到实体类,所以需要配置api Module-->
<dependency>
    <groupId>com.nick</groupId>
    <artifactId>springcloud-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

然后添加测试、数据库、springBoot、热部署等依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
  • 配置文件

使用yml格式,配置端口号、Mybatis、和spring

server:
  port: 8001

# Mybatis配置
mybatis:
  type-aliases-package: com.nick.springcloud.pojo
  config-locations: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

spring:
  application:
    name: springcloud-provider-dept
  datasource:
    # 数据源 org.gjt.mm.mysql.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/nick_database?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: 数据库名称
    password: 数据库密码
  • dao层
@Mapper
@Repository
public interface DeptDao {
    public boolean addDept(Dept dept);
    public Dept queryById(Long id);
    public List<Dept> queryAll();
}
  • Service层

接口类

public interface DeptService {
    public boolean addDept(Dept dept);
    public Dept queryById(Long id);
    public List<Dept> queryAll();
}

实现类

@Service
public class DeptServiceImpl implements DeptService{
    @Autowired
    private DeptDao deptDao;

    @Override
    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryById(Long id) {
        return deptDao.queryById(id);
    }

    @Override
    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}
  • controller层
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    @PostMapping("/dept/add")
    public boolean addDept(Dept dept) {
        return deptService.addDept(dept);
    }

    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return deptService.queryById(id);
    }

    @GetMapping("/dept/list")
    public List<Dept> queryAll() {
        return deptService.queryAll();
    }
}
  • 启动类
@SpringBootApplication
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class, args);
    }
}
  • 效果 image.png

4. 消费者模块--springcloud-consumerdept-8080

image.png

  • 添加依赖
<dependency>
    <groupId>com.nick</groupId>
    <artifactId>springcloud-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
  • Configration类
@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  • controller层
@RestController
public class DeptConsumerController {
    /**
     * 消费者不应该有Service层
     * RestTemplate 直接调用就可以
     * (url, 实体Map, responseType)
     * 提供多种便捷访问远程HTTP的方法,简单的RestFul服务模板
     */
    @Autowired
    private RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    /**
     * http://localhost:8001/dept/get/{id}
     * @param id
     * @return
     */
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}
  • 启动类
@SpringBootApplication
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}
  • 效果

先启动成产者,再启动消费者 image.png 访问8080

image.png

五、Eureka服务注册与发现

1. 什么是Eureka

  • Netflix在设计Eureka时,遵循AP原则
  • Eureka是Netflix的一个子模块,是基于REST服务,用于定位服务,已实现云端中间层服务发现和故障转移,服务注册与发现,只需要使用服务的标识符,就可以访问到服务,功能类似于Dubbo的注册中心,比如Zookeeper。

2. 原理讲解

image.png

  • SpringCloud封装了NetFlex模块来实现服务注册与发现(对比Zookeeper)
  • Eureka采用C-S架构设计,EurekaServer作为服务注册功能的服务器,是服务注册中心
  • 系统中其他微服务,使用Eureka客户端连接到EurekaServer并维持心跳连接,这样维护人员就可以通过EurekaServer来监控微服务

两个组件

  • Eureka Server: 提供服务注册服务,各个节点启动后,会在EurekaServer中注册,这样其中的服务注册表中将会有所有的可用服务节点可被外界看到
  • Eureka Client: Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置轮训负载均衡器,应用启动后,将会向EurekaServer发送心跳,如果多个心跳周期内没有心跳,EurekaServer将会从服务注册表中移除该服务

三大角色

  • EurekaServer:提供服务的注册与发现
  • ServiceProvider: 将自身服务注册到Eureka中,从而使消费者能够找到
  • ServiceConsumer: 消费从Eureka中获取注册服务列表,从而找到消费服务

3. EurekaServer创建

image.png

  • 配置Eureka
server:
  port: 7001
# Eureka配置
eureka:
  instance:
    # Eureka服务端的实例名称
    hostname: localhost
  client:
    # 表示是否向Eureka注册中心注册自己,因为服务器不需要注册自己
    register-with-eureka: false
    # 如果为false,则表示自己为注册中心
    fetch-registry: false
    service-url:
      # 监控页面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • 创建启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class, args);
    }
}
  • 访问7001端口 image.png

4. 服务注册至EurekaServer

将之前实现的生产模块springcloud-provider-dept-8001注册至EurekaServer

  • 8001模块的POM中添加Eureka依赖
<!-- 添加Eureka依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
  • 添加配置
# 配置Eureka 服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    # 修改Eureka上的默认描述信息
    instance-id: springcloud-provider-dept-8001

其中defaultZone的链接就是在EurekaServer配置文件中监控页面的路径

  • 开启服务

在启动类中开启EurekaClient,服务启动后会自动注册到Eureka服务中

@SpringBootApplication
@EnableEurekaClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class, args);
    }
}
  • 查看效果 image.png

这个默认描述信息点击是一个网址,是一些监控信息,目前是没有信息的,如果要添加,进行如下操作:

  • 8001模块POM添加依赖
<!-- 完善Eureka监控信息 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 8001配置Info
# Info监控服务
info:
  app.name: nick-springcloud
  company.name: https://juejin.cn/user/4117012877420045/posts
  • 点击配置信息查看效果 image.png

5. Eureka自我保护机制

好死不如赖活着

image.png

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

  • 默认情况,如果EurekaServer在一定时间没有接收到某微服务实例的心跳,EurekaServer会注销该实例,但是如果是因为网络问题,微服务无法和Server通信,但是其本身是好的,就不应该注销该服务。因此Eureka通过自我保护机制来解决这个问题,当Server短时间内丢失客户端心跳,该节点会进入自我保护模式,Server会保护服务注册表中的信息,网路恢复后自动退出自我保护模式
  • 在EurekaCloud中,可以使用eureka.server.enable-self-preservation=false禁用自我保护模式(不推荐关闭

6. 通过Controller获取配置信息

  • 在Controller注入DiscoveryClient对象
@Autowired
private DiscoveryClient client;

注意DiscoveryClient要引下图的包,引错了无法调用其方法 image.png

  • 接口编写
@GetMapping("/dept/discovery")
public Object discovery() {
    // 获取微服务列表清单
    List<String> services = client.getServices();
    System.out.println("discovery=>service: " + services);

    // 得到一个具体的微服务信息,通过具体的微服务id,applicationName
    List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
    instances.forEach(item->{
        System.out.println(
                item.getHost() + "\t" +
                item.getPort() + "\t" +
                item.getUri() + "\t" +
                item.getServiceId()
        );
    });
    return this.client;
}
  • 网页访问 image.png

  • 控制台输出 image.png

六、Eureka集群

image.png

  • 创建集群

其中7002、7003是复制7001,并修改配置文件端口号和启动类中类名 image.png

  • host域名映射(注意备份)

改域名映射只是为了方便后续理解,命名不同易于看出效果,实际访问的还是localhost image.png

  • 集群互联 image.png

  • 配置生产服务springcloud-provider-dept-8001 image.png

  • 效果测试 image.png

查看7001(7002和7003同样的效果,这里补贴图了) image.png

  • 将服务springcloud-provider-dept-8001启动,注册到集群 image.png

  • 查看效果 image.png

六、对比Zookeeper

1. 回顾CAP原则

RDBMS(MySQL、Oracle、sqlServer)--> ACID NoSQL(redis、mongdb)--> CAP

  • ACID是什么
    • A(Atomicity)原子性
    • C(Consistency)一致性
    • I(Isolation)隔离性
    • D(Durability)持久性
  • CAP是什么
    • C(Consistency)强一致性
    • A(Availability)可用性
    • P(Partition Tolerance)分区容错性

2. Eureka比Zookeeper好在哪里

CAP理论指出,一个分布式系统不能同时满足CAP,由于分区容错性P在分布式系统是是必须要保证的,因此我们需要在C、A中进行权衡。

  • Zookeeper保证了CP
  • Eureka保证了AP

Zookeeper保证了CP
当向注册中心查询服务列表是,可以容忍注册中心返回几分钟前的注册信息,但不能接收服务直接down掉。也就是说,服务注册功能对可用性要求高于一致性。Zookeeper会有这种情况,master节点因为网络故障down掉,剩余节点会重新选举leader,数据量越大选举时间越长,选举过程中,服务不可用。(可用性低了)

Eureka保证的是AP
因此设计时优先保证可用性Eureka各个节点时平等的,几个节点挂了,剩余节点立马提供注册和查询服务,只不过查到的信息可能不是最新的。此外Eureka有自我保护机制,如果在15分钟内超过85%节点都没有正常的心跳,那么Eureka认为客户端与注册中心出现了网络故障,会出现:

  • Eureka不在从注册列表中移除因为长时间没有收到心跳的过期服务
  • Eureka任然能能够接收新服务的注册和查询,但是不会被同步到其他节点(即保证当前节点可用)
  • 网络稳定时,当前实例新的注册信息会被同步到其他节点

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

七、Ribbon实现负载均衡

(之后有时间会更新这部分...)