Comparison and implementation of springboot integrating connection pool jedis and lettuce in Redis

Comparison and implementation of springboot integrating connection pool jedis and lettuce in Redis

Why use Redis connection pool

The Redis connection pool is a component used to manage and maintain connections to the Redis server. It plays a key role in applications that communicate with Redis and has the following important roles:

  1. Improved performance: The Redis connection pool can maintain a set of reusable connections, reducing the overhead of creating and destroying connections each time it needs to communicate with the Redis server. This helps improve application performance, since the creation and destruction of connections are typically relatively expensive operations.
  2. Reduce resource consumption: The connection pool can control the number of connections to ensure that too many connections are not created, thereby reducing the consumption of memory and CPU resources. This is important to protect Redis servers and application servers from unnecessary load.
  3. Connection reuse: Connection pooling can reuse existing connections instead of creating new connections for each request. This reduces the number of connection establishment and destruction times and improves efficiency.
  4. Connection management: The connection pool can manage the status of the connection, including checking the availability of the connection, maintaining the health status of the connection, and automatically reconnecting failed connections. This helps ensure a stable connection between the application and Redis.
  5. Connection timeout control: The connection pool can configure the timeout period of the connection to ensure that no active connections will be closed within a period of time, thereby releasing resources and preventing connection leaks.
  6. Concurrency control: The connection pool can limit the number of connections used at the same time to avoid the impact of too many concurrent requests on the Redis server. This helps handle concurrent loads smoothly.
  7. Performance optimization: The connection pool can optimize the reuse of connections and reduce the idle time of connections to ensure that the connection remains in the best state and responds quickly to requests.

In short, the main function of the Redis connection pool is to improve performance, reduce resource consumption, manage connection status and reuse connections, thereby ensuring efficient and stable communication with the Redis server. In high-concurrency applications, appropriate connection pool configuration is crucial to maintaining system stability and performance.

The difference between jedis and lettuce

When considering choosing an appropriate Redis connection pool, a more detailed comparison can cover various aspects, including performance, configuration, maintainability, and applicable scenarios. Here is a more detailed comparison of Jedis and Lettuce, two common Java Redis client connection pools:

  1. performance:

    • Jedi:

      • Jedis performs well in low concurrency situations because it uses blocking I/O.
      • In high concurrency scenarios, the performance of Jedis may be limited because each connection is blocked and needs to wait for the IO operation to complete.
    • Lettuce:

      • Lettuce uses non-blocking I/O, so it performs better in high-concurrency environments and can make full use of system resources.
      • It supports asynchronous operations and reactive programming, making it excellent in asynchronous programming.
  2. Connection pool configuration:

    • Jedi:

      • The connection pool configuration of Jedis is relatively simple, and you need to manually set parameters such as the maximum number of connections, the maximum number of idle connections, and connection timeout.
      • Management of connection pools needs to be implemented manually.
    • Lettuce:

      • Lettuce provides richer connection pool configuration options, including connection pool behavior, topology refresh, etc.
      • It has a built-in high-performance connection pool and does not require manual management of the connection pool.
  3. Maintainability:

    • Jedi:

      • Jedis is relatively simple and easy to use, but requires manual management of connection pools and error handling.
      • It has less community support and can be relatively difficult to maintain.
    • Lettuce:

      • Lettuce has more features and maintainability, and has better documentation and community support.
      • It has some advanced features built-in, such as topology refreshing and reactive programming.
  4. Applicable scene:

    • Jedi:

      • Suitable for simple applications or low concurrency environments.
      • For traditional synchronous programming needs, it does the job.
    • Lettuce:

      • Suitable for high-concurrency, high-throughput applications.
      • Especially useful for asynchronous and reactive programming needs.
  5. Ecosystem integration:

    • Jedi:

      • Jedis has better integrated support in some older Java frameworks.
      • It may be more suitable in some projects that need to use older versions of the framework.
    • Lettuce:

      • Lettuce has better integration support in modern Java frameworks, especially the Spring framework.
      • It is more popular in projects using modern Java technologies such as Spring Boot.

To sum up, if your application needs to handle high concurrency or needs asynchronous programming support, Lettuce may be a better choice. And if your application is relatively simple, or runs under a traditional Java framework, Jedis can also be considered. No matter which one you choose, the connection pool will need to be configured and tested based on your specific needs to ensure optimal performance and stability.

Implementation of both in springboot

Lettuce

To integrate a Redis connection pool in a Spring Boot project, you can use Spring Data Redis to simplify the integration process. The following are the general steps for integrating Redis connection pools:

  1. Add dependencies:pom.xml First, add the Spring Data Redis dependency in the file of your Spring Boot project :
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. Configure Redis connection properties: Configure Redis connection properties in application.propertiesor . application.ymlHere is an example configuration:
# redis配置,如果是单个节点就不需要看注释的
#spring.redis.cluster.nodes=localhost:6399,localhost:6396  
spring.redis.host=localhost  
spring.redis.port=6396
# 如果没有密码的话就可以不填
spring.redis.password=  
#spring.redis.cluster.max-redirects=3  
spring.redis.timeout=10000  
# 连接到 Redis 哨兵  
#spring.redis.sentinel.master=mymaster  
#spring.redis.sentinel.nodes=192.168.1.75:26379,192.168.1.75:26378,192.168.1.75:26377
  1. Create a Redis configuration class: Create a Java configuration class to configure the Redis connection pool and RedisTemplate. For example:
package com.todoitbo.baseSpringbootDasmart.config;  
  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.data.redis.connection.RedisConnectionFactory;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;  
import org.springframework.data.redis.serializer.StringRedisSerializer;  
  
/**  
 * @author xiaobo  
 */
@Configuration  
@Slf4j  
public class RedisConfiguration {
    
      
  
	@Bean
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
      
  
        log.info("开始创建redis模板对象");  
  
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();  
  
        // 设置redis连接工厂对象  
        redisTemplate.setConnectionFactory(redisConnectionFactory);  
  
        // 设置 redis key 的序列化器,可以解决乱码问题  
        redisTemplate.setKeySerializer(new StringRedisSerializer());  
  
        // 设置 redis 值的序列化器,可以解决乱码问题
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));  
  
        return redisTemplate;  
    }  
}

This configuration class uses Lettuce as the Redis connection pool. You can customize the properties of the connection pool as needed.

  1. Use RedisTemplate: Inject it in your Service or Controller RedisTemplate, and then use it to perform Redis operations. For example:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class MyService {
    
    

    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public MyService(RedisTemplate<String, Object> redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
    }

    public void addToRedis(String key, Object value) {
    
    
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getFromRedis(String key) {
    
    
        return redisTemplate.opsForValue().get(key);
    }
}

In this way, you can successfully integrate the Redis connection pool in your Spring Boot project and use it to RedisTemplateperform various Redis operations. Make sure to adjust the configuration and operation appropriately to your actual needs.

Jedis

If you want to use Jedis, you can configure it as follows:

  1. Add Jedis dependency:

pom.xmlAdd Jedis dependencies in :

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  1. Create a Jedis connection pool:
package com.todoitbo.tallybookdasmart.config;  
  
  
import cn.hutool.core.text.CharSequenceUtil;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.data.redis.connection.RedisPassword;  
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;  
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;  
import org.springframework.data.redis.listener.RedisMessageListenerContainer;  
import redis.clients.jedis.JedisPool;  
import redis.clients.jedis.JedisPoolConfig;  
  
/**  
 * @author xiaobo  
 * @date 2022/7/25  
 */  
@Slf4j  
@Configuration  
@EnableAutoConfiguration  
public class JedisConfig {
    
      
  
    @Value("${spring.redis.host}")  
    private String host;  
  
    @Value("${spring.redis.port}")  
    private int port;  
  
    @Value("${spring.redis.password}")  
    private String password;  
  
    @Value("${spring.redis.timeout}")  
    private int timeout;  
  
    @Value("${spring.redis.jedis.pool.max-active}")  
    private int maxActive;  
  
    @Value("${spring.redis.jedis.pool.max-wait}")  
    private int maxWait;  
  
    @Value("${spring.redis.jedis.pool.max-idle}")  
    private int maxIdle;  
  
    @Value("${spring.redis.jedis.pool.min-idle}")  
    private int minIdle;  
  
    @Value("${spring.redis.database}")  
    private int db;  
  
    @Bean  
    public JedisPool redisPoolFactory() {
    
      
        try {
    
      
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();  
            jedisPoolConfig.setMaxIdle(maxIdle);  
            jedisPoolConfig.setMaxWaitMillis(maxWait);  
            jedisPoolConfig.setMaxTotal(maxActive);  
            jedisPoolConfig.setMinIdle(minIdle);  
            String pwd = CharSequenceUtil.isBlank(password) ? null : password;  
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, pwd, db);  
            log.info("初始化Redis连接池JedisPool成功!地址: " + host + ":" + port);  
            return jedisPool;  
        } catch (Exception e) {
    
      
            log.error("初始化Redis连接池JedisPool异常:" + e.getMessage());  
        }  
        return null;  
    }  
  
    /**  
     * description: 创建jedisConnectionFactory工厂  
     *  
     * @author bo  
     * @date 2023/4/9 14:49  
     */    
    @Bean  
    public JedisConnectionFactory jedisConnectionFactory() {
    
      
        RedisStandaloneConfiguration redisStandaloneConfiguration =  
                new RedisStandaloneConfiguration();  
        redisStandaloneConfiguration.setHostName(host);  
        redisStandaloneConfiguration.setDatabase(db);  
        redisStandaloneConfiguration.setPassword(RedisPassword.of(password));  
        redisStandaloneConfiguration.setPort(port);  
        return new JedisConnectionFactory(redisStandaloneConfiguration);  
    }  
  
    /**  
     * description: 创建RedisMessageListenerContainer  
     *     * @author bo  
     * @version 1.0  
     * @date 2023/4/9 14:50  
     */    
    @Bean  
    public RedisMessageListenerContainer container(JedisConnectionFactory jedisConnectionFactory) {
    
      
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();  
        container.setConnectionFactory(jedisConnectionFactory);  
        return container;  
    }  
}

This configuration class uses Jedis as the Redis connection pool. You can customize the properties of the connection pool as needed. In JedisConnectionFactory, you can set the parameters of the connection pool, such as the maximum number of connections and the maximum number of idle connections.

  1. Tool class implementation
package com.todoitbo.tallybookdasmart.utils;  
  
  
import cn.hutool.core.util.StrUtil;  
import com.alibaba.fastjson2.JSON;  
import com.alibaba.fastjson2.JSONArray;  
import com.alibaba.fastjson2.JSONObject;  
import com.todoitbo.tallybookdasmart.exception.BusinessException;  
import lombok.NonNull;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
import redis.clients.jedis.Jedis;  
import redis.clients.jedis.JedisPool;  
import redis.clients.jedis.JedisPubSub;  
  
import java.nio.charset.StandardCharsets;  
import java.util.Arrays;  
import java.util.List;  
import java.util.Map;  
import java.util.Set;  
  
  
/**  
 * @author xiaobo  
 */
@Slf4j  
@Component  
public class JedisUtil {
    
      
  
    /**  
     * 静态注入JedisPool连接池   
     * JedisUtil直接调用静态方法即可  
     */    
    private static JedisPool jedisPool;  
  
    private static final String RETURN_OK = "OK";  
  
    @Autowired  
    public void setJedisPool(JedisPool jedisPool) {
    
      
        JedisUtil.jedisPool = jedisPool;  
    }  
  
  
    /**  
     * description: getJedis
     * @return redis.clients.jedis.Jedis  
     * @author bo  
     * @date 2022/7/22 9:06 AM  
     */    
     public static synchronized Jedis getJedis() {
    
      
        try {
    
      
            if (jedisPool != null) {
    
      
                return jedisPool.getResource();  
            } else {
    
      
                return null;  
            }  
        } catch (Exception e) {
    
      
            throw new BusinessException("获取Jedis资源异常:" + e.getMessage());  
        }  
    }  
  
    /**  
     * description: 释放资源  
     *  
     * @author bo  
     * @date 2022/7/22 9:06 AM  
     */    
     public static void closePool() {
    
      
        try {
    
      
            jedisPool.close();  
        } catch (Exception e) {
    
      
            throw new BusinessException("释放Jedis资源异常:" + e.getMessage());  
        }  
    }  
  
  
    /**  
     * description: 获取对象  
     *  
     * @param key k  
     * @return java.lang.Object  
     * @author bo  
     * @date 2022/7/22 9:07 AM  
     */    
     public static Object getObject(String key) {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            byte[] bytes = jedis.get(key.getBytes());  
            if (!Assert.isEmpty(bytes)) {
    
      
                return JSON.parse(bytes);  
            }  
        } catch (Exception e) {
    
      
            throw new BusinessException("获取Redis键值getObject方法异常:key=" + key + " cause=" + e.getMessage());  
        }  
        return null;  
    }  
  
    /**  
     * description: setObject
     * @param key,Object k v  
     * @return java.lang.String  
     * @author bo  
     * @date 2022/7/22 9:09 AM  
     */    
     public static String setObject(String key, Object value) {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            return jedis.set(key.getBytes(), JSONObject.toJSONString(value).getBytes(StandardCharsets.UTF_8));  
        } catch (Exception e) {
    
      
            throw new BusinessException("设置Redis键值setObject方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());  
        }  
    }  
  
  
    /**  
     * description: 过期时间  
     *  
     * @param key,Object,long k,v,mm  
     * @return java.lang.String  
     * @author bo  
     * @date 2022/7/22 9:11 AM  
     */    
     public static String setObject(String key, Object value, long expiretime) {
    
      
        String result;  
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            result = jedis.set(key.getBytes(), JSON.toJSONString(value).getBytes(StandardCharsets.UTF_8));  
            if (RETURN_OK.equals(result)) {
    
      
                jedis.pexpire(key.getBytes(), expiretime);  
            }  
            return result;  
        } catch (Exception e) {
    
      
            throw new BusinessException("设置Redis键值setObject方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());  
        }  
    }  
  
  
    /**  
     * description: getJson
     * @param key k  
     * @return java.lang.String  
     * @author bo  
     * @date 2022/7/22 9:12 AM  
     */    
     public static String getJson(String key) {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            return jedis.get(key);  
        } catch (Exception e) {
    
      
            throw new BusinessException("获取Redis键值getJson方法异常:key=" + key + " cause=" + e.getMessage());  
        }  
    }  
  
    /**  
     * description: setJson     *     * @param key,Object k,v  
     * @return java.lang.String  
     * @author bo  
     * @date 2022/7/22 9:12 AM  
     */    
     public static String setJson(String key, String value) {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            return jedis.set(key, value);  
        } catch (Exception e) {
    
      
            throw new BusinessException("设置Redis键值setJson方法异常:key=" + key + " value=" + value + " cause=" + e.getMessage());  
        }  
    }  
  
    /**  
     * description: 删除指定的key  
     * @param key k  
     * @throws Exception e  
     * @author bo  
     * @date 2022/7/22 1:32 PM  
     */    
     public static void deleteKey(@NonNull String key) throws Exception {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            jedis.del(key);  
        } catch (Exception e) {
    
      
            log.error("redis删除 【key={}】出现异常: {}", key, e.getMessage());  
            throw new BusinessException("redis删除" + key + "出现异常");  
        }  
    }  
  
    /**  
     * description: 批量删除指定的key  
     * @param key k  
     * @throws Exception e  
     * @author bo  
     * @date 2022/7/22 1:33 PM  
     */    
     public static void deleteKeys(String... key) throws Exception {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            jedis.del(key);  
        } catch (Exception e) {
    
      
            log.error("redis删除 【keys= [{}]】出现异常: {}", key, e.getMessage());  
            throw new BusinessException("redis删除" + Arrays.toString(key) + "出现异常:" + e.getMessage());  
        }  
    }  
  
    /**  
     * description: 推送消息  
     *  
     * @param channel,message 消息通道,消息体  
     * @author bo  
     * @date 2022/7/22 1:34 PM  
     */    public static void publish(String channel, String message) {
    
      
        try (Jedis jedis = new Jedis()) {
    
      
            jedis.publish(channel, message);  
        } catch (Exception e) {
    
      
            log.error("redis发布消息出现异常: {}", e.getMessage());  
            throw new BusinessException("redis发布消息出现异常:" + e.getMessage());  
        }  
  
    }  
  
    /**  
     * 监听消息通道  
     *  
     * @param jedisPubSub 对象  
     * @param channels    消息通道  
     */  
    public static void subscribe(JedisPubSub jedisPubSub, String... channels) {
    
      
        try (Jedis jedis = getJedis()) {
    
      
            assert jedis != null;  
            jedis.subscribe(jedisPubSub, channels);  
        } catch (Exception e) {
    
      
            log.error("redis监听消息出现异常: {}", e.getMessage());  
            throw new BusinessException("redis监听消息出现异常:" + e.getMessage());  
        }  
    }  
  
}

Here is the key pointtry-with-resources

try-with-resourcesIt is an important language feature introduced in Java 7. It is used to automatically manage resources (such as files, network connections, database connections, etc.) to ensure that resources are correctly closed and released after the code block is executed. This feature helps reduce the risk of resource leaks and improves code readability and maintainability.

Here are try-with-resourcesthe key points about:

  1. Syntax: try-with-resources Use trya block to wrap a piece of code, where the resources tryare initialized at the beginning of the block and tryautomatically closed and released at the end of the block. Resource initialization is done through AutoCloseablethe interface's implementation classes, which need to execute methods when the resource is closed close(). This usually includes some classes in the Java standard library, such as InputStream, OutputStream, Reader, Writer, Socket, Jedisetc.

  2. Automatic resource closing: At try-with-resourcesthe end of the block, there is no need to manually call the resource's close()methods, they are automatically called. This ensures proper closing of the resource regardless of whether the code block executes normally or throws an exception.

  3. Multiple resource management:try-with-resources Multiple resources can be managed in the same block. These resources are initialized and closed in the tryreverse order of their declaration in the block.

  4. Exception handling: If tryan exception is thrown in a block, try-with-resourcesall initialized resources will be closed first, and then the exception will be thrown. This ensures that resources are closed properly and no resource leaks occur.

  5. Scope of application: try-with-resources Applicable to classes that implement AutoCloseableinterfaces. If you customize a resource class, you can make it implement AutoCloseablethe interface and override close()the methods for try-with-resourcesuse in it.

Here is a simple example that demonstrates how to try-with-resourcesuse a file to read a resource:

try (FileReader fileReader = new FileReader("example.txt")) {
    
    
    int data;
    while ((data = fileReader.read()) != -1) {
    
    
        // 处理文件内容
    }
} catch (IOException e) {
    
    
    // 处理异常
}

In this example, FileReaderit is a resource class that implements AutoCloseablethe interface. When try-with-resourcesthe block ends, fileReaderit is automatically closed without the need to manually call close()the method.

In short, try-with-resourcesit is an important Java language feature that helps simplify resource management, reduce the risk of resource leaks, and improve the readability and maintainability of code. It is useful when dealing with resources that need to be closed manually, including files, database connections, network connections, and other resources that need to be closed explicitly.

suggestion

In a word, it is recommended to use Lettuce

Using Jedis as a Redis client may cause some problems in certain situations, including:

  1. Performance issues: Jedis may not perform as well as Lettuce in high concurrency situations. Because Jedis uses blocking I/O, each connection is blocking, which can cause a performance bottleneck, especially when a large number of concurrent requests arrive at the same time.

  2. Connection management: Jedis requires manual management of the connection pool and the status of the connection. This means you need to configure the connection pool parameters yourself, handle the creation and destruction of connections, and manage the health of the connections. This can cause issues with connection leaks or connections being improperly closed.

  3. Thread safety: Instances of Jedis are not thread-safe, which means additional synchronization is required in a multi-threaded environment to ensure correct concurrent access. This increases development complexity.

  4. Exception handling: Exception handling in Jedis requires careful handling by developers. For example, when a connection expires or a network problem occurs, exceptions need to be caught and handled to ensure the stability of the application.

  5. Community support: Compared to Lettuce, Jedis has relatively little community support. This means it may not be easy for you to find solutions to related issues or get updated maintenance and support.

  6. Not suitable for reactive programming: If your application needs to use a reactive programming model, Jedis may not be the best choice because it does not support reactive operations. Lettuce is more suitable in this regard.

  7. Version compatibility: Some old versions of Jedis may not be compatible with new versions of Redis servers, so you need to pay special attention when upgrading Redis.

While Jedis is still a full-featured Redis client and can still suffice in some cases, Lettuce is generally recommended because it offers more functionality when it comes to high concurrency, performance, and modern application needs. , better performance, and more suitable for modern Java programming models. Therefore, using Jedis may cause the above problems, and you need to carefully consider whether to choose it

Guess you like

Origin blog.csdn.net/Mrxiao_bo/article/details/132895319