apache / incubator-seata

:fire: Seata is an easy-to-use, high-performance, open source distributed transaction solution.
https://seata.apache.org/
Apache License 2.0
25.25k stars 8.77k forks source link

启动apache/seata-server:2.1.0.jre17镜像时,无法加载mysql驱动 #6760

Closed Self-revolution closed 1 month ago

Self-revolution commented 1 month ago

Ⅰ. Issue Description

启动apache/seata-server:2.1.0.jre17镜像时,无法加载mysql驱动,我尝试将mysql-connector-java-8.0.30.jarmysql-connector-j-8.4.0.jar挂载到/lib/jdbc目录下,但还是报错

Ⅱ. Describe what happened

启动脚本:

docker run -d \
--net=host \
--restart=always \
--name=seata \
-v "$directory"/application.yml:/seata-server/resources/application.yml \
-v "$directory"/logs:/root/logs/seata \
-v "$directory"/mysql-connector-java-8.0.30.jar:/lib/jdbc/mysql-connector-java.jar \
-e SEATA_IP=192.168.88.171 \
-e SEATA_PORT=8091 \
-e TZ=Asia/Shanghai \
apache/seata-server:2.1.0.jre17

报错内容:

00:47:05.841  WARN --- [                     main] [letWebServerApplicationContext] [             refresh]  [] : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'branchSessionController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'branchSessionDBServiceImpl' defined in file [/seata-server/classes/org/apache/seata/server/console/impl/db/BranchSessionDBServiceImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.seata.server.console.impl.db.BranchSessionDBServiceImpl$$EnhancerBySpringCGLIB$$7af44348]: Constructor threw exception; nested exception is org.apache.seata.common.loader.EnhancedServiceNotFoundException: not found service provider for : org.apache.seata.core.store.db.DataSourceProvider caused by java.lang.IllegalStateException: Extension instance(definition: org.apache.seata.common.loader.ExtensionDefinition@b24fb6c2, class: interface org.apache.seata.core.store.db.DataSourceProvider)  could not be instantiated: the {com.mysql.cj.jdbc.Driver} can't be found in the path /lib/jdbc/, please copy database driver dependencies, such as `mysql-connector-java.jar` to the path.
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.createNewExtension(EnhancedServiceLoader.java:495)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.getExtensionInstance(EnhancedServiceLoader.java:478)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.loadExtension(EnhancedServiceLoader.java:453)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.load(EnhancedServiceLoader.java:347)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.access$200(EnhancedServiceLoader.java:278)
        at org.apache.seata.common.loader.EnhancedServiceLoader.load(EnhancedServiceLoader.java:116)
        at org.apache.seata.common.loader.EnhancedServiceLoader.load(EnhancedServiceLoader.java:101)
        at org.apache.seata.server.console.impl.db.BranchSessionDBServiceImpl.<init>(BranchSessionDBServiceImpl.java:71)
        at org.apache.seata.server.console.impl.db.BranchSessionDBServiceImpl$$EnhancerBySpringCGLIB$$7af44348.<init>(<generated>)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
        at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
        at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289)
        at org.apache.seata.server.ServerApplication.main(ServerApplication.java:30)
Caused by: org.apache.seata.common.exception.StoreException: the {com.mysql.cj.jdbc.Driver} can't be found in the path /lib/jdbc/, please copy database driver dependencies, such as `mysql-connector-java.jar` to the path.
        at org.apache.seata.core.store.db.AbstractDataSourceProvider.validate(AbstractDataSourceProvider.java:103)
        at org.apache.seata.core.store.db.AbstractDataSourceProvider.generate(AbstractDataSourceProvider.java:84)
        at org.apache.seata.core.store.db.AbstractDataSourceProvider.init(AbstractDataSourceProvider.java:75)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.initInstance(EnhancedServiceLoader.java:707)
        at org.apache.seata.common.loader.EnhancedServiceLoader$InnerEnhancedServiceLoader.createNewExtension(EnhancedServiceLoader.java:492)
        ... 49 more

我将版本修改为seataio/seata-server:1.8.0.jre17,进入镜像后,可以看到我的脚本确实可以将驱动jar包挂载到/lib/jdbc目录下

funky-eyes commented 1 month ago

/seata-server/libs/jdbc

Self-revolution commented 1 month ago

我看1.8.0版本中驱动jar包确实在/seata-server/libs/jdbc目录下,但我将挂载方式改为-v "$directory"/mysql-connector-java-8.0.30.jar:/seata-server/libs/jdbc/mysql-connector-java.jar,还是报同样的错误。 我甚至将1.8.0中的/seata-server/libs/jdbc目录拷贝出来,再挂载到2.1.0版本的/seata-server/libs/jdbc目录下,还是报同样的错误 image

funky-eyes commented 1 month ago

抱歉,容器镜像的依赖确实在lib中,你试试把driver直接放到lib下,看看结果如何? Sorry, the container image dependencies are indeed in lib. Try placing the driver directly in the lib directory and see how it goes.

funky-eyes commented 1 month ago

我怀疑这有一个bug,异常日志在main线程输出,可以看到触发该异常的线程的classloader是main线程的classloader。 而seata去加载driver用的是MysqlDriverClassLoader。 mian线程里面校验driver是否存在用的是main线程的classloader,如何能加载到mysql的driver进行正确校验呢?(其它数据库的driver都在lib下,只有mysql放到了jdbc中) I suspect there might be a bug. The exception logs are output on the main thread, and you can see that the class loader of the thread triggering the exception is the main thread's class loader. However, Seata uses MysqlDriverClassLoader to load the driver. In the main thread, the driver existence check is performed using the main thread's class loader. How can it correctly verify the MySQL driver if the driver is loaded with a different class loader? (Other database drivers are in lib, but only MySQL is placed in jdbc.)

funky-eyes commented 1 month ago

临时解决方法,在lib和lib/jdbc都加上这个driver的jar就可以解决该问题了。 https://github.com/apache/incubator-seata/pull/6332 原因是这个pr增加了校验,却没有将classloader统一导致的,将在2.2上解决该问题。 A temporary solution is to add the driver's JAR to both lib and lib/jdbc, which will resolve the issue. The reason is that this PR introduced additional checks but did not unify the class loaders, leading to the problem. This will be addressed in version 2.2

Self-revolution commented 1 month ago

问题解决! -v "$pwd"/mysql-connector-j-8.4.0.jar:/seata-server/libs/mysql-connector-j-8.4.0.jar

Self-revolution commented 1 month ago

我只在/seata-server/libs/目录下挂载了驱动,并没有在/seata-server/libs/jdbc目录下挂载驱动,是可以正常启动的

临时解决方法,在lib和lib/jdbc都加上这个driver的jar就可以解决该问题了。 #6332 原因是这个pr增加了校验,却没有将classloader统一导致的,将在2.2上解决该问题。 A temporary solution is to add the driver's JAR to both lib and lib/jdbc, which will resolve the issue. The reason is that this PR introduced additional checks but did not unify the class loaders, leading to the problem. This will be addressed in version 2.2

funky-eyes commented 1 month ago

我只在/seata-server/libs/目录下挂载了驱动,并没有在/seata-server/libs/jdbc目录下挂载驱动,是可以正常启动的

临时解决方法,在lib和lib/jdbc都加上这个driver的jar就可以解决该问题了。 #6332 原因是这个pr增加了校验,却没有将classloader统一导致的,将在2.2上解决该问题。 A temporary solution is to add the driver's JAR to both lib and lib/jdbc, which will resolve the issue. The reason is that this PR introduced additional checks but did not unify the class loaders, leading to the problem. This will be addressed in version 2.2

libs下也可以通用的,实际上jdbc那个只是为了做多版本driver加载用的,里面放5.1.x和8.x的driver,你只有一个driver实际上不放在jdbc下也可以 It can also be used universally under libs. In fact, the jdbc one is only for multi-version driver loading. There are 5.1.x and 8.x drivers in it. You only have one driver. In fact, it can be placed under jdbc.

Self-revolution commented 1 month ago

意思是如果有5.1.x和8.x两个driver,需要在/seata-server/libs/目录和/seata-server/libs/jdbc目录都加载驱动jar包是吗

lyl2008dsg commented 1 month ago

I would like to claim this task