SpringCloud的适用场景

功能服务的不断增加,多服务间的相互调用频繁时,需要用SpringCloud进行服务治理,防止在服务更新的过程中,没有合理通信,导致数据的丢失。

SpringCloud的原理

1、服务的注册和发现 Eureka

1.1Eureka简介

和Zookeeper类似,Eureka是⼀个⽤于服务注册和发现的组件,最开始主要应⽤与亚⻢逊公司的云 计算服务平台AWS,Eureka分为Eureka Server和Eureka Client,Eureka Server为Eureka服务注册中 ⼼,Eureka Client为Eureka客户端

Eureka负责微服务的注册和发现⼯作,它记录了服务和服务地址的 映射关系。在分布式架构中,服务会注册到Eureka注册中⼼,当服务需要调⽤其它服务时,就从Eureka 找到服务的地址,进⾏调⽤。Eureka在Spring Cloud中的作⽤是⽤来作为服务治理实现服务注册和发 现。**Eureka主要涉及到三⼤⻆⾊:服务提供者、服务消费者、注册中⼼ **

  • 服务注册是指,各个微服务在启动时,将⾃⼰的⽹络地址等信息注册到Eureka,服务提供者将⾃⼰ 的服务信息,如服务名、IP等告知服务注册中⼼。
  • 服务发现是指当⼀个服务消费者需要调⽤另外⼀个服务时,服务消费者从Eureka查询服务提供者的 地址,并通过该地址调⽤服务提供者的接⼝。⼀个服务既可以是服务消费者,也可以是服务发现者。

各个微服务与注册中⼼使⽤⼀定机制(例如⼼跳)通信。如果Eureka与某微服务⻓时间⽆法通信, Eureka会将该服务实例从服务注册中⼼中剔除,如果剔除掉这个服务实例过了⼀段时间,此服务恢复⼼ 跳,那么服务注册中⼼将该实例重新纳⼊到服务列表中,Eureka架构图

1.2Eureka的一些概念

  1. Register — 服务注册

当Eureka Client向Eureka Server注册时,Eureka Client提供⾃身的元数据,⽐如IP地址、端⼝、
运⾏状况指标的URL,主⻚地址等信息。

  1. Renew — 服务续约

Eureka Client在默认情况下会每隔30秒发送⼀次⼼跳来进⾏服务续约,通过服务续约来告知
Eureka Server该Eureka Client依然可⽤,正常情况下,如果Eureka Server在90秒内没有收到Eureka
Client的⼼跳,Eureka Server会将Eureka Client实例从注册列表中删除,注意:官⽹建议不要更改服
务续约的间隔时间。

  1. Fetch Registries — 获取服务注册列表信息

Eureka Client从Eureka Server获取服务注册表信息,并将其缓存到本地。Eureka Client 会使⽤服
务注册列表信息查找其他服务的信息,从⽽进⾏远程调⽤,改注册列表信息定时(每隔30秒)更新⼀
次,每次返回的注册列表信息可能与Eureka Client的缓存信息不同,Erueka Client会重新获取整个注册表信息。Eureka Server缓存了所有的服务注册表信息,并且进⾏了压缩。Eureka Client和Eureka
Server可以使⽤json和xml的数据格式进⾏通信
,默认,Eureka Client使⽤JSON格式_⽅式来获取服务器
_注册列表信息

  1. Cancel (取消)— 服务下线

Eureka Client在程序关闭时可以向Eureka Server发送下线请求,发送请求后,该客户端的实例信
息将从Eureka Server的服务注册列表信息中删除。改下线请求不会⾃动完成,需要在程序关闭时调⽤
以下代码
DiscoveryManager.getInstance().shutdownComponent();

  1. Eviction(驱赶) — 服务

在默认情况下,Eureka Client连续90秒没有想Eureka Server发送服务续约(⼼跳)时,Eureka
Server会将该服务实例从服务列表中删除。即服务剔除。

1.3Eureka的自我保护

当有⼀个新的Eureka Server出现时,他尝试从相邻的Peer节点获取所有服务实例注册信息。如果 从相邻的Peer节点获取信息时出现了故障,Eureka Server会尝试其他的Peer节点。如果Eureka Server 能够成功获取所有的服务实例信息。则根据配置信息设置服务续约的阈值。在任何时间,如果Eureka Server接收到的服务续约低于为该值配置的百分⽐(默认为15分钟内低于85%),则服务器开启⾃我保 护模式,即不再剔除注册列表的信息
这样做的好处在于,如果Eureka Server⾃身的⽹络问题⽽导致Eureka Client⽆法续约,Eureka Client的注册列表信息不再被删除,也就是Eureka Client还可以被其他服务消费。 (即eureak宕机了,服务依旧可以进行)
⾃我保护开启后,效 果如图2-6所示。

在默认情况下,Eureka Server的⾃我保护模式是开启的,⽣产环境下这很有效,保证了⼤多数 服务依然可⽤,但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭⾃我保护模式。代 码如下

eureka:server:enable-self-preservation: false # 关闭⾃我保护模式(缺省为打开)eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

2、服务调用

2.1 RestTemplate简介

RestTemplate是Spring Resources中⼀个访问第三⽅RESTful API接⼝的⽹络请求框架。 RestTemplate的设计原则和其他的Spring Template(例如JdbcTemplate)类似,都是为了执⾏复杂任务提供了⼀个具有默认⾏为的简单⽅法。** RestTemplate是⽤来消费REST服务的**,所以RestTemplate的主要⽅法都与REST的HTTP协议的 ⼀些⽅法紧密相连,例如**HEAD、GET、POST、PUT、DELETE、OPTIONS**等⽅法,这些⽅法在 RestTemplate类对应的⽅法为**headForHeaders(),getForObject()、postForObject()、put()、delet()** 等。
举例说明,在订单服务通过RestTemplate的getForObject⽅法调⽤⽀付服务,并且将调⽤结果反 序列化成Payment对象,代码如下。

@GetMapping("/payment/{id}")public ResponseEntity<Payment> getPaymentById(@PathVariable("id") Integerid) {String url = "http://localhost:9001/payment/" + id;List<ServiceInstance> serviceInstances =discoveryClient.getInstances("cloud-payment-service");ServiceInstance serviceInstance = serviceInstances.get(0);url = "http://" + serviceInstance.getHost() + ":" +serviceInstance.getPort() + "/payment/" + id;Payment payment = restTemplate.getForObject(url, Payment.class);return ResponseEntity.ok(payment);}
RestTemplate⽀持常⻅的Http协议请求⽅法,例如post, get, delete等,所以⽤RestTemplate很 容易构建RESTfule API。上述案例结果返回json对象,使⽤jackson框架完成。

2.2 LoadBalancer负载均衡

负载均衡是指将负载分摊到多个执⾏单元上,常⻅的**负载均衡有两种⽅式**。

  • ⼀种独⽴进程单元,通过负载均衡策略,将请求转发到不同的执⾏单元上,例如Nginx
  • 另⼀种是将负载均衡逻辑**以代码的形式封装到服务消费者的客户端上**,服务消费者客户端维护了⼀份服务提供者的信息列表,有了信息表, 通过负载均衡策略将请求分摊给多个服务提供者,从⽽达到负载均衡的⽬的。

SpringCloud原有的客户端负载均衡⽅案Ribbon已经被废弃,取⽽代之的是SpringCloud LoadBalancer,LoadBalancer是Spring Cloud Commons的⼀个⼦项⽬,他属于上述的第⼆种⽅式,是将负载均衡逻辑封装到客户端中,并且运⾏在客户端的进程⾥。
在Spring Cloud构件微服务系统中,LoadBalancer作为服务消费者的负载均衡器,有两种使⽤⽅ 式,⼀种是和RestTemplate相结合,另⼀种是和Feign相结合,Feign已经默认集成了LoadBalancer

2.2.1 LoadBlancerClient简介

负载均衡的核⼼类为LoadBalancerClient,LoadBalancerClient可以获取负载均衡的服务提供者实 例信息。在OrderController增加演示代码如下

@Autowiredprivate LoadBalancerClient loadBalancerClient;@GetMapping("/test-load-balancer")public String testLoadBalancer() {ServiceInstance instance = loadBalancerClient.choose("cloud-paymentservice");return instance.getHost() + ":" + instance.getPort();}

2.3 Spirng Cloud OpenFeign

Feign是⼀个声明式的HTTP客户端组件,它旨在是编写Http客户端变得更加容易。OpenFeign添加 了对于Spring MVC注解的⽀持,同时集成了Spring Cloud LoadBalancer和Spring Cloud CircuitBreaker,在使⽤Feign时,提供负载均衡和熔断降级的功能

OpenFeign集成了

  • 超时配置
  • 集成熔断器
  • 请求和响应压缩
  • 日志打印功能

2.3.1入门案例

1、添加依赖
在订单⼯程⼯程的pom.xml中添加如下依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
2、开启Feign功能 

使⽤@EnableFeignClients开启Feign功能

@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublic class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}}

3、 创建Feign客户端
在注解@FeignClient注解中,“cloud-payment-service”是服务名,使⽤这个名字来从Eureka服务 列表中得到相应的服务,来创建LoadBalancer客户端,也可以使⽤url属性,指定服务的URL。

@FeignClient(value = "cloud-payment-service")public interface PaymentClient {@GetMapping("/payment/{id}")public Payment payment(@PathVariable("id") Integer id);}

4、OrderController
在OrderController中使⽤FeignClient访问⽀付服务,代码如下。

@Autowiredprivate PaymentClient paymentClient;@GetMapping("/feign/payment/{id}")public ResponseEntity<Payment> getPaymentByFeign(@PathVariable("id") Integer id) {Payment payment = paymentClient.payment(id);return ResponseEntity.ok(payment);}

5、 启动并测试
分别启动⽀付服务9000端⼝,9001端⼝,订单服务,访问http://localhost:9002/order/feign/payment/123,执⾏效果如图3-4所示。

多次执⾏发现9000、9001,顺序显示。因此得知Feign集成了负载均衡LoadBalancer组件

2.3.2超时配置

通过分析上述案例的执⾏现象,得到结论OpenFeign集成了负载均衡组件LoadBalancer, OpenFeign提供了2个超时参数。

  • connectTimeout防⽌由于服务器处理时间⻓⽽阻塞调⽤者。
  • readTimeout 从连接建⽴时开始应⽤,在返回响应时间过⻓时触发。

对于所有的FeignClient配置,可以使⽤”default”假名,代码如下。

feign:client:config:default:connectTimeout: 5000 #防⽌由于服务器处理时间⻓⽽阻塞调⽤者readTimeout: 5000 #从连接建⽴时开始应⽤,在返回响应时间过⻓时触发
如果只对于具体FeignClient配置,可以把default换成具体的FeignClient的名字,代码如下
feign:client:config:feignName:connectTimeout: 5000 #防⽌由于服务器处理时间⻓⽽阻塞调⽤者readTimeout: 5000 #从连接建⽴时开始应⽤,在返回响应时间过⻓时触发

3、服务容错Reslilience4j

3.1 微服务容错简介

在⾼并发访问下,⽐如天猫双11,流量持续不断的涌⼊,服务之间的相互调⽤频率突然增加,引发 系统负载过⾼,这时系统所依赖的服务的稳定性对系统的影响⾮常⼤,⽽且还有很多不确定因素引起雪 崩,如⽹络连接中断,服务宕机等。⼀般微服务容错组件提供了限流、隔离、降级、熔断等⼿段,可以 有效保护我们的微服务系统。

3.1.1隔离

微服务系统A调⽤B,⽽B调⽤C,这时如果C出现故障,则此时调⽤B的⼤量线程资源阻塞,慢慢的 B的线程数量持续增加直到CPU耗尽到100%,整体微服务不可⽤,这时就需要对不可⽤的服务进⾏隔 离。服务调⽤关系

处理方法:

  1. 线程池隔离:

线程池隔离就是通过Java的线程池进⾏隔离,B服务调⽤C服务给予固定的线程数量⽐如12个线 程,如果此时C服务宕机了就算⼤量的请求过来,调⽤C服务的接⼝只会占⽤12个线程不会占⽤其他⼯作 线程资源,因此B服务就不会出现级联故障。

  1. 信号量隔离:

隔离信号量隔离是使⽤Semaphore来实现的,当拿不到信号量的时候直接拒接因此不会出现超时占 ⽤其他⼯作线程的情况

Semaphore semaphore = new Semaphore(10,true);//获取信号量semaphore.acquire();//do something here//释放信号量semaphore.release();

线程池隔离和信号量隔离的区别

  • 线程池隔离针对不同的资源分别创建不同的线程池,不同服务调⽤都发⽣在不同的线程池中,在线 程池排队、超时等阻塞情况时可以快速失败。
  • 线程池隔离的好处是隔离度⽐较⾼,可以针对某个资源的 线程池去进⾏处理⽽不影响其它资源,但是代价就是线程上下⽂切换的 overhead ⽐较⼤,特别是对低延时的调⽤有⽐较⼤的影响。
  • ⽽信号量隔离⾮常轻量级,仅限制对某个资源调⽤的并发数,⽽不是显式地去创建线程池,所以 overhead ⽐较⼩,但是效果不错,也⽀持超时失败。

3.1.2熔断

当下游的服务因为某种原因突然变得不可⽤或响应过慢,上游服务为了保证⾃⼰整体服务的可⽤ 性,不再继续调⽤⽬标服务,直接返回,快速释放资源。如果⽬标服务情况好转则恢复调⽤

熔断器模型的状态机有3个状态

  • Closed:关闭状态(断路器关闭),所有请求都正常访问。
  • Open:打开状态(断路器打开),所有请求都会被降级。熔断器会对请求情况计数,当⼀定时间内 失败请求百分⽐达到阈值,则触发熔断,断路器会完全打开。
  • Half Open:半开状态,不是永久的,断路器打开后会进⼊休眠时间。随后断路器会⾃动进⼊半开 状态。此时会释放部分请求通过,若这些请求都是健康的,则会关闭断路器,否则继续保持打开, 再次进⾏休眠计时

3.1.3降级

**_降级是指当⾃身服务压⼒增⼤时,系统将某些不重要的业务或接⼝的功能降低_**,可以只提供部分功 能,也可以完全停⽌所有不重要的功能。⽐如,下线⾮核⼼服务以保证核⼼服务的稳定、降低实时性、 降低数据⼀致性,**_降级的思想是丢⻋保帅。 _**

举个例⼦,⽐如,⽬前很多⼈想要下订单,但是我的服务器除了处理下订单业务之外,还有⼀些其 他的服务在运⾏,⽐如,搜索、定时任务、⽀付、商品详情、⽇志等等服务。然⽽这些不重要的服务占 ⽤了JVM的不少内存和CPU资源,为了应对很多⼈要下订单的需求,设计了⼀个动态开关,把这些不重 要的服务直接在最外层拒绝掉。这样就有跟多的资源来处理下订单服务(下订单速度更快了)

3.1.4限流

限流,就是限制最⼤流量。系统能提供的最⼤并发有限,同时来的请求⼜太多,就需要限流,⽐如 商城秒杀业务,瞬时⼤量请求涌⼊,服务器服务不过来,就只好排队限流了,就跟去景点排队买票和去 银⾏办理业务排队等号道理相同。下⾯介绍下四种常⻅的限流算法。

  1. 漏桶算法

漏桶算法的思路,⼀个固定容量的漏桶,按照常量固定速率流出⽔滴。如果桶是空的,则不需流出 ⽔滴。可以以任意速率流⼊⽔滴到漏桶。如果流⼊⽔滴超出了桶的容量,则流⼊的⽔滴溢出了(被丢 弃),⽽漏桶容量是不变的。

  1. 令牌算法

令牌桶算法:假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌。桶中最多存放b个令牌, 当桶满时,新添加的令牌被丢弃或拒绝。当⼀个n个字节⼤⼩的数据包到达,将从桶中删除n个令牌,接 4.1.4 限流 7 着数据包被发送到⽹络上。如果桶中的令牌不⾜n个,则不会删除令牌,且该数据包将被限流(要么丢 弃,要么缓冲区等待)。

令牌桶限流服务器端可以根据实际服务性能和时间段改变⽣成令牌的速度和⽔桶的容量。 ⼀旦需要 提⾼速率,则按需提⾼放⼊桶中的令牌的速率。
⽣成令牌的速度是恒定的,⽽请求去拿令牌是没有速度限制的。这意味着当⾯对瞬时⼤流量,该算 法可以在短时间内请求拿到⼤量令牌,⽽且拿令牌的过程并不是消耗很⼤。

  1. 固定时间窗⼝算法

在固定的时间窗⼝内,可以允许固定数量的请求进⼊。超过数量就拒绝或者排队,等下⼀个时间段 进⼊。这种实现计数器限流⽅式由于是在⼀个时间间隔内进⾏限制,如果⽤户在上个时间间隔结束前请 求(但没有超过限制),同时在当前时间间隔刚开始请求(同样没超过限制),在各⾃的时间间隔内, 这些请求都是正常的,但是将间隔临界的⼀段时间内的请求就会超过系统限制,可能导致系统被压垮。

由于计数器算法存在时间临界点缺陷,因此在时间临界点左右的极短时间段内容易遭到攻击。⽐如 设定每分钟最多可以请求100次某个接⼝,如12:00:00-12:00:59时间段内没有数据请求,⽽12:00:59- 12:01:00时间段内突然并发100次请求,⽽紧接着跨⼊下⼀个计数周期,计数器清零,在12:01:00- 12:01:01内⼜有100次请求。那么也就是说在时间临界点左右可能同时有2倍的阀值进⾏请求,从⽽造成 后台处理请求过载的情况,导致系统运营能⼒不⾜,甚⾄导致系统崩溃。

  1. 滑动时间窗⼝算法

滑动窗⼝算法是把固定时间⽚进⾏划分,并且随着时间移动,移动⽅式为开始时间点变为时间列表 中的第⼆时间点,结束时间点增加⼀个时间点,不断重复,通过这种⽅式可以巧妙的避开计数器的临界 点的问题。
滑动窗⼝算法可以有效的规避计数器算法中时间临界点的问题,但是仍然存在时间⽚段的概念。同 时滑动窗⼝算法计数运算也相对固定时间窗⼝算法⽐较耗时。滑动时间窗⼝算法,

3.2 Resilience4j简介

3.2.1 Resilience4j简介

Netflix的Hystrix微服务容错库已经停⽌更新,官⽅推荐使⽤Resilience4j代替Hystrix,或者使⽤ Spring Cloud Alibaba的Sentinel组件。
Resilience4j是受到Netflix Hystrix的启发,为Java8和函数式编程所设计的轻量级容错框 架。整个框架只是使⽤了Varr的库,不需要引⼊其他的外部依赖。与此相⽐,Netflix Hystrix对 Archaius具有编译依赖,⽽Archaius需要更多的外部依赖,例如Guava和Apache Commons Configuration。
Resilience4j提供了提供了⼀组⾼阶函数(装饰器),包括断路器,限流器,重试机制,隔离 机制。你可以使⽤其中的⼀个或多个装饰器对函数式接⼝,lambda表达式或⽅法引⽤进⾏装饰。 这么做的优点是你可以选择所需要的装饰器进⾏装饰
在使⽤Resilience4j的过程中,不需要引⼊所有的依赖,只引⼊需要的依赖即可。 核⼼模块

  • resilience4j-circuitbreaker: 熔断
  • resilience4j-ratelimiter: 限流
  • resilience4j-bulkhead: 隔离
  • resilience4j-retry: ⾃动重试
  • resilience4j-cache: 结果缓存
  • resilience4j-timelimiter: 超时处理

3.2.2 Resilience4j和Hystrix的异同

  • Hystrix使⽤HystrixCommand来调⽤外部的系统,⽽R4j提供了⼀些⾼阶函数,例如断路器、 限流器、隔离机制等,这些函数作为装饰器对函数式接⼝、lambda表达式、函数引⽤进⾏装 饰。此外,R4j库还提供了失败重试和缓存调⽤结果的装饰器。你可以在函数式接⼝、 lambda表达式、函数引⽤上叠加地使⽤⼀个或多个装饰器,这意味着隔离机制、限流器、重 试机制等能够进⾏组合使⽤。这么做的优点在于,你可以根据需要选择特定的装饰器。任何 被装饰的⽅法都可以同步或异步执⾏,异步执⾏可以采⽤ CompletableFuture 或RxJava。
  • 当有很多超过规定响应时间的请求时,在远程系统没有响应和引发异常之前,断路器将会开 启。
  • 当Hystrix处于半开状态时,Hystrix根据只执⾏⼀次请求的结果来决定是否关闭断路器。⽽ R4j允许执⾏可配置次数的请求,将请求的结果和配置的阈值进⾏⽐较来决定是否关闭断路 器。
  • R4j提供了⾃定义的Reactor和Rx Java操作符对断路器、隔离机制、限流器中任何的反应式类 型进⾏装饰。
  • Hystrix和R4j都发出⼀个事件流,系统可以对发出的事件进⾏监听,得到相关的执⾏结果和延 迟的时间统计数据都是⼗分有⽤的。

4、服务网关 spring cloud Gateway

4.1微服务网关简介

当然,因为是REST API接⼝,外部客户端直接调⽤各个微服务是没有问题的。但出于种种原因,这 并不是⼀个好的选择。让客户端直接与各个微服务通讯,会存在以下⼏个问题。

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性。
  2. 存在跨域请求,在⼀定场景下处理会变得相对⽐较复杂。
  3. 实现认证复杂,每个微服务都需要独⽴认证。
  4. 难以重构,项⽬迭代可能导致微服务重新划分。如果客户端直接与微服务通讯,那么重构将会很难 实施。
  5. 如果某些微服务使⽤了防⽕墙、浏览器不友好的协议,直接访问会有⼀定困难。

⾯对类似上⾯的问题,我们要如何解决呢?答案就是:服务⽹关!在微服务系统中微服务资源⼀般 不直接暴露给我外部客户端访问,这样做的好处是将内部服务隐藏起来,从⽽解决上述问题。 ⽹关有很多重要的意义,具体体现在下⾯⼏个⽅⾯

  • ⽹关可以做⼀些身份认证、权限管理、防⽌⾮法请求操作服务等,对服务起⼀定保护作⽤。

  • ⽹关将所有微服务统⼀管理,对外统⼀暴露,外界系统不需要知道微服务架构个服务相互调⽤的复 杂性,同时也避免了内部服务⼀些敏感信息泄露问题。

  • 易于监控。可在微服务⽹关收集监控数据并将其推送到外部系统进⾏分析。

  • 客户端只跟服务⽹关打交道,减少了客户端与各个微服务之间的交互次数。

  • 多渠道⽀持,可以根据不同客户端(WEB端、移动端、桌⾯端…)提供不同的API服务⽹关。

  • ⽹关可以⽤来做流量监控。在⾼并发下,对服务限流、降级。

  • ⽹关把服务从内部分离出来,⽅便测试。

     微服务⽹关能够实现,**_路由、负载均衡等多种功能_**。类似Nginx,反向代理的功能。在微服务架构 中,后端服务往往不直接开放给调⽤端,⽽是_**通过⼀个API⽹关根据请求的URL,路由到相应的服务**_。 当添加API⽹关后,在第三⽅调⽤端和服务提供⽅之间就创建了⼀⾯墙,在API⽹关中进⾏权限控制,同 时API⽹关将请求以负载均衡的⽅式发送给后端服务。 

4.2 Spring Cloud Gateway简介

4.2.1 简介

SpringCloud Gateway 是 Spring Cloud 的⼀个全新项⽬,该项⽬是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的⽹关,它旨在为微服务架构提供⼀种简单有效的统⼀的 API 路由管理⽅式。
SpringCloud Gateway 作为 Spring Cloud ⽣态系统中的⽹关,⽬标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新⾼性能版本进⾏集成,仍然还是使⽤的Zuul 2.0之前的⾮Reactor模式的⽼版本。⽽为了提升⽹关的性能,SpringCloud Gateway是基于WebFlux框 架实现的,⽽WebFlux框架底层则使⽤了⾼性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的⽬标,**不仅提供统⼀的路由⽅式,并且基于 Filter 链的⽅式提供了⽹关 基本的功能,例如:安全,监控/指标,和限流。 **
注意:Spring Cloud Gateway 底层使⽤了⾼性能的通信框架Netty。

4.2.2 特征

SpringCloud官⽅,对SpringCloud Gateway 特征介绍如下:

  1. 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  2. 集成 Spring Cloud DiscoveryClient
  3. Predicates 和 Filters 作⽤于特定路由,易于编写的 Predicates 和 Filters
  4. 具备⼀些⽹关的⾼级功能:动态路由、限流、路径重写
  5. 集成Spring Cloud DiscoveryClient
  6. 集成熔断器CircuitBreaker

从以上的特征来说,和Zuul的特征差别不⼤。SpringCloud Gateway和Zuul主要的区别,还是在底 层的通信框架上。 简单说明⼀下上⽂中的三个术语:
(1)Filter(过滤器):
和Zuul的过滤器在概念上类似,可以使⽤它拦截和修改请求,并且对下游的响应,进⾏⼆次处理。 过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
(2)Route(路由):
⽹关配置的基本组成模块,和Zuul的路由配置模块类似。⼀个Route模块由⼀个 ID,⼀个⽬标 URI,⼀组断⾔和⼀组过滤器定义。如果断⾔为真,则路由匹配,⽬标URI会被访问。
(3)Predicate(断⾔):
这是⼀个 Java 8 的 Predicate,可以使⽤它来匹配来⾃ HTTP 请求的任何内容,例如 headers 或 参数。断⾔的输⼊类型是⼀个 ServerWebExchange。

5、配置管理 spring cloud Config

5.1springcloudconfig简介

配置⽂件想必⼤家都不陌⽣。在Spring Boot项⽬中,默认会提供⼀个application.properties或者 application.yml⽂件,我们可以把⼀些全局性的配置或者需要动态维护的配置写⼊改⽂件,不如数据库 连接,功能开关,限流阈值,服务地址等。为了解决不同环境下服务连接配置等信息的差异,Spring Boot还提供了基于spring.profiles.active={profile}的机制来实现不同的环境的切换。
随着单体架构向微服务架构的演进,各个应⽤⾃⼰独⽴维护本地配置⽂件的⽅式开始显露出它的不 ⾜之处。主要有下⾯⼏点。

  • 配置的动态更新:在实际应⽤会有动态更新位置的需求,⽐如修改服务连接地址、限流配置等。在 传统模式下,需要⼿动修改配置⽂件并且重启应⽤才能⽣效,这种⽅式效率太低,重启也会导致服 务暂时不可⽤。
  • 配置多节点维护:在微服务架构中某些核⼼服务为了保证⾼性能会部署上百个节点,如果在每个节 点中都维护⼀个配置⽂件,⼀旦配置⽂件中的某个属性需要修改,可想⽽知,⼯作量是巨⼤的。
  • 不同部署环境下配置的管理:前⾯提到通过profile机制来管理不同环境下的配置,这种⽅式对于⽇ 常维护来说也⽐较繁琐。

统⼀配置管理就是弥补上述不⾜的⽅法,简单说,最近本的⽅法是把各个应⽤系统中的某些配置放 在⼀个第三⽅中间件上进⾏统⼀维护。然后,对于统⼀配置中⼼上的数据的变更需要推送到相应的服务节点实现动态跟新,所以微服务架构中,配置中⼼也是⼀个核⼼组件,⽽Spring Cloud Config就是⼀ 个配置中⼼组件,并且可以Git,SVN,本地⽂件等作为存储。

5.2配置自动刷新

Spring Cloud Config在项⽬启动时⾃动加载配置内容这⼀机制,导致了他的⼀个缺陷,配置不能⾃ 动刷新,在上述案例中,修改git仓库中的key1的值”key1=v11″,发现⽀付服务得到的配置项key1的值还 是旧的配置内容,新的内容不会⾃动刷新过来,在微服务架构中,动辄上百个节点如果都需要重启,这 个问题⾮常麻烦。
我们可以使⽤Spring Cloud Bus和Spring Boot Actuator实现⾃动刷新

6、链路追踪 spring cloud Sleuth

6.1 链路追踪简介

6.1.1 为什么需要链路追踪

微服务架构是⼀个分布式架构,它按业务划分服务单元,⼀个分布式系统往往有很多个服务单元。 由于服务单元数量众多,业务的复杂性,**_如果出现了错误和异常,很难去定位_**。主要体现在,⼀个请求 可能需要调⽤很多个服务,⽽内部服务的调⽤复杂性,决定了问题难以定位。所以微服务架构中,必须 实现分布式链路追踪,去跟进⼀个请求到底有哪些服务参与,参与的顺序⼜是怎样的,从⽽达到每个请 求的步骤清晰可⻅,出了问题,很快定位。 ⽬前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿⾥的Eagleeye (鹰眼)等,它们 都是⾮常优秀的链路追踪开源组件。

6.1.2基本术语

Spring Cloud Sleuth采⽤的是Google的开源项⽬Dapper的专业术语

  • Span:基本⼯作单元,发送⼀个远程调度任务 就会产⽣⼀个Span,Span有⼀个64位ID唯⼀标识 的,Trace是⽤另⼀个64位ID唯⼀标识的,Span还有其他数据信息,⽐如摘要、时间戳事件、Span 的ID、以及进度ID。
  • Trace:⼀系列Span组成的⼀个树状结构。请求⼀个微服务系统的API接⼝,这个API接⼝,需要调 ⽤多个微服务,调⽤每个微服务都会产⽣⼀个新的Span,所有由这个请求产⽣的Span组成了这个 Trace。
  • Annotation:⽤来及时记录⼀个事件的,⼀些核⼼注解⽤来定义⼀个请求的开始和结束 。这些注解 包括以下:
    • cs – Client Sent -客户端发送⼀个请求,这个注解描述了这个Span的开始
    • sr – Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到 ⽹络传输的时间。
    • ss – Server Sent (服务端发送响应)–该注解表明请求处理的完成(当请求返回客户端),如果 ss的时间戳减去sr时间戳,就可以得到服务器请求的时间。
    • cr – Client Received (客户端接收响应)-此时Span的结束,如果cr的时间戳减去cs时间戳 便可以得到整个请求所消耗的时间。