Consumer.java
public static void main(String[] args) {
FlowRule flowRule = new FlowRule();
flowRule.setResource(
"com.alibaba.cloud.examples.FooService:hello(java.lang.String)");
// 设置1s内只能调用10次
flowRule.setCount(10);
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setLimitApp("default");
// 注册限流规则
FlowRuleManager.loadRules(Collections.singletonList(flowRule));
SpringApplicationBuilder consumerBuilder = new SpringApplicationBuilder();
ApplicationContext applicationContext = consumerBuilder
.web(WebApplicationType.NONE).sources(SentinelDubboConsumerApp.class)
.run(args);
FooServiceConsumer service = applicationContext.getBean(FooServiceConsumer.class);
// 测试调用15次,期望应该是有5调用失败的
for (int i = 0; i < 15; i++) {
try {
String message = service.hello("Jim");
System.out.println((i + 1) + " -> Success: " + message);
}
catch (SentinelRpcException ex) {
System.out.println("Blocked");
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
FooService服务在Sentinel下对应的资源名是 com.alibaba.cloud.examples.FooService:hello(java.lang.String) 。详细见下面的Filter的getResourceName
, 定义该资源名对应的限流规则:
public static String getResourceName(Invoker<?> invoker, Invocation invocation) {
StringBuilder buf = new StringBuilder(64);
buf.append(invoker.getInterface().getName())
.append(":")
.append(invocation.getMethodName())
.append("(");
boolean isFirst = true;
for (Class<?> clazz : invocation.getParameterTypes()) {
if (!isFirst) {
buf.append(",");
}
buf.append(clazz.getName());
isFirst = false;
}
buf.append(")");
return buf.toString();
}
由于设置的qps是10。调用15次查看是否被限流
1 -> Success: hello, Jim
2 -> Success: hello, Jim
3 -> Success: hello, Jim
4 -> Success: hello, Jim
5 -> Success: hello, Jim
6 -> Success: hello, Jim
7 -> Success: hello, Jim
8 -> Success: hello, Jim
9 -> Success: hello, Jim
10 -> Success: hello, Jim
Blocked
Blocked
Blocked
Blocked
Blocked
可以看到是正常被限流的,那么看看是如何限流的。
@Activate(group = "consumer")
public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
public SentinelDubboConsumerFilter() {
RecordLog.info("Sentinel Dubbo consumer filter initialized");
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
String resourceName = getResourceName(invoker, invocation);
interfaceEntry = SphU.entry(invoker.getInterface().getName(), ResourceTypeConstants.COMMON_RPC,
EntryType.OUT);
methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Throwable e = result.getException();
// Record common exception.
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
}
return result;
} catch (BlockException e) {
return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
methodEntry.exit();
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
}
}
}
重点是:
public class SentinelDubboProviderFilter implements Filter {
public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
String application = DubboUtils.getApplication(invocation, "");
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
String resourceName = DubboUtils.getResourceName(invoker, invocation);
String interfaceName = invoker.getInterface().getName();
// Only need to create entrance context at provider side, as context will take effect
// at entrance of invocation chain only (for inbound traffic).
ContextUtil.enter(resourceName, application);
interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC,
EntryType.IN, invocation.getArguments());
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Throwable e = result.getException();
// Record common exception.
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
}
return result;
} catch (BlockException e) {
return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
ContextUtil.exit();
}
}
}
与消费者主要有以下的不同
可以联想到我们之前定义的那个规则:
QPS:10
那么可以之间看到FlowSlot
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
private final FlowRuleChecker checker;
public FlowSlot() {
this(new FlowRuleChecker());
}
/**
* Package-private for test.
*
* @param checker flow rule checker
* @since 1.6.1
*/
FlowSlot(FlowRuleChecker checker) {
AssertUtil.notNull(checker, "flow checker should not be null");
this.checker = checker;
}
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
checkFlow(resourceWrapper, context, node, count, prioritized);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
throws BlockException {
// 真正的流控逻辑
checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
@Override
public Collection<FlowRule> apply(String resource) {
// 捞取我们刚才定义的FlowRule
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
return flowRules.get(resource);
}
};
}
竟态资源
,那么它是怎么处理的呢?以上两个问题可能需要读代码来解决了。。。。
未完待续。。。。