最近正准备用阿里Sentinel,发现RESTful接口支持的不是很好。有些童鞋可能对Sentinel不是很了解,我们先简单介绍一下。

Sentinel简介

Sentinel是一套阿里巴巴开源的流量防卫框架,Github地址是:https://github.com/alibaba/Sentinel。随着微服务的流行,服务与服务之间的稳定性越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

更多介绍可以在Github文档中了解。

问题描述

在Spring MVC或者Spring Boot中的RESTful接口中,有大量的@PathVariable注解,也就是把参数放在URL里,比如:

@RestController
public class DemoController {@GetMapping(value = "/hello/{name}")public String helloWithName(@PathVariable String name) {return "Hello, " + name;}
}

但是在Sentinel中把每一次请求的URL作为唯一的资源名,进行匹配和流量控制的,这就造成了一个接口本应是一个资源却被当作多个资源看待,无法达到流量控制的目的。

白嫖小贴士:什么是资源?只要通过 Sentinel API 包围起来的代码,就是资源,能够被 Sentinel 保护起来。例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。每一个资源都有自己唯一的资源名,用于标识这个资源。

查找问题原因

问题的根本原因就在于:Sentinel是如何把每一次请求URL作为唯一的资源名的?阅读和调试Sentinel的源码后,我找到CommonFilterdoFilter方法,以下是主要代码:

//调用filterTarget方法获取当前请求的URL
String target = FilterUtil.filterTarget(sRequest);
UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
if (urlCleaner != null) {target = urlCleaner.clean(target);
}
if (!StringUtil.isEmpty(target)) {String origin = parseOrigin(sRequest);String contextName = webContextUnify ? WebServletConfig.WEB_SERVLET_CONTEXT_NAME : target;ContextUtil.enter(contextName, origin);if (httpMethodSpecify) {//如果配置加HTTP方法名做前缀,URL前加HTTP方法名后作为资源名。String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + COLON + target;urlEntry = SphU.entry(pathWithHttpMethod, ResourceTypeConstants.COMMON_WEB, EntryType.IN);} else {//如果不加HTTP方法名做前缀,就直接使用URL作为资源名。urlEntry = SphU.entry(target, ResourceTypeConstants.COMMON_WEB, EntryType.IN);}
}

在上面的代码中,我们看见了请求URL作为资源名的整个过程,同时也发现有一个UrlCleaner接口,请求URL会经过它的clean方法进行处理。我们就在这个UrlCleaner接口上做文章了。

解决方案

RestfulPattern

首先我们先创建一个类,用来存放URL和对应的正则表达式:

package onemore.study.sentineldemo;import java.util.regex.Pattern;/*** @author 万猫学社*/
public class RestfulPattern implements Comparable<RestfulPattern> {private Pattern pattern;private String realResource;public RestfulPattern(Pattern pattern, String realResource) {this.pattern = pattern;this.realResource = realResource;}public Pattern getPattern() {return pattern;}public String getRealResource() {return realResource;}@Overridepublic int compareTo(RestfulPattern o) {return o.getPattern().pattern().compareTo(this.getPattern().pattern());}
}

RestfulUrlCleaner

再写一个实现UrlCleaner接口的类,在clean方法中写自己的逻辑:

package onemore.study.sentineldemo;import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @author 万猫学社*/
public class RestfulUrlCleaner implements UrlCleaner {private List<RestfulPattern> patterns = new ArrayList<>();private RestfulUrlCleaner() {}/*** 根据流量控制规则创建与之匹配的RestfulUrlCleaner* @param rules 流量控制规则* @return RestfulUrlCleaner*/public static RestfulUrlCleaner create(List<FlowRule> rules) {RestfulUrlCleaner cleaner = new RestfulUrlCleaner();if (rules == null || rules.size() == 0) {return cleaner;}Pattern p = Pattern.compile("\\{[^\\}]+\\}");for (FlowRule rule : rules) {Matcher m = p.matcher(rule.getResource());//如果发现类似{xxx}的结构,断定其为RESTful接口if (m.find()) {cleaner.patterns.add(new RestfulPattern(Pattern.compile(m.replaceAll("\\\\S+?")), rule.getResource()));}}//根据正则表达式重新排序Collections.sort(cleaner.patterns);return cleaner;}@Overridepublic String clean(String originUrl) {for (RestfulPattern pattern : patterns) {if (pattern.getPattern().matcher(originUrl).matches()) {return pattern.getRealResource();}}return originUrl;}
}

单元测试

为了验证代码的正确性,我们再写一下单元测试:

package onemore.study.sentineldemo;import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import org.junit.Assert;
import org.junit.Test;import java.util.ArrayList;
import java.util.List;/*** @author 万猫学社*/
public class RestfulUrlCleanerTest {@Testpublic void test(){List<FlowRule> rules = new ArrayList<>();rules.add(new FlowRule("/hello"));rules.add(new FlowRule("/hello/{name}"));rules.add(new FlowRule("/hello/{firstName}/{lastName}"));rules.add(new FlowRule("/hello/{firstName}/and/{lastName}"));RestfulUrlCleaner cleaner = RestfulUrlCleaner.create(rules);Assert.assertEquals("/hello", cleaner.clean("/hello"));Assert.assertEquals("/hello/{name}", cleaner.clean("/hello/onemore"));Assert.assertEquals("/hello/{firstName}/{lastName}", cleaner.clean("/hello/onemore/study"));Assert.assertEquals("/hello/{firstName}/and/{lastName}", cleaner.clean("/hello/onemore/and/study"));}
}

运行一下单元测试,发现没有错误。

设置UrlCleaner

在实际开发中,流量控制规则可能配置在Redis、ZooKeeper或者 Apollo中。无论在哪里,流量控制规则每次发生变更时都要重新设置UrlCleaner。我们就以硬编码流量控制规则为例:

package onemore.study.sentineldemo;import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;@Configuration
public class DemoConfiguration {@PostConstructpublic void initRules() {List<FlowRule> rules = new ArrayList<>();FlowRule rule = new FlowRule();rule.setResource("/hello/{name}");rule.setGrade(RuleConstant.FLOW_GRADE_QPS);//设置QPS限流阈值为1rule.setCount(1);rules.add(rule);WebCallbackManager.setUrlCleaner(RestfulUrlCleaner.create(rules));FlowRuleManager.loadRules(rules);}
}

至此,RESTful接口多资源的问题被完美解决。

文章持续更新,微信搜索「 万猫学社 」第一时间阅读。

想要年薪百万,阿里Sentinel支持RESTful接口都搞不定?相关推荐

  1. 年薪百万的程序员,上网都在看什么?

    这个问题相信不少人都好奇,我揪出一位年薪百万的程序员老友,翻遍他的收藏夹,总结整理了6个网站,甩出来给大家. 有几个干货网站大家记得当场保存,要不然划过就忘了!! 国际各行业报告: 德勤Deloitt ...

  2. 阿里、字节年薪百万的测开专家,都在关注哪些前沿技术

    近些年,随着计算机技术高速发展,软件测试行业也正处于转型期.传统的测试技术逐步淘汰,各种新的测试技术层出不穷,测试人员的薪水也水涨船高.与此同时,各大厂对测试人员的要求也越来越高.打开任意一个招聘AP ...

  3. 一名年薪百万阿里P8架构师写给Java程序员一些建议(架构师必备)

    阿里P8架构师-Peter 毕业于海南大学,曾就职于阿里.苏宁易购等一线互联公司,十余年Java从业经验,系统架构师及微服务之先驱,JEECG急速开发框架的设计与布道者.重点关注微服务设计与编排.高并 ...

  4. 年薪百万阿里大佬工资全交!家务全包!却被老婆嘲讽嫌弃!网友都怒了

    男人挣钱少会被老婆嫌弃,那挣钱多的男人是不是就没有这种烦恼了?好像也不一定. 一个阿里大佬发帖控诉老婆对自己实行精神虐待,要知道,他可是年薪150万+所有收入全部上交+家务全包,简直是"完美 ...

  5. 入职阿里巴巴,成为年薪百万阿里P7高级架构师需要必备哪些技术栈

    阿里巴巴是大部分程序员梦想的大厂,进阿里也不是一件容易的事,这里分享一份阿里内部PPT.从零开始学架构视频资料,Java系统性核心知识体系助你备战阿里 研发篇部分截图一览,感谢大佬们的分享 算法篇部分 ...

  6. 绝了!毕业10年年薪百万,今天总结一下我都学了什么!

    先从初级Java开始,当你还是一个初级Java的时候,要想拿到offer,首先要关注自己的简历,简历是让你表达自己的第一关键因素,我认为简历就是让我把自己的优势尽情展现出来,就好比我们大学时竞聘学生会 ...

  7. sentinel接入网关应用_阿里Sentinel整合Zuul网关详解

    前面我们讲解了Sentinel整合Spring Cloud Gateway,详细请查看文章:阿里Sentinel支持Spring Cloud Gateway啦 目前来说,大部分公司线上的网关应该是Zu ...

  8. 区块链技术培训—从技术小白到年薪百万区块链工程师的进阶之路

    近期,百度.小米.京东.360.联想等行业巨头纷纷开出高薪招聘区块链开发,甚至有公司开出了500万的年薪在找区块链工程师. 喂,正在埋头苦干.默默搬砖的你,有没有想过自己有一天也可以过上年薪百万的日子 ...

  9. 互联网圈都是什么人年薪百万?这份报告有真相

    允中 发自 凹非寺  量子位 报道 | 公众号 QbitAI 硕士毕业5年,总包130万,什么水平? 某厂8级,50W总包什么水平? 某厂3年,p7总包120,拿了一个香港总包.Offer 170W是 ...

最新文章

  1. git搭建局域网服务器
  2. SylixOS 无Uboot版BSP
  3. 15.RDD 创建内幕解析
  4. python2和python3的不同点_Python2和Python3的区别,新手学习Python应该如何选择
  5. 黑马程序员——生成html静态页面,方便seo,加快加载速度
  6. 基于jQuery的对象切换插件:soChange 1.5 (点击下载)
  7. mysql优化原理_【MySQL】我必须得告诉你们的MySQL优化原理3(下)INNODB配置
  8. 从M2M迁移到IIoT工业物联网
  9. Golang笔记——goroutine(协程)
  10. python画条形图-Python数据可视化:基于matplotlib绘制「条形图」
  11. windows上telnet用法 测试端口号
  12. MongoDB学习day10--数据库导入导出
  13. 5个小众视频素材网站,你知道吗?
  14. Java 设计模式最佳实践:四、结构模式
  15. 一,EAIDK-310开箱
  16. 恋爱计时:只要时间在走,我们的爱就在继续
  17. uniapp(微信小程序)上传图片到阿里oss
  18. png格式解析+java代码生成png图片
  19. springboot使用actuator
  20. openwrt系统下修改网关_OpenWRT路由配置技巧

热门文章

  1. Excel函数--SUM计算累计销量
  2. 【笔记】css实现文字横向排列/竖向排列
  3. css案例 - 评分效果的星星✨外衣
  4. 834. 树中距离之和
  5. 千亿云计算市场,相见恨晚的企业私有云存储平台
  6. ASP.NET协同OA办公服务管理平台源码
  7. vue-element-admin sidebar分析
  8. 《史蒂夫·乔布斯传》读书笔记
  9. 马云退休后首次演讲!听众是全球1500名校长
  10. 电池_电池容量的测试——手机、电池、充电器三件套之电池篇3