(一)Spring Cloud核心组件:Hystrix
在微服务架构里,一个系统会有很多的服务。
以上面的业务场景为例:订单服务在一个业务流程里需要调用三个服务。
现在假设订单服务自己最多只有 100 个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服
务调用积分服务的时候,都会卡住几秒钟,然后抛出 — 个超时异常 出现问题: 微服务架构中的服务雪崩问题
如果系统处于高并发的场景下,大量请求涌过来的时候,订单服务的 100 个线程都会卡在请求积分
服务这块。导致订单服务没有一个线程可以处理请求
然后就会导致别人请求订单服务的时候,发现订单服务也挂了,不响应任何请求了
Hystrix 是隔离、熔断以及降级的一个框架。
比如订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。
每个线程池里的线程就仅仅用于请求那个服务。
积分服务挂了
会导致订单服务里的那个用来调用积分服务的线程都卡死不能工作!
但是由于订单服务调用库存服务、仓储服务的这两个线程池都是正常工作的,所以这两个服务不会受到
任何影响。
这个时候如果别人请求订单服务,订单服务还是可以正常调用库存服务扣减库存,调用仓储服务通知发
货。
只不过调用积分服务的时候,每次都会报错。但是如果积分服务都挂了,每次调用都要去卡住几秒钟!
所以我们直接对积分服务熔断不就得了,比如在 5 分钟内请求积分服务直接就返回了,不要去走网络请求
卡住几秒钟,这个过程,就是所谓的熔断!
那人家又说,兄弟,积分服务挂了你就熔断,好歹你干点儿什么啊!别啥都不干就直接返回啊?
没问题,咱们就来个降级:
每次调用积分服务,你就在数据库里记录一条消息,说给某某用户增加了多少积分,因为积分服务挂
了,导致没增加成功!
这样等积分服务恢复了,你可以根据这些记录手工加一下积分。这个过程,就是所谓的降级。
(二)问题分析
主要问题:
限流问题:市民在长时间无法刷出健康码的情况下,多次退出刷新重试,新的流量到达服务器,导
致服务器压力变大、承受负载增加,说明 “ 西安一码通 ” 系统没有做好限流措施。
服务器问题:无论是企业和个人在租用服务器的时候都会受到峰值承受限制的,一旦超过服务器的
承受能力,就会导致服务器瘫痪,应用程序暂停,网站无法访问。服务器是有峰值限制的,不可能
承受无上限的并发能力。而造成服务器瘫痪的原因就是在同一段时间内,访问人数多,造成高流量
的突进,超出了服务器的承受范围。
架构问题: “ 西安一码通 ” 功能影响 “ 核酸检测 ” 服务,说明模块间从界面到数据调用互相影响,可能
不是微服务架构。
性能过载:典型的性能过载场景,不论内部根因是数据库瓶颈点,还是网络链接数瓶颈点等等,外
因都是因为过载导致。
设计漏洞:没有考虑高流量高负载的情况,导致测试不充分;产品设计未考虑千万级的并发访问,
交付前未进行同等级的压力测试。
压力测试:在市民长时间无法看到健康码的情况下,多次退出刷新重试,新的流量到达服务器,导
致服务器压力变大、承受负载增加。说明压力测试不够。
限流
限流的目的是通过对并发访问 / 请求进行限速或者对一个时间窗口内的请求进行限速来保护系统,一旦达
到限制速率则可由拒绝服务,就是定向到错误页或友好的展示页,排队或等待。
限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击。在高并发的应用中,限流是
一个绕不开的话题。
令牌桶算法
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶
里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
QPS
每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。
提高带宽
(三)Gateway限流的实现
1.需要把版本改为2.3.7
Spring Cloud Gateway 官方提供了 RequestRateLimiterGatewayFilterFactory 类,使用 redis 和 lua 脚本
来实现令牌桶的方式。
2.导入redis的依赖
Gateway 通过内置的 RequestRateLimiter 过滤器实现限流,使用令牌桶算法,借助 Redis 保存中间数
据。用户可通过自定义 KeyResolver 设置限流维度。
对请求的目标 URL 进行限流
对来源 IP 进行限流
特定用户进行限流
3.添加redis配置
注:记得开启你的redis
如果redis连接失败,限流功能将不能开启。因为没有redis作为容器来保存令牌,限流功能自然就失效 了。
可以将redis的配置信息保存到nacos中,通过添加nacos配置中心客户端的方式进行读取
4.导入请求限流的配置类
package com.gateway.code;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
/**
* 请求限流配置
*/
@SuppressWarnings("all")
@Configuration
public class RequestRateLimiterConfig {
/**
* 按IP来限流
*/
@Bean
@Primary
public KeyResolver ipAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
/**
* 按用户限流
*/
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
/**
* 按URL限流,即以每秒内请求数按URL分组统计,超出限流的url请求都将返回429状态
*
* @return
*/
@Bean
KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
}
}
5.添加限流的配置
注:在添加限流配置之前把前面写的自定义配置类关掉(把
DynamicRoutingConfig类中的@Component注解注释掉即可)
运行效果如下:
(四)JMeter压力测试
1.把apache-jmeter-5.2.1.taz解压
2.进入文件夹点击jmeter.bat直接运行即可
3.点击Options->Choose Language ->Chinese 切换为中文
4.点击添加线程
5.点击配置HTTP请求
6.点击添加监听器用表格查看结果
7.开始进行JMeter压力测试
(五)熔断
在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用,
后端服务不可避免的会产生调用失败(超时或者异常),失败时不能让请求堆积在网关上,需要快速失
败并返回回去,
这就需要在网关上做熔断、降级操作。
1.导入依赖
2.导入yml配置
3.编写TestController进行熔断测试
package com.gateway.code;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class TestController {
@RequestMapping("/fallback")
public Object fallback(){
Map<String,Object> map =new HashMap<>();
map.put("code",204);
map.put("msg","服务已经降级了");
return map;
}
}