Redis在多線程情況下寫入失敗的問(wèn)題及解決
出現(xiàn)場(chǎng)景
同一時(shí)間多次調(diào)用jedis的set方法,出現(xiàn):
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket is not connected: socket write error
at redis.clients.jedis.Protocol.sendCommand(Protocol.java:98)
at redis.clients.jedis.Protocol.sendCommand(Protocol.java:78)
at redis.clients.jedis.Connection.sendCommand(Connection.java:101)
at redis.clients.jedis.BinaryClient.set(BinaryClient.java:99)
at redis.clients.jedis.Client.set(Client.java:29)
at redis.clients.jedis.Jedis.set(Jedis.java:72)
at com.castle.cache.JedisUtils.setSingle(JedisUtils.java:21)
at com.castle.cache.JedisUtils$1.run(JedisUtils.java:36)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.net.SocketException: Socket is not connected: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at redis.clients.util.RedisOutputStream.flushBuffer(RedisOutputStream.java:31)
at redis.clients.util.RedisOutputStream.write(RedisOutputStream.java:38)
at redis.clients.jedis.Protocol.sendCommand(Protocol.java:84)
... 10 more
問(wèn)題重現(xiàn)代碼
環(huán)境:jre7 32bit
jedis:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.3</version> <type>jar</type> <scope>compile</scope> </dependency>
private static Jedis jedis = new Jedis("localhost",6379);;
// 過(guò)期時(shí)間/生存時(shí)間
// protected static int expireTime = 60 * 60 *24;
public static Jedis getInstance(){
return jedis;
}
public static void setSingle(String key,String value){
jedis.set(key, value);
// jedis.expire(key, expireTime);
}
public static String getSingle(String key){
// jedis.expire(key, expireTime);
return jedis.get(key);
}
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 1; i <= 1000; i++) {
final String ii = "TEST:TEST_NUM-"+i;
cachedThreadPool.execute(new Thread(){
public void run(){
setSingle(ii, ii);
}
});
}
}運(yùn)行代碼然后就出現(xiàn):

原因分析
猜測(cè)多線程獲取連接,同一時(shí)間獲取同一個(gè)連接導(dǎo)致卡死
解決方案
擬用連接池:
private static JedisPool pool;
/**
* 初始化Redis連接池
*/
private static void initializePool() {
//redisURL 與 redisPort 的配置文件
JedisPoolConfig config = new JedisPoolConfig();
//設(shè)置最大連接數(shù)(100個(gè)足夠用了,沒必要設(shè)置太大)
config.setMaxTotal(20);
//最大空閑連接數(shù)
config.setMaxIdle(5);
//獲取Jedis連接的最大等待時(shí)間(50秒)
config.setMaxWaitMillis(50 * 1000);
//在獲取Jedis連接時(shí),自動(dòng)檢驗(yàn)連接是否可用
config.setTestOnBorrow(true);
//在將連接放回池中前,自動(dòng)檢驗(yàn)連接是否有效
config.setTestOnReturn(true);
//自動(dòng)測(cè)試池中的空閑連接是否都是可用連接
config.setTestWhileIdle(true);
//創(chuàng)建連接池
pool = new JedisPool(config, "localhost",6379);
}
/**
* 多線程環(huán)境同步初始化(保證項(xiàng)目中有且僅有一個(gè)連接池)
*/
private static synchronized void poolInit() {
if (null == pool) {
initializePool();
}
}
public static Jedis getJedis() {
if (pool == null) {
poolInit();
}
//如果沒有以下代碼會(huì)造成初始化的jedis拿不到 jedis對(duì)象
Jedis jedis = null;
try {
if (pool != null) {
jedis = pool.getResource();
}
}
catch (Exception e) {
e.printStackTrace();
}
return jedis;
}
/**
* 釋放Jedis資源
*
* @param jedis
*/
public static void returnResource(Jedis jedis) {
if (null != jedis) {
pool.returnResourceObject(jedis);
}
}
//private static Jedis jedis = new Jedis("localhost",6379);;
// public static Jedis getInstance(){
// return jedis;
// }
// 過(guò)期時(shí)間/生存時(shí)間
protected static int expireTime = 60 * 60 *24;
public static void put(String key,String value){
// jedis.set(key, value);
Jedis jedis = getJedis();
while (true) {
if (null != jedis) {
break;
} else {
jedis = getJedis();
}
}
jedis.set(key, value);
returnResource(jedis);
// jedis.expire(key, expireTime);
}
public static String get(String key){
// jedis.expire(key, expireTime);
// return jedis.get(key);
Jedis jedis = getJedis();
while (true) {
if (null != jedis) {
break;
} else {
jedis = getJedis();
}
}
String value = jedis.get(key);
returnResource(jedis);
return value;
}
public static Set<String> keys(String keyMatch){
// jedis.expire(key, expireTime);
//
Jedis jedis = getJedis();
while (true) {
if (null != jedis) {
break;
} else {
jedis = getJedis();
}
}
Set<String> res = jedis.keys(keyMatch);
returnResource(jedis);
return res;
}
public static void remove(String key){
// jedis.del(key);
Jedis jedis = getJedis();
while (true) {
if (null != jedis) {
break;
} else {
jedis = getJedis();
}
}
jedis.del(key);
returnResource(jedis);
}測(cè)試代碼:
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 1; i <= 1000; i++) {
final String ii = "TEST:TEST_NUM-"+i;
cachedThreadPool.execute(new Thread(){
public void run(){
put(ii, ii);
}
});
}
}插入成功:

這是在jdk7 32位的情況下,我在jdk8 64位運(yùn)行會(huì)報(bào)異常的代碼仍然能插入。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
簡(jiǎn)單粗暴的Redis數(shù)據(jù)備份和恢復(fù)方法
這里我們來(lái)講解一個(gè)簡(jiǎn)單粗暴的Redis數(shù)據(jù)備份和恢復(fù)方法,有一個(gè)在不同主機(jī)上遷移Redis數(shù)據(jù)的示例,還有一個(gè)備份腳本實(shí)現(xiàn)的關(guān)鍵點(diǎn)提示,一起來(lái)看一下:2016-06-06
銀河麒麟V10sp1服務(wù)器系統(tǒng)安裝redis不能使用的快速解決辦法
這篇文章主要介紹了銀河麒麟V10sp1服務(wù)器系統(tǒng)安裝redis不能使用的快速解決辦法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
為什么RedisCluster設(shè)計(jì)成16384個(gè)槽
本文主要介紹了為什么RedisCluster設(shè)計(jì)成16384個(gè)槽,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Redis 數(shù)值范圍查詢(Numeric Range Queries)的實(shí)現(xiàn)
Java實(shí)現(xiàn)多級(jí)緩存的方法詳解

