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); } }
--------------------------------更新-------------------------------------