SpringMvc集成开源流量监控、限流、熔断降级、负载保护组件Sentinel | 京东云技术团队

前言:作者查阅了Sentinel官网、51CTO、CSDN、码农家园、博客园等很多技术文章都没有很准确的springmvc集成Sentinel的示例,因此整理了本文,主要介绍SpringMvc集成Sentinel

SpringMvc集成Sentinel

一、Sentinel 介绍

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

GitHub主页:https://github.com/alibaba/Sentinel

中文文档:https://sentinelguard.io/zh-cn/docs/introduction.html

控制台文档:https://sentinelguard.io/zh-cn/docs/dashboard.html

核心类解析:https://github.com/alibaba/Sentinel/wiki/Sentinel-核心类解析

Sentinel示例项目:https://github.com/alibaba/Sentinel/tree/master/sentinel-demo

https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example

二、Sentinel 基本概念

资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

三、springMVC集成Sentinel

这里用的是spring

<spring.version>5.3.18</spring.version>

<servlet.api.version>2.5</servlet.api.version> <sentinel.version>1.8.6</sentinel.version>

1、springmvc项目引入依赖pom

        <!-- 这是sentinel的核心依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <!-- 这是将自己项目和sentinel-dashboard打通的依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <!-- 这是使用sentinel对限流资源进行AOP -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <!--这是使用sentinel适配Web Servlet的依赖-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-web-servlet</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <!-- 这是使用sentinel热点参数限流功能依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parameter-flow-control</artifactId>
            <version>${sentinel.version}</version>
        </dependency>

2、添加配置文件

在application.properties下创建同级文件sentinel.properties,内容如下

# 集成到sentinel的项目名称
project.name=spring-sentinel-demo
# 对应的sentinel-dashboard地址
csp.sentinel.dashboard.server=localhost:8080

3、添加配置类引入配置文件

创建配置类SentinelAspectConfiguration并引入配置文件sentinel.properties

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

@Configuration
@PropertySources({@PropertySource("classpath:/sentinel.properties")})
public class SentinelAspectConfiguration {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}

4、web.xml新增如下过滤器配置

 <filter>
        <filter-name>SentinelCommonFilter</filter-name>
        <filter-class>com.alibaba.csp.sentinel.adapter.servlet.CommonFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>SentinelCommonFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

接入 filter 之后,所有访问的 Web URL 就会被自动统计为 Sentinel 的资源—来源于 https://sentinelguard.io/zh-cn/docs/open-source-framework-integrations.html

开源框架适配的Web 适配下的Web Servlet

5、创建Controller接口

@Controller
public class WebMvcTestController {

    @GetMapping("/hello")
    @ResponseBody
    public String apiHello(String id) {
        System.out.println("id = " + id);
        Date date = new Date();
        System.out.println("date = " + date);
        return "Hello!";
    }

    @GetMapping("/doBusiness")
    @ResponseBody
    public String doBusiness(String id) {
        System.out.println("doBusiness = ");
        return "Oops...";
    }
 }

6、下载控制台-即图形化实时监控平台

参考 https://sentinelguard.io/zh-cn/docs/dashboard.html Sentinel 控制台的下载和启动

访问 https://github.com/alibaba/Sentinel/releases/tag/1.8.6

点击下载

7、运行控制台

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

运行控制台后运行springmvc项目,然后访问某一个路径,就可以在控制台看到了,实时监控如果这个路径没有被访问是显示不出来的

这个时候我们配置限流为即Qps为2

8、测试限流

这里我们用postman进行压测,填写请求的路径保存。并点击run调出压测执行器

执行后查看结果如下

四、自定义异常返回

1、定义如下接口(这里只进行了常见的url定义方式的定义)

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/test")
public class WebMvcTestController {
  
    /**
     * 1.未配置流控规则 1000次请求没有间隔发起请求,应该在1秒中完成09:44:33
     * @param id
     * @return
     */
    @RequestMapping("/RequestMapping")
    @ResponseBody
    public String RequestMapping(String id) {
        return "RequestMapping!";
    }

    @GetMapping("/GetMapping")
    @ResponseBody
    public String GetMapping(String id) {
        return "GetMapping...";
    }

    @PostMapping("/PostMapping")
    @ResponseBody
    public String PostMapping(String id) {
        return "PostMapping...";
    }

    /*
    * 路径变量(Path Variables):使用花括号 {} 来标记路径中的变量,并通过 @PathVariable 注解将其绑定到方法参数上
    * */
    @GetMapping("/GetMapping/{id}")
    @ResponseBody
    public String apiFoo(@PathVariable("id") Long id) {
        return "Hello " + id;
    }

    /*
    * Ant风格的路径匹配:
    *   使用 ? 表示任意单个字符,
    *   * 表示任意多个字符(不包含路径分隔符 /),
    *   ** 表示任意多个字符(包含路径分隔符 /)。
    * */
    @GetMapping("/Ant/*/{id}")
    @ResponseBody
    public String Ant(@PathVariable("id") Long id) {
        return "Ant " + id;
    }

    /*
    * 正则表达式路径匹配:使用 @RequestMapping 注解的 value 属性结合正则表达式来定义请求路径。
    * */
    @GetMapping(value = "/users/{id:\\d+}")
    @ResponseBody
    public String pattern(@PathVariable("id") int id) {
        return "Ant " + id;
    }
}



2、自定义限流返回处理Handler

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSON;
import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;


public class SentinelExceptionHandler implements UrlBlockHandler {

    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String requestURI = request.getRequestURI();
        String url = request.getRequestURL().toString();
        System.out.println("servletPath = " + servletPath);
        System.out.println("requestURI = " + requestURI);
        System.out.println("url = " + url);
        if (contextPath == null) {
            contextPath = "";
        }
        ApplicationContext controllerApplicationContext = LiteFlowApplicationContext.getControllerApplicationContext();
        RequestMappingHandlerMapping handlerMapping = controllerApplicationContext.getBean(RequestMappingHandlerMapping.class);
        // 查找匹配的处理方法
        Class<?> returnType = null;
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            RequestMappingInfo requestMappingInfo = entry.getKey();
            HandlerMethod handlerMethod = entry.getValue();
            //匹配url
            if (requestMappingInfo.getPatternsCondition().getPatterns().contains(requestURI)) {
                System.out.println("Controller: " + handlerMethod.getBeanType().getName());
                System.out.println("Method: " + handlerMethod.getMethod().getName());
                System.out.println("getReturnType: " + handlerMethod.getMethod().getReturnType());
                returnType = handlerMethod.getMethod().getReturnType();
                break;
            }
            //匹配路径带参数的url
            UrlPathHelper pathHelper = new UrlPathHelper();
            String lookupPath = pathHelper.getPathWithinApplication(request);
            PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
            if (patternsCondition != null && patternsCondition.getMatchingPatterns(lookupPath).size() > 0) {
                System.out.println("Controller1111: " + handlerMethod.getBeanType().getName());
                System.out.println("Method111: " + handlerMethod.getMethod().getName());
                System.out.println("getReturnType: " + handlerMethod.getMethod().getReturnType());
                returnType = handlerMethod.getMethod().getReturnType();
                break;
            }
        }
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ResponseData data = null;
        if (returnType != null) {
            if (returnType == String.class) {
                String str = "返回类型为字符串方法限流";
                httpServletResponse.getWriter().write(str);
                return;
            } else if (returnType == Integer.class) {
                httpServletResponse.getWriter().write(new Integer(1));
                return;
            } else if (returnType == Long.class) {
                httpServletResponse.getWriter().write(2);
                return;
            } else if (returnType == Double.class) {

            } else if (returnType == Boolean.class) {

            } else if (returnType == Float.class) {

            } else if (returnType == Byte.class) {

            }
        }
        //BlockException 异常接口,包含Sentinel的五个异常
        // FlowException 限流异常
        // DegradeException 降级异常
        // ParamFlowException 参数限流异常
        // AuthorityException 授权异常
        // SystemBlockException 系统负载异常
        if (e instanceof FlowException) {
            data = new ResponseData(-1, "流控规则被触发......");
        } else if (e instanceof DegradeException) {
            data = new ResponseData(-2, "降级规则被触发...");
        } else if (e instanceof AuthorityException) {
            data = new ResponseData(-3, "授权规则被触发...");
        } else if (e instanceof ParamFlowException) {
            data = new ResponseData(-4, "热点规则被触发...");
        } else if (e instanceof SystemBlockException) {
            data = new ResponseData(-5, "系统规则被触发...");
        }
        httpServletResponse.getWriter().write(JSON.toJSONString(data));
    }
}

3、使用自定义限流处理类

import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SentinelConfig {


    public SentinelConfig() {
        WebCallbackManager.setUrlBlockHandler(new SentinelExceptionHandler());
    }
}

4、配置限流并测试结果

当开启限流后访问触发自定义UrlBlockHandler后结果如下

访问对应得url后控制台打印

servletPath = /test/RequestMapping
requestURI = /test/RequestMapping
url = http://localhost:8081/test/RequestMapping
Controller: org.example.WebMvcTestController
Method: RequestMapping
getReturnType: class java.lang.String
servletPath = /test/GetMapping
requestURI = /test/GetMapping
url = http://localhost:8081/test/GetMapping
Controller: org.example.WebMvcTestController
Method: GetMapping
getReturnType: class java.lang.String
servletPath = /test/PostMapping
requestURI = /test/PostMapping
url = http://localhost:8081/test/PostMapping
Controller: org.example.WebMvcTestController
Method: PostMapping
getReturnType: class java.lang.String
servletPath = /test/GetMapping/4
requestURI = /test/GetMapping/4
url = http://localhost:8081/test/GetMapping/4
Controller1111: org.example.WebMvcTestController
Method111: apiFoo
getReturnType: class java.lang.String
servletPath = /test/Ant/a/5
requestURI = /test/Ant/a/5
url = http://localhost:8081/test/Ant/a/5
Controller1111: org.example.WebMvcTestController
Method111: Ant
getReturnType: class java.lang.String
servletPath = /test/users/5
requestURI = /test/users/5
url = http://localhost:8081/test/users/5
Controller1111: org.example.WebMvcTestController
Method111: pattern
getReturnType: class java.lang.String

5、页面效果如下

五、springboot集成Sentinel

因为springboot集成文章较多,这里不多做赘述

Sentinel 与 Spring Boot/Spring Cloud 的整合见 Sentinel Spring Cloud Starter。

作者:京东健康 马仁喜

来源:京东云开发者社区 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/203673.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

git报错invalid object xxx和unable to read tree xxxxxx

电脑出问题了&#xff0c;导致git仓库像是被损坏了一样&#xff0c;执行git status就会报错unable to read ree&#xff0c;无法正常提交代码至仓库&#xff0c;原因是本地代码仓库.git文件损坏了&#xff0c;无法找到正确的提交历史和路径。 找到了一个解决办法&#xff1a; …

Android NDK项目配置CMake:一个CMakeList.txt配置多个子目录的源代码

文章目录 源码目录CMakeList.txt配置 分享一个项目的CMake配置&#xff0c;或许给你的项目配置提供参考&#xff1a; 源码目录 其中&#xff0c;除了include文件夹只包含头文件&#xff0c;其他文件夹都包含c文件和头文件&#xff1a; CMakeList.txt配置 # For more informa…

JavaScript中的异步处理方法

JavaScript中的异步处理是开发者在日常开发过程中必须面对的一个重要问题。由于JavaScript是单线程的&#xff0c;因此对于一些可能需要长时间执行的操作&#xff0c;如网络请求、IO操作等&#xff0c;如果采用同步的方式&#xff0c;可能会导致应用程序的阻塞&#xff0c;降低…

传感器:探索Android中的传感器功能与使用

传感器&#xff1a;探索Android中的传感器功能与使用 一、传感器介绍1.1 Android 平台三大类传感器1.2 Android 平台支持的传感器1.3 传感器框架 二、传感器的使用2.1 识别传感器和传感器特性2.2 针对不同制造商的传感器或传感器的不同版本优化2.3 监控传感器事件2.4 处理不同的…

什么是消息队列

什么是消息队列 MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出队列&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff0c;M…

数据链路层之差错控制、奇偶校验码、CRC循环冗余码、海明码

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

【论文笔记】SDCL: Self-Distillation Contrastive Learning for Chinese Spell Checking

文章目录 论文信息Abstract1. Introduction2. Methodology2.1 The Main Model2.2 Contrastive Loss2.3 Implementation Details(Hyperparameters) 3. Experiments代码实现个人总结值得借鉴的地方 论文信息 论文地址&#xff1a;https://arxiv.org/pdf/2210.17168.pdf Abstrac…

金融行业CRM系统:有效跟踪客户数据,实现精准营销

今年市场经济下行&#xff0c;投资趋向于保守、人们消费降级&#xff0c;对于金融行业来说影响很大。受经济形式的影响加上行业的数字化转型升级&#xff0c;金融企业都在寻求客户管理的新策略&#xff0c;维护好忠实客户、吸引新客户投资。小编认为CRM系是管理客户的不二之选&…

不想花钱用aspera?这些免费的替代方案也同样快速哦

Aspera FASP是一款高速数据传输软件&#xff0c;被广泛应用于大文件的快速传输。然而&#xff0c;Aspera FASP并不便宜&#xff0c;对于一些小型企业或个人用户来说可能无法负担。因此&#xff0c;为了满足大家的需求&#xff0c;本文将介绍一些免费且同样快速的Aspera替代方案…

python之pyqt专栏8-信号与槽4

信号重载 在上一篇python之pyqt专栏7-信号与槽3-CSDN博客&#xff0c;我们知道在自定义信号时&#xff0c;可以设定信号参数数据类型。pyqt还支持信号重载。 信号定义 sendText pyqtSignal([int],[str]) 代码意思是定义重载信号sendText&#xff0c;槽函数的参数可以是int数…

【knife4j-spring-boot】Springboot + knife4j-spring-boot 整合swagger脚手架

swagger-boostrap-ui从1.x版本到如今2.x&#xff0c;同时也更改名字Knife4j 在此记录下 knife4j-spring-boot-starter 的整合。 只需要引入knife4j-spring-boot-starter&#xff0c;无需引入其他的swagger包&#xff0c;knife4j-spring-boot-starter已经包含。 官方版本说明…

ardupilot开发 --- ROS 篇

0. 前言 关于ROS/ROS2; 1. ROS/ROS2 1.1 概念碎片 LTS&#xff1a;long term support &#xff0c;一般指长期支持的版本&#xff1b;LTS 版本意味着更稳定&#xff0c;Latest 版本意味着键兼更多的platforms和拥有更新更多的ROS packages&#xff1b;尽管已经有了LTS版本&…

深入解析进程

在现代计算机系统中&#xff0c;进程是一个核心概念&#xff0c;它代表了程序的执行实例。通过并发执行多个进程&#xff0c;计算机能够提高效率和资源利用率。 1. 进程的概念 进程是指在计算机系统中正在执行的程序的实例。每个进程都有自己的地址空间、寄存器集合、堆栈和文…

java源码-数组

背景 上传图片&#xff0c;需要对图片格式进行校验&#xff0c;这是就可以使用数组 1、什么是数组&#xff1f; Java 语言中提供的数组是用来存储固定大小的同类型元素。 如&#xff1a;可以声明一个数组变量&#xff0c;如 numbers[100] 来代替直接声明 100 个独立变量 numb…

GoLong的学习之路,进阶,Redis

这个redis和上篇rabbitMQ一样&#xff0c;在之前我用Java从原理上进行了剖析&#xff0c;这里呢&#xff0c;我做项目的时候&#xff0c;也需要用到redis&#xff0c;所以这里也将去从怎么用的角度去写这篇文章。 文章目录 安装redis以及原理redis概念redis的应用场景有很多red…

第二证券:股票几点到几点开盘?

作为股民或许投资者&#xff0c;我们都知道股票是每天都有开盘和收盘时间的。但是&#xff0c;关于股票的开盘时间&#xff0c;很多人并不是很清楚&#xff0c;特别是初学者。在本文中&#xff0c;我们将从多个视点分析股票开盘时间&#xff0c;并为大家供给一些有用的信息。 …

深度学习毕设项目 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&a…

科聪停车移动机器人整体解决方案!

停车机器人是用于在停车场或车库等场所中进行车辆的自动停放和取车操作。随着智能交通和智能城市的发展&#xff0c;停车机器人在解决停车难、提高停车效率和减少停车空间浪费等方面具有广阔的应用前景。 科聪停车机器人解决方案&#xff1a; 本方案中核心部分采用的是科聪通用…

kibana安装

kibana安装下载注意事项 地址&#xff1a;curl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.16.3-linux-x86_64.tar.gz 下载后直接解压启动即可 1. 但需要使用非root用户启动 &#xff0c;root用户启动会报错 2. kibana需要和elasticsearch版本一致 不然…

CRM系统的数据分析和报表功能对企业重要吗?

竞争日益激烈&#xff0c;企业需要更加高效地管理客户关系&#xff0c;以获取更多的商机。为此&#xff0c;许多企业选择使用CRM系统。在CRM中&#xff0c;数据分析功能扮演着重要的角色。下面就来详细说说&#xff0c;CRM系统数据分析与报表功能对企业来说重要吗&#xff1f; …