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

Sentinel简介

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

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

问题描述

在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 保护起来。例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。每一个资源都有自己唯一的资源名,用于标识这个资源。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

查找问题原因

问题的根本原因就在于: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接口上做文章了。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

解决方案

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;
} @Override
public 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;
} @Override
public 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 { @Test
public 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"));
}
}

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

设置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 {
@PostConstruct
public void initRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("/hello/{name}");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置QPS限流阈值为1
rule.setCount(1);
rules.add(rule); WebCallbackManager.setUrlCleaner(RestfulUrlCleaner.create(rules));
FlowRuleManager.loadRules(rules);
}
}

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

微信公众号:万猫学社

微信扫描二维码

获得更多Java技术干货

想要年薪百万,阿里Sentinel支持RESTful接口都搞不定?的更多相关文章

  1. 阿里Sentinel支持Spring Cloud Gateway啦

    1. 前言 4月25号,Sentinel 1.6.0 正式发布,带来 Spring Cloud Gateway 支持.控制台登录功能.改进的热点限流和注解 fallback 等多项新特性,该出手时就出 ...

  2. 阿里Sentinel整合Zuul网关详解

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

  3. 7年老Android收到阿里offer,跟领导提离职被怼:为年薪百万不做兄弟?

    在当今社会,钱就是衡量一个人价值的标准,如果你在一家公司,领导再怎么重用你,但是薪资待遇却很低,这样根本是很难留住人,毕竟工作就是为了赚钱,要是连工资都满足不了,谈其他根本就是扯淡. 最近在职业论坛看 ...

  4. 阿里sentinel源码研究深入

    1. 阿里sentinel源码研究深入 1.1. 前言 昨天已经把sentinel成功部署到线上环境,可参考我上篇博文,该走的坑也都走了一遍,已经可以初步使用它的限流和降级功能,根据我目前的实践,限流 ...

  5. polaris: 一个用go实现的支持restful的web框架

    介绍 polaris是一个用go实现的支持restful的web框架,主要参考tornado进行设计. 虽然在go里面搭建一个http server非常的简单,这里强烈推荐gorilla,但并没有很好 ...

  6. RestKit ,一个用于更好支持RESTful风格服务器接口的iOS库

    简介 RestKit 是一个用于更好支持RESTful风格服务器接口的iOS库,可直接将联网获取的json/xml数据转换为iOS对象. 项目主页: RestKit 最新示例: 点击下载 注意: 如果 ...

  7. 安装更新时出现一些问题,但我们稍后会重试。如果你继续看到此错误,并且想要搜索 Web 或联系支持人员以获取相关信息,以下信息可能会对你有帮助: (0x80070426)

    安装更新时出现一些问题,但我们稍后会重试.如果你继续看到此错误,并且想要搜索 Web 或联系支持人员以获取相关信息,以下信息可能会对你有帮助: (0x80070426) https://answers ...

  8. SSM整合 完美支持RESTful(Jsp和客户端<android ios...>)

    一 RESTful简介 RESTful是一种网络应用程序的设计风格和开发方式 它结构清晰 符合标准 易于理解 扩展方便 REST 即Representational State Transfer的缩写 ...

  9. 微信小程序语音识别服务搭建全过程解析(https api开放,支持新接口mp3录音、老接口silk录音)

    silk v3(或新录音接口mp3)录音转olami语音识别和语义处理的api服务(ubuntu16.04服务器上实现) 重要的写在前面 重要事项一: 所有相关更新,我优先更新到我个人博客中,其它地方 ...

随机推荐

  1. pyinstaller打包

    参考 官网:http://www.pyinstaller.org/ pyinstaller参数使用 使用spec文件 安装 Windows依赖pypiwin32,新版的pyinstaller已经包含了 ...

  2. 虚拟机体验NAS私人云全揭秘:序言——虚拟机体验NAS私人云缘由

    "世界在新冠肺炎疫情后将永远改变",对于2020春天在全球蔓延的新冠肺炎疫情,美国前国务卿基辛格做了这样的评价.确实,也改变了我们.春节期间,本着少添乱的原则,响应国家号召,自我隔 ...

  3. Nmap-脚本检测CVE漏洞

    Nmap的一个鲜为人知的部分是NSE,即Nmap Scripting Engine,这是Nmap最强大和最灵活的功能之一.它允许用户编写(和共享)简单脚本,以自动执行各种网络任务.Nmap内置了全面的 ...

  4. mybatis源码学习:从SqlSessionFactory到代理对象的生成

    目录 一.根据XML配置文件构建SqlSessionFactory 二.通过SqlSessionFactory创建SqlSession 三.getMapper获取动态代理对象 一.根据XML配置文件构 ...

  5. 树莓派3b在rt-thread上移植LittlevGL

    树莓派3b在rt-thread上移植LittlevGL 目录 树莓派3b在rt-thread上移植LittlevGL 1.本文概述 2.资源准备 3.上手体验 4.rt-thread与lvgl进行无缝 ...

  6. 探索ORACLE之ASM概念(完整版)

    探索ORACLE之ASM概念(完整版) 本文出自https://www.jb51.net/article/43527.htm ASM是Oracle 10g R2中为了简化Oracle数据库的管理而推出 ...

  7. 【转载】pyinstaller的使用和几个坑

    1.-w是不显示命令窗口,  -i 图标文件的路径  这是改变图标的,但是我发现只能改变任务栏里的和命令窗口的图标,并不能改变exe文件的图标.另外这些参数要加载pyinstaller和路径中间. 2 ...

  8. Docker简单操作(二)

    1.docker容器简单操作 docker search 镜像名 #搜索镜像.如docker search nginx docker pull alpine #拉取镜像.alpine是比较小的镜像 d ...

  9. [转] Exchange 2013 安装部署详解

    ·Exchange 2013 部署:系统要求 823 / 3 部署 系统要求 Exchange 2013 zhou_ping 2013-02-17 ·Exchange 2013 部署:先决条件 752 ...

  10. flutter中使用redux之多界面互动

    在上一篇文章,我们介绍了如何在flutter中使用redux.在上一篇文章的例子中,我们使用了单界面集成redux,但是在实际项目中,我们通常涉及多个模块,每个模块涉及多个界面,那么如何使用redux ...