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

Error while deploying the Spring Boot app using Redis server in Tomcat server

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

Eclipse中的部署作为Spring启动应用程序顺利,应用程序可以连接到Redis服务器,我们可以store/retrieve会话属性

但是当我想在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 兼容性的问题吗?我错过了什么 ? 提前感谢您的帮助。

错误说的很清楚:

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

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

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

基本上,错误的出现是因为程序正在调用 RedisConnection class 但无法在 class 中找到 getConfig 方法(相同的签名)。 我的猜测是您有两个相互冲突的库并且调用了错误的库。您必须删除或清理依赖项。

经过长时间的检查,发现 Redis 库和 Tomcat 服务器内部库之间存在冲突

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

在application.properties文件中

  • 设置应用属性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
);

多亏了它,我摆脱了仅用于存储会话数据的额外的整个服务器