目录
二、SpringCloud02
2.1 nacos集群的搭建
在实际开发过程中,如果使用Nacos的话,为了确保高可用,我们一般都会对其进行集群的部署。Nacos规定集群中Nacos节点的数量需要大于等于3个;同时,单机模式下Nacos的数据默认保存在其内嵌数据库中derby,不方便观察数据存储的基本情况。
而且如果集群中启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储;此外,我们还需要借助Nginx实现负载均衡。这一过程的部署架构图如下所示:
在搭建集群之前,可以先将nacos文件夹复制一份,放在同一个目录下,原本的nacos文件留作单机模式使用
修改时,修改nacos8849文件夹中的文件
2.1.1 创建数据库
默认每个nacos都内置了一个derby数据库,如果使用内置的数据库,则nacos集群之间无法共享数据
/nacos8849/conf/nacos-mysql.sql
在mysql中建立一个数据库,我这里将该数据库命名为nacos
将上面的sql文件拉到nacos数据库中,建表
2.1.2 指定mysql作为数据存储
进入到/nacos8849/conf/application.properties文件中
2.1.3 修改启动文件为集群模式
进入nocas8849/bin/startup.cmd文件,
将模式改为集群模式,大概在第26行 set MODE="cluster"
2.1.4 配置集群文件
进入nacos8849/conf
将文件 cluater.conf.example 名字更改为 cluater.conf
然后编辑此文件
2.1.5 复制多个nacos
先停止nacos,将文件赋值三份,并重命名
nacos8849,nacos8850,nacos8851
2.1.6 修改nacos端口号
修改这几个nacos集群的端口号
进入每一个nacos文件夹的 配置文件/conf/application.properties
修改第21行的端口号
2.1.7 nacos集群测试
启动这三个nacos服务,进行集群测试
访问其中任意一个nacos服务,查看集群列表
2.1.8 注册服务到nacos集群
修改服务中的配置文件
#配置nacos
spring.cloud.nacos.discovery.server-addr=localhost:8849,localhost:8850,localhost:8851
spring.application.name=springcloud-order
spring.cloud.nacos.discovery.group=qy160-spring-cloud
启动项目
访问8849的nacos服务,查看服务列表,是否有对应的服务
nacos集群一般都是搭建为奇数个,因为当集群中的节点down了半数的时候,就会认为这个集群有问题
这里有一个弊端,就是当集群数量过多时,我们不能把每一个集群节点的端口号都排列出来,那样就太过于麻烦了,但这里只用于测试还是这么做的。
这个问题可以使用nginx解决
2.1.9 使用nginx解决问题
使用nginx解决上述问题
1、修改nginx.conf配置文件
进入到nginx-1.8.0\conf\nginx.conf文件中
#配置要被代理的ip
upstream qy160 {
server 192.168.0.112:8849;
server 192.168.0.112:8850;
server 192.168.0.112:8851;
}
server {
listen 81; #要访问的反向代理端口号
server_name localhost; #配置IP地址
location / {
proxy_pass http://qy160; #要反向代理的ip
}
}
2、启动nginx
双击nginx.exe文件启动nginx
黑窗口会一闪而过,想要看是否启动成功,在资源管理器的详细信息中,看到两个正在运行的nginx.exe文件就是成功
3、访问测试
上面nginx文件中配置的代理端口号为81
所以这里访问localhost:81/nacos,查看是否能够正常进入,可以进入,就可以看到正常的集群搭建
4、修改服务中心配置文件的代码
#配置nacos
spring.cloud.nacos.discovery.server-addr=localhost:81
spring.application.name=springcloud-order
spring.cloud.nacos.discovery.group=qy160-spring-cloud
这里直接将addr改为代理点的端口号81
然后重新启动服务,再次进行访问,依旧可以出现上面这张图片2.2
2.2 网关
2.2.1 为什么使用网关
l 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
l 认证复杂,每个服务都需要独立认证。
l 存在跨域请求,在一定场景下处理相对复杂。
所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服 务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控(黑白名单)、路由转发等等。
2.2.2 网关组件
l Ngnix+lua
使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用lua是一种脚本语言,可以来编写一些简单的逻辑, nginx支持lua脚本
l Kong
基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。
问题: 只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。
l Zuul 1.0(慢 servlet 2.0 ) zuul2.0 没出来。
Netflix开源的网关,功能丰富,使用JAVA开发,易于二次开发
问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx
----------------l Spring Cloud Gateway -----------------
Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。
2.2.3 GateWay简介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控,限流,黑白名单和敏感词。
优点:
l 性能强劲:是第一代网关Zuul的1.6倍
l 功能强大:内置了很多实用的功能,例如转发、监控、限流等
l 设计优雅,容易扩展.
缺点:
l 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
l 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行 web.Jar
l 需要Spring Boot 2.0及以上的版本,才支持.
因为gateway它内置的服务器Netty,所以不能在使用Tomcat服务器。
2.2.4 如何使用gateway网关
gateway本身也是一个服务,该服务的主要作用,转发,监控,限流等
1、创建一个子模块当做网关服务
子模块名称为springcloud-gateway
2、引入依赖
<dependencies>
<!--
引入gateway的依赖之后就不能再引用spring-boot-starter-web的依赖
因为web依赖中存在tomcat服务器
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
2、配置文件
#端口号
server:
port: 82
#服务名称
spring:
application:
name: springcloud-gateway
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: http://localhost:8080 #真实路由转发的地址
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
- id: springcloud-order
uri: http://localhost:8090
predicates:
- Path=/order/**
这里有一个可以将yml文件格式转换为prop文件格式的工具
转换后
server.port=82
spring.application.name=springcloud-gateway
spring.cloud.gateway.routes[0].id=springcloud-prduct
spring.cloud.gateway.routes[0].uri=http://localhost:8080
spring.cloud.gateway.routes[0].predicates[0]=Path=/product/**
spring.cloud.gateway.routes[1].id=springcloud-order
spring.cloud.gateway.routes[1].uri=http://localhost:8090
spring.cloud.gateway.routes[1].predicates[0]=Path=/order/**
路由转发规则的参数
private String id;
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList();
@Valid
private List<FilterDefinition> filters = new ArrayList();
@NotNull
private URI uri;
private Map<String, Object> metadata = new HashMap();
private int order = 0;
3、启动类
@SpringBootApplication
public class GatewayApp {
public static void main(String[] args) {
SpringApplication.run(GatewayApp.class,args);
}
}
4、启动项目进行测试
1、测试商品微服务
访问网关的路径:http://localhost:82/product/getProductById/2
成功结果:
{"pid":2,"pname":"vivo手机","pprice":2999.0,"stock":"100"}2、测试订单微服务
访问网关的路径:http://localhost:82/order/createOrder/1/10
成功结果:
{"oid":37,"uid":2,"username":"lwl","pid":1,"pname":"华为手机","pprice":3999.22,"number":10}
注:在这里进行访问测试时,可能会有404的错误,这里出现404错误可能有两种情况
1、断言错误[断言处写错]
2、访问路径错误[访问路径写错]
2.2.5 使用网关存在的问题
思考: 对于上面的网关配置
1.我们把路由的地址写出死代码。如果路由的服务发生改变。我们就得需要修改网关的配置
2.如果路由的服务是一个集群,这里也无法配置
3.如果后期增加很多微服务,则需要在网关这都要配置,造成泛滥
解决: 我们的网关也是一个微服务,那么该网关也可以注册拉取服务从注册中心。
对于问题一
1、在网关微服务中引入nacos注册中心依赖
<!--引入nacos注册中心服务--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
2、修改网关配置
#端口号 server: port: 82 #服务名称 spring: application: name: springcloud-gateway #路由转发规则 cloud: gateway: routes: - id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成 uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名 predicates: - Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址 - id: springcloud-order uri: lb://springcloud-order predicates: - Path=/order/** #指定注册中心的地址 nacos: discovery: server-addr: localhost:8848
注:这里在使用网关从注册中心拉取服务时,网关微服务只能拉取同组的微服务,如果不是同一组的微服务不能进行拉取
举例:
假如订单微服务规定了组别
spring.cloud.nacos.discovery.group=qy160-spring-cloud
而网关微服务使用的是默认组别,那么网关微服务就不能拉取订单微服务
2.2.6 网关自动定位
这个技术是为了解决网关问题二
这里使用的application.properties文件
server.port=82
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.application.name=springcloud-gateway
#开启自动定位功能
spring.cloud.gateway.discovery.locator.enabled=true
启动项目,如果报错Connection refused: connect 那么多半是nacos服务没有开启
如果nacos黑窗口卡了,Ctrl+c终止,他会自动向下运行
访问时:
路径中要加上服务名【springcloud-product】
http://localhost:82/springcloud-product/product/getProductById/2
2.2.7 gateway简介
路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:
l id,路由标识符,区别于其他 Route。默认生成一个
l uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
l order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
l predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
l filter,过滤器用于修改请求和响应信息。在到达真正路由微服务前经过。
2.7.1 过滤器举例
在哪个微服务中添加,哪个服务才可以使用
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
filters:
- SetStatus=250 #状态后置过滤器,将状态设置为250
2.7.2 断言
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配体如下:
l 基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai] 只有时间在此之后的请求可以通过
l 基于远程地址的断言工厂RemoteAddrRoutePredicateFactory:
接收一个IP地址段,判断请求主机地址是否在地址段中
-RemoteAddr=192.168.1.1/24 只有该ip可以访问
l 基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.
l 基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+ 只有请求头中有key值为X-Request-Id并且value值满足正则表达式的请求可以通过
l 基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
l 基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
l 基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment} 基于Query请求参数的断言工厂,请求满足/foo/QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
-Query=baz, ba.
l 基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes: -id: weight_route1 uri: host1 predicates: -Path=/product/** -Weight=group3, 1 -id: weight_route2 uri: host2 predicates: -Path=/product/** -Weight= group3, 9
1、时间类型
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
- After=2050-12-31T23:59:59.789+08:00[Asia/Shanghai] #在此时间之前无法访问该服务
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
- Between=2023-03-13T23:59:59.789+08:00[Asia/Shanghai],2023-03-14T23:59:59.789+08:00[Asia/Shanghai] #在此之间可以访问
2、必须携带请求头
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
- Header=token, \d+ #必须携带key为token,value为数字的请求头
2.2.8 gateway自定义全局过滤器
鉴权过滤器:判断当前用户是否登录
1、添加依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.12</version>
</dependency>
2、返回统一对象
package com.aaa.gateway.vo;
import lombok.Data;
@Data
public class Result<T> {
private Integer code=200;
private String msg="操作成功!";
private T t;
public Result() {
}
public Result(T t) {
this.t = t;
}
public Result(Integer code, String msg, T t) {
this.code = code;
this.msg = msg;
this.t = t;
}
}
3、编写登录过滤器
package com.aaa.gateway.filter;
import com.aaa.gateway.vo.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* GlobalFilter:全局过滤器
* Ordered:用于服务排序,优先级
*/
@Component
public class LoginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//1、获取请求路径
RequestPath path = request.getPath();
//判断请求是否为登录路径
if ("/login".equals(path)){
return chain.filter(exchange);
}
//2、获取token,因为请求头中可能有多个请求
String token = request.getHeaders().getFirst("token");
if (StringUtils.hasText(token) && "admin".equals(token)){
return chain.filter(exchange);
}
//3、封装返回数据
Result<String> result = new Result<>(401,"请先登录",null);
//将数据转为json格式
byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bytes);
//4.调用Mono中的just方法,返回要写给前端的JSON数据
return response.writeWith(Mono.just(buffer));
}
//值越大,优先级越高
@Override
public int getOrder() {
return 0;
}
}
4、测试
2.2.9 全局跨域配置
#服务名称
spring:
application:
name: springcloud-gateway
#路由转发规则
cloud:
gateway:
routes:
- id: springcloud-prduct #路由标识,只要唯一即可,如果没有设置默认按照UUID生成
uri: lb://springcloud-product #真实路由转发的地址 lb[负载均衡策略]://服务名
predicates:
- Path=/product/** #断言:只有符合断言的请求才会转发到响应的uri地址
# - Header=token, \d+ #必须携带key为token,value为数字的请求头
- id: springcloud-order
uri: lb://springcloud-order
predicates:
- Path=/order/**
#设置跨域配置
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]': # 拦截的请求
allowedOrigins: # 允许跨域的请求
- "http://localhost:8080"
allowedMethods: # 允许跨域的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许请求中携带的头信息
allowedCredentials: true #是否允许带cookies,Properties中key值为allowCredentials
maxAge: 36000 #跨域检测的有效期,不写就代表是无限日期
2.3 分布式链路追踪
2.3.1 链路追踪介绍
在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心【区域】,也就意味着这种架构形式也会存在一些问题:
l 如何快速发现问题?
l 如何判断故障影响范围?
l 如何梳理服务依赖?
分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上IP、每个服务节点的请求状态200 500等等。
常见的链路追踪技术有下面这些:
cat 由大众点评开源,基于Java开发的实时应用监控平台,包括实时应用监控,业务监控 。 集成方案是通过代码埋点的方式来实现监控,比如: 拦截器,过滤器等。 对代码的侵入性很大,集成成本较高。风险较大。
pinpoint Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能强大,接入端无代码侵入。 【很牛 --但是国内很少用】
skywalking 【未来企业会使用的多】 SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入端无代码侵入。目前已加入Apache孵化器。--[未来企业会很多使用]
Sleuth (日志记录每一条链路上的所有节点,以及这些节点所在的机器,和耗时。)log4jSpringCloud 提供的分布式系统中链路追踪解决方案。【打印每个节点的信息到日志文件中】
zipkin 由Twitter公司开源,开放源代码分布式的跟踪系统,用于==收集服务的定时数据==,以解决微服务架构中的延迟问题,包括:数据的收集、存储、查找和展现《图形化》。该产品结合spring-cloud-sleuth 使用较为简单, 集成很方便, 但是功能较简单。
注意:
SpringCloud alibaba技术栈中并没有提供自己的链路追踪技术的,我们可以采用Sleuth+Zipkin来做链路追踪解决方案。 sleuth+zipkin===产生分布式日志+收集分布式日志并以图形化展示
2.3.2 seluth入门
SpringCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。它大量借用了Google Dapper的设计, 先来了解一下Sleuth中的术语和相关概念。
Trace(一条完整链路--包含很多span(微服务接口))
由一组Trace Id(贯穿整个链路)相同的Span串联形成一个树状结构。为了实现请求跟踪,当请求到达分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的标识(即TraceId),同时在分布式系统内部流转的时候,框架始终保持传递该唯一值,直到整个请求的返回。那么我们就可以使用该唯一标识将所有的请求串联起来,形成一条完整的请求链路。
Span
代表了一组基本的工作单元。为了统计各处理单元的延迟,当请求到达各个服务组件的时候,也通过一个唯一标识(SpanId)来标记它的开始、具体过程和结束。通过SpanId的开始和结束时间戳,就能统计该span的调用时间,除此之外,我们还可以获取如事件的名称。请求信息等元数据。
Annotation
用它记录一段时间内的事件,内部使用的重要注释:
l cs(Client Send)客户端发出请求,开始一个请求的命令
l sr(Server Received)服务端接受到请求开始进行处理, sr-cs = 网络延迟(服务调用的时间)
l ss(Server Send)服务端处理完毕准备发送到客户端,ss - sr = 服务器上的请求处理时间
l cr(Client Reveived)客户端接受到服务端的响应,请求结束。 cr - cs = 请求的总时间
2.3.3 sleuth使用
1、微服务接入sleuth
<!--父工程中添加sleuth依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
</dependencies>
2、测试使用
访问:http://localhost:82/product/getProductById/1
在网关微服务和商品微服务里面就会出现链路信息
测试弊端:
现在项目中有一个地方耗时比较长,我们想快速定位耗时较多的方法和接口,该怎么办
我们可以通过sleuth产生的日志,通过人为的方式来查出哪个微服务的执行时间比较长。 但是我们未来服务是非常多的,如果一一查看日志,是非常麻烦的一件事。 要是有一个图形化界面查看界面更方便。--zipkin 收集日志并以图形化产生,
2.3.4 zipkin的集成
1、ZipKin介绍
Zipkin 是 Twitter 的一个开源项目,它基于Google Dapper实现,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储展现、查找和我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源
除了面向开发的 API 接口之外,它也提供了方便的UI组件来帮助我们直观的搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户请求的处理时间等。
Zipkin 提供了可插拔数据存储方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。
从上面的图可以看出 zipkin有个服务端, 我们的微服务只需要接入到zipkin服务端,zipkin服务端就会通过里面的Collector搜集器来搜集微服务的日志。
2.3.5 启动zipkin服务端
1、下载地址:Quickstart · OpenZipkin
2、下载好的jar包存放在文件夹中[目录中不要有中文]
在该文件夹打开cmd窗口,运行此jar包
java -jar zipkin-server-2.12.9-exec.jar
然后浏览器进行访问
3、微服务接入zipkin[加在父工程即可]
<dependencies>
<!--父工程中添加sleuth依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--父工程中添加zipkin依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
4、配置配置文件
每一个服务都要配置,common服务可以不配置
#指定zipkin服务器的地址
spring.zipkin.base-url=http://localhost:9411
#zipkin不注册到nacos,因为zipkin也是一个服务,加不加都可以
spring.zipkin.discovery-client-enabled=false
#抽样比例,配置sleuth生成的日志中抽取多少作为zipkin数据资料,一般大型项目是0.03,也就是3%
spring.sleuth.sampler.probability=1.0
5、查看zipkin视图
启动这些微服务,并运行两次,因为项目运行第一次加载时间会比较长
然后我们查看这个路径下的代码
2.4 配置中心
问题1: 微服务集群他们使用相同的配置文件,如果配置文件的内容发生改变,需要一一对集群机器进行修改。
问题2: 如果n个微服务之间可能存在相同的配置内容,如果这些相同的配置内容发生改变,则需要一一修改n个微服务的配置
如何解决上述问题: 我们可以使用配置中心统一管理微服务的配置。
2.4.1 配置中心的组件
l Apollo
Apollo是由携程开源的分布式配置中心。特点有很多,比如:配置更新之后可以实时生效,支持灰度发布功能,并且能对所有的配置进行版本管理、操作审计等功能,提供开放平台API。并且资料也写的很详细。
l Disconf
Disconf是由百度开源的分布式配置中心。它是基于Zookeeper来实现配置变更后实时通知和生效的。
l SpringCloud Config -- 淘汰了
这是Spring Cloud中带的配置中心组件。它和Spring是无缝集成,使用起来非常方便,并且它的配置存储支持Git<git没学> 。不过它没有可视化的操作界面,配置的生效也不是实时的,需要重启或去刷新。
l Nacos
这是SpingCloud alibaba技术栈中的一个组件,前面我们已经使用它做过服务注册中心。其实它也集成了服务配置的功能,我们可以直接使用它作为服务配置中心。
2.4.2 nacos配置中心
这里进行测试是相对于商品微服务进行测试的,所有的操作都在商品微服务中进行
1、在配置中心创建配置
然后点击发布,确定,再回到主页面
已经发布成功
2、微服务中添加依赖
<!--引入nacos配置中心的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
3、修改微服务配置文件(bootstrap.application)
spring boot 核心的两个配置文件: bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖; bootstrap可以读取外部配置的内容。
application (. yml 或者 . properties):用于 spring boot 项目的自动化配置。
#指定配置中心的地址
spring.cloud.nacos.config.server-addr=localhost:8848
#可以写一下微服务的名字
spring.application.name=springcloud-product
#指定使用配置中心的哪个文件,如果没写,那么默认使用和服务名一致的配置,且默认的配置文件后缀为.properties
#这里配置文件后缀
#spring.cloud.nacos.config.file-extension=.properties
#如果服务名和配置名不同,那么需要在这指定配置名
#spring.cloud.nacos.config.name=springcloud-product
4、添加测试代码
import com.aaa.entity.ShopProduct;
import com.aaa.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getProductById/{pid}")
public ShopProduct getProductById(@PathVariable Long pid){
return productService.getProductById(pid);
}
//@Value注解:读取配置文件中指定的内容,这里是获取配置文件中key值为student.name的value值
@Value("${student.name}")
private String name;
@GetMapping("getName")
public String getName(){
return "配置中心拉取的内容:"+name;
}
}
5、启动商品微服务进行测试
拿到了配置中心里面的配置内容
6、异常情况测试
当服务名和配置名不一致时,项目启动会报错
新建配置文件,配置名为springcloud,配置文件中不添加配置名进行测试
找不到对应的配置文件中有该key值,所以会直接报错
修改配置文件
#指定配置中心的地址
spring.cloud.nacos.config.server-addr=localhost:8848
#可以写一下微服务的名字
spring.application.name=springcloud-product
#指定使用配置中心的哪个文件,如果没写,那么默认使用和服务名一致的配置,且默认的配置文件后缀为.properties
#这里配置文件后缀
#spring.cloud.nacos.config.file-extension=.properties
#如果服务名和配置名不同,那么需要在这指定配置名
spring.cloud.nacos.config.name=springcloud
项目正常启动,然后进行测试,就可以了
2.4.3 命名空间和组
组名一般和项目名相同
1、新建命名空间
public命名空间是默认存在的,是不能够删除的
新建的命名空间会自动生成命名空间ID
克隆完之后可以把public中的配置给删除掉,避免影响接下来的操作
编辑dev命名空间中的配置文件,将内容改为开发环境
2、测试不同的命名空间中的内容
现在商品微服务想要使用dev命名空间中的配置文件
修改配置文件(bootstrap.application)
#指定配置中心的地址
spring.cloud.nacos.config.server-addr=localhost:8848
#可以写一下微服务的名字
spring.application.name=springcloud-product
#配置中心的命名空间,默认是public的命名空间,这里使用的命名空间ID
spring.cloud.nacos.config.namespace=a6ed9c0f-72ff-4bff-b14d-969ad995f421
#指定使用配置中心的哪个文件,如果没写,那么默认使用和服务名一致的配置,且默认的配置文件后缀为.properties
#这里配置文件后缀
#spring.cloud.nacos.config.file-extension=.properties
#如果服务名和配置名不同,那么需要在这指定配置名
spring.cloud.nacos.config.name=springcloud
启动商品微服务,进行测试
3、对组名进行测试
删掉原来的组,新建一个组名为springcloud,配置名为springcloud的配置文件
如果不配置组名,就启动项目,也是拿不到配置文件中的内容,因为他获取配置文件中内容时,默认是从DEFAULT_GROUP组中进行获取的
4、修改配置文件
#指定配置中心的地址
spring.cloud.nacos.config.server-addr=localhost:8848
#可以写一下微服务的名字
spring.application.name=springcloud-product
#配置中心的命名空间,默认是public的命名空间
spring.cloud.nacos.config.namespace=a6ed9c0f-72ff-4bff-b14d-969ad995f421
#配置需要使用的组名,默认是DEFAULT_GROUP
spring.cloud.nacos.config.group=springcloud
#指定使用配置中心的哪个文件,如果没写,那么默认使用和服务名一致的配置,且默认的配置文件后缀为.properties
#这里配置文件后缀
#spring.cloud.nacos.config.file-extension=.properties
#如果服务名和配置名不同,那么需要在这指定配置名
spring.cloud.nacos.config.name=springcloud
启动商品微服务项目进行测试
5、实时获取配置文件中的内容
现在商品微服务使用的dev命名空间中的配置文件,但是当配置文件内容进行修改时,需要重启商品微服务才能够获得最新的配置文件内容,需要解决这个问题,需要使用注解@RefreshScope
修改controller代码(添加一个类注解即可)
import com.aaa.entity.ShopProduct;
import com.aaa.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("product")
@RefreshScope //实时刷新配置中心的配置文件
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getProductById/{pid}")
public ShopProduct getProductById(@PathVariable Long pid){
return productService.getProductById(pid);
}
//@Value注解:读取配置文件中指定的内容,这里是获取配置文件中key值为student.name的value值
@Value("${student.name}")
private String name;
@GetMapping("getName")
public String getName(){
return "配置中心拉取的内容:"+name;
}
}
然后启动项目,对配置文件中的内容进行更改,即使不重启微服务,也能获取最新的内容,控制台也能看到配置刷新的key值
2.4.4 多个微服务共享配置
1、将配置文件放置到配置中心
举例:将商品微服务的配置文件添加到配置中心,将微服务中的配置文件注释掉或者删除掉
同样的,订单微服务,网关微服务也添加依赖,上传配置文件到命名空间,编写bootstrap.properties配置文件
2、现在几个微服务的配置文件都已经添加进去配置中心,开始提取公共配置
这里配置nacos注册中心的代码都是重复的,所以可以先提取出来放到公共的配置文件中,其他的配置文件中,将公共代码删除
3、在微服务中的配置文件中引入公共配置
#引入其他的公共配置内容,参数为一个集合,其中每个集合中有以下参数,如果有两种公共配置,那么取下一个公共配置时,下标为1
#配置文件名
spring.cloud.nacos.config.extension-configs[0].data-id=nacos-config.properties
#配置文件组名
spring.cloud.nacos.config.extension-configs[0].group=springcloud
#是否实时刷新
spring.cloud.nacos.config.extension-configs[0].refresh=true
4、启动服务进行测试
2.5 微服务保护组件
2.5.1 微服务雪崩现象
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是服务雪崩。
雪崩问题 解决雪崩问题的常见方式有四种:
超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离.
熔断降级:由断路器统计业务执行的异常比例,如果超出闻值则会熔断该业务,拦截访问该业务的一切请求
流量控制: 限制业务访问的QPS,避免服务因流量的突增而故障
什么是雪崩问题? 微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。 1、如何避免因瞬间高并发流量而导致服务故障? 流量控制 2、如何避免因服务故障引起的雪崩问题? 超时处理 线程隔离 降级熔断
2.5.2 服务保护组件
其中慢调用是指:响应时间超过规定时间
认识Sentinel Sentinel是阿里巴巴开源的一款微服务流量控制组件。
官网地址:!https://sentinelquard.io/zh-cn/index.htmlSentinel具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo. gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的SPI扩展点: Sentinel 提供简单易用、完善的 SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑例如定制规则管理、适配动态数据源等。
2.5.3 使用sentinel的UI控制台
1、运行sentinel
java -jar -Dserver.port=8070 sentinel-dashboard-1.8.1.jar
因为和tomcat的端口号冲突,所以这里在黑窗口进行运行时直接将端口号设置为8070
2、浏览器访问
localhost:8070/#/senetinel
或者
localhost:8070/senetinel
密码和用户名都为sentinel
2.5.4 微服务接入到sentinel控制台
1、父类中引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、订单微服务接入sentinel
#指定sentinel控制台的地址
spring.cloud.sentinel.transport.dashboard=localhost:8070
3、启动项目访问订单微服务,然后观察sentinel控制台
2.5.5 sentinel的规则
簇点链路 簇点链路: 就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMV(的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源
流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
当前只有一个资源
再添加一个资源
在order微服务中的controller层中,编写代码,因为每一个controller层的方法都是一个资源
@GetMapping("getInfo")
public String getInfo(){
return "簇点链路中的资源+1";
}
启动项目,执行该方法
观察sentinel控制台
2.6 流控模式
流控模式有以下三种:
2.6.1 直接限流
直接:统计当前资源的请求,触发闯值时对当前资源直接限流,也是默认的模式
点击资源后面的流控按钮,添加流控规则
![]()
然后对该资源进行访问,当每秒请求两次时,就会被限流
案例:
需求:给 /order/getInfo 这个资源设置流控规则,QPS不能超过 5。然后利用jemeter测试。
实现
然后使用压测工具jmeter
![]()
运行压测,查看结果[先启用计划,再启动]
2.6.2 关联模式
关联:统计与当前资源相关的另一个资源,触发闯值时,对当前资源限流
使用场景:
比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争业务需求是有限支付和更新订单的业务,因此当修改订单业务触发闽值时,需要对查询订单业务限流
点击资源后面的流控按钮,添加流控规则
编写两个资源进行测试
@GetMapping("read")
public String read(){
return "读取资源";
}
@GetMapping("write")
public String write(){
return "编写资源";
}
编写完资源后,重启订单微服务,分别使用浏览器访问这两个资源,从而使这两个资源加载到sentinel中
当/write资源访问量触发闻值时,就会对/read资源限流,避免影响/write资源
需求: 在OrderController新建两个端点: /order/read和/order/write,无需实现业务配置流控规则,当/order/write资源被访问的QPS超过5时,对/order/read请求限流
![]()
在进行压力测试时,访问读操作,会发现读操作已经被限流了
因为阈值为5,而压测软件中的QPS=10,所以读操作就会被限制
查看压测结果,全是成功的请求,因为只对读操作进行了限制,写操作没有被限制
小结 满足下面条件可以使用关联模式两个有竞争关系的资源一个优先级较高,一个优先级较低
2.6.3 链路模式
链路:统计从指定链路访问到本资源的请求,触发闯值时,对指定链路限流
需求
需求:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。 步骤: 1、在OrderService中添加一个queryGoods方法,不用实现业务
2、在OrderController中,改造/order/query端点,调用OrderService中的queryGoods方法
3、在OrderController中添加一个/order/save的端点,调用OrderService的queryGoods方法
4、给queryGoods设置限流规则,从/order/query进入queryGoods的方法限制QPS必须小于2
编写资源进行测试
service层:
/**sentinel只会为mvc(也就是controller层)生成资源,不会为service层生成资源
* Sentinel默认只标记Controller中的方法为资源,如果要标记其它方法,需要利用@SentinelResource注解
* sentinel将该方法标记为注解时,名字为goods
*/
@Override
@SentinelResource("goods")
public void queryGoods() {
}
controller层
@GetMapping("insert")
public String insert(){
orderService.queryGoods();
return "添加订单";
}
@GetMapping("query")
public String query(){
orderService.queryGoods();
return "查询订单";
}
bootstrap.properties文件
#Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效
#关闭context上下文资源
spring.cloud.sentinel.web-context-unify=false
点击资源后面的流控按钮,添加流控规则
进行测试
insert请求的设置
query请求的设置
2.7 流控效果
流控效果 流控效果是指请求达到流控阑值时应该采取的措施,包括三种:
快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
warm up:预热模式,对超出值的请求同样是拒绝并抛出异常。但这种模式闽值会动态变化,从一个较小值逐渐增加到最大阙值。
排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
2.7.1 快速失败
前面测试使用的都是快速失败措施,当达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。
2.7.2 预热模式
warm up也叫预热模式,是应对服务冷启动的一种方案。
请求值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。coldFactor默认值为3.
需求:
给/order/getInfo这个资源设置限流,最大QPS为10,利用warm up效果,预热时长为5秒
压测工具测试
阈值为10,初始默认值为3,所以计算得出刚开始只有三个请求能够成功,后面全都是成功请求
2.7.3 排队等待
当请求超过QPS闽值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照闯值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝,在某一时刻,服务很忙,而其他时刻,服务很闲。
需求:给/order/getInfo)这个资源设置限流,最大QPS为10,利用排队的流控效果,超时时长设置为5s
压测工具测试
小结:
流控效果有哪些? 快速失败:QPS超过闯值时,拒绝新的请求 warm up: QPS超过闽值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机。 排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝
2.8 热点参数限流
之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求判断是否超过QPS阈值。
在热点参数限流的高级选项中,可以对部分参数设置例外设置:
结合上一个配置,这里的含义是对O号的long类型参数限流,每1秒相同参数的QPS不能超过5,有两个例外:如果参数值是100,则每1秒允许的QPS为10如果参数值是1O1,则每1秒允许的QPS为15
1、订单微服务中编写测试代码
@GetMapping("queryHot/{id}")
@SentinelResource("hot") //因为参数热点的限流,默认为springmvc簇点链路是无效的,所以添加资源需要定义
public String queryHot(@PathVariable Integer id){
orderService.queryGoods();
return "查询订单"+id;
}
启动项目,访问定义的资源路径,将服务添加到sentinel中
添加完之后,该资源就叫hot
给/order/{hot}这个资源添加热点参数限流,规则如下:
默认的热点参数规则是每1秒请求量不超过2
给102这个参数设置例外:每1秒请求量不超过4
给103这个参数设置例外:每1秒请求量不超过10
压测工具进行测试
查看结果
id为1的每两条就会有错误的
id为102的每四条会有错误的
id为103的没有请求失败的
2.9 隔离和降级
虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
不管是线程隔离还是熔断降级,都是对客户端(调用方) 的保护。
2.9.1 feign整合sentinel
springcloud中,微服务调用都是通过调用feign实现的,因此做客户端保护必须整合feign和sentinel
1、编写配置文件,开启feign的sentinel功能
#开启feign整合sentinel
feign.sentinel.enabled=true
2、编写一个降级处理类(保证服务稳定)
/**
* 当调用这个方法失败时,会调用这个方法
* 方式一:FallbackClass,无法对远程调用的异常做处理
* 方式二:FallbackFactory,可以对远程调用的异常处理,选择这种
*/
@Component
public class ProductFallbackFactory implements FallbackFactory<ProductFeign> {
@Override
public ProductFeign create(Throwable throwable) {
//创建ProductFeign实现类,实现其中的方法,编写失败降级的处理逻辑
return new ProductFeign() {
@Override
public ShopProduct getProductById(Long pid) {
//创建ShopProduct对象,设置一下提示信息,降级业务逻辑
ShopProduct shopProduct = new ShopProduct();
shopProduct.setPname("业务正忙,请稍等……");
return shopProduct;
}
};
}
}
3、改变feign接口代码
/**
*@FeignClient(value = "springcloud-product"):声明客户端,值是要调用的微服务的名字
*
* 方法声明:把要调用的方法【除了方法体以外的所有东西都拿过来】
*
* fallbackFactory = ProductFallbackFactory.class,将降级逻辑的文件类引入到里面
*/
@FeignClient(value = "springcloud-product",fallbackFactory = ProductFallbackFactory.class)
public interface ProductFeign {
@GetMapping("product/getProductById/{pid}")
public ShopProduct getProductById(@PathVariable Long pid);
}
4、启动项目进行测试
在浏览器进行访问,添加到sentinel控制台,然后添加限流规则
然后再进行访问,没超过阈值时正常访问,超过阈值时。
2.10 线程隔离
线程隔离有两种实现方式:
1、线程池隔离
2、信号量隔离(sentinel默认使用)
信号量隔离 | 线程池隔离 | |
---|---|---|
优点 | 轻量级,无额外开销 | 支持主动超时,支持异步调用 |
缺点 | 不支持主动超时,不支持异步调用 | 线程额外开销较大 |
场景 | 高频调用,高扇出 | 低扇出 |
2.10.1 线程隔离(舱壁模式)
QPS:就是每秒的请求数,在快速入门中已经演示过
线程数: 是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式
需求:需求:给ProductFeign的查询用户接口设置流控规则,线程数不能超过 ,利用jemeter测试。
利用压测工具进行测试
2.11 熔断降级
熔断降级
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出闽值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
断路器熔断策略有三种:慢调用、异常比例、异常数
2.11.1 慢调用
慢调用:业务的响应时长 (RT) 大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的闯值,则触发熔断。例如:
编写测试代码:在订单微服务中进行修改
@GetMapping("insertSlow/{id}")
public String insertSlow(@PathVariable Integer id){
if (id==1){ //id=1时,线程休眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if (id==2){ //id=2时,线程抛出异常
throw new RuntimeException("异常");
}
orderService.queryGoods();//除此之外的订单都是正常业务
return "添加订单";
}
2、定义降级规则
浏览器进行测试:
使用id为1进行多次测试(超过5次)这个时候会出现429,已经熔断
然后再访问正常的请求,id=3,因为此时已经熔断,所以此时正常的请求也不能访问
过20秒之后,此服务处于半开状态,如果使用正常请求那么此服务器会恢复成正常状态,如果使用异常请求进行访问,那么此服务还会再次进入熔断状态
2.11.2 异常比例
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例闯值(或超过指定异常数),则触发熔断。例如:
测试操作和慢调用的方法相同即可
2.11.3 异常数
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例闯值(或超过指定异常数),则触发熔断。例如:
2.12 规则持久化
1、原始模式
2、pull模式
3、push模式
2.12.1 持久化操作
sentinel的持久化,我们希望这样:
可以在 sentinel 控制台中编辑 限流配置,并且同步到 nacos 做持久化
在 nacos 中修改了限流配置,也可以同步到 sentinel 控制台
要实现上述第一个功能需要对 sentinel 控制台的源码有所了解,并加依改造。
但 GitHub上已经有人改造好了,做了个加强版控制台。
1、解压文件
2、打开解压后文件中根目录下的pom.xml文件,修改sentinel的版本
因为这里的sentinel控制台版本为1.8.1,所以这里的版本也修改为1.8.1
3、进入项目根目录,输入cmd,执行
mvn clean package
命令根目录:就是解压好的文件夹点进去就是根目录
执行
mvn clean package
命令,先清理项目,后打包
![]()
找到打包好的jar包:
4、执行命令,启动修改好的sentinel【停止原来的sentinel服务】
java -Dserver.port=8070 -Dnacos.serverAddr=localhost:8848 -jar sentinel-dashboard.jar # -Dserver.port 控制台端口号 # -Dnacos.serverAddr: nacos 地址 # -Dnacos.namespace: 你项目所在的 nacos 命名空间 如果命名空间就是public可以省略该参数
5、访问sentinel界面
2.12.2 使用sentinel-nacos
1、添加依赖
<!--监听nacos配置中心的sentinel的规则-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.1</version>
</dependency>
2、修改配置文件[bootstrap.properties]
#指定sentinel控制台的地址,重复就删掉
spring.cloud.sentinel.transport.dashboard=localhost:8070
#ds1-flow是自己命名的名字
spring.cloud.sentinel.datasource.ds1-flow.nacos.server-addr=localhost:8848
#生成的规则,服务名-flow-rules
spring.cloud.sentinel.datasource.ds1-flow.nacos.data-id=${spring.application.name}-flow-rules
# 还可以是
# degrade:降级规则
# authority
# flow:限流规则
# param-flow:热点参数
spring.cloud.sentinel.datasource.ds1-flow.nacos.rule-type=flow
#什么格式显示
spring.cloud.sentinel.datasource.ds1-flow.nacos.data-type=json
#设置他的组
spring.cloud.sentinel.datasource.ds1-flow.nacos.group-id=SENTINEL_GROUP
3、启动订单微服务,访问其中的任意一个资源,将服务接入到sentinel中
4、添加流控规则
接入成功
增加流控规则:一定要点 带 2 的,只有带 2 的才能推送到 nacos,在 流控规则 选项中添加也不行,只能点击 蓝色方框才能推送到 nacos
然后查看nacos配置中心
5、测试实时刷新
更改并发布完配置文件之后,查看sentinel控制台
再次更改后,观察nacos配置中心
6、测试持久化
将项目重启,再看流控规则是否存在,如果存在持久化就是成功的