dubbo集群广播模式的坑
场景:消费者需要依次调用某个服务的所有实例,当某个实例报错则消费者能得到异常信息。
dubbo官网的介绍如下:
https://dubbo.gitbooks.io/dubbo-user-book/content/demos/fault-tolerent-strategy.html
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
于是乎,看代码:
消费者端:
@RestController
public class UserController {
@Reference(cluster="broadcast")
private UserService us;
@RequestMapping("/")
public String index() {
String s = null;
try {
s = us.getName();
} catch (Exception e) {
System.out.println("客户端开始报错了");
e.printStackTrace();
}
return s;
}
}
服务1:
@Service(cluster="broadcast")
public class UserServerImpl implements UserService{
public String getName() throws Exception {
System.out.println("UserService.getName1...");
int i = 1/0;
return "UserService.getName1...";
}
}
服务2:
@Service(cluster="broadcast")
public class UserServerImpl implements UserService{
public String getName() {
System.out.println("UserService.getName2...");
return "UserService.getName2...";
}
}
启动2个服务,接着启动消费者,调用UserController接口,效果为依次调用了2个服务的getName()方法。
但是,调用了n遍,服务1报错,服务2正常,消费端却没有得到异常信息,这并不是我们想要的结果,因为,某个服务异常,我们需要消费端得到通知并做相应的处理。
于是乎,在消费端debug,进入dubbo源码分析,层层进入,最终到达
com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol这个类的refer()方法:
public <T> Invoker<T> refer(final Class<T> type, final URL url) throws RpcException {
final Invoker<T> tagert = proxyFactory.getInvoker(doRefer(type, url), type, url);
Invoker<T> invoker = new AbstractInvoker<T>(type, url) {
@Override
protected Result doInvoke(Invocation invocation) throws Throwable {
try {
Result result = tagert.invoke(invocation);
Throwable e = result.getException();
if (e != null) {
for (Class<?> rpcException : rpcExceptions) {
if (rpcException.isAssignableFrom(e.getClass())) {
throw getRpcException(type, url, invocation, e);
}
}
}
return result;
} catch (RpcException e) {
if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) {
e.setCode(getErrorCode(e.getCause()));
}
throw e;
} catch (Throwable e) {
throw getRpcException(type, url, invocation, e);
}
}
};
invokers.add(invoker);
return invoker;
请看第9行,debug的结果表明,e就是服务1抛出的异常。
但是,程序并没有走到throw getRpcException(type, url, invocation, e);这一步。
再看rpcExceptions的值:
org.springframework.remoting.RemoteAccessException
java.rmi.RemoteException
而e.getClass()为:java.lang.ArithmeticException,所以该异常并不会抛给消费者。
解决方法:
服务1抛出的异常封装为RemoteAccessException或者RemoteException。
@Service(cluster="broadcast")
public class UserServerImpl implements UserService{
public String getName() throws RemoteException {
try {
System.out.println("UserService.getName1...");
int i = 1/0;
}catch (Exception e) {
throw new RemoteException(e.getMessage(),e);
}
return "UserService.getName1...";
}
}
此时,服务1抛异常,消费者能接收到了。
over