sentinel 整合dubbo限流
相关依赖
<!-- 服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- dubbo服务调用 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- sentinel dubbo限流 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
</dependency>
<!-- 数据上传到sentinel控制台 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
限流原理
在服务端、消费端添加filter实现限流
# dubbo/org.apache.dubbo.rpc.Filter
sentinel.dubbo.provider.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
sentinel.dubbo.consumer.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
dubbo.application.context.name.filter=com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter
SentinelDubboProviderFilter:服务端限流过滤器
@Activate(
group = {"provider"}
)
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized", new Object[0]);
}
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
}
String getInterfaceName(Invoker invoker, String prefix) {
return DubboUtils.getInterfaceName(invoker, prefix);
}
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
if (null == origin) {
origin = "";
}
Entry interfaceEntry = null;
Entry methodEntry = null;
String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
String interfaceResourceName = this.getInterfaceName(invoker, prefix);
String methodResourceName = this.getMethodName(invoker, invocation, prefix);
Result var10;
try {
ContextUtil.enter(methodResourceName, origin);
interfaceEntry = SphU.entry(interfaceResourceName, 2, EntryType.IN);
methodEntry = SphU.entry(methodResourceName, 2, EntryType.IN, invocation.getArguments());
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Tracer.traceEntry(result.getException(), interfaceEntry);
Tracer.traceEntry(result.getException(), methodEntry);
}
var10 = result;
return var10;
} catch (BlockException var15) {
var10 = DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, var15);
} catch (RpcException var16) {
Tracer.traceEntry(var16, interfaceEntry);
Tracer.traceEntry(var16, methodEntry);
throw var16;
} finally {
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
ContextUtil.exit();
}
return var10;
}
}
SentinelDubboConsumerFilter:消费端限流过滤器
@Activate(
group = {"consumer"}
)
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
public SentinelDubboConsumerFilter() {
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized", new Object[0]);
}
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
}
String getInterfaceName(Invoker invoker, String prefix) {
return DubboUtils.getInterfaceName(invoker, prefix);
}
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
return InvokeMode.SYNC == invokeMode ? this.syncInvoke(invoker, invocation) : this.asyncInvoke(invoker, invocation);
}
private Result syncInvoke(Invoker<?> invoker, Invocation invocation) {
Entry interfaceEntry = null;
Entry methodEntry = null;
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
String interfaceResourceName = this.getInterfaceName(invoker, prefix);
String methodResourceName = this.getMethodName(invoker, invocation, prefix);
Result var9;
try {
interfaceEntry = SphU.entry(interfaceResourceName, 2, EntryType.OUT);
methodEntry = SphU.entry(methodResourceName, 2, EntryType.OUT, invocation.getArguments());
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Tracer.traceEntry(result.getException(), interfaceEntry);
Tracer.traceEntry(result.getException(), methodEntry);
}
var9 = result;
return var9;
} catch (BlockException var14) {
var9 = DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, var14);
} catch (RpcException var15) {
Tracer.traceEntry(var15, interfaceEntry);
Tracer.traceEntry(var15, methodEntry);
throw var15;
} finally {
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
}
return var9;
}
private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) {
LinkedList<SentinelDubboConsumerFilter.EntryHolder> queue = new LinkedList();
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
String interfaceResourceName = this.getInterfaceName(invoker, prefix);
String methodResourceName = this.getMethodName(invoker, invocation, prefix);
try {
queue.push(new SentinelDubboConsumerFilter.EntryHolder(SphU.asyncEntry(interfaceResourceName, 2, EntryType.OUT), (Object[])null));
queue.push(new SentinelDubboConsumerFilter.EntryHolder(SphU.asyncEntry(methodResourceName, 2, EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments()));
Result result = invoker.invoke(invocation);
result.whenCompleteWithContext((r, throwable) -> {
Throwable error = throwable;
if (throwable == null) {
error = (Throwable)Optional.ofNullable(r).map(Result::getException).orElse((Object)null);
}
while(!queue.isEmpty()) {
SentinelDubboConsumerFilter.EntryHolder holder = (SentinelDubboConsumerFilter.EntryHolder)queue.pop();
Tracer.traceEntry(error, holder.entry);
this.exitEntry(holder);
}
});
return result;
} catch (BlockException var8) {
while(!queue.isEmpty()) {
this.exitEntry((SentinelDubboConsumerFilter.EntryHolder)queue.pop());
}
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, var8);
}
}
private void exitEntry(SentinelDubboConsumerFilter.EntryHolder holder) {
if (holder.params != null) {
holder.entry.exit(1, holder.params);
} else {
holder.entry.exit();
}
}
static class EntryHolder {
private final Entry entry;
private final Object[] params;
public EntryHolder(Entry entry, Object[] params) {
this.entry = entry;
this.params = params;
}
}
}
BaseSeninelDubboFilter
public abstract class BaseSentinelDubboFilter implements Filter {
public BaseSentinelDubboFilter() {
}
abstract String getMethodName(Invoker var1, Invocation var2, String var3);
abstract String getInterfaceName(Invoker var1, String var2);
}
Filter
@SPI
public interface Filter {
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
public interface Listener {
void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
}
}
使用示例
**********
服务端
application.yml
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
dubbo:
#registry:
# address: spring-cloud://localhost:8848
protocol:
name: dubbo
port: 20880
HelloService
public interface HelloService {
String hello();
}
HelloServiceImpl
@DubboService
public class HelloServiceImpl implements HelloService {
@Override
public String hello() {
return "hello";
}
}
DemoApplication
@EnableDubbo //开启dubbo
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
**********
消费端
application.yml
spring:
application:
name: nacos-consuemr
cloud:
nacos:
discovery:
server-addr: localhost:8848
dubbo:
protocol:
name: dubbo
port: 20881
server:
port: 8081
HelloService
public interface HelloService {
String hello();
}
HelloController
@RestController
public class HelloController {
@DubboReference
private HelloService helloService;
@RequestMapping("/hello")
public String hello(){
System.out.println(helloService.hello());
return "success";
}
}
DemoApplication
@EnableDubbo
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
**********
本地限流配置
CustomFlowRule
public class CustomFlowRule implements InitFunc {
@Override
public void init() throws Exception {
List<FlowRule> flowRules = new ArrayList<>();
FlowRule flowRule = new FlowRule();
flowRule.setResource("com.example.demo.service.HelloService"); //限制接口,对所有的接口方法限流(加总限流)
//flowRule.setResource("com.example.demo.service.HelloService:hello()");
//单个接口方法限流
//flowRule.setResource("com.example.demo.service.HelloService:hello(java.lang.String)");
//单个接口方法限流,方法有参数
flowRule.setCount(1);
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
flowRules.add(flowRule);
FlowRuleManager.loadRules(flowRules);
}
}
META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
com.example.demo.rule.CustomFlowRule
jmeter 测试
2022-03-31 13:13:34.076 INFO 3296 --- [ main] o.a.d.config.bootstrap.DubboBootstrap : [DUBBO] DubboBootstrap has started., dubbo version: 2.7.8, current host: 192.168.5.12
2022-03-31 13:13:34.077 INFO 3296 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 3.577 seconds (JVM running for 4.167)
2022-03-31 13:13:35.001 WARN 3296 --- [client.listener] a.c.d.m.r.DubboServiceMetadataRepository : Current application will subscribe all services(size:2) in registry, a lot of memory and CPU cycles may be used, thus it's strongly recommend you using the externalized property 'dubbo.cloud.subscribed-services' to specify the services
2022-03-31 13:13:39.904 INFO 3296 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-03-31 13:13:39.904 INFO 3296 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-03-31 13:13:39.920 INFO 3296 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms
hello
2022-03-31 13:13:39.980 ERROR 3296 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.000 ERROR 3296 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.005 ERROR 3296 --- [nio-8081-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.010 ERROR 3296 --- [nio-8081-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.015 ERROR 3296 --- [nio-8081-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.019 ERROR 3296 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.024 ERROR 3296 --- [nio-8081-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.028 ERROR 3296 --- [nio-8081-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:13:40.032 ERROR 3296 --- [nio-8081-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
**********
控制台限流配置
启动sentinel dashboard
java -Dserver.port=8000 -Dcsp.sentinel.dashboard.server=localhost:8000 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
参数说明:
-Dserver.port=8080:指定控制台启动端口
-Dcsp.sentinel.dashboard.server:指定控制台地址和端口
-Dproject.name=sentinel-dashboard:指定控制台项目名称
nacos-provider添加启动参数
-Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8000 -Dproject.name=nacos-provider
nacos-consumer添加启动参数
-Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8000 -Dproject.name=nacos-consumer
sentinel控制台
jmeter 测试
2022-03-31 13:38:51.158 INFO 3815 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 3.814 seconds (JVM running for 4.334)
2022-03-31 13:38:52.060 WARN 3815 --- [client.listener] a.c.d.m.r.DubboServiceMetadataRepository : Current application will subscribe all services(size:2) in registry, a lot of memory and CPU cycles may be used, thus it's strongly recommend you using the externalized property 'dubbo.cloud.subscribed-services' to specify the services
2022-03-31 13:38:58.567 INFO 3815 --- [erverWorker-4-1] o.a.d.r.t.netty4.NettyServerHandler : [DUBBO] The connection of /192.168.5.12:53422 -> /192.168.5.12:20881 is established., dubbo version: 2.7.8, current host: 192.168.5.12
2022-03-31 13:39:11.710 INFO 3815 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-03-31 13:39:11.710 INFO 3815 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-03-31 13:39:11.726 INFO 3815 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms
hello
hello
2022-03-31 13:39:34.726 ERROR 3815 --- [nio-8081-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.732 ERROR 3815 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.737 ERROR 3815 --- [nio-8081-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.742 ERROR 3815 --- [nio-8081-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.746 ERROR 3815 --- [nio-8081-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.751 ERROR 3815 --- [io-8081-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.755 ERROR 3815 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]
2022-03-31 13:39:34.759 ERROR 3815 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: SentinelBlockException: FlowException] with root cause
java.lang.RuntimeException: SentinelBlockException: FlowException
at com.alibaba.csp.sentinel.slots.block.BlockException.block(BlockException:0) ~[sentinel-core-1.8.0.jar:1.8.0]