Spring cloud gateway 整合 sentinel 做限流和熔断(最新)

作者:青山常在人不老   阅读 (1822)  |  收藏 (0)  |  点赞 (0)

摘要

本文将会讲述Spring cloud 整合sentinel 做限流和熔断的整合过程,本文的代码均为自己经过实际测试验证通过的,绝对不是什么网上一大抄抄来的垃圾文章。


原文链接:Spring cloud gateway 整合 sentinel 做限流和熔断(最新)

前言

凡是能看到这个页面的朋友,大概都是知道Spring Cloud gateway本身有自己原生的限流配置,但是由于Spring cloud Gateway中的那些限流配置不太适合生产实际使用,我才决定使用Sentinel来做限流和熔断。

Sentinel提供了限流、熔断、以及和数据持久化的配置,因此我认为Sentinel和Spring cloud 原生的限流和熔断机制(hystrix)相比,它简直是太强大了。

一些误区说明

要使用Sentinel ,首先要了解的一个情况是我们必须使用它的可视化界面dashboard,为什么呢,因为我们要用这个页面做可视化的限流、熔断规则配置啊;其次,网上有绝大多数Spring Cloud 整合Sentinel的过程中都使用到了nacos,当时我很不解,为什么非要把nacos和Sentinel放在一起使用呢?现在我理解了,我给你们一个明确的答复:除非你们要用nacos做服务发现、配置中心,否则并非一定非要Sentinel和nacos一起使用(Spring cloud 的服务发现Eureka和Nacos不能同时使用,你看着办哦)。

至于为什么网上很多教程要把Sentinel和nacos一起使用,是因为他们要用nacos当做Sentinel的持久化配置的工具(呵呵),但实际上,Sentinel支持很多 持久化数据库,打脸要证明的哈,来一个图让你们看:

Sentinel支持的持久化数据库类型:file, nacos, zk, apollo,redis 这5种类型


啥,还不信,来链接来:https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md

哦,我又看到了另一个引用的地方,这个地方说明的更详细,甚至连demo都准备好了https://github.com/alibaba/Sentinel/wiki/Dynamic-Rule-Configuration

至此,我可以说,如果你只是做限流和熔断,我们不用必须把Sentinel和Nacos联合起来一起使用(经历了迷茫和疑惑,我可以自豪的说,那些抄来的垃圾文章,你们滚蛋吧)。

开始配置

从上一章我们可以分析到,配置的前提是 :

  1. 我们要安装Sentinel dashboard

  2. 我们整合Sentinel不必非要使用Nacos

  3. 我决定使用Redis作为Sentinel持久化的工具(分布式最好的选择)

安装Sentinel dashboard

首先我们要进行第一步,安装Sentinel dashboard,请访问最新的Sentinel dashboard 进行下载,我使用的V1.7.2。请下载sentinel-dashboard-x.y.x.jar

image.png

下载到本地后,请使用如下命令运行这个jar(注意,你的机器必须安装了JDK,且请替换下面的jar的名字)

java -jar sentinel-dashboard-1.7.2.jar -Dserver.port=8719 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard

注意:

  1. Dserver.port=8719指的是你的程序和Sentinel交互时使用的端口(http端口)

  2. -Dcsp.sentinel.dashboard.server=localhost:8080指的是你在浏览器中访问的Sentinel dashboard的URL

启动成功后,通过浏览器访问以下链接,即可进入Sentinel dashboard登录页面

http://localhost:8080/

Sentinel dashboard 登录页面

用户名和密码均为sentinel

登录成功后,进入dashboard首页(默认没有应用接入,空屏)

dashboard首页

至此,Sentinel dashboard 安装完毕

配置Spring Cloud Gateway

修改Pom文件

向Gateway的pom中添加如下依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

 注意:


  1. 如果引用报错,请参考另一篇文章配置Spring cloud 整合 alibaba.cloud pom依赖报错的解决办法解决

  2. 以上依赖不保证会多引用了,但是保证能用(这一点确实是我偷懒了,因为我发现官方文档在这里也是东一锤子西一棒子,弄得我索性全引进来了)

修改application.yaml文件

spring:
  # 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
  ## 简单尝试
  profiles: route_test
  application:
    # 应用名称
    name: bobfintech-gateway
  cloud:
    sentinel:
      transport: #dashboard地址
        dashboard: localhost:8080  #对应自己的sentinel控制台端口
        port: 8719 
    gateway:
      discovery:
        locator:
          # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
          enabled: true
      # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
      routes:
      # 路由标识(id:标识,具有唯一性)   简单尝试
      - id: route_simple
        # 目标服务地址(uri:地址,请求转发后的地址)
        uri: lb://bobfintech-userservice
        # 路由条件(predicates:断言,匹配 HTTP 请求内容)
        predicates:
        ## 转发地址格式为 uri/archive
        - Path=/archive

注意,这里的关键是sentinel那些配置,必须有的哦,详细的参数配置可以看下官方原文:https://github.com/alibaba/Sentinel/wiki/Common-Configuration 

创建sentinel config

package cn.com.xxxx.sentinel;

import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;

/***
 * @project: xxxx
 * @description: sentinel config
 * @version 1.0.0
 * @errorcode
 *            错误码: 错误描述
 * @author
 *         <li>2020-07-15 guopengfei@xxxx.com.cn Create 1.0
 * @copyright ©2019-2020 xxxx,版权所有。
 *            //
 */
    @Configuration
    public class GatewayConfiguration {
    
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;
    
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
    ServerCodecConfigurer serverCodecConfigurer) {
    this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
    this.serverCodecConfigurer = serverCodecConfigurer;
    }
    
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
    // Register the block exception handler for Spring Cloud Gateway.
    return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
    
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
    return new SentinelGatewayFilter();
    }

 // @PostConstruct
 // public void doInit() {
 // initCustomizedApis();
 // initGatewayRules();
 // }

 // private void initCustomizedApis() {
 // Set<ApiDefinition> definitions = new HashSet<>();
 // ApiDefinition api1 = new ApiDefinition("some_customized_api")
 // .setPredicateItems(new HashSet<ApiPredicateItem>() {{
 // add(new ApiPathPredicateItem().setPattern("/ahas"));
 // add(new ApiPathPredicateItem().setPattern("/product/**")
 // .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
 // }});
 // ApiDefinition api2 = new ApiDefinition("another_customized_api")
 // .setPredicateItems(new HashSet<ApiPredicateItem>() {{
 // add(new ApiPathPredicateItem().setPattern("/**")
 // .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
 // }});
 // definitions.add(api1);
 // definitions.add(api2);
 // GatewayApiDefinitionManager.loadApiDefinitions(definitions);
 // }

 // private void initGatewayRules() {
 // Set<GatewayFlowRule> rules = new HashSet<>();
 // rules.add(new GatewayFlowRule("aliyun_route")
 // .setCount(10)
 // .setIntervalSec(1)
 // );
 // rules.add(new GatewayFlowRule("aliyun_route")
 // .setCount(2)
 // .setIntervalSec(2)
 // .setBurst(2)
 // .setParamItem(new GatewayParamFlowItem()
 // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
 // )
 // );
 // rules.add(new GatewayFlowRule("httpbin_route")
 // .setCount(10)
 // .setIntervalSec(1)
 // .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
 // .setMaxQueueingTimeoutMs(600)
 // .setParamItem(new GatewayParamFlowItem()
 // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
 // .setFieldName("X-Sentinel-Flag")
 // )
 // );
 // rules.add(new GatewayFlowRule("httpbin_route")
 // .setCount(1)
 // .setIntervalSec(1)
 // .setParamItem(new GatewayParamFlowItem()
 // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
 // .setFieldName("pa")
 // )
 // );
 // rules.add(new GatewayFlowRule("httpbin_route")
 // .setCount(2)
 // .setIntervalSec(30)
 // .setParamItem(new GatewayParamFlowItem()
 // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
 // .setFieldName("type")
 // .setPattern("warn")
 // .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS)
 // )
 // );
 //
 // rules.add(new GatewayFlowRule("some_customized_api")
 // .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
 // .setCount(5)
 // .setIntervalSec(1)
 // .setParamItem(new GatewayParamFlowItem()
 // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
 // .setFieldName("pn")
 // )
 // );
 // GatewayRuleManager.loadRules(rules);
 // }
 }

注意,这个配置类的作用是:

  1. 配置路由规则(被我注释掉了,因为我在application.yam中已经配置了,如果你想在程序中配置,请放开注释)

  2. 配置API组,这个组(不好意思啊,给忘了这个干啥了,自己翻去吧,哈哈哈嗝~)

  3. 这两个作用并非必须的,是否注释看你的需求,我本身没这个需求,所以注释掉了

还需要配置啥

不好意思,这个真没了(启动类不需要因为加入Sentinel而做任何的改变)

启动项目

请运行你的Spring Cloud Gateway启动类,进行启动。启动成功后,请通过浏览器或者postman随便访问下你的gateway中的任意一个controller地址(很重要哦,要是不听话,你一会可能因为在Sentinel dashboard中看不到你的应用而骂娘,而且注意不要是Login、logout的那些东西)。

当你访问Controller成功后,前往Sentinel dashboard查看(刷新),是否已经看到了你的应用了呢,如果看到了,恭喜您,你可以在Sentinel dashboard配置你的限流和熔断了(这个下一篇将会讲解);如果仍旧没有看到,请检查application中配置的ip/端口等是否正确(还不行的话,您留言吧,我真没遇到这个问题)。

总结

经过这篇文章,现在遗留的问题如下:

  1. 持久化Redis配置

  2. 如何配置限流和熔断

这将在下一篇文章中进行分析,请进入我的空间查看系列文章

各位亲朋好友,如果您认为这个文章不错,请多多转发,让更多的人走出迷雾,走向光明大道。

分类   Spring boot 开发
字数   8389

博客标签    Spring Cloud 整合Sentinel  

评论