sofastack / sofa-jraft

A production-grade java implementation of RAFT consensus algorithm.
https://www.sofastack.tech/projects/sofa-jraft/
Apache License 2.0
3.53k stars 1.12k forks source link

Parse protocol file failed #1065

Closed SABERYJS closed 6 months ago

SABERYJS commented 6 months ago

Describe the bug

seata-server-2.0.0使用到了jraft库(版本1.3.13),但是会出现 Parse protocol file failed 问题,具体打印堆栈如下:

Caused by: java.lang.ExceptionInInitializerError: null
        at com.alipay.sofa.jraft.core.CliServiceImpl.init(CliServiceImpl.java:88)
        at com.alipay.sofa.jraft.core.CliServiceImpl.init(CliServiceImpl.java:73)
        at com.alipay.sofa.jraft.RaftServiceFactory.createAndInitCliService(RaftServiceFactory.java:65)
        at io.seata.server.cluster.raft.RaftServerFactory$SingletonHandler.<clinit>(RaftServerFactory.java:233)
        at io.seata.server.cluster.raft.RaftServerFactory.getInstance(RaftServerFactory.java:78)
        at io.seata.server.session.GlobalSession.addBranch(GlobalSession.java:316)
        at io.seata.server.coordinator.AbstractCore.lambda$branchRegister$0(AbstractCore.java:84)
        at io.seata.server.storage.db.session.DataBaseSessionManager.lockAndExecute(DataBaseSessionManager.java:153)
        at io.seata.server.session.SessionHolder.lockAndExecute(SessionHolder.java:332)
        at io.seata.server.coordinator.AbstractCore.branchRegister(AbstractCore.java:77)
        at io.seata.server.coordinator.DefaultCore.branchRegister(DefaultCore.java:107)
        at io.seata.server.coordinator.DefaultCoordinator.doBranchRegister(DefaultCoordinator.java:299)
        at io.seata.server.AbstractTCInboundHandler$4.execute(AbstractTCInboundHandler.java:184)
        at io.seata.server.AbstractTCInboundHandler$4.execute(AbstractTCInboundHandler.java:179)
        at io.seata.core.exception.AbstractExceptionHandler.exceptionHandleTemplate(AbstractExceptionHandler.java:131)
        at io.seata.server.AbstractTCInboundHandler.handle(AbstractTCInboundHandler.java:179)
        at io.seata.core.protocol.transaction.BranchRegisterRequest.handle(BranchRegisterRequest.java:136)
        at io.seata.server.coordinator.DefaultCoordinator.onRequest(DefaultCoordinator.java:523)
        at io.seata.core.rpc.processor.server.ServerOnRequestProcessor.handleRequestsByMergedWarpMessage(ServerOnRequestProcessor.java:288)
        at io.seata.core.rpc.processor.server.ServerOnRequestProcessor.lambda$onRequestMessage$1(ServerOnRequestProcessor.java:178)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: java.lang.IllegalStateException: Parse protocol file failed.
        at com.alipay.sofa.jraft.rpc.ProtobufMsgFactory.load(ProtobufMsgFactory.java:88)
        at com.alipay.sofa.jraft.rpc.impl.AbstractClientService.<clinit>(AbstractClientService.java:63)
        ... 27 common frames omitted

经排查,导致该问题的原因为com.alipay.sofa.jraft.util.JRaftServiceLoader类的loader字段初始化导致,原始load方法为:

public static <S> JRaftServiceLoader<S> load(final Class<S> service) {
        return JRaftServiceLoader.load(service, Thread.currentThread().getContextClassLoader());
    }

进而导致,在 com.alipay.sofa.jraft.util.JRaftServiceLoader$LazyIterator的hasNext方法中

//改动之前的loader这里获取到的configs是空的
this.configs = this.loader.getResources(fullName); 

while ((this.pending == null) || !this.pending.hasNext()) {
                if (!this.configs.hasMoreElements()) {
                   //代码进入到这里
                    System.out.println("noeles----------------------------");
                    return false;
                }
               //System.out.printf("parsepending---------------------%s\n",this.configs.nextElement());
                this.pending = parse(this.service, this.configs.nextElement());
                System.out.println("parsepending---------------------\n");
}

建议改为:

public static <S> JRaftServiceLoader<S> load(final Class<S> service) {
        return JRaftServiceLoader.load(service, JRaftServiceLoader.class.getClassLoader());
    }

经过测试,该方法有效

Environment

fengjiachun commented 6 months ago

@SABERYJS 谢谢你的排查和提供的信息,看起来是初始化 RaftServiceFactory 的那个线程的 ClassLoader 无法加载到 config,我想你建议的改法是正确的,应该默认使用 JRaftServiceLoader 的 ClassLoader

你愿意提交一个 PR 来修复这个问题吗? @SABERYJS ,期望能赶上 v1.3.14 release

fengjiachun commented 5 months ago

已发布 1.3.14 @SABERYJS ,请使用最新版本

SABERYJS commented 5 months ago

已发布 1.3.14 @SABERYJS ,请使用最新版本

好的