作为一个RPC框架,Dubbo同时提供了兼具微服务的一些服务管理功能:
服务降级、限流
Dubbo中服务降级可以通过mock
实现,在消费端,通过配置mock选项,来支持服务降级
如:
@DubboReference(mock = "force:return null")
@DubboReference(mock = "return null")
@DubboReference(mock = "throw") // 抛出 RpcException
@DubboReference(mock = "throw com.xxx.XXXException") // 抛出RpcException,并传入自定义异常
@DubboReference(mock = "com.xxx.XXXServiceImpl") // 实现调用接口,mock时调用该实现对应的接口方法
可以通过如上几种方式,其中force
是强制mock,不管调用的服务是否正常,都走mock流程,其他的都是在调用失败之后走mock。
mock代码实现在MockClusterInvoker
中:
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
}
result = doMockInvoke(invocation, null);
} else {
//fail-mock
try {
result = this.invoker.invoke(invocation);
if(result.getException() != null && result.getException() instanceof RpcException){
RpcException rpcException= (RpcException)result.getException();
if(rpcException.isBiz()){
throw rpcException;
}else {
result = doMockInvoke(invocation, rpcException);
}
}
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
}
result = doMockInvoke(invocation, e);
}
}
return result;
}
可以看到,对于force
这种mock,不管服务提供方是否正常都走mock,而具体执行mock在MockInvoker
中:
public Result invoke(Invocation invocation) throws RpcException {
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
String mock = null;
if (getUrl().hasMethodParameter(invocation.getMethodName())) {
mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY);
}
if (StringUtils.isBlank(mock)) {
mock = getUrl().getParameter(MOCK_KEY);
}
if (StringUtils.isBlank(mock)) {
throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
}
mock = normalizeMock(URL.decode(mock));
if (mock.startsWith(RETURN_PREFIX)) {
mock = mock.substring(RETURN_PREFIX.length()).trim();
try {
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
Object value = parseMockValue(mock, returnTypes);
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
} catch (Exception ew) {
throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
+ ", mock:" + mock + ", url: " + url, ew);
}
} else if (mock.startsWith(THROW_PREFIX)) {
mock = mock.substring(THROW_PREFIX.length()).trim();
if (StringUtils.isBlank(mock)) {
throw new RpcException("mocked exception for service degradation.");
} else { // user customized class
Throwable t = getThrowable(mock);
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
} else {
try {
Invoker<T> invoker = getInvoker(mock);
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implementation class " + mock, t);
}
}
}
服务限流,为了防止服务的QPS突然飙升或者太大导致服务频繁失败甚至崩溃重启,对一些重要服务进行限流,虽然会导致一些请求失败,但是能够保证服务平稳运行
Dubbo中的限流有如下几个措施:
accepts:
限制服务器端接受的连接数
,对于服务提供方,Dubbo默认基于Netty协议的底层网络可以限制客户端连接数量进行限制dubbo.protocol.accepts=10
或者dubbo.provider.accepts=10
只能在服务提供端
connections:
限制客户端服务使用连接数
,使用@DubboReference(connections = 10)
或者
@DubboService(connections = 10)
作用在接口或方法级别上,如果两个都有,@DubboReference(connections = 10)
优先
executes
:服务提供者并发限制,作用在服务提供者接口或者方法上
,@DubboService(connections = 10,executes = 10)
或@Method(executes = 10)
,Dubbo中会根据接口或者方法上的该配置,然后在每次执行方法前,会判断当前接口或者方法并行的数量是否大于该值,如果大于抛出异常,否则当前接口和方法active+1.在ExecuteLimitFilter
实现了该过滤处理
actives
:与executes
类似,但是可以作用在服务提供者和消费者的接口或方法上,但是不同于executes
不同的是,这里是表示同事能够活跃的处理数,如果超过该值,则需要等待timeout
ms,超时则抛出异常,在ActiveLimitFilter
实现了active处理