SpringBoot 1.5.22 Release version configures redis to switch database through yml file

1. Project background

Question: I am working on a project recently, after caching the data to redis through jackson, and then fetching the data according to the key is null.

Reason: The SpringBoot version uses SpringBoot 1.5.22 Release, uses yml to configure redis, and uses 1 for database. But in fact, the system has been using database 0, so the fetched data is null.

 redis:
        database: 1
        host: 192.168.1.xxx
        password: xxx
        pool:
            max-active: 8
            max-idle: 8
            max-wait: -1
            min-idle: 0
        port: 6380
        timeout: 60000

The original redisConfig configuration:

	@Bean(name="redisTemplate")
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
    
    
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String,Object>();
		redisTemplate.setConnectionFactory(factory);

		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);

		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setHashKeySerializer(stringSerializer);
		redisTemplate.setHashValueSerializer(stringSerializer);
		return redisTemplate;
	}

2. Knowledge preparation

The difference between Jedis and Lettuce
Both jedis and Lettuce are Redis clients, and they can both connect to the Redis server, but after SpringBoot2.0, the Lettuce client is used to connect to the Redis server by default. Because when using the Jedis client to connect to the Redis server, each thread must use the Jedis instance created by itself to connect to the Redis client. When there are many threads, not only the overhead is high, it is necessary to repeatedly create and close a Jedis connection, but also It is also thread-unsafe. After one thread changes the data in the Redis server through the Jedis instance, it will affect another thread;

But if you use the Lettuce client to connect to the Redis server, the above situation will not occur. The bottom layer of Lettuce is Netty. When there are multiple threads that need to connect to the Redis server, you can ensure that only one Lettuce connection is created. All threads share this Lettuce connection, which can reduce the overhead of creating and closing a Lettuce connection; and this method is also thread-safe, and it will not affect another thread after one thread changes the data in the Redis server through Lettuce Condition;

spring-boot-starter-data-redis has two implementations: lettuce and jedis. However the default is to use lettuce.

In the spring-boot-starter-data-redis of spring boot 2, lettuce is used as the redis client by default. The main differences between it and jedis are as follows:

1. Jedis:
Jedis is synchronous and does not support asynchrony. Jedis client instances are not thread-safe and require one Jedis instance per thread, so Jedis is generally used through a connection pool.

advantage:

Provides a relatively comprehensive API for Redis operation features
The API basically corresponds to Redis instructions one-to-one, easy to use and easy to understand
Disadvantages:

Synchronous blocking IO
does not support asynchronous
threads.
If you do not use the default lettuce but use jedis, you can exclude lettuce dependencies and manually add jedis dependencies. The configuration is as follows:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>


2. Lettuce:
Lettuce is an event-driven Redis client based on the Netty framework. Its method calls are asynchronous. The API of Lettuce is also thread-safe, so multiple threads can operate a single Lettuce connection to complete various operations. At the same time, Lettuce also Support for connection pooling.

advantage:

Thread safety
Event-driven communication based on the Netty framework, which can be called asynchronously
Suitable for distributed cache
Disadvantages:

The API is more abstract, and the cost of learning and using is high
pom:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring2.X集成redis所需common-pool2-->
<!-- 如果使用Lettuce作为连接池,需要引入commons-pool2包,
否则会报错bean注入失败 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

3. Proper configuration

RedisConnectionFactory below SpringBoot 2.0 has no problem configuring the database through the properties file, but when passing through the yml file, the default is 0, and changing to other databases does not take effect, and it needs to be reset through JedisConnectionFactory.

@Bean(name="redisTemplate")
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
    
    
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String,Object>();

		//因为配置文件改为 yml以后,配置文件中的spring.redis.database 非0时无效,此处手动设置成配置文件中内容。
		int database = Integer.valueOf(environment.getProperty("spring.redis.database"));
		JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;
		jedisConnectionFactoryFactory.setDatabase(database);

		redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);

		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);

		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setHashKeySerializer(stringSerializer);
		redisTemplate.setHashValueSerializer(stringSerializer);
		return redisTemplate;
	}

Full code:
RedisCacheConfig

package com.wzw.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCaching
public class RedisCacheConfig {
    
    

	@Resource
	private Environment environment;

	@Bean
	public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate){
    
    
		RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
		cacheManager.setDefaultExpiration(3600);//默认缓存1小时

		Map<String, Long> expiresMap=new HashMap<String, Long>();
		expiresMap.put("ibsLongTimeCache", 7*24*3600L);//长时间缓存区域   7天   缓存基本不会改变的数据
		expiresMap.put("ibsTempCache", 600L);//临时缓存区域  10分钟  改动基本稍多实时性要求不高的数据
		cacheManager.setExpires(expiresMap);
		return cacheManager;
	}

	@Bean(name="redisTemplate")
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
    
    
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String,Object>();

		//因为配置文件改为 yml以后,配置文件中的spring.redis.database 非0时无效,此处手动设置成配置文件中内容。
		int database = Integer.valueOf(environment.getProperty("spring.redis.database"));
		JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;
		jedisConnectionFactoryFactory.setDatabase(database);

		redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);

		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);

		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setHashKeySerializer(stringSerializer);
		redisTemplate.setHashValueSerializer(stringSerializer);
		return redisTemplate;
	}

	@Bean
	public KeyGenerator keyGenerator() {
    
    
		return new KeyGenerator() {
    
    
			@Override
			public Object generate(Object target, Method method, Object... params) {
    
    
				StringBuilder sb = new StringBuilder();
				sb.append(target.getClass().getName());
				sb.append(method.getName());
				for (Object obj : params) {
    
    
					sb.append(obj.toString());
				}
				return sb.toString();
			}
		};
	}

}

RedisService

package com.wzw.service;

import java.util.List;
import java.util.Map;

public interface RedisService {
    
    
	/**
	 * 批量删除对应的value
	 *
	 * @param keys
	 */
	public void remove(final String... keys);

	/**
	 * 批量删除key
	 *
	 * @param pattern
	 */
	public void removePattern(final String pattern);

	/**
	 * 删除对应的value
	 *
	 * @param key
	 */
	public void remove(final String key);

	/**
	 * 判断缓存中是否有对应的value
	 *
	 * @param key
	 * @return
	 */
	public boolean exists(final String key);

	/**
	 * 读取缓存
	 *
	 * @param key
	 * @return
	 */
	public Object get(final String key);

	/**
	 * 读取缓存
	 *
	 * @param key
	 * @return
	 */
	public Map<Object,Object> getHashKey(final String key);


	public List<Object> getHashKey(final String key, List<Object> hashkeys);

	/**
	 * 写入缓存
	 *
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean set(final String key, Object value);

	/**
	 * 写入缓存
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean set(final String key, Object value, Integer expireTime);

	/**
	 * 设置失效时间
	 * @param key
	 * @param expireTime
	 * @return
	 */
	public boolean expire(final String key, Integer expireTime);
}

RedisServiceImpl

package com.wzw.service.impl;

import com.wzw.service.RedisService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@Service("redisService")
public class RedisServiceImpl implements RedisService{
    
    

	@Autowired
	@Qualifier("redisTemplate")
	private RedisTemplate<String, Object> redisTemplate;
	/**
	 * 批量删除对应的value
	 *
	 * @param keys
	 */
	@Override
	public void remove(final String... keys) {
    
    
		for (String key : keys) {
    
    
			remove(key);
		}
	}

	/**
	 * 批量删除key
	 *
	 * @param pattern
	 */
	@Override
	public void removePattern(final String pattern) {
    
    
		Set<String> keys = redisTemplate.keys(pattern);
		if (keys.size() > 0)
			redisTemplate.delete(keys);
	}

	/**
	 * 删除对应的value
	 *
	 * @param key
	 */
	@Override
	public void remove(final String key) {
    
    
		if (exists(key)) {
    
    
			redisTemplate.delete(key);
		}
	}

	/**
	 * 判断缓存中是否有对应的value
	 *
	 * @param key
	 * @return
	 */
	@Override
	public boolean exists(final String key) {
    
    
		return redisTemplate.hasKey(key);
	}

	/**
	 * 读取缓存
	 *
	 * @param key
	 * @return
	 */
	@Override
	public Object get(final String key) {
    
    
		Object result = null;
		ValueOperations<String, Object> operations = redisTemplate.opsForValue();
		result = operations.get(key);
		return result;
	}

	/**
	 * 读取缓存
	 *
	 * @param key
	 * @return
	 */
	@Override
	public Map<Object,Object> getHashKey(final String key) {
    
    
		Map<Object,Object> objectMap = redisTemplate.opsForHash().entries(key);;
		return objectMap;
	}

	/**
	 * 读取缓存
	 *
	 * @param key
	 * @return
	 */
	@Override
	public List<Object> getHashKey(final String key, List<Object> hashkeys) {
    
    
		List<Object> objectMap = redisTemplate.opsForHash().multiGet(key,hashkeys);
		return objectMap;
	}

	/**
	 * 写入缓存
	 *
	 * @param key
	 * @param value
	 * @return
	 */
	@Override
	public boolean set(final String key, Object value) {
    
    
		boolean result = false;
		try {
    
    
			ValueOperations<String, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			result = true;
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 写入缓存
	 * @param key
	 * @param value
	 * @return
	 */
	@Override
	public boolean set(final String key, Object value, Integer expireTime) {
    
    
		boolean result = false;
		try {
    
    
			ValueOperations<String, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 设置失效时间
	 * @param key
	 * @param expireTime
	 * @return
	 * @author Jone
	 * create date:2018年2月27日
	 */
	@Override
	public boolean expire(final String key, Integer expireTime) {
    
    
		boolean result = false;
		try {
    
    
			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
		return result;
	}
}

yml

spring:
	redis:
        database: 1
        host: 192.168.1.xxx
        password: asdffdsa
        pool:
            max-active: 8
            max-idle: 8
            max-wait: -1
            min-idle: 0
        port: 6380
        timeout: 60000

4. Summary

  1. Redis below spirngboot 2.0 uses jedis, jedis is synchronous, does not support asynchrony, and is not thread-safe; after 2.0, it uses Lettuce, and the bottom layer uses redis to support asynchronous and thread-safe.
  2. In springboot 2.0, there is no problem in using properties to configure redis database. When using yml file configuration, the default value is 0, and modification through the configuration file does not take effect. You need to re-specify it through JedisConnectionFactory in redisConfig.
int database = Integer.valueOf(environment.getProperty("spring.redis.database"));
		JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;
		jedisConnectionFactoryFactory.setDatabase(database);

		redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);

Supongo que te gusta

Origin blog.csdn.net/u014212540/article/details/129531343
Recomendado
Clasificación