赞
踩
pom.xml
文件,引入 Sentinel starter。<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
接入限流埋点
@SentinelResource
注解来完成限流的埋点,示例代码如下:@SentinelResource("resource")
public String hello() {
return "Hello";
}
当然也可以通过原始的
SphU.entry(xxx)
方法进行埋点,可以参见
Sentinel 文档
。
配置限流规则
Sentinel 提供了两种配置限流规则的方式:代码配置 和 控制台配置。本示例使用的方式为通过控制台配置。
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule = new FlowRule();
rule.setResource(str);
// set limit qps to 10
rule.setCount(10);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
首先需要获取 Sentinel 控制台,支持直接下载和源码构建两种方式。
启动控制台,执行 Java 命令
java -jar -Dserver.port=6999 sentinel-dashboard.jar
完成 Sentinel 控制台的启动。
控制台默认的监听端口为 8080,本项目修改为6999。
修改 pom.xml
文件,引入 Sentinel starter。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
@SentinelResource
注解来完成限流的埋点,示例代码如下:@SentinelResource("resource")
public String hello() {
return "Hello";
}
当然也可以通过原始的 SphU.entry(xxx)
方法进行埋点,可以参见 Sentinel 文档。
在应用的 /src/main/resources/application.properties
中添加基本配置信息
spring.cloud.sentinel.transport.dashboard=localhost:6999
Sentinel 能通过控制台动态配置规则,不过这样配置不会持久化不建议这样做,建议搭配nacos实现动态配置并且配置持久化
Sentinel 内部提供了动态规则的扩展实现 ReadableDataSource。
Sentinel starter 整合了目前存在的几类 ReadableDataSource。只需要在配置文件中进行相关配置,即可在 Spring 容器中自动注册 DataSource。
比如要定义两个ReadableDataSource,分别是 FileRefreshableDataSource
和 NacosDataSource
,配置如下:
spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=degrade
spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
ds1
和 ds2
表示ReadableDataSource的名称,可随意编写。ds1
和 ds2
后面的 file
和 nacos
表示ReadableDataSource的类型。
目前支持file
, nacos
, zk
, apollo
这4种类型。
其中nacos
,zk
,apollo
这3种类型的使用需要加上对应的依赖sentinel-datasource-nacos
, sentinel-datasource-zookeeper
, sentinel-datasource-apollo
。
当ReadableDataSource加载规则数据成功的时候,控制台会打印出相应的日志信息:
[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule
[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule
public class CustomUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
// todo add your logic
}
}
WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler());
@SentinelResource
注解下的限流异常处理@SentinelResource
注解的 blockHandler
属性(针对所有类型的 BlockException
,需自行判断)或 fallback
属性(针对熔断降级异常),注意对应方法的签名和位置有限制,详情见 Sentinel 注解支持文档。示例实现如下:public class TestService { // blockHandler 是位于 ExceptionUtil 类下的 handleException 静态方法,需符合对应的类型限制. @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) public void test() { System.out.println("Test"); } // blockHandler 是位于当前类下的 exceptionHandler 方法,需符合对应的类型限制. @SentinelResource(value = "hello", blockHandler = "exceptionHandler") public String hello(long s) { return String.format("Hello at %d", s); } public String exceptionHandler(long s, BlockException ex) { // Do some log here. ex.printStackTrace(); return "Oops, error occurred at " + s; } } public final class ExceptionUtil { public static void handleException(BlockException ex) { System.out.println("Oops: " + ex.getClass().getCanonicalName()); } }
Sentinel
的理念是开发者只需要关注资源的定义,当资源定义成功后可以动态增加各种流控降级规则。Sentinel
提供两种方式修改规则:
loadRules
)DataSource
适配不同数据源修改通过 API 修改比较直观,可以通过以下几个 API 修改不同的规则:
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则
SystemRuleManager.loadRules(List<SystemRule> rules); // 修改系统规则
AuthorityRuleManager.loadRules(List<AuthorityRule> rules); // 修改授权规则
上述loadRules()
方法只接受内存态的规则对象,但更多时候规则存储在文件、数据库或者配置中心当中。DataSource
接口给我们提供了对接任意配置源的能力。相比直接通过 API 修改规则,实现DataSource
接口是更加可靠的做法。
我们推荐通过控制台设置规则后将规则推送到统一的规则中心,客户端实现ReadableDataSource
接口端监听规则中心实时获取变更,流程如下:
DataSource
扩展常见的实现方式有:
实现拉模式的数据源最简单的方式是继承AutoRefreshDataSource
抽象类,然后实现readSource()
方法,在该方法里从指定数据源读取字符串格式的配置数据。比如基于文件的数据源。
实现推模式的数据源最简单的方式是继承AbstractDataSource
抽象类,在其构造方法中添加监听器,并实现readSource()
从指定数据源读取字符串格式的配置数据。比如基于 Nacos 的数据源。
控制台通常需要做一些改造来直接推送应用维度的规则到配置中心。功能示例可以参考AHAS Sentinel 控制台的规则推送功能。改造指南可以参考在生产环境中使用 Sentinel 控制台。
通常需要调用以下方法将数据源注册至指定的规则管理器中:
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser);
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
若不希望手动注册数据源,可以借助 Sentinel 的InitFunc
SPI 扩展接口。只需要实现自己的InitFunc
接口,在init
方法中编写注册数据源的逻辑。比如:
package com.test.init;
public class DataSourceInitFunc implements InitFunc {
@Override
public void init() throws Exception {
final String remoteAddress = "localhost";
final String groupId = "Sentinel:Demo";
final String dataId = "com.alibaba.csp.sentinel.demo.flow.rule";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
}
接着将对应的类名添加到位于资源目录(通常是resource
目录)下的META-INF/services
目录下的com.alibaba.csp.sentinel.init.InitFunc
文件中,比如:
com.test.init.DataSourceInitFunc
这样,当初次访问任意资源的时候,Sentinel 就可以自动去注册对应的数据源了。
Sentinel Dashboard通过客户端自带的规则 API来实时查询和更改内存中的规则。
注意: 要使客户端具备规则 API,需在客户端引入以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentienl-http-simple-transport</artifactId>
<version>x.y.z</version>
</dependency>
这个示例展示 Sentinel 是如何从文件获取规则信息的。FileRefreshableDataSource
会周期性的读取文件以获取规则,当文件有更新时会及时发现,并将规则更新到内存中。使用时只需添加以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
<version>x.y.z</version>
</dependency>
Nacos是阿里中间件团队开源的服务发现和动态配置中心。Sentinel 针对 Nacos 作了适配,底层可以采用 Nacos 作为规则配置数据源。使用时只需添加以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>x.y.z</version>
</dependency>
然后创建NacosDataSource
并将其注册至对应的 RuleManager 上即可。比如:
// remoteAddress 代表 Nacos 服务端的地址
// groupId 和 dataId 对应 Nacos 中相应配置
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
详细示例可以参见sentinel-demo-nacos-datasource。
Sentinel 针对 ZooKeeper 作了相应适配,底层可以采用 ZooKeeper 作为规则配置数据源。使用时只需添加以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-zookeeper</artifactId>
<version>x.y.z</version>
</dependency>
然后创建ZookeeperDataSource
并将其注册至对应的 RuleManager 上即可。比如:
// remoteAddress 代表 ZooKeeper 服务端的地址
// path 对应 ZK 中的数据路径
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ZookeeperDataSource<>(remoteAddress, path, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
详细示例可以参见sentinel-demo-zookeeper-datasource。
Sentinel 针对Apollo作了相应适配,底层可以采用 Apollo 作为规则配置数据源。使用时只需添加以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<version>x.y.z</version>
</dependency>
然后创建ApolloDataSource
并将其注册至对应的 RuleManager 上即可。比如:
// namespaceName 对应 Apollo 的命名空间名称
// ruleKey 对应规则存储的 key
// defaultRules 对应连接不上 Apollo 时的默认规则
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName, ruleKey, defaultRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
详细示例可以参见sentinel-demo-apollo-datasource。
Sentinel
核心库目前已可用于生产环境,目前除了阿里巴巴以外,也有多家企业在生产环境中使用它们。
生产环境的 Sentinel Dashboard
需要具备下面几个特性:
sentinel-core
提供 API 和扩展接口来接收信息。开发者需要根据自己的环境,选取一个可靠的推送规则方式;同时,规则最好在控制台中集中管理。sentinel-core
记录秒级的资源运行情况,并且提供 API 来拉取资源运行信息。当机器大于一台以上的时候,可以通过 Dashboard
来拉取,聚合,并且存储这些信息。这个时候,Dashboard
需要有一个存储媒介,来存储历史运行情况。由于开发者有各自不一样的环境和需求,我们会对“规则管理和推送”,“监控”这两个方面给出建议以及最佳实践;对于鉴权,由于每个开发者的环境都不一样,我们在最佳实践中仅仅使用了简单的认证。开发者可以依循自己的需求,生产环境,选择最适合自己的方式。
一般来说,规则的推送有下面三种模式:
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource ) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource ), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource ),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
如果不做任何修改,Dashboard的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的WritableDataSourceRegistry
中。以本地文件数据源为例:
public class FileDataSourceInit implements InitFunc { @Override public void init() throws Exception { String flowRulePath = "xxx"; ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>( flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}) ); // 将可读数据源注册至 FlowRuleManager. FlowRuleManager.register2Property(ds.getProperty()); WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson); // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. WritableDataSourceRegistry.registerFlowDataSource(wds); } private <T> String encodeJson(T t) { return JSON.toJSONString(t); } }
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-40hVXh3i-1655885717026)(https://camo.githubusercontent.com/991ac2f6a21b0a54263576341d3beca9edc93389/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f6c61726b2f302f323031382f706e672f34373638382f313533363636303331313832362d61646466346666362d396663392d343538362d626138622d3463616633613931343537642e706e67)]
首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。
这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
我们提供了 ZooKeeper, Apollo, Nacos 等的动态数据源实现。以 ZooKeeper 为例子,如果要使用第三方的配置中心作为配置管理,您需要做下面的几件事情:
/sentinel_rules/{appName}/{ruleType}
,e.g.sentinel_rules/appA/flowRule
)。InMemFlowRuleStore
),可以对其进行改造使其支持应用维度的规则缓存(key 为 appName),每次添加/修改/删除规则都先更新内存中的规则缓存,然后需要推送的时候从规则缓存中获取全量规则,然后通过上面实现的 Client 将规则推送到 ZooKeeper 即可。从 Sentinel 1.4.0 开始,Sentinel 控制台提供DynamicRulePublisher
和DynamicRuleProvider
接口用于实现应用维度的规则推送和拉取,并提供了相关的示例。Sentinel 提供应用维度规则推送的示例页面(/v2/flow
),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。改造详情可参考应用维度规则推送示例。
Sentinel 会记录资源访问的秒级数据(若没有访问则不进行记录)并保存在本地日志中,具体格式请见秒级监控日志文档。Sentinel 控制台可以通过Sentinel 客户端预留的 HTTP API从秒级监控日志中拉取监控数据,并进行聚合。
目前 Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。若需要监控数据持久化的功能,可以自行扩展实现MetricsRepository
接口(0.2.0 版本),然后注册成 Spring Bean 并在相应位置通过@Qualifier
注解指定对应的 bean name 即可。MetricsRepository
接口定义了以下功能:
save
与saveAll
:存储对应的监控数据queryByAppAndResourceBetween
:查询某段时间内的某个应用的某个资源的监控数据listResourcesOfApp
:查询某个应用下的所有资源其中默认的监控数据类型为MetricEntity
,包含应用名称、时间戳、资源名称、异常数、请求通过数、请求拒绝数、平均响应时间等信息。
同时用户可以自行进行扩展,适配 Grafana 等可视化平台,以便将监控数据更好地进行可视化。
对于监控数据的存储,用户需要根据自己的存储精度,来考虑如何存储这些监控数据。
我们提供了一个 Demo,这个版本目前部署在阿里云上。通过这个版本,开发者可以看到一个完整的生产环境的控制台的功能全集。它主要包括:
使用这个 Demo,您需要申请阿里云的帐号(免费),下载一个 JAR 包。这个 JAR 包主要包含sentinel-core
、配置中心 ACM 的客户端(与 Nacos 同源)以及阿里云控制台的认证功能,认证功能保证您的机器信息等只能被您自己看到,别人无法看到。如果您不想使用这个 Demo,停止并删除这个 JAR 包即可。这个 JAR 包在运行的时候不会在您的机器上留下任何的记录。
请参考这个链接来参考如何在生产环境中使用控制台。
Awesome Sentinel里记录非常多的社区用户的一些扩展和解决方案,也欢迎大家将一些比较好的扩展实现添加进来。
Sentinel 网关流控支持针对不同的路由和自定义的 API 分组进行流控,支持针对请求属性(如 URL 参数,Client IP,Header 等)进行流控。Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
spring: # sentinel动态配置规则 cloud: sentinel: zuul: enabled: true order: pre: 2000 post: 500 error: -100 filter: enabled: false datasource: # 限流 ds1: nacos: server-addr: ${zlt.nacos.server-addr} dataId: ${spring.application.name}-sentinel-gw-flow groupId: DEFAULT_GROUP rule-type: gw-flow # api分组 ds2: nacos: server-addr: ${zlt.nacos.server-addr} dataId: ${spring.application.name}-sentinel-gw-api-group groupId: DEFAULT_GROUP rule-type: gw-api-group
绑定
gw-flow(限流)
和gw-api-group(api分组)
的规则数据源为nacos
并指定nacos
上对应的dataId
和groupId
Data ID:api-gateway-sentinel-gw-flow
Group:DEFAULT_GROUP
配置内容:
[
{
"resource": "user",
"count": 0,
"paramItem": {
"parseStrategy": 3,
"fieldName": "name"
}
},
{
"resource": "uaa_api",
"count": 0
}
]
规则1:所有
user
的请求只要参数带有name
的都拦截(qps=0),user
为zuul路由配置上的routeId
规则2:api分组为uaa_api
的所有请求都拦截(qps=0)
Data ID:api-gateway-sentinel-gw-api-group
Group:DEFAULT_GROUP
配置内容:
[
{
"apiName": "uaa_api",
"predicateItems": [
{
"pattern": "/user/login"
},
{
"pattern": "/api-uaa/oauth/**",
"matchStrategy": 1
}
]
}
]
上面配置意思为满足规则的api都统一分组为
uaa_api
分组规则1:精准匹配/user/login
分组规则2:前缀匹配/api-uaa/oauth/**
需要在接入端原有启动参数的基础上添加-Dcsp.sentinel.app.type=1
启动以将您的服务标记为 API Gateway,在接入控制台时您的服务会自动注册为网关类型,然后您即可在控制台配置网关规则和 API 分组,例如:
java -Dcsp.sentinel.app.type=1 -jar zuul-gateway.jar
API管理(分组)
网关流控规则
所有user
的请求只要参数带有name
的都拦截(qps=0)
api分组为uaa_api
的所有请求都拦截(qps=0)
/api-uaa/oauth/**
/user/login
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。