wenweihu86 / raft-java

Raft Java implementation which is simple and easy to understand.
Apache License 2.0
1.17k stars 405 forks source link

brpc exception, serviceInterface must not be set repeatedly, please use another RpcClient #37

Open liuzihua699 opened 3 years ago

liuzihua699 commented 3 years ago

问题描述:遇到了和 #23 描述的情况,当客户端发起顺序/并发写请求时报错 问题复现:

启动3节点raft-server:
1. git clone https://github.com/wenweihu86/raft-java
2. cd raft-java/raft-java-example && sh deploy.sh

使用如下代码测试写功能会报错:

String ipPorts = "list://127.0.0.1:8051,127.0.0.1:8052,127.0.0.1:8053";
RpcClient rpcClient = new RpcClient(ipPorts);
ExampleService exampleService = BrpcProxy.getProxy(rpcClient, ExampleService.class);

for (int i = 0; i < 10; i++) {
    String key = UUID.randomUUID().toString();
    String value = UUID.randomUUID().toString();

    ExampleProto.SetRequest setRequest = ExampleProto.SetRequest.newBuilder().setKey(key).setValue(value).build();
    ExampleProto.SetResponse setResponse = exampleService.set(setRequest);
    System.out.print(setResponse);
}
rpcClient.stop();

异常如下:

Caused by: com.baidu.brpc.exceptions.RpcException: serviceInterface must not be set repeatedly, please use another RpcClient
    at com.baidu.brpc.protocol.standard.BaiduRpcProtocol.decodeResponse(BaiduRpcProtocol.java:162)
    at com.baidu.brpc.protocol.standard.BaiduRpcProtocol.decodeResponse(BaiduRpcProtocol.java:70)
    at com.baidu.brpc.client.handler.ClientWorkTask.run(ClientWorkTask.java:65)
    at com.baidu.brpc.utils.ThreadPool.consume(ThreadPool.java:131)
    at com.baidu.brpc.utils.ThreadPool.access$000(ThreadPool.java:37)
    at com.baidu.brpc.utils.ThreadPool$1.run(ThreadPool.java:79)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)
xiaoquanju commented 3 years ago

我也遇到了

xiaoquanju commented 3 years ago

想用到产品里 遇到这个问题 有点不敢用了

day210 commented 2 years ago

+1,有进展了吗,单条写没问题,一次多条就报错了,具体能多少也不固定,有时候能一次写10条,有时候4个就开始报这个错了。 现在一次一条太连续也不行了,得隔十几秒。

day210 commented 2 years ago

https://github.com/AprilYoLies/raft-java 这个可以用

fwhdzh commented 1 month ago

我发现楼主和其他人已经提了对这个问题的PR #38 #24 ,但是作者并没有进一步合并。

这个问题的关键在于ExampleServiceImpl.java的这里

else if (raftNode.getLeaderId() != raftNode.getLocalServer().getServerId()) {
            onLeaderChangeEvent();
            ExampleService exampleService = BrpcProxy.getProxy(leaderRpcClient, ExampleService.class);
            ExampleProto.SetResponse responseFromLeader = exampleService.set(request);
            responseBuilder.mergeFrom(responseFromLeader);
        }

这里会在每一次有用户set请求时,使用leaderRpcClient生成一个新的ExampleService实例。但是对于同一个RpcClient对象来说,多次使用BrpcProxy.getProxy生成新的Service实例是不被brpc-java允许的。 因此这里应该重写下代码,不在leaderRpcClient没有被赋值为新对象的情况下重新生成ExampleService实例。具体可以参考楼主和其他人的PR。