在这里插入图片描述SpringCloud为开发人员提供了快速构建分布式系统架构的工具,例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁定,领导选举,分布式会话,集群状态等。作为开发人员,我们只负责写业务代码即可。从上图分析出,SpringCloud为我们提供的轮子如下:

  1. Spring Cloud Consul、Eureka、Nacos
    服务治理组件,包括服务端的注册中心和客户端的服务发现机制。
  2. Spring Cloud Config
    集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。
  3. Spring Cloud Ribbon
    负载均衡的服务调用组件,具有多种负载均衡调用策略;
  4. Spring Cloud Hystrix、Sentinel
    服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;
  5. Spring Cloud Gateway、Zuul
    API网关组件,对请求提供路由及过滤功能。
  6. Spring Cloud OpenFeign
    基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在SpringCloud 2.0中已经取代Feign成为了一等公民。;
  7. Spring Cloud Security
    安全工具包,对网关代理中的负载均衡OAuth2客户端及登录认证进行支持。
  8. Spring Cloud Task
    用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。
  9. Spring Cloud Bus
    用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。
  10. Spring Cloud Sleuth
    SpringCloud应用程序的分布式请求链路跟踪,支持使用Zipkin、HTrace和基于日志(例如ELK)的跟踪。
    11.== Spring Cloud Stream==
    轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。
  11. Spring Boot Admin、Spring Boot Actuator
    Spring Boot Admin 可以对SpringBoot应用的各项指标进行监控,可以作为微服务架构中的监控中心来使用。

接下来逐个介绍去用每个轮子:

1 Spring Cloud Consul:注册中心

目前主流服务注册中心有Consul、Nacos以及Eurake,它们的原理类似,下图是Eurake的架构图

在这里插入图片描述
Eureka Server:提供服务注册与发现。
Service Provider:服务提供方,将自身服务注册到Eureka Server,从而使服务消费者能够找到。
Service Customer:服务消费方,从Eureka Server上获取注册服务列表,从而能够消费服务。

本文选择Consul来作为注册中心来介绍,Consul是HashiCorp公司推出的开源软件,提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。

Spring Cloud Consul 具有如下特性:

  • 支持服务治理:Consul作为注册中心时,微服务中的应用可以向Consul注册自己,并且可以从Consul获取其他应用信息;
  • 支持客户端负责均衡:包括Ribbon和Spring Cloud LoadBalancer;
  • 支持Zuul:当Zuul作为网关时,可以从Consul中注册和发现应用;
  • 支持分布式配置管理:Consul作为配置中心时,使用键值对来存储配置信息;
  • 支持控制总线:可以在整个微服务系统中通过 Control Bus 分发事件消息。

1.1 使用Consul作为注册中心

1.1.1 创建应用注册到Consul

我们通过改造user-service和ribbon-service来演示下服务注册与发现的功能,主要是将应用原来的Eureka注册中心支持改为Consul注册中心支持。

  • 创建consul-user-service模块和consul-ribbon-service模块;
  • 修改相关依赖,把原来的Eureka注册发现的依赖改为Consul的,并添加SpringBoot Actuator的依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 修改配置文件application.yml,将Eureka的注册发现配置改为Consul的:
server:
  port: 8206
spring:
  application:
    name: consul-user-service
  cloud:
    consul: #Consul服务注册发现配置
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
  • 运行两个consul-user-service和一个consul-ribbon-service,在Consul页面上可以看到如下信息:
    在这里插入图片描述

1.1.2 负载均衡功能

由于我们运行了两个consul-user-service,而consul-ribbon-service默认会去调用它的接口,我们调用consul-ribbon-service的接口来演示下负载均衡功能。

多次调用接口:http://localhost:8308/user/1 ,可以发现两个consul-user-service的控制台交替打印如下信息。

2019-10-20 10:39:32.580  INFO 12428 --- [io-8206-exec-10] c.macro.cloud.controller.UserController

2 Spring Cloud Config:外部集中化配置管理

2.1 Spring Cloud Config 简介

Spring Cloud Config分为服务端和客户端两个部分。服务端被称为分布式配置中心,它是个独立的应用,可以从配置仓库获取配置信息并提供给客户端使用。客户端可以通过配置中心来获取配置信息,在启动时加载配置。Spring Cloud Config 的配置中心默认采用Git来存储配置信息,所以天然就支持配置信息的版本管理,并且可以使用Git客户端来方便地管理和访问配置信息。

2.2 在Git仓库中准备配置信息

由于Spring Cloud Config 需要一个存储配置信息的Git仓库,这里我们先在Git仓库中添加好配置文件再演示其功能,Git仓库地址为:https://gitee.com/macrozheng/springcloud-config%E3%80%82

2.2.1 配置仓库目录结构

在这里插入图片描述

2.2.2 master分支下的配置信息

config-dev.yml:

config:
  info: "config info for dev(master)"

config-test.yml:

config:
  info: "config info for test(master)"

config-prod.yml:

config:
  info: "config info for prod(master)"

2.2.3 dev分支下的配置信息

config-dev.yml:**

config:
  info: "config info for dev(dev)"

config-test.yml:

config:
  info: "config info for test(dev)"

config-prod.yml:

config:
  info: "config info for prod(dev)"

2.3 创建config-server模块

这里我们创建一个config-server模块来演示Spring Cloud Config 作为配置中心的功能。

在pom.xml中添加相关依赖

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

2.3.1 在application.yml中进行配置

server:
  port: 8901
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git: #配置存储配置信息的Git仓库
          uri: https://gitee.com/macrozheng/springcloud-config.git
          username: macro
          password: 123456
          clone-on-start: true #开启启动时直接从git获取配置

2.3.2 在启动类上添加@EnableConfigServer注解来启用配置中心功能

@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigServerApplication {

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

}

2.3.3 通过config-server获取配置信息

这里我们通过config-server来演示下如何获取配置信息。

2.3.4 获取配置文件信息的访问格式

2.3.5 获取配置信息

/{label}/{application}-{profile}
# 获取配置文件信息
/{label}/{application}-{profile}.yml

2.3.6 占位符相关解释

  • application:代表应用名称,默认为配置文件中的spring.application.name,如果配置了spring.cloud.config.name,则为该名称;
  • label:代表分支名称,对应配置文件中的spring.cloud.config.label;
  • profile:代表环境名称,对应配置文件中的spring.cloud.config.profile。

2.3.7 获取配置信息演示

  • 启动eureka-server、config-server服务;
  • 访问http://localhost:8901/master/config-dev来获取master分支上dev环境的配置信息;
    在这里插入图片描述
  • 访问http://localhost:8901/master/config-dev.yml来获取master分支上dev环境的配置文件信息,对比上面信息,可以看出配置信息和配置文件信息并不是同一个概念;在这里插入图片描述
  • 访问http://localhost:8901/master/config-test.yml来获取master分支上test环境的配置文件信息:
    在这里插入图片描述
    访问http://localhost:8901/dev/config-dev.yml来获取dev分支上dev环境的配置文件信息:
    在这里插入图片描述

2.4 创建config-client模块

我们创建一个config-client模块来从config-server获取配置。

  • 在pom.xml中添加相关依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 在bootstrap.yml中进行配置
server:
  port: 9001
spring:
  application:
    name: config-client
  cloud:
    config: #Config客户端配置
      profile: dev #启用配置后缀名称
      label: dev #分支名称
      uri: http://localhost:8901 #配置中心地址
      name: config #配置文件名称
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8001/eureka/
  • 添加ConfigClientController类用于获取配置
/**
 * Created by macro on 2019/9/11.
 */
@RestController
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo() {
        return configInfo;
    }
}

2.4.1 演示从配置中心获取配置

  • 启动config-client服务;
  • 访问http://localhost:9001/configInfo,可以获取到dev分支下dev环境的配置;
config info for dev(dev)

2.5 刷新配置

当Git仓库中的配置信息更改后,我们可以通过SpringBoot Actuator的refresh端点来刷新客户端配置信息,以下更改都需要在config-client中进行。

在pom.xml中添加Actuator的依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 在bootstrap.yml中开启refresh端点:
management:
  endpoints:
    web:
      exposure:
        include: 'refresh'
  • 在ConfigClientController类添加@RefreshScope注解用于刷新配置:
/**
 * Created by macro on 2019/9/11.
 */
@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo() {
        return configInfo;
    }
}
  • 重新启动config-client后,调用refresh端点进行配置刷新:
    在这里插入图片描述
  • 访问http://localhost:9001/configInfo进行测试,可以发现配置信息已经刷新。
update config info for config dir dev(dev)

2.6 配置中心添加安全认证

我们可以通过整合SpringSecurity来为配置中心添加安全认证。

2.6.1 创建config-security-server模块

  • 在pom.xml中添加相关依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 在application.yml中进行配置:
server:
  port: 8905
spring:
  application:
    name: config-security-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/macrozheng/springcloud-config.git
          username: macro
          password: 123456
          clone-on-start: true #开启启动时直接从git获取配置
  security: #配置用户名和密码
    user:
      name: macro
      password: 123456
  • 启动config-security-server服务。

2.6.2 修改config-client的配置

  • 添加bootstrap-security.yml配置文件,主要是配置了配置中心的用户名和密码:
server:
  port: 9002
spring:
  application:
    name: config-client
  cloud:
    config:
      profile: dev #启用配置后缀名称
      label: dev #分支名称
      uri: http://localhost:8905 #配置中心地址
      name: config #配置文件名称
      username: macro
      password: 123456
  • 使用bootstrap-security.yml启动config-client服务;

  • 访问http://localhost:9002/configInfo进行测试,发现可以获取到配置信息。

config info for dev(dev)

3 Spring Cloud Ribbon:负载均衡的服务调用

3.1 Ribbon简介

在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,如何保证负载均衡是个不得不去考虑的问题。负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。

3.2 RestTemplate负载均衡

3.2.1 使用@LoadBalanced注解赋予RestTemplate负载均衡的能力

可以看出使用Ribbon的负载均衡功能非常简单,和直接使用RestTemplate没什么两样,只需给RestTemplate添加一个@LoadBalanced即可。

/**
 * Created by macro on 2019/8/29.
 */
@Configuration
public class RibbonConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3.3 Ribbon的常用配置

3.3.1 全局配置

ribbon:
  ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
  ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
  OkToRetryOnAllOperations: true #对超时请求启用重试机制
  MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
  MaxAutoRetries: 1 # 切换实例后重试最大次数
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

3.3.2 指定服务进行配置

与全局配置的区别就是ribbon节点挂在服务名称下面,如下是对ribbon-service调用user-service时的单独配置。

user-service:
  ribbon:
    ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
    ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
    OkToRetryOnAllOperations: true #对超时请求启用重试机制
    MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
    MaxAutoRetries: 1 # 切换实例后重试最大次数
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

3.4 Ribbon的负载均衡策略

所谓的负载均衡策略,就是当A服务调用B服务时,此时B服务有多个实例,这时A服务以何种方式来选择调用的B实例,ribbon可以选择以下几种负载均衡策略。

  • com.netflix.loadbalancer.RandomRule:从提供服务的实例中以随机的方式;
  • com.netflix.loadbalancer.RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过;
  • com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例;
  • com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择;
  • com.netflix.loadbalancer.BestAvailableRule:选择并发较小的实例;
  • com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
  • com.netflix.loadbalancer.ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。

4 Spring Cloud Hystrix:服务容错保护

4.1 Hystrix 简介

在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于长时间得不到响应而占用线程,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。

4.2 服务降级

  • 在UserHystrixController中添加用于测试服务降级的接口:
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
    return userService.getUser(id);
}
  • 在UserService中添加调用方法与服务降级方法,方法上需要添加@HystrixCommand注解:
@HystrixCommand(fallbackMethod = "getDefaultUser")
public CommonResult getUser(Long id) {
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

public CommonResult getDefaultUser(@PathVariable Long id) {
    User defaultUser = new User(-1L, "defaultUser", "123456");
    return new CommonResult<>(defaultUser);
}
  • 调用接口进行测试:http://localhost:8401/user/testFallback/1
    在这里插入图片描述

  • 关闭user-service服务重新测试该接口,发现已经发生了服务降级:
    在这里插入图片描述

4.3 @HystrixCommand详解

@HystrixCommand中的常用参数

  • fallbackMethod:指定服务降级处理方法;
  • ignoreExceptions:忽略某些异常,不发生服务降级;
  • commandKey:命令名称,用于区分不同的命令;
  • groupKey:分组名称,Hystrix会根据不同的分组来统计命令的告警及仪表盘信息;
  • threadPoolKey:线程池名称,用于划分线程池。

4.4 请求缓存

当系统并发量越来越大时,我们需要使用缓存来优化系统,达到减轻并发请求线程数,提供响应速度的效果。

4.4.1 相关注解

  • @CacheResult:开启缓存,默认所有参数作为缓存的key,cacheKeyMethod可以通过返回String类型的方法指定key;
  • @CacheKey:指定缓存的key,可以指定参数或指定参数中的属性值为缓存key,cacheKeyMethod还可以通过返回String类型的方法指定;
  • @CacheRemove:移除缓存,需要指定commandKey。

4.4.2 测试使用缓存

  • 在UserHystrixController中添加使用缓存的测试接口,直接调用三次getUserCache方法:
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
    userService.getUserCache(id);
    userService.getUserCache(id);
    userService.getUserCache(id);
    return new CommonResult("操作成功", 200);
}
  • 在UserService中添加具有缓存功能的getUserCache方法:
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache")
    public CommonResult getUserCache(Long id) {
    LOGGER.info("getUserCache id:{}", id);
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

/**
 * 为缓存生成key的方法
 */
public String getCacheKey(Long id) {
    return String.valueOf(id);
}
  • 调用接口测试http://localhost:8401/user/testCache/1,这个接口中调用了三次getUserCache方法,但是只打印了一次日志,说明有两次走的是缓存:
    在这里插入图片描述

4.5 请求合并

微服务系统中的服务间通信,需要通过远程调用来实现,随着调用次数越来越多,占用线程资源也会越来越多。Hystrix中提供了@HystrixCollapser用于合并请求,从而达到减少通信消耗及线程数量的效果。

4.5.1 @HystrixCollapser的常用属性

  • batchMethod:用于设置请求合并的方法;
  • collapserProperties:请求合并属性,用于控制实例属性,有很多;
  • timerDelayInMilliseconds:collapserProperties中的属性,用于控制每隔多少时间合并一次请求;

4.5.2 功能演示

  • 在UserHystrixController中添加testCollapser方法,这里我们先进行两次服务调用,再间隔200ms以后进行第三次服务调用:
@GetMapping("/testCollapser")
public CommonResult testCollapser() throws ExecutionException, InterruptedException {
    Future<User> future1 = userService.getUserFuture(1L);
    Future<User> future2 = userService.getUserFuture(2L);
    future1.get();
    future2.get();
    ThreadUtil.safeSleep(200);
    Future<User> future3 = userService.getUserFuture(3L);
    future3.get();
    return new CommonResult("操作成功", 200);
}
  • 使用@HystrixCollapser实现请求合并,所有对getUserFuture的的多次调用都会转化为对getUserByIds的单次调用:
@HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
    @HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
})
public Future<User> getUserFuture(Long id) {
    return new AsyncResult<User>(){
    @Override
    public User invoke() {
        CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
        Map data = (Map) commonResult.getData();
        User user = BeanUtil.mapToBean(data,User.class,true);
        LOGGER.info("getUserById username:{}", user.getUsername());
        return user;
        }
    };
}

@HystrixCommand
public List<User> getUserByIds(List<Long> ids) {
    LOGGER.info("getUserByIds:{}", ids);
    CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, CollUtil.join(ids,","));
    return (List<User>) commonResult.getData();
}
  • 访问接口测试http://localhost:8401/user/testCollapser,由于我们设置了100毫秒进行一次请求合并,前两次被合并,最后一次自己单独合并了。
    在这里插入图片描述

4.6 Hystrix的常用配置

4.6.1 全局配置

hystrix:
  command: #用于控制HystrixCommand的行为
    default:
      execution:
        isolation:
          strategy: THREAD #控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略
          thread:
            timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理
            interruptOnTimeout: true #配置HystrixCommand执行超时的时候是否要中断
            interruptOnCancel: true #配置HystrixCommand执行被取消的时候是否要中断
          timeout:
            enabled: true #配置HystrixCommand的执行是否启用超时时间
          semaphore:
            maxConcurrentRequests: 10 #当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝
      fallback:
        enabled: true #用于控制是否启用服务降级
      circuitBreaker: #用于控制HystrixCircuitBreaker的行为
        enabled: true #用于控制断路器是否跟踪健康状况以及熔断请求
        requestVolumeThreshold: 20 #超过该请求数的请求会被拒绝
        forceOpen: false #强制打开断路器,拒绝所有请求
        forceClosed: false #强制关闭断路器,接收所有请求
      requestCache:
        enabled: true #用于控制是否开启请求缓存
  collapser: #用于控制HystrixCollapser的执行行为
    default:
      maxRequestsInBatch: 100 #控制一次合并请求合并的最大请求数
      timerDelayinMilliseconds: 10 #控制多少毫秒内的请求会被合并成一个
      requestCache:
        enabled: true #控制合并请求是否开启缓存
  threadpool: #用于控制HystrixCommand执行所在线程池的行为
    default:
      coreSize: 10 #线程池的核心线程数
      maximumSize: 10 #线程池的最大线程数,超过该线程数的请求会被拒绝
      maxQueueSize: -1 #用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue
      queueSizeRejectionThreshold: 5 #用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数

4.6.2 实例配置

实例配置只需要将全局配置中的default换成与之对应的key即可。

hystrix:
  command:
    HystrixComandKey: #将default换成HystrixComrnandKey
      execution:
        isolation:
          strategy: THREAD
  collapser:
    HystrixCollapserKey: #将default换成HystrixCollapserKey
      maxRequestsInBatch: 100
  threadpool:
    HystrixThreadPoolKey: #将default换成HystrixThreadPoolKey
      coreSize: 10
Logo

开源、云原生的融合云平台

更多推荐