整合 Redis

Netty-Boot 项目中,通过 java-dbjedis 可以快速、高效地连接和操作 Redis 数据库。Redis 作为一个高性能的键值对存储系统,能够支持多种数据结构,同时具备高可扩展性和极快的响应速度。本文将展示如何在 Netty-Boot 中配置和使用 Redis,并对其工作原理进行详细解析。

Redis 连接原理

Redis 是一种基于内存的数据存储系统,它通过 TCP/IP 协议与客户端进行通信。客户端连接 Redis 的过程通常分为以下几步:

  1. 解析 Redis URL:客户端从配置文件中获取 Redis 的连接信息(如主机地址、端口、密码等)。
  2. 连接 Redis 服务器:客户端使用这些信息通过 Jedis 库与 Redis 服务器建立连接。
  3. 选择数据库:Redis 支持多数据库功能,默认使用 0 号数据库,客户端可以指定其他数据库。
  4. 数据序列化和反序列化:由于 Redis 是基于内存存储的,常规数据需要经过序列化才能存储。Java 中可以使用 fst 序列化库来实现这一功能。
  5. 数据操作:客户端可以通过各种 Redis 命令来执行数据的增删改查操作。
  6. 连接池管理:为了提高性能,客户端通常通过连接池来管理 Redis 连接,避免频繁的创建和销毁连接带来的性能开销。

jedis 是 Redis 官方提供的 Java 客户端,它提供了一个简单而高效的 API,使开发者能够轻松地与 Redis 进行交互。

Netty-Boot 连接 Redis 示例

1. 添加依赖

pom.xml 中添加 java-dbjedis 以及序列化库 fst 的依赖:

<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>java-db</artifactId>
  <version>${java-db.version}</version>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>3.6.3</version>
</dependency>
<dependency>
  <groupId>de.ruedigermoeller</groupId>
  <artifactId>fst</artifactId>
  <version>2.57</version> <!-- 注意:更高版本不支持 JDK 8 -->
</dependency>

2. 添加 Redis 配置

在环境变量或 .env 配置文件中添加 Redis 的连接信息:

REDIS_URL=redis://redis:123456@192.168.3.9:6379
redis.database=1
  • REDIS_URL:Redis 连接字符串,包含了 Redis 服务器的地址、端口、密码等信息。
  • redis.database:指定 Redis 使用的数据库索引,默认情况下 Redis 支持 16 个数据库,索引从 0 到 15。

3. 配置 Redis 连接

创建 ImRedisConfig 配置类,通过 RedisPlugin 来初始化 Redis 连接池并启动连接:

package com.litongjava.study.netty.boot.config;

import com.litongjava.annotation.AConfiguration;
import com.litongjava.annotation.AInitialization;
import com.litongjava.hook.HookContainer;
import com.litongjava.model.dsn.RedisInfo;
import com.litongjava.redis.Redis;
import com.litongjava.redis.RedisDb;
import com.litongjava.redis.RedisPlugin;
import com.litongjava.tio.utils.dsn.RedisUrlParser;
import com.litongjava.tio.utils.environment.EnvUtils;

import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

@AConfiguration
@Slf4j
public class ImRedisConfig {

  @Initialization
  public void config() {
    String redisUrl = EnvUtils.get("REDIS_URL");
    log.info("redisUrl:{}", redisUrl);
    if (redisUrl == null) {
      return;
    }

    // 解析 Redis 连接信息
    RedisInfo redisInfo = new RedisUrlParser().parse(redisUrl);
    int redisTimeout = EnvUtils.getInt("redis.timeout", 600);
    int redisDatabase = EnvUtils.getInt("redis.database", 0);
    String cacheName = EnvUtils.get("redis.cacheName", "main");

    // 创建并启动 Redis 插件
    RedisPlugin mainRedis = new RedisPlugin(cacheName, redisInfo.getHost(), redisInfo.getPort(), redisTimeout,
      redisInfo.getPswd(), redisDatabase);
    mainRedis.setSerializer(null); // 不使用序列化器,默认以字符串存储
    mainRedis.start();

    // 测试 Redis 连接
    RedisDb redisDb = Redis.use(cacheName);
    Jedis jedis = redisDb.getJedis();
    try {
      jedis.connect();
      log.info("connect redis successful");
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 服务器关闭时,释放 Redis 资源
    HookContainer.me().addDestroyMethod(mainRedis::stop);
  }
}

4. 测试 Redis 连接和缓存

创建 TestHandler,通过 Redis 查询缓存数据,如果不存在则从数据库查询并写入 Redis:

package com.litongjava.study.netty.boot.handler;

import com.litongjava.db.activerecord.Db;
import com.litongjava.netty.boot.utils.HttpResponseUtils;
import com.litongjava.redis.Redis;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TestHandler {

  public FullHttpResponse test(ChannelHandlerContext ctx, FullHttpRequest httpRequest) {
    // 从 Redis 获取缓存数据
    Integer value = Redis.getInt("key");

    if (value == null) {
      // 如果 Redis 中没有数据,则从数据库查询
      value = Db.queryInt("select 1");
      log.info("get value from db:{}", value);

      // 将查询结果写入 Redis
      Redis.setInt("key", value);
    }

    // 返回结果
    return HttpResponseUtils.txt(value + "");
  }
}

Netty-Boot 中注册该处理器,访问 /test 路由时,先尝试从 Redis 获取缓存数据,如果没有则从数据库获取并写入 Redis。

TestHandler testHandler = Aop.get(TestHandler.class);
httpRequestRouter.add("/test", testHandler::test);

5. 验证效果

启动 Netty 服务后,访问 http://localhost/im/test,如果缓存中有数据则直接返回缓存中的值,否则从数据库查询数据并写入 Redis,然后返回查询结果。

Redis 连接和缓存的工作原理

  1. 解析 Redis URL:在应用启动时,配置类会从环境变量中获取 Redis 的连接信息,并通过 RedisUrlParser 解析 Redis 的主机、端口、密码等。
  2. 连接 Redis 服务器:通过 Jedis 客户端与 Redis 服务器建立连接。RedisPlugin 管理连接池并配置序列化器,确保连接的稳定性和高效性。
  3. 缓存机制:在业务处理过程中,优先从 Redis 获取缓存数据,如果不存在则从数据库查询并将结果缓存到 Redis 中。这样避免了频繁的数据库查询操作,提高了系统的性能。
  4. 资源管理:通过 HookContainer,确保在服务器关闭时正确释放 Redis 连接和其他资源,避免资源泄漏。

通过 Redis 的集成,我们可以有效利用内存存储的高速读写能力,增强系统的性能和扩展性。

结论

通过上述配置,我们在 Netty-Boot 中成功集成了 Redis,并实现了简单的缓存机制。当访问 /test 接口时,首先会尝试从 Redis 获取缓存,如果缓存中没有数据,则从数据库中查询并写入 Redis。这样可以显著减少数据库的压力,并提高应用的响应速度。