Spring Cloud Alibaba中文参考文档:Spring Cloud Alibaba 参考文档 (spring-cloud-alibaba-group.github.io)
由于现在用的是springcloudalibaba的相关组件,不需要之前的一些组件了,所以吧项目之前的组件全部清除掉,只留下book-service、borrow-service、user-service,并且清除掉之前的依赖和配置
1、服务端部署
访问https://github.com/alibaba/nacos
点击Releases
下滑
点击下载
吧下载好的压缩包解压,后将里边的nacos放到spring_cloud_study项目中
要启动nacos可以进入nacos=>bin中在cmd中用startup.cmd -m standalone来启动,直接startup.cmd会报错
解决方法2:编辑startup.cmd,将startup.cmd中的MODE改为standalone,意思就是打开方式为单机版方式启动,这样直接双击startup.cmd也可以启动
启动后在浏览器中输入http://localhost:8848/nacos用8848端口访问nacos并且使用用户名nacos和密码nacos登录
在idea中配置nacos服务的启动
(27条消息) Idea中配置Nacos服务的启动_idea nacos_lucky赵的博客-CSDN博客
有的idea在Edit Configurations中没有shell script,在设置中的Plugins中下载BashSupport,重启idea
再次进入Edit Configurations,加号有Bash,点击后配置
Apply后OK,点击启动键后启动nacos
访问http://localhost:8848/nacos/用来测试nacos配置完成情况
2、nacos的服务注册于发现
在父类项目中添加依赖
<!--这里引入最新的SpringCloud依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.1</version> <type>pom</type> <scope>import</scope> </dependency> <!--这里引入最新的SpringCloudAlibaba依赖,2021.0.1.0版本支持SpringBoot2.6.X--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.1.0</version> <type>pom</type> <scope>import</scope> </dependency>
目前完整pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>user-service</module> <module>book-service</module> <module>borrow-service</module> <!--<module>eureka-server</module>--> <!--<module>hystrix-dashboard</module>--> <!--<module>gateway-server</module>--> <!--<module>config-server</module>--> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.10</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>spring_cloud_study</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring_cloud_study</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> </dependencies> <!--由于不是每个子项目都要用mybatis,因此只再父项目做版本的管理,子项目自行做引入--> <dependencyManagement> <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.3.0</version> </dependency> <!--这里引入最新的SpringCloud依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.1</version> <type>pom</type> <scope>import</scope> </dependency> <!--这里引入最新的SpringCloudAlibaba依赖,2021.0.1.0版本支持SpringBoot2.6.X--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
在三个子项目中添加依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
目前完整的pom.xml(user-service子项目的)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring_cloud_study</artifactId> <groupId>com.example</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>user-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
配置文件中配置:
application.yml
cloud: nacos: discovery: # 配置Nacos注册中心地址 server-addr: localhost://8848
启动查看nacos
将剩下两个子项目也注册到nacos中
3、使用openfeign,实现服务发现远程调用以及负载均衡
去borrow-service子项目中导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--这里需要单独导入LoadBalancer依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
完整pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring_cloud_study</artifactId> <groupId>com.example</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>borrow-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--这里需要单独导入LoadBalancer依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies> </project>
创建client包,在里边创建UserClient.java,和之前的一样
UserClient.java
package com.test.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; /** * @Date 2023/3/29 - 13:53 */ @FeignClient(value="userservice",fallback = UserFallbackCilent.class)//声明为userservice服务的HTTP请求客户端 public interface UserClient { @RequestMapping("/UserController/hello/{user}") String hello(@PathVariable("user") String user); }
创建controller包,里边创建BorrowController.java
BorrowController.java
package com.test.controller; //import com.netflix.discovery.converters.Auto; //import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.test.client.UserClient; import org.springframework.beans.factory.annotation.Autowired; 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; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * @Date 2023/3/27 - 15:22 */ @RestController @RequestMapping("BorrowController") public class BorrowController { @Resource private UserClient userClient; // @HystrixCommand(fallbackMethod = "onError") @GetMapping("/hello/{borrow}") public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request){ // 测试gateway过滤器 System.out.println(request.getHeader("Test")); String user=userClient.hello(borrow); return borrow+user; } //// 备选方案,这里直接返回一个字符串 //// 注意参数和返回值要和上面一致 // String onError(String borrow){ // System.out.println("2"); // return "error:"+borrow; // } }
在启动类中添加注解开启openfeign
@EnableFeignClients
UserController.java
package com.test.controller; 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; /** * @Date 2023/3/27 - 15:24 */ @RestController @RequestMapping("UserController") public class UserController { @GetMapping("/hello/{user}") public String hello(@PathVariable("user") String user){ System.out.println("hello Userservice"); return user; } }
测试:
在创建一个user-service实例
点加号=>Spring Boot,然后配置
把之前有的userservice改个名字
Apply=>OK
重启两个userservice
去nacos中看眼
测试:
刷新五次,可以看见两个服务已经被负载均衡
4、通过配置文件进行修改临时实例:
临时和非临时区别:
给borrow-service子项目添加配置
# ephemeral改为false,表示非临时实例 ephemeral: false
重启服务,去nacos中查看,点击详情
看到临时实例为false,不加上边的配置为true
关闭borrow-service服务再次查看nacos,可以看到没有在nacos中消失,而是标记健康实例数为0,详情中的健康状态为false
5、nacos的集群分区
消费者调用生产者应该优先调用同一个区域的,这样响应速度更快。
默认的分区是default
修改分区:给userservice-01和userservice-02分别添加环境变量
spring.cloud.nacos.discovery.cluster-name=Chongqing
也可以在配置文件中添加配置
重启两个服务,在nacos中查看
再把borrow-service的集群改成成都的,重启查看nacos
这时候请求borrow-service测试四次,发现userservice-01和userservice-02都有打印语句,说明现在的服务并没有按照集群划分。
因此在borrow-service下添加配置
# 将loadbalancer的nacos支持开启,集成nacos负载均衡 loadbalancer: nacos: enabled: true
注意这个loadbalancer是cloud下的
再次重启测试,请求五次,发现请求全是和borrow-service同一集群的userservice-02响应的
在nacos中把userservice-02下线,再次请求五次,发现这次请求全是userservice-01响应的。
6、权重机制
根据区域优先调用之外,同一区域内的实例也可以单独设置权重,nacos会优先选择权重更大的实例进行调用,我们可以直接在nacos管理页面配置,或者是在配置文件中配置
配置文件:
7、nacos配置中心
在nacos管理界面的配置管理
添加配置信息,把userservice的application.yml中的配置粘贴到配置内容中,发布。
在userservice中添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
添加配置:bootstrap.yml
spring: application: name: userservice profiles: # 环境也是和配置文件保持一致 active: dev cloud: nacos: config: # 配置文件后缀名 file-extension: yml # 配置中心服务器地址,也是nacos地址 server-addr: localhost:8848
重启测试
nacos还支持文件的热更新,比如我们在配置文件中添加了一个属性,而这时候可能需要实时修改,并在后端实时更新。
测试:
在userservice中的controller中做点改动
UserController.java
package com.test.controller; import org.springframework.beans.factory.annotation.Value; 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; /** * @Date 2023/3/27 - 15:24 */ @RestController @RequestMapping("UserController") public class UserController { @Value("${test.aa}")//从配置文件中读取test.aa的字符串值,作为test接口的返回值 String test; @GetMapping("/hello/{user}") public String hello(@PathVariable("user") String user){ System.out.println("hello Userservice"); System.out.println(test); return user; } }
然后再nacos中在userservice的配置添加test.aa以及值
重启userservice测试
现在在nacos中修改配置,把aa改成1111111
如果不重启服务响应的还是之前的nihao
添加注解@RefreshScope就可以实现自动刷新了
在UserController.java上加注解
重启userservice服务,先请求看test.aa的值,在去nacos中修改配置值后再次查看。
请求一次
修改test.aa的值
userservice的日志已经更新,发现test.aa被修改
再次请求,响应的是22222
8、nacos命名空间
在nacos界面的命名空间中点击新建命名空间
创建一个开发环境
点击dev可以查看刚创建的空间情况
把bookservice服务放到dev中
在bookservice服务的配置文件中添加配置信息
这里namespace的值为nacos中生成dev空间的id值
重启bookservice查看nacos
把userservice也放进dev空间中
现在是borrowservice没在dev中,userservice在dev中,两个服务没有在同一个命名空间中,请求borrowservice,会发现userservice不能被发现
把borrowservice也放进dev中就可以发现了。
又能访问了。
这里的分组也是在配置中能配置。默认就是DEFAULT_GROUP
9、nacos在linux环境中搭建集群
在navicat中创建数据库,运行SQL文件,运行的文件为nacos-server=>conf=>nacos-mysql.sql
把nacos-server传到linux环境中,并且解压
解压命令:unzip 包名
进入nacos/conf/中编辑application.properties
### Default web server port: server.port=8801 #*************** Network Related Configurations ***************# ### If prefer hostname over ip for Nacos server addresses in cluster.conf: # nacos.inetutils.prefer-hostname-over-ip=false ### Specify local server's IP: # nacos.inetutils.ip-address= #*************** Config Module Related Configurations ***************# ### If use MySQL as datasource: spring.datasource.platform=mysql ### Count of DB: db.num=1 ### Connect URL of DB: db.url.0=jdbc:mysql://xxx/nacos_demo?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=nacos db.password.0=nacos
db.url.0、db.user.0、db.password.0都修改成自己的,就是连接这个图中的nacos_demo数据库
然后配置集群
把cluster.conf.example的.example去掉
编辑cluster.conf之前用ifconfig看内网ip,是127.0.0.1
编辑cluster.conf
编辑前
编辑后
保存退出
修改nacos的内存分配以及前台启动,直接修改startup.sh文件
改动前
改动后
在拷贝一个nacos出来,名字为nacos2
用两个会话分别打开两个nacos
下载nginx
wget http://nginx.org/download/nginx-1.13.7.tar.gz
解压
tar -xvf nginx-1.13.7.tar.gz
进入nginx-1.13.7=>conf
编辑nginx.conf,内容都改成自己的
...
1.配置sentinel
去Releases · alibaba/Sentinel (github.com)下载jar
在spring_cloud_study项目中创建目录sentinel,把下好的jar包放进去
idea中Edit
加号,JAR Application
配置
应用=>OK
启动,测试,用户和密码都是sentinel
把服务连接到控制台中,给三个服务添加依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
修改配置文件,在cloud下边,spring.cloud.sentinel.transport.dashboard
sentinel:
transport:
# 添加监控页面地址
dashboard: localhost:8858
启动测试,这时候还是没内容
进行一次请求
QPS是每秒查询率
2.sentinel流量控制
在sentinel中点想要限流的接口后边的流控
单机阈值定为1,阈值类型为qps,意思就是一秒之内只能有一条请求
流控模式Sentinel 三种流控模式_sentinel流控模式_杨 戬的博客-CSDN博客
流控效果是sentinel的三种限流措施Sentinel 三种流控效果_sentinel流控效果_杨 戬的博客-CSDN博客
方案一就是快速失败、方案二就是warm up、方案三就是排队等待(排队等待的超时时间单位为毫秒)
设置好后点击新增。
流控规则中就有一条刚新增的规则
测试:正常一秒请求一次和一秒多点几次
测试流控模式:关联
修改流控模式为关联,并且关联资源为/error
去postman中设置一个对/error的请求,请求100次,间隔500毫秒
postman右下角的runner,在把左边保存的请求拖到run order中,在右边设置迭代和间隔,点击run
开始请求
现在在BorrowController/hello/aaaass接口的相关接口/error请求时,在浏览器中对BorrowController/hello/aaaass进行请求
怎么请求都会被限流
在/error请求完后这个接口不被限流
测试流控模式:链路
我们可以用@SentinelResource注解对某个方法进行标注,对某一个方法进行限流控制,无论是谁在何处调用了他,那么就会进行监控。
这里给borrow-service的controller层的hello方法加@SentinelResource注解,并且命名为test
然后在application.yml中添加配置
# 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制 web-context-unify: false
完整
server: port: 8301 # 配置数据源信息 spring: application: name: borrowservice datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 username: root password: root cloud: nacos: discovery: # 配置Nacos注册中心地址 server-addr: localhost:8848 # ephemeral改为false,表示非临时实例 ephemeral: false cluster-name: Chengdu # 权重大小,越大越优先调用,默认为1 weight: 0.5 namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf # 将loadbalancer的nacos支持开启,集成nacos负载均衡 loadbalancer: nacos: enabled: true sentinel: transport: # 添加监控页面地址 dashboard: localhost:8858 # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制 web-context-unify: false
然后重启服务
重新对borrowservice进行请求后
刷新sentinel控制台
然后可以针对这个test入口进行添加限流策略,点击test后边的流控
这里的流控模式也可以用直接、关联或者是链路,直接使用链路,设置入口资源(想限制那个入口就写那个)
新增
再次请求,点一次正常,连点两次报错限流
控制台报错
新增系统规则
除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流,在系统规则的右上角可以新增系统规则
3、限流和异常处理
如果我们不想让限流后返回默认的界面,而是自己自定义的结果。
在borrow-service的BorrowController中创建要返回的结果
@RequestMapping("/blocked") JSONObject blocked(){ JSONObject object=new JSONObject(); object.put("code",403); object.put("success",false); object.put("message","您的请求过块,请稍后在试!"); return object; }
在application.yml中做配置
# 将刚刚编写的请求映射设定为限流页面
block-page: /BorrowController/blocked
完整
server: port: 8301 # 配置数据源信息 spring: application: name: borrowservice datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 username: root password: root cloud: nacos: discovery: # 配置Nacos注册中心地址 server-addr: localhost:8848 # ephemeral改为false,表示非临时实例 ephemeral: false cluster-name: Chengdu # 权重大小,越大越优先调用,默认为1 weight: 0.5 namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf # 将loadbalancer的nacos支持开启,集成nacos负载均衡 loadbalancer: nacos: enabled: true sentinel: transport: # 添加监控页面地址 dashboard: localhost:8858 # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制 web-context-unify: false # 将刚刚编写的请求映射设定为限流页面 block-page:/BorrowController/blocked
重启测试,被限流的情况
对于方法级别的限流:
在之前针对方法级别的限流上边的@SentinelResource注解上边添加一些设置
注意这个
- 必须是public修饰
- 返回类型与原方法一致
- 参数类型需要和原方法相匹配,并在最后加BlockException类型的参数。
在访问指定的方法会返回替代方案,不是默认的界面
测试,在给test加了限流后重新请求
注意blockHandler只能处理限流情况下抛出的异常,包括下面要介绍的热点参数限流也是同理,如果是方法本身抛出的其他类型异常,不在管控范围内,但是可以通过其他参数进行处理
修改borrow-service中的BorrowController
当他里边抛出异常时会走替代方案except
package com.test.controller; //import com.netflix.discovery.converters.Auto; //import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import com.sun.deploy.security.BlockedException; import com.test.client.UserClient; import org.springframework.beans.factory.annotation.Autowired; 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; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Collections; /** * @Date 2023/3/27 - 15:22 */ @RestController @RequestMapping("BorrowController") public class BorrowController { @Resource private UserClient userClient; // @HystrixCommand(fallbackMethod = "onError") @SentinelResource(value = "test", blockHandler = "oooo"//监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称, // 这个注解可以加在任何方法上边,包括Controller中的请求映射方法,跟HystrixCommand很像。指定blockHandler, // 也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了 , fallback = "except", //fallback指定出现异常时的替代方案(指的是所有异常,blockHandler是BlockException的子异常) exceptionsToIgnore = IOException.class //忽略那些异常,也就是说这些异常出现时不适用替代方案 ) @GetMapping("/hello/{borrow}") public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request) { throw new RuntimeException("helloworld"); // 测试gateway过滤器 // System.out.println(request.getHeader("Test")); // String user = userClient.hello(borrow); // return borrow + user; } // 替代方案,注意参数和返回值需要保持一致,最后可以添加一个Throwable作为参数接受异常 public String except(String borrow, HttpServletRequest request, Throwable t) { return "error throwable"; } // 替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException public String oooo(String borrow, HttpServletRequest request, BlockException e) { return "error"; } @RequestMapping("/blocked") JSONObject blocked() { JSONObject object = new JSONObject(); object.put("code", 403); object.put("success", false); object.put("message", "您的请求过块,请稍后在试!"); return object; } //// 备选方案,这里直接返回一个字符串 //// 注意参数和返回值要和上面一致 // String onError(String borrow){ // System.out.println("2"); // return "error:"+borrow; // } }
重启测试
注意:如果blockHandler和fallback同时生效,优先级高的是blockHandler
4、热点参数限流
在borrow-service中的BorrowController中添加一个接口映射方法
@RequestMapping("/abc") @SentinelResource("abc") String abc(@RequestParam(value = "a",required = false) String a, @RequestParam(value = "b",required = false) String b, @RequestParam(value = "c",required = false) String c){ return "success a="+a+",b="+b+",c="+c; }
package com.test.controller; //import com.netflix.discovery.converters.Auto; //import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import com.sun.deploy.security.BlockedException; import com.test.client.UserClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Collections; /** * @Date 2023/3/27 - 15:22 */ @RestController @RequestMapping("BorrowController") public class BorrowController { @Resource private UserClient userClient; // @HystrixCommand(fallbackMethod = "onError") @SentinelResource(value = "test", blockHandler = "oooo"//监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称, // 这个注解可以加在任何方法上边,包括Controller中的请求映射方法,跟HystrixCommand很像。指定blockHandler, // 也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了 , fallback = "except", //fallback指定出现异常时的替代方案(指的是所有异常,blockHandler是BlockException的子异常) exceptionsToIgnore = IOException.class //忽略那些异常,也就是说这些异常出现时不适用替代方案 ) @GetMapping("/hello/{borrow}") public String hello(@PathVariable("borrow") String borrow, HttpServletRequest request) { // throw new RuntimeException("helloworld"); // 测试gateway过滤器 System.out.println(request.getHeader("Test")); String user = userClient.hello(borrow); return borrow + user; } // 替代方案,注意参数和返回值需要保持一致,最后可以添加一个Throwable作为参数接受异常 public String except(String borrow, HttpServletRequest request, Throwable t) { return "error throwable"; } // 替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException public String oooo(String borrow, HttpServletRequest request, BlockException e) { return "error"; } @RequestMapping("/blocked") JSONObject blocked() { JSONObject object = new JSONObject(); object.put("code", 403); object.put("success", false); object.put("message", "您的请求过块,请稍后在试!"); return object; } //// 备选方案,这里直接返回一个字符串 //// 注意参数和返回值要和上面一致 // String onError(String borrow){ // System.out.println("2"); // return "error:"+borrow; // } @RequestMapping("/abc") @SentinelResource("abc") String abc(@RequestParam(value = "a",required = false) String a, @RequestParam(value = "b",required = false) String b, @RequestParam(value = "c",required = false) String c){ return "success a="+a+",b="+b+",c="+c; } }
重启请求测试
然后对携带参数a的请求进行限流
进入sentinel控制台的热点规则,点击新建热点限流规则
资源名为@SentinelResource注解后边设置的名字,参数索引是第几个请求参数,这里0就是a
测试,一秒点一次正常响应,多点几次会被拦截
如果携带参数b,怎么都不会被拦截
如果携带参数a和参数b,也会被当作携带a来处理
如果我们想限流带a的参数,但是又希望a=10时不被限流,qps达到3在进行限流
在热点规则中编辑刚添加的规则,在高级选项中设置,参数类型为参数的类型,比如a的参数类型是string,参数值就是指定的参数值,限流阈值就是qps值,设置完后点击添加再点击保存。
测试,再一秒点击三次的时候会被拦截,a不等于10的时候是一秒点击一次的时候被拦截
5、服务熔断和降级
sentinel中的熔断和降级:再sentinel的控制台中点击熔断规则,然后新增熔断规则
慢调用比例策略:
rt是响应时间超过后会被判定为慢调用。
比例阈值是被判定为慢调用的请求比例超过阈值会触发熔断。
熔断时长是在熔断之前是一个状态,熔断后就会进入另一个状态进行试探,这个状态的持续时间就是熔断时长。
最小请求数和统计时长是在统计时长内达到最小请求数后才能正常的统计(按照上边的规则进行判断),如果统计时长内没有执行到最小请求数时,虽然他阈值超过也不会熔断,如果统计时长内超过到最小请求数时,阈值也超过了就会熔断。
测试:
先修改borrow-service中的BorrowController的接口
给hello这个接口加个睡眠时间为1秒
在创建个熔断规则
新增后测试
请求localhost:8301/BorrowController/hello/aaaass后被熔断,熔断和限流一样会走限流页面。
在等5秒后重新请求localhost:8301/BorrowController/hello/aaaass会正常,再次请求会又被重定向到限流页面
异常比例策略
修改borrow-service中的BorrowController的接口,让这个hello接口直接抛异常就行
新建熔断规则
这个策略就是执行的过程中出现异常的次数,1s(统计时长)请求2次(最小请求数),如果有1次(比例阈值)出现问题就会被熔断,被熔断5秒(熔断时长)。点击新增
测试:请求慢点就是正常的异常情况,请求快点就会走限流页面。
注意:如果异常有替代方案,则会走异常的替代方案,不会去限流页面
异常数策略就是具体的异常数量,到了数量就熔断
自定义服务降级也是用@SentinelResource注解的blockHandler参数,和限流一样
让Feign也支持Sentinel,前面我们使用Hystrix的时候,就可以直接对Feign的每个接口调用单独进行服务降级,而使用Sentinel也可以
在borrow-service的配置文件application.yml中加配置
# 开启feign对sentinel的支持 feign: sentinel: enabled: true
完整
server: port: 8301 # 配置数据源信息 spring: application: name: borrowservice datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 username: root password: root cloud: nacos: discovery: # 配置Nacos注册中心地址 server-addr: localhost:8848 # ephemeral改为false,表示非临时实例 ephemeral: false cluster-name: Chengdu # 权重大小,越大越优先调用,默认为1 weight: 0.5 namespace: f34784a5-ec4d-4659-a907-f464a4ff7aaf # 将loadbalancer的nacos支持开启,集成nacos负载均衡 loadbalancer: nacos: enabled: true sentinel: transport: # 添加监控页面地址 dashboard: localhost:8858 # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制 web-context-unify: false # 将刚刚编写的请求映射设定为限流页面 block-page: /BorrowController/blocked # 开启feign对sentinel的支持 feign: sentinel: enabled: true
添加好UserClient.java和 UserFallbackCilent.java用于接口降级
UserClient.java
package com.test.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; /** * @Date 2023/3/29 - 13:53 */ @FeignClient(value="userservice",fallback = UserFallbackCilent.class)//声明为userservice服务的HTTP请求客户端 public interface UserClient { @RequestMapping("/UserController/hello/{user}") String hello(@PathVariable("user") String user); }
UserFallbackCilent.java
package com.test.client; import org.springframework.stereotype.Component; /** * @Date 2023/3/30 - 14:22 */ @Component public class UserFallbackCilent implements UserClient { @Override public String hello(String user) { return "error:"+user; } }
正常重启请求,然后把userservice停止服务,会走替代方案
userservice停止服务后
传统的RestTemplate
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言