熔断器 Hystrix

  在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成 整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供 者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。 如果下图所示:A 作为服务提供者,B 为 A 的服务消费者,C 和 D 是 B 的服务消费者。A 不可用引起了 B 的不可用,并将不可用像滚雪球一样放大到 C 和 D 时,雪崩效应就形成了。 alt 雪崩效应 $\color{red}{如何避免产生这种雪崩效应呢?我们可以使用 Hystrix 来实现熔断器。}$

什么是 Hystrix

Hystrix [hɪst’rɪks]的中文含义是豪猪, 因其背上长满了刺,而拥有自我保护能力。
alt hystrix Hystrix 能使你的系统在出现依赖服务失效的时候,通过隔离系统所依赖的服务,防止服务 级联失败,同时提供失败回退机制,更优雅地应对失效,并使你的系统能更快地从异常中恢 复。
了解熔断器模式请看下图: alt 熔断器模型图 Feign 本身支持 Hystrix,不需要额外引入依赖。但需要开启

1
2
3
feign:
	hystrix:
		enabled: true

远程调用接口

1
2
3
4
5
6
7
8
9
@FeignClient(value = "mingxuan-base",fallback = LabelClientImpl.class)
public interface LabelClient {
/**
     * 查询所有标签数据
     * @return
     */
    @RequestMapping(value = "/label",method = RequestMethod.GET)
    public Result selectAllLabel();
}

实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Component
public class LabelClientImpl implements LabelClient {

    /**
     * 查询所有标签
     * @return
     */
    @Override
    public Result selectAllLabel() {
        return new Result(true, StatusCode.OK,"熔断器开启成功");
    }
}

等所依赖的服务或者所要远程调用的服务挂了,就执行实现类,让其优雅的报错

微服务网关 Zuul

为什么需要微服务网关

  不同的微服务一般有不同的网络地址,而外部的客户端可能需要调用多个服务的接口才能完 成一个业务需求。比如一个电影购票的收集 APP,可能回调用电影分类微服务,用户微服务, 支付微服务等。如果客户端直接和微服务进行通信,会存在一下问题:

  1. 客户端会多次请求不同微服务,增加客户端的复杂性
  2. 存在跨域请求,在一定场景下处理相对复杂
  3. 认证复杂,每一个服务都需要独立认证
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务,如果客户端直接和微服务通信,那么重构会难以实施
  5. 某些微服务可能使用了其他协议,直接访问有一定困难
    $\color{red}{上述问题,都可以借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中 间层,所有的外部请求都会先经过微服务网关。}$

什么是 Zuul

  Zuul 是 Netflix 开源的微服务网关,他可以和 Eureka,Ribbon,Hystrix 等组件配合使 用。Zuul 组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  1. 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
  2. 审查与监控:
  3. 动态路由:动态将请求路由到不同后端集群
  4. 压力测试:逐渐增加指向集群的流量,以了解性能
  5. 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  6. 静态响应处理:边缘位置进行响应,避免转发到内部集群
  7. 多区域弹性:跨域 AWS Region 进行请求路由,旨在实现 ELB(ElasticLoad Balancing)使用多样化
    Spring Cloud 对 Zuul 进行了整合和增强。
    使用 Zuul 后,架构图演变为以下形式: alt zuul模拟图

Zuul 路由转发

导入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

配置文件application.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
server:
  port: 9012
spring:
  application:
    name: mingxuan-manage
eureka:
  client:
    register-with-eureka: true # 是否把该服务注册到eureka服务
    fetch-registry: true # 是否需要从eureka获取信息
    service-url:   # eureka注册地址
      defaultZone: http://127.0.0.1:6868/eureka
  instance:   # 在多个相同微服务的时候,需要eureka自动给每个微服务分配ip地址
    prefer-ip-address: true
zuul:
  routes:
    mingxuan-base:
      path: /base/**
      serviceId: mingxuan-base  # 微服务名称
    mingxuan-qa:
      path: /qa/**
      serviceId: mingxuan-qa
    mingxuan-user:
      path: /user/**
      serviceId: mingxuan-user
    mingxuan-friend:
      path: /friend/**
      serviceId: mingxuan-friend
    mingxuan-recruit:
      path: /recruit/**
      serviceId: mingxuan-recruit
    mingxuan-sms:
      path: /sms/**
      serviceId: mingxuan-sms
    mingxuan-spit:
      path: /spit/**
      serviceId: mingxuan-spit
    mingxuan-user:
      path: /user/**
      serviceId: mingxuan-user
    mingxuan-search:
      path: /search/**
      serviceId: mingxuan-search

启动器

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ManageApplication {
    public static void main(String[] args) {
        SpringApplication.run(ManageApplication.class,args);
    }
}

集中配置组件 SpringCloudConfig

SpringCloudConfig 简介

  在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需 要分布式配置中心组件。在 Spring Cloud 中,有分布式配置中心组件 spring cloud config ,它 支持配置服务放在配置服务的内存中(即本地),也支持放在远程 Git 仓库中。在 spring cloud config 组件中,分两个角色,一是 config server,二是 config client。

  Config Server 是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境 下的配置,默认使用 Git 存储配置文件内容,也可以使用 SVN 存储,或者是本地文件存储。

  Config Client 是 Config Server 的客户端,用于操作存储在 Config Server 中的配置内容。 微服务在启动时会请求 Config Server 获取配置文件的内容,请求到后再启动容器。 详细内容看在线文档: https://springcloud.cc/spring-cloud-config.html

配置服务端

将配置文件提交到码云

  使用 GitHub 时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的 情况。如果我们希望体验 Git 飞一般的速度,可以使用国内的 Git 托管服务——码云(gitee.com)。 和 GitHub 相比,码云也提供免费的 Git 仓库。此外,还集成了代码质量检测、项目演示等 功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务。
创建项目 tensquare-config (点击右上角的加号 ,下拉菜单选择创建项目)
上传配置文件,将工程的 application.yml 改名为 base-dev.yml 后上传 alt 创建giteen仓库 alt 上传文件
文件命名规则: {application}-{profile}.yml 或{application}-{profile}.properties application 为应用名称 profile 指的开发环境(用于区分开发环境,测试环境、 生产环境等)
复制git地址,备用

配置中心微服务服务端

新建config微服务工程

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

创建启动类 ConfigServerApplication

1
2
3
4
5
6
7
@EnableConfigServer //开启配置服务
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

编写配置文件 application.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
server:
  port: 12000
spring:
  application:
    name: xxx-config
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/laoer123/xxx.git
   rabbitmq:
       host: 192.168.2.2
management: #暴露触发消息总线的地址
  endpoints:
     web:
       exposure:
         include: bus-refresh

浏览器测试:http://localhost:12000/base-dev.yml 可以看到配置内容

配置客户端

xxx_base工程添加依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

添加 bootstrap.yml ,删除 application.yml

1
2
3
4
5
6
7
spring:
  cloud:
    config:
      name: base
      profile: dev
      label: master
      uri: http://127.0.0.1:12000

测试: 启动工程 xxx_eureka xxx_config xxx_base,看是否可以正常运行
http://localhost:9001/label

消息总线 SpringCloudBus

SpringCloudBus 简介

  如果我们更新码云中的配置文件,那客户端工程是否可以及时接受新的配置信息 呢?我们现在来做有一个测试,修改一下码云中的配置文件中 mysql 的端口 ,然后测试 http://localhost:9001/label 数据依然可以查询出来,证明修改服务器中的配置并没有更新立 刻到工程,只有重新启动程序才会读取配置。 那我们如果想在不重启微服务的情况下更新 配置如何来实现呢? 我们使用 SpringCloudBus 来实现配置的自动更新。

配置服务端

修改xxx_config微服务

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

修改 application.yml ,添加配置

1
2
3
4
5
6
7
rabbitmq:
       host: 192.168.2.2
management: #暴露触发消息总线的地址
  endpoints:
     web:
       exposure:
         include: bus-refresh

配置客户端

修改 xxx_base 工程 ,引入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在码云的配置文件中配置 rabbitMQ 的地址:

1
2
rabbitmq:
	host: 192.168.2.2
  1. 启动 xxx_eureka 、xxx_config 和 xxx_base 看是否正常运行
  2. 修改码云上的配置文件 ,将数据库连接 IP 改为 127.0.0.1 ,在本地部署一份数据库。
  3. 这一步是将数据进行同步,刷新缓存
1
postman 测试 Url: http://127.0.0.1:12000/actuator/bus-refresh Method: post    
  1. 再次观察输出的数据是否是读取了本地的 mysql 数据

自定义配置的读取

修改码云上的配置文件,增加自定义配置

1
2
sms:
 ip: 127.0.0.1

##测试在 xxx_base 工程中新建 controller

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@RestController
@RequestMapping("ip")
public class IpController {
    @Value("${sms.ip}")
    private String ip;
    @RequestMapping(value = "/ip", method = RequestMethod.GET)
    public String ip() {
        return ip;
    }
}

alt 测试ip 运行测试看是否能够读取配置信息 ,OK
修改码云上的配置文件中的自定义配置

1
2
sms:
 ip: 192.168.2.2

通 过 postman 测 试 Url: http://127.0.0.1:12000/actuator/bus-refresh
Method:post
测试后观察,发现并没有更新信息。(貌似有更新) 这是因为我们的 controller 少了一个注解@RefreshScope 此注解用于刷新配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@RefreshScope
@RestController
public class TestController {
@Value("${sms.ip}")
private String ip;
@RequestMapping(value = "/ip", method = RequestMethod.GET)
public String ip() {
return ip;
}
}