Ali-Sentinel-Spring WebMVC 流控

归档

  • GitHub: Ali-Sentinel-Spring WebMVC 流控

测试

  • 模块:sentinel-dashboard

    • 先启动 DashboardApplication
    • 访问 http://localhost:8080/#/dashboard
      • 登录:sentinel / sentinel
  • 模块:sentinel-demo-spring-webmvc

    • WebMvcDemoApplication 类的 main() 方法改成如下:
        public static void main(String[] args) {System.setProperty("csp.sentinel.dashboard.server", "127.0.0.1:8080");System.setProperty("project.name", "My-Test-8866");SpringApplication.run(WebMvcDemoApplication.class);}
    
    • 再启动 WebMvcDemoApplication
      • 访问 http://localhost:10000/hello
      • dashboard 才会显示

原理

  • demo-webmvc 依赖模块:
    • sentinel-spring-webmvc-adapter
      • 用于链路控制适配
    • sentinel-transport-simple-http (相同的有 sentinel-transport-netty-http)
      • 用于控制台交互和心跳检测

链路控制适配

  • com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor
/** 使用 SpringMVC 拦截器进行拦截,在 WebMvcConfigurer 里进行配置 */
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {private final SentinelWebMvcConfig config;public SentinelWebInterceptor() {this(new SentinelWebMvcConfig());}public SentinelWebInterceptor(SentinelWebMvcConfig config) {super(config);... // 省略}}
  • com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor
/** Sentinel 拦截器 (做控制逻辑) */
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {// 拦截前处理@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {try {String resourceName = getResourceName(request);... // resourceName 为空返回 trueif (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {return true;  // 只对首个进行拦截处理}... // 省略上下文处理Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); // 正式流控request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);                // 做记录,方便后面退出处理return true;} catch (BlockException e) {try {handleBlockException(request, response, e); // 异常处理 sign_m_010} finally {ContextUtil.exit();}return false; // 流控限制}}// sign_m_010 异常处理protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)throws Exception {if (baseWebMvcConfig.getBlockExceptionHandler() != null) {baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);} else {throw e;}}// 完成后处理@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), -1) != 0) {return; // 不在最后一个 (相当于首个) 不处理}Entry entry = getEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName());if (entry == null) {... // log warnreturn;}traceExceptionAndExit(entry, ex); // 退出处理removeEntryInRequest(request);    // 移除 request 属性ContextUtil.exit();}}
拦截器添加示例
  • com.alibaba.csp.sentinel.demo.spring.webmvc.config.InterceptorConfig
// 使用 Spring 配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {addSpringMvcInterceptor(registry);  }private void addSpringMvcInterceptor(InterceptorRegistry registry) {SentinelWebMvcConfig config = new SentinelWebMvcConfig();... // 省略其他配置config.setOriginParser(request -> request.getHeader("S-user"));// 添加到拦截器链registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");}
}

控制台交互和心跳检测

  • sentinel-transport-netty-http 模块做示例
交互服务启动
  • SPI 设置 CommandCenter 的实现为 NettyHttpCommandCenter

  • 启动栈示例:

java.lang.RuntimeException: 栈跟踪at com.alibaba.csp.sentinel.transport.command.NettyHttpCommandCenter.start(NettyHttpCommandCenter.java:46)    // sign_m_204at com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc.init(CommandCenterInitFunc.java:40)at com.alibaba.csp.sentinel.init.InitExecutor.doInit(InitExecutor.java:53)at com.alibaba.csp.sentinel.Env.<clinit>(Env.java:36)at com.alibaba.csp.sentinel.SphU.entry(SphU.java:294)at com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor.preHandle(AbstractSentinelInterceptor.java:105)... // 来自 HTTP 处理链
  • com.alibaba.csp.sentinel.transport.command.NettyHttpCommandCenter
@Spi(order = Spi.ORDER_LOWEST - 100)
public class NettyHttpCommandCenter implements CommandCenter {private final HttpServer server = new HttpServer();@Overridepublic void beforeStart() throws Exception {// sign_use_010  SPI 加载实例Map<String, CommandHandler> handlers = CommandHandlerProvider.getInstance().namedHandlers();server.registerCommands(handlers);  // 注册命令处理器}// sign_m_204@Overridepublic void start() throws Exception {new RuntimeException("栈跟踪").printStackTrace();pool.submit(new Runnable() {@Overridepublic void run() {try {server.start(); // sign_m_205} ... // catch}});}
}
  • com.alibaba.csp.sentinel.transport.command.netty.HttpServer
public final class HttpServer {private static final int DEFAULT_PORT = 8719;// sign_m_205public void start() throws Exception {... // EventLoopGrouptry {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new HttpServerInitializer()); // 增加 HttpServerHandler 到双向处理链,ref: sign_c_205int port;... // 读取 portint retryCount = 0;ChannelFuture channelFuture = null;while (true) {int newPort = getNewPort(port, retryCount); // 尝试 3 次才端口递增 1try {channelFuture = b.bind(newPort).sync();... // logbreak;} catch (Exception e) {TimeUnit.MILLISECONDS.sleep(30);... // logretryCount ++;}}... // channel 赋值} ...   // finally}}
  • com.alibaba.csp.sentinel.transport.command.netty.HttpServerHandler
// sign_c_205 Netty 入站处理器
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpRequest httpRequest = (FullHttpRequest)msg;try {CommandRequest request = parseRequest(httpRequest);             // 组装消息if (StringUtil.isBlank(HttpCommandUtils.getTarget(request))) {writeErrorResponse(BAD_REQUEST.code(), "Invalid command", ctx);return;}handleRequest(request, ctx, HttpUtil.isKeepAlive(httpRequest)); // 处理请求 sign_m_210} ... // catch}// sign_m_210 处理请求private void handleRequest(CommandRequest request, ChannelHandlerContext ctx, boolean keepAlive)throws Exception {String commandName = HttpCommandUtils.getTarget(request);CommandHandler<?> commandHandler = getHandler(commandName);         // 查找处理器if (commandHandler != null) {CommandResponse<?> response = commandHandler.handle(request);   // sign_use_100  处理命令writeResponse(response, ctx, keepAlive);} else {writeErrorResponse(BAD_REQUEST.code(), String.format("Unknown command \"%s\"", commandName), ctx);}}
}
命令处理器
  • 接口为 com.alibaba.csp.sentinel.command.CommandHandler
  • SPI 加载参考:交互服务启动 sign_use_010
    • 具体实现为:com.alibaba.csp.sentinel.command.CommandHandlerProvider #namedHandlers
  • 使用者参考:交互服务启动 sign_use_100
发送心跳
  • SPI 设置 HeartbeatSender 的实现为 HttpHeartbeatSender

  • 启动栈示例:

java.lang.RuntimeException: 心跳启动栈跟踪at com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc.scheduleHeartbeatTask(HeartbeatSenderInitFunc.java:87)   // sign_m_310at com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc.init(HeartbeatSenderInitFunc.java:61)at com.alibaba.csp.sentinel.init.InitExecutor.doInit(InitExecutor.java:53)at com.alibaba.csp.sentinel.Env.<clinit>(Env.java:36)at com.alibaba.csp.sentinel.SphU.entry(SphU.java:294)at com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor.preHandle(AbstractSentinelInterceptor.java:105)... // 来自 HTTP 处理链
  • com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc
    // sign_m_310 开启心跳定时任务private void scheduleHeartbeatTask(final HeartbeatSender sender, long interval) {new RuntimeException("心跳启动栈跟踪").printStackTrace();pool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {sender.sendHeartbeat(); // 发心跳 sign_m_320} ... // catch}}, 5000, interval, TimeUnit.MILLISECONDS);... // log}
  • com.alibaba.csp.sentinel.transport.heartbeat.HttpHeartbeatSender
@Spi(order = Spi.ORDER_LOWEST - 100)
public class HttpHeartbeatSender implements HeartbeatSender {private final Protocol consoleProtocol; // 控制台通信协议private final String consoleHost;       // 控制台 IPprivate final int consolePort;          // 控制台端口// sign_m_320 发心跳@Overridepublic boolean sendHeartbeat() throws Exception {if (StringUtil.isEmpty(consoleHost)) {return false;}URIBuilder uriBuilder = new URIBuilder();uriBuilder.setScheme(consoleProtocol.getProtocol()).setHost(consoleHost).setPort(consolePort)// setPath() 默认用 "/registry/machine" (相当于注册,这也是为什么要请求后控制台才显示)// 处理方法为 MachineRegistryController #receiveHeartBeat().setPath(TransportConfig.getHeartbeatApiPath()).setParameter("app", AppNameUtil.getAppName()).setParameter("app_type", String.valueOf(SentinelConfig.getAppType())).setParameter("v", Constants.SENTINEL_VERSION).setParameter("version", String.valueOf(System.currentTimeMillis())).setParameter("hostname", HostNameUtil.getHostName()).setParameter("ip", TransportConfig.getHeartbeatClientIp()).setParameter("port", TransportConfig.getPort()).setParameter("pid", String.valueOf(PidUtil.getPid()));HttpGet request = new HttpGet(uriBuilder.build());request.setConfig(requestConfig);// Send heartbeat request.CloseableHttpResponse response = client.execute(request);response.close();... // 省略状态判断}}
总结
  • 要有请求,才会去注册
  • 控制台才会显示服务

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全新Transformer模型:全球与局部双重突破!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;探索视觉变换器在对象重识别中的全局与局部特征 在对象重识别&#xff08;Re-ID&#xff09;的研究领域中&#xff0c;如何有效地从不同时间…

学习RAG踩坑解决记录

学习 https://zhuanlan.zhihu.com/p/675509396 和 https://zhuanlan.zhihu.com/p/668082024 文章了解RAG&#xff0c;使用langchain实现一种简单的RAG问答应用示例。 问题一&#xff1a;import langchain 报错 pydantic.errors.PydanticUserError: If you use root_validator…

奇舞周刊第527期:​Virtual DOM(虚拟DOM) 的地位再一次被挑战 !!!

奇舞推荐 ■ ■ ■ Virtual DOM&#xff08;虚拟DOM&#xff09; 的地位再一次被挑战 &#xff01;&#xff01;&#xff01; 最近在前端圈有一个 Github Repo 算是蛮受关注的 - proposal-signals&#xff0c;这是一个由 Daniel Ehrenberg 为主导&#xff0c;向 TC39 提案的项目…

多语言环境下的正则表达式实战:校验整数、小数

在软件开发中&#xff0c;正则表达式是验证用户输入数据格式的强大工具&#xff0c;特别是在处理表单验证时。本文将通过JavaScript、Java、Python、C、Rust、Go、C六种编程语言展示如何使用正则表达式来校验输入是否为整数或小数&#xff0c;特别强调小数点后最多保留两位的场…

26版SPSS操作教程(高级教程第二十章)

目录 前言 粉丝及官方意见说明 第二十章一些学习笔记 第二十章一些操作方法 神经网络与支持向量机 人工神经网络&#xff08;artificial neural network&#xff0c;ANN&#xff09; 假设数据 具体操作 结果解释 对案例的进一步分析 结果解释 ​编辑 尝试将模型复…

自动驾驶技术与传感器数据处理

目录 自动驾驶总体架构 感知系统 决策系统 定位系统 ​计算平台​ 仿真平台​ 自动驾驶公开数据集 激光点云 点云表征方式 1) 原始点云 2) 三维点云体素化 3)深度图 4)鸟瞰图 点云检测障碍物的步骤 PCL点云库 车载毫米波雷达 车载相机 设备标定 自动驾驶…

手把手带你构建一个自己的LLM训练框架【1】

手把手带你构建一个自己的LLM训练框架【1】 随着到模型的日益发展&#xff0c;构建一个属于自己的训练框架是有趣的。 计划写一系列教程来构建属于自己的训练框架。 目前项目仍在持续更新中&#xff08;未完成&#xff09;&#xff0c;详情见github项目&#xff1a;https://g…

Matlab如何导出高质量论文插图?科研效率UpUp第8期

当你用Matlab绘制了一张论文插图&#xff1a; 想要所见即所得&#xff0c;原封不动地将其保存下来&#xff0c;该怎么操作呢&#xff1f; 虽说以前总结过7种方法&#xff08;Matlab导出论文插图的7种方法&#xff09;&#xff0c;但要说哪一种可以满足上面的要求&#xff0c;想…

Flutter 中的 TextField 小部件:全面指南

Flutter 中的 TextField 小部件&#xff1a;全面指南 在 Flutter 中&#xff0c;TextField 是一个允许用户输入文本的小部件。它非常灵活&#xff0c;支持多种文本输入场景&#xff0c;如单行文本、多行文本、密码输入、数值输入等。TextField 还提供了丰富的定制选项&#xf…

UV胶固化时使用的UV灯要如何选择才适合!

近日&#xff0c;在使用UV灯固化 UV胶的过程中&#xff0c;遇到了不同的问题&#xff0c;最基本的就是很多人对于固化UV胶时&#xff0c;使用什么样的UV灯不清楚&#xff0c;从而导致了UV胶在实际使用过程中没有固化&#xff0c;或者没有完全固化&#xff0c;胶水仍处是液体流动…

2024OD机试卷-连续字母长度 (java\python\c++)

题目:连续字母长度 题目描述 给定一个 字符串 ,只包含大写字母,求在包含同一字母的子串中,长度第 k 长的子串的长度,相同字母只取最长的那个子串。 输入描述 第一行有一个子串(1<长度<=100),只包含大写字母。 第二行为 k的值 输出描述 输出连续出现次数第k多…

2024年AIGC发展趋势报告

来源&#xff1a;靠谱二次元 AIGC&#xff0c;即人工智能生成内容&#xff0c;是近年来在人工智能领域兴起的一项重要技术。 它通过使用机器学习和深度学习等技术&#xff0c;使得计算机能够自动生成各种形式的数字内容&#xff0c;如文本、图像、音频和视频等。 AIGC的发展可…

批量处理文件,高效分发数据:一键操作解决繁琐工作的技巧

在数字化时代&#xff0c;文件处理和数据分发已经成为许多行业日常工作中不可或缺的一部分。然而&#xff0c;面对大量的文件和数据&#xff0c;传统的手动处理方式往往显得繁琐且效率低下。幸运的是&#xff0c;现代技术为我们提供了办公提效工具批量处理文件&#xff0c;高效…

【谷粒商城】01-环境准备

1.下载和安装VirtualBox 地址&#xff1a;https://www.virtualbox.org/wiki/Downloads 傻瓜式安装VirtualBox 2.下载和安装Vagrant官方镜像 地址&#xff1a;https://app.vagrantup.com/boxes/search 傻瓜式安装 验证是否安装成功 打开CMD,输入vagrant命令&#xff0c;是否…

Transformer模型详解04-Encoder 结构

文章目录 简介基础知识归一化作用常用归一化 残差连接 Add & NormFeed Forward代码实现 简介 Transformer 模型中的 Encoder 层主要负责将输入序列进行编码&#xff0c;将输入序列中的每个词或标记转换为其对应的向量表示&#xff0c;并且捕获输入序列中的语义和关系。 具…

Linux基础之进程-fork()函数的详解

目录 一、前言 二、fork()函数 2.1 fork()函数的基本概念 2.2 问题一的解答 2.3 问题二的解答 2.4 问题三的解答 2.5 问题四的解答 2.6 问题五的解答 一、前言 在上节内容中我们已经学会了使用我们的getpid()和我们的getppid()去查看我们进程的pid&#xff0c;并且学习到…

Pyecharts简介

Pyecharts 是一个用于生成各种交互式图表的 Python 图表库。它是基于开源的 Echarts 图表库构建的&#xff0c;Echarts 是由百度团队开发的一个使用 JavaScript 实现的开源可视化库。Pyecharts 使得在 Python 中创建复杂的图表变得更加容易&#xff0c;它支持多种类型的图表&am…

FastAPI:Python打造高效API的终极武器

在Python的世界里&#xff0c;如果你想要一个既快速又现代的方式来构建API&#xff0c;那么FastAPI可能是你的首选。这个库基于Starlette&#xff08;用于Web编程&#xff09;和Pydantic&#xff08;用于数据验证&#xff09;&#xff0c;专门为速度和易用性设计。 什么是FastA…

【系统架构师】-案例篇(三)NoSQL与分布式对象调用

1、NoSQL 一个基于Web 2.0的大型社交网络系统。就该系统的数据架构而言&#xff0c;李工决定采用公司熟悉的数据架构&#xff0c;使用通用的商用关系型数据库&#xff0c;系统内部数据采用中央集中方式存储。该系统投入使用后&#xff0c;初期用户数量少&#xff0c;系统运行平…

【LeetCode】每日一题 2024_5_13 腐烂的橘子(经典多源 BFS)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;找出不同元素数目差数组题目描述代码与解题思路 每天进步一点点 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 好久没写每日一题题解了&#xff0c;今天重新起航 干…