motan icon indicating copy to clipboard operation
motan copied to clipboard

异步回调里再调rpc接口问题

Open tangzhihong opened this issue 5 years ago • 4 comments

循环异步调用接口,在异步调用接口的回调里再次调用rpc接口,会抛出com.weibo.api.motan.exception.MotanServiceException: error_message: NettyChannel send request to server Error

找了很久,发现这是由于回调线程是netty的nioEventLoopGroup线程,而再次调用rpc接口也是用这个线程,导致了netty的ChannelFuture.awaitUninterruptibly()=false,是我写法不对,还是本来就有这个问题 我想到的解决方法:用另一个线程去执行回调 代码:

static void async(TestServiceAsync serviceAsync) {
        try {
            for (int i = 0; i < 10; i++ ) {
                ResponseFuture future = serviceAsync.getUserAsync(1);
                future.addListener(new FutureListener() {
                    @Override
                    public void operationComplete(Future future) {
                        User user = (User) future.getValue();
                        user.setId(2);
                        serviceAsync.updateUserAsync(user);
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
com.weibo.api.motan.exception.MotanServiceException: error_message: NettyChannel send request to server Error: url=motan://192.168.10.80:8002/test.service.TestService local=/192.168.10.80:65078 requestId=1630936993823195139 interface=test.service.TestService method=getUser(int), status: 503, error_code: 10001,r=null
	at com.weibo.api.motan.transport.netty4.NettyChannel.request(NettyChannel.java:91)
	at com.weibo.api.motan.transport.netty4.NettyClient.request(NettyClient.java:111)
	at com.weibo.api.motan.transport.netty4.NettyClient.request(NettyClient.java:76)
	at com.weibo.api.motan.protocol.rpc.DefaultRpcReferer.doCall(DefaultRpcReferer.java:52)
	at com.weibo.api.motan.rpc.AbstractReferer.call(AbstractReferer.java:64)
	at com.weibo.api.motan.cluster.ha.FailoverHaStrategy.call(FailoverHaStrategy.java:74)
	at com.weibo.api.motan.cluster.support.ClusterSpi.call(ClusterSpi.java:73)
	at com.weibo.api.motan.proxy.AbstractRefererHandler.invokeRequest(AbstractRefererHandler.java:77)
	at com.weibo.api.motan.proxy.RefererInvocationHandler.invoke(RefererInvocationHandler.java:72)
	at com.sun.proxy.$Proxy12.getUserAsync(Unknown Source)
	at test.ClientApplicationXML.async(ClientApplicationXML.java:38)
	at test.ClientApplicationXML.main(ClientApplicationXML.java:21)
Caused by: java.util.concurrent.CancellationException
	at io.netty.util.concurrent.DefaultPromise.cancel(...)(Unknown Source)

tangzhihong avatar Apr 16 '19 02:04 tangzhihong

建议在你实现的FutureListener中使用独立线程池进行异步调用,netty io线程数量有限,只适合做一些简单操作,不宜进行远程调用等重量级的操作

rayzhang0603 avatar Apr 16 '19 03:04 rayzhang0603

为什么官方不自己写一个独立的线程池来回调,这是我的

public class AsyncCaller {
    static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void call(final Callable callable) {
        call(callable, null);
    }

    public static void call(final Callable<ResponseFuture> callable, final AsyncResponseListener listener) {
        try {
            ResponseFuture future = callable.call();
            future.addListener(new FutureListener() {
                @Override
                public void operationComplete(Future future) {
                    if (future.isSuccess() && future.isDone()) {
                        if (listener != null) {
                            executorService.submit(() -> listener.onSucc(future.getValue()));
                        }
                        return;
                    } else {
                        if ((future.getException() != null)) {
                            Exception e = future.getException();
                            log.error("motan async call " + callable.toString() + "error:", e);
                        }
                        if (listener != null) {
                            executorService.submit(() -> listener.onFail());
                        }
                    }
                }
            });
        } catch (Exception e) {
            log.error("motan async call " + callable.toString() + "error:", e);
        }
    }
}

tangzhihong avatar Apr 16 '19 03:04 tangzhihong

这个比较偏业务,因此只提供了接口,没有考虑提供具体实现类

rayzhang0603 avatar Apr 16 '19 03:04 rayzhang0603

嗯,估计之前有几个人报这个错误,如果排除网络原因,也是这个问题

tangzhihong avatar Apr 16 '19 03:04 tangzhihong