Redis——SpringBoot项目使用Lettuce和Jedis接入Redis集群
Jedis连接Redis:
非线程安全
如果是多线程环境下共用一个Jedis连接池,会产生线程安全问题,可以通过创建多个Jedis实例来解决,但是创建许多socket会影响性能,因此好一点的方法是使用JedisPool
https://blog.csdn.net/lihao21/article/details/46830553
https://www.jianshu.com/p/5e4a1f92c88f
为什么 jedis不是线程安全的,可以通过一个demo来说明:
public class BadConcurrentJedisTest {
private static final ExecutorService pool = Executors.newFixedThreadPool(20);
private static final Jedis jedis = new Jedis("127.0.0.1", 6379);
public static void main(String[] args) {
for(int i=0;i<20;i++){
pool.execute(new RedisSet());
}
}
static class RedisSet implements Runnable{
@Override
public void run() {
while(true){
jedis.set("hello", "world");
}
}
}
这时候后台报错:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket Closed
主要的原因就在于jedis实例中的两个成员变量:RedisInputStream和RedisOutputStream
jedis在执行每一个命令之前都会先执行connect方法,socket是一个共享变量,在多线程的情况下可能存在:线程1执行到:
outputStream = new RedisOutputStream(socket.getOutputStream());
inputStream = new RedisInputStream(socket.getInputStream());
线程2执行到:
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), connectionTimeout);
Lettuce连接Redis:
线程安全
Lettuce是基于netty的,性能比较好。
多线程使用同一连接实例时,是线程安全的。
application-test.yml
redis: nodes: - host1:port1 - host2:port2
-----------------------------------------------------------------Lettuce-----------------------------------------------------------------
导入依赖:
//Redis compile 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
配置类:
import io.lettuce.core.RedisURI; import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; import io.lettuce.core.cluster.api.sync.RedisClusterCommands; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; @Configuration @ConfigurationProperties(prefix = "redis") public class LettuceConfig { private List<String> nodes; public List<String> getNodes() { return nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } @Bean RedisClusterCommands<String, String> redisCommands() { List<RedisURI> uriList = new ArrayList<>(); nodes.forEach(node -> { String[] addrStr = node.split(":"); String host = addrStr[0]; int port = Integer.parseInt(addrStr[1]); RedisURI redisUri = RedisURI.Builder.redis(host).withPort(port).build(); uriList.add(redisUri); }); RedisClusterClient redisClient = RedisClusterClient.create(uriList); StatefulRedisClusterConnection<String, String> connection = redisClient.connect(); RedisClusterCommands<String, String> syncCommands = connection.sync(); return syncCommands; } }
-----------------------------------------------------------------Jedis-----------------------------------------------------------------
导入依赖:
compile group: 'redis.clients', name: 'jedis', version: '2.9.0'
配置类:
package com.youdao.outfox.interflow.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; import java.util.HashSet; import java.util.List; import java.util.Set; @Configuration @ConfigurationProperties(prefix = "redis") public class JedisClusterConfig { private List<String> nodes; public List<String> getNodes() { return nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } @Bean public JedisCluster jedisCluster() { Set<HostAndPort> jedisClusterNodes = new HashSet<>(); nodes.forEach(node -> { String[] addrStr = node.split(":"); String host = addrStr[0]; int port = Integer.parseInt(addrStr[1]); jedisClusterNodes.add(new HostAndPort(host, port)); }); return new JedisCluster(jedisClusterNodes, 5, 2); } }
--------------------------------更新-------------------------------------