問題描述
想在一個JAVA Class 向同一個Redis實例的不同 dbindex 寫入數據,非常類似於StackOverflowe上的[How can select dbIndex when I use RedisTemplate in Spring-Data-Redis?。在這篇文章中描述了如何使用Spring boot訪問Redis,在創建JedisConnectionFactory的時候指定dbindex:
JedisConnectionFactory factory = new JedisConnectionFactory();
...
factory.setDatabase(databaseId);//set dbindex
因此,大概思路是配置2個RedisTemplate,其中一個RedisTemplate負責訪問dbindex=1的數據庫;另一個RedisTemplate負責訪問dbindex=3的數據庫。
根據這篇文章,因此通過 @Bean(name=) 生成多個RedisTemplate。但是由於生成RedisTemplate需要傳入JedisConnectionFactory實例,而我們是在JedisConnectionFactory中指定了訪問Redis的哪個數據庫(dbindex)。因此,就在創建JedisConnectionFactory實例的時候,使用 @Scope(scopeName = "prototype") 注解,這樣的話Jedis連接工廠就不再是單例模式了。因此,就有兩個JedisConnectionFactory實例,每個實例通過jedisConnectionFactory.setDatabase()設置不同的dbindex。這種方式可能非常愚蠢,會引起嚴重的性能問題。
下面,來看看具體是怎么配置的:
@Scope(scopeName = "prototype")
public JedisConnectionFactory jedisConnectionFactory() {
JedisPoolConfig config = getRedisConfig();
JedisConnectionFactory factory = new JedisConnectionFactory(config);
factory.setUsePool(true);
factory.setHostName(host);
factory.setPort(port);
return factory;
}
每調用一次jedisConnectionFactory() 返回一個新的JedisConnectionFactory實例。
然后定義2個RedisTemplate Bean,jedisConnectionFactory.setDatabase() 方法分別設置不同的dbindex
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Map;
/**
* Created by Administrator on 2018/4/9.
*/
@Configuration
public class LoginMacRedisConfig {
private static final Logger logger = LoggerFactory.getLogger(LoginMacRedisConfig.class);
@Value("1")
private int logmacDatabaseId;
@Value("3")
private int mobmaskDatabaseId;
@Bean
public JedisPoolConfig getRedisConfig() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(8);
config.setMinIdle(0);
return config;
}
@Scope(scopeName = "prototype")
public JedisConnectionFactory jedisConnectionFactory() {
JedisPoolConfig config = getRedisConfig();
JedisConnectionFactory factory = new JedisConnectionFactory(config);
factory.setUsePool(true);
factory.setHostName(host);
factory.setPort(port);
return factory;
}
@Bean(name = "login_mac")
public RedisTemplate<String, Map<String, String>> logmacRedisTemplate() {
final RedisTemplate<String, Map<String, String>> template = new RedisTemplate<>();
JedisConnectionFactory jedisConnectionFactory = jedisConnectionFactory();
jedisConnectionFactory.setDatabase(logmacDatabaseId);
template.setConnectionFactory(jedisConnectionFactory);
logger.info("host:{}, port:{}, database:{}", jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort(), jedisConnectionFactory.getDatabase());
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(stringRedisSerializer);
return template;
}
@Bean(name = "mobile_mask")
public RedisTemplate<String, Map<String, String>> mobileMaskRedisTemplate() {
final RedisTemplate<String, Map<String, String>> template = new RedisTemplate<>();
JedisConnectionFactory jedisConnectionFactory = jedisConnectionFactory();
jedisConnectionFactory.setDatabase(mobmaskDatabaseId);
template.setConnectionFactory(jedisConnectionFactory);
logger.info("host:{}, port:{}, database:{}", jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort(), jedisConnectionFactory.getDatabase());
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(stringRedisSerializer);
return template;
}
}
最后,再寫一個Service類,就可以同時注入這兩個RedisTemplate,操作同一個Redis服務器上的不同的dbindex了。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Administrator on 2018/4/10.
*/
@Service
public class RedisTestService {
@Autowired
@Qualifier("login_mac")
private RedisTemplate<String, Map<String, String>> template1;
@Autowired
@Qualifier("mobile_mask")
private RedisTemplate<String, Map<String, String>> template2;
public void write2Redis() {
HashOperations<String, String, String> hashOperations = template1.opsForHash();
Map<String, String> values = new HashMap<>();
values.put("dbindex", "1");
hashOperations.putAll("123", values);
template2.opsForHash().put("123", "dbindex", "3");
}
}
Application.java 啟動類
@SpringBootApplication
public class Application implements CommandLineRunner{
@Autowired
private RedisTestService redisTestService;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... strings) throws Exception {
redisTestService.write2Redis();
}
}
在redisTestService對象中:有兩個RedisTemplate實例:
兩個RedisTemplate實例分別封裝了兩個JedisConnectionFactory:
調試結果下:
2018-04-10 20:18:34.754 INFO 13512 --- [ main] c.y.t.c.redis.LoginMacRedisConfig : host:192.168.107.253, port:6379, database:1
2018-04-10 20:19:06.972 INFO 13512 --- [ main] c.y.t.c.redis.LoginMacRedisConfig : host:192.168.107.253, port:6379, database:3
最終查看寫入Redis結果,可以看出:dbindex 1 和 dbindex 3 都分別成功寫入了數據。
redis 192.168.107.253:6379> SELECT 1
OK
redis 192.168.107.253:6379[1]> KEYS *
1) "123"
redis 192.168.107.253:6379[1]> HGET 123 dbindex
"1"
redis 192.168.107.253:6379[1]> SELECT 3
OK
redis 192.168.107.253:6379[3]> KEYS *
1) "123"
redis 192.168.107.253:6379[3]> HGET 123 dbindex
"3"
額外補充
其實要在同一個應用中訪問不同的dbindex,一種方式是使用JedisPool,JedisPool創建Jedis,然后調用select方法選擇dbindex。具體實現可參考這篇文章。但這樣的話,就不能使用RedisTemplate的各種方便的接口讀寫Redis了。
@Bean
public JedisPool redisPoolFactory() {
JedisPool jedisPool = new JedisPool(jedisPoolConfig(), host, port);
Jedis jedis = jedisPool.getResource();
jedis.select(3);
return jedisPool;
}
其實是可以像說的:通過RedisConnectionCommand的 select 方法來選擇dbindex的,但是還是同樣的問題,用不了RedisTemplate。
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
DefaultStringRedisConnection stringRedisConnection = new DefaultStringRedisConnection(redisConnection);
stringRedisConnection.select(2);
stringRedisConnection.set("test", "test");
另外這里也有一篇Spring Boot Redis多實例配置,也可以參考一下。Spring Boot 兼Redis新手,只能這樣了。