在Tomcat服务器中使用Redis服务器部署Spring Boot应用程序时出错

时间:2019-12-28 17:01:03

标签: spring-boot redis tomcat9

我有一个基于 Spring boot (2.1.9.RELEASE)并使用 Redis 服务器(作为服务运行的Redis-x64-3.2.100)的Web应用程序会话属性存储

随着Spring Boot App顺利进行,在 Eclipse 中的部署,该应用程序可以连接到Redis服务器,并且我们可以存储/检索Session属性

但是当我想在 Tomcat (版本9)中进行部署时,我遇到了与springframework中的redis api相关的问题:

Caused by: java.lang.LinkageError: loader constraint violation:
when resolving method "io.reactivex.Flowable.fromPublisher(Lorg/reactivestreams/Publisher;)Lio/reactivex/Flowable;"
the class loader (instance of org/apache/catalina/loader/ParallelWebappClassLoader) of the current class,
org/springframework/core/ReactiveAdapterRegistry$RxJava2Registrar, and the class loader (instance of java/net/URLClassLoader)
for the method's defining class, io/reactivex/Flowable, have different Class objects for the type org/reactivestreams/Publisher used in the signature

鉴于我已在Tomcat服务器中的context.xml中设置了所有必需的配置:

<ResourceLink name="bean/redisson"
              global="bean/redisson"
      type="org.redisson.api.RedissonClient" />

<Manager className="org.redisson.tomcat.JndiRedissonSessionManager"
     readMode="REDIS"
     jndiName="bean/redisson" />
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Manager className="org.redisson.tomcat.RedissonSessionManager"
  configPath="${catalina.base}/conf/redisson.yaml" 
  readMode="REDIS" updateMode="DEFAULT" broadcastSessionEvents="false"/>

server.xml中的更改:

<Resource name="bean/redisson"
      auth="Container"
          factory="org.redisson.JndiRedissonFactory"
          configPath="${catalina.base}/conf/redisson.yaml"
      closeMethod="shutdown"/>

redisson.yaml包含:

singleServerConfig:
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: null
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://127.0.0.1:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 24
  connectionPoolSize: 64
  database: 0
  dnsMonitoringInterval: 5000
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.FstCodec> {}
transportMode: "NIO"

然后将redisson-all-3.11.6.jar和redisson-tomcat-9-3.11.6.jar放在Tomcat lib文件夹中

关于Redis与Tomcat的兼容性是否存在问题?我错过了什么 ? 感谢您的提前帮助。

2 个答案:

答案 0 :(得分:0)

错误已明确指出:

Caused by: java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.getConfig(Ljava/lang/String;)Ljava/util/List;

请阅读有关java.lang.NoSuchMethodError的信息,这基本上是在告诉您它在运行时找不到此方法。您必须执行以下步骤:

  1. 找出哪个库(jar)文件具有此方法,然后验证方法签名。
  2. 确保此库是您的类路径的一部分,即应该是您的maven或gradle文件的一部分。
  3. 检查是否存在冲突。如果您还有其他也提供相同类的jar文件(即RedisConnection),则必须删除该冗余jar文件。

基本上,该错误即将来临,因为该程序正在调用RedisConnection类,但无法在该类中找到getConfig方法(相同的签名)。 我的猜测是您有两个冲突的库,并且正在调用错误的库。您必须删除或清理依赖关系。

答案 1 :(得分:0)

在仔细研究了该问题之后,发现Redis库和Tomcat服务器内部库之间存在冲突

最后,我决定将会话存储切换到JDBC并完全丢弃Redis:

  • 设置应用程序属性spring.session.store-type = jdbc
  • 设置应用程序属性spring.session.jdbc.table-name = SPRING_SESSION
  • 设置应用程序属性spring.jpa.hibernate.ddl-auto = none(直接从sql脚本创建架构)
  • 在schema.sql中添加两个用于管理会话的表

管理会话的数据库表为:

CREATE TABLE IF NOT EXISTS SPRING_SESSION (
    PRIMARY_ID CHAR(36) NOT NULL,
    SESSION_ID CHAR(36) NOT NULL,
    CREATION_TIME BIGINT NOT NULL,
    LAST_ACCESS_TIME BIGINT NOT NULL,
    MAX_INACTIVE_INTERVAL INT NOT NULL,
    EXPIRY_TIME BIGINT NOT NULL,
    PRINCIPAL_NAME VARCHAR(100),
    CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE UNIQUE INDEX IF NOT EXISTS SPRING_SESSION_IX1 ON MATFRONT.SPRING_SESSION (SESSION_ID);
CREATE INDEX IF NOT EXISTS SPRING_SESSION_IX2 ON MATFRONT.SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX IF NOT EXISTS SPRING_SESSION_IX3 ON MATFRONT.SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE IF NOT EXISTS SPRING_SESSION_ATTRIBUTES (
    SESSION_PRIMARY_ID CHAR(36) NOT NULL,
    ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
    ATTRIBUTE_BYTES BLOB NOT NULL,
    CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
    CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES MATFRONT.SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
);

由于这一点,我摆脱了仅用于存储会话数据的整个服务器