【问题标题】:Different connection exceptions in RedisRedis中的不同连接异常
【发布时间】:2015-11-15 11:51:06
【问题描述】:

我在服务器模式下使用 Redis,而不是在集群模式下。我有 3 个 Redis Server 实例正在运行,3 个从属对应于这 3 个主控。我还有 3 个 Sentinel,每个 Sentinel 监视所有三个 master。

要连接到我的 Sentinel 并创建一个池,我使用的是 ShardedJedisSentinelPool。现在我从这个池中获取连接的类看起来像这样

public class RedisServerConnectionDAL 
{
    private RedisServerConnectionDAL()
    {

    }

    private static ShardedJedisSentinelPool pool;
    private static ShardedJedis jedis;
    private static ShardedJedisPipeline pipeline;
    private static Set<String> redissentinels;
    private static List<String> redismasters;
    private static int redistimeout;
    private static RedisServerConnectionDAL redisdal;
    private static Map<String,String> redisconfmap;
    public static int WAIT_IF_FAIL;
    public static int RETRIES;

    public synchronized static RedisServerConnectionDAL getInstance()
    {
         if(redisdal==null)
         {
             Properties redisprops = new Properties();

             redisprops = ConfigReader.ReadConfig(Constant.REDIS_CONNECTION_CONFIG_FILE);

             GenericObjectPoolConfig config = new GenericObjectPoolConfig();

             redisconfmap = new HashMap(redisprops);

             redisdal = new RedisServerConnectionDAL();

             String redisNodesString = redisconfmap.get(Constant.REDIS_SENTINELS);

             redissentinels = new HashSet<String>(Arrays.asList(redisNodesString.split(Constant.CONFIGURATION_FILE_MULTIVALUE_SEPERATOR)));

             String redisMastersString = redisconfmap.get(Constant.REDIS_MASTERS);

             redismasters = new ArrayList<String>();

             redismasters.addAll(Arrays.asList(redisMastersString.split(Constant.CONFIGURATION_FILE_MULTIVALUE_SEPERATOR)));

             redistimeout = Integer.parseInt(redisconfmap.get(Constant.REDIS_TIMEOUT));

             WAIT_IF_FAIL = Integer.parseInt(redisconfmap.get(Constant.REDIS_WAIT_IF_FAIL));

             RETRIES = Integer.parseInt(redisconfmap.get(Constant.REDIS_RETRIES));

             pool = new ShardedJedisSentinelPool(redismasters, redissentinels, config, redistimeout);

             jedis = pool.getResource();

             pipeline = jedis.pipelined();

         }
         return redisdal;
    }

    public ShardedJedis getJedisResource()
    {
        jedis = pool.getResource();
        return jedis;
    }

    public ShardedJedisPipeline getPipelineResource()
    {
        pipeline = jedis.pipelined();
        return pipeline;
    }

    public ShardedJedisPipeline getPipeline()
    {
        return pipeline;
    }

    public ShardedJedis getJedis()
    {
        return jedis;
    }

    public static void resetRedisServerConnectionDal()
    {
        pipeline = null;
        jedis = null;
        redisdal = null;
    }
} 

所以,现在无论我想在哪里与 Redis 交互,我都会做这样的事情

Jedis jedis = RedisServerConnectionDAL.getInstance.getJedis();

try
{
    //code.....
}
catch(JedisConnectionException jce)
{
        log.error(jce.getMessage());
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        jce.printStackTrace(pw);
        log.error("Error:" + sw.toString());
        if(retrycount < RedisServerConnectionDAL.RETRIES)
        {
            retrycount++;
            log.error("Waiting for " + RedisServerConnectionDAL.WAIT_IF_FAIL + ", after which it will reconnect to Redis.");
            try 
            {
                Thread.sleep(RedisServerConnectionDAL.WAIT_IF_FAIL);
            }
            catch (InterruptedException e) 
            {
                log.error(e.getMessage());
                e.printStackTrace(pw);
                log.error("Error:" + sw.toString());
                log.error("Error while Sleeing!!!");
            }
            RedisServerConnectionDAL.resetRedisServerConnectionDal();

            jedis = RedisServerConnectionDAL.getInstance();.getJedisResource();
            pipeline = redisserverconnectiondal.getPipelineResource();
        }
        else
        {
            log.error("Redis retries exhausted");
            log.error(jce.getMessage());
            StringWriter sw1 = new StringWriter();
            PrintWriter pw1 = new PrintWriter(sw1);
            jce.printStackTrace(pw1);
            log.error("Error:" + sw1.toString());
            log.error("Error while executing pipeline in Redis!");
            retrycount = 0;;
        }
    }
    catch(Exception e)
    {
        log.error(e.getMessage());
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        log.error("Error:" + sw.toString());
        log.error("Error while executing pipeline in Redis!");
    }

但我不断收到连接拒绝异常、连接关闭异常等异常。

有时我也会在一行的 catch 块中得到空指针异常

jedis = RedisServerConnectionDAL.getInstance();.getJedisResource();

典型的异常日志如下所示

ERROR  com.cleartrail.entityprofiling.rediscachecomponent.InMemoryDataAccessLayer -     Error:redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset
    at redis.clients.jedis.Protocol.sendCommand(Protocol.java:94)
    at redis.clients.jedis.Protocol.sendCommand(Protocol.java:74)
    at redis.clients.jedis.Connection.sendCommand(Connection.java:78)
    at redis.clients.jedis.BinaryClient.hexists(BinaryClient.java:257)
    at redis.clients.jedis.Client.hexists(Client.java:174)
    at redis.clients.jedis.Jedis.hexists(Jedis.java:705)
    at redis.clients.jedis.ShardedJedis.hexists(ShardedJedis.java:213)
    at com.cleartrail.entityprofiling.rediscachecomponent.InMemoryDataAccessLayer.checkForNewEntity(InMemoryDataAccessLayer.java:339)
    at com.cleartrail.entityprofiling.rediscachecomponent.InMemoryDataAccessLayer.writeInMemoryData(InMemoryDataAccessLayer.java:215)
    at com.cleartrail.entityprofiling.rediscachecomponent.InMemoryDataAccessLayer.run(InMemoryDataAccessLayer.java:715)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.SocketException: Connection reset
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
    at redis.clients.util.RedisOutputStream.flushBuffer(RedisOutputStream.java:31)
    at redis.clients.util.RedisOutputStream.writeIntCrLf(RedisOutputStream.java:186)
    at redis.clients.jedis.Protocol.sendCommand(Protocol.java:81)
    ... 10 more

我知道我没有以正确的方式使用连接池。请建议正确的方法。另外,我使用的 jedis 是多线程环境,其中两个线程将调用 RedisServerConnectionDAL.getInstance().getJedisResource() 方法并与 Redis 交互。 Jedis 线程安全吗?

【问题讨论】:

    标签: java multithreading redis jedis


    【解决方案1】:

    Jedis 实例不是线程安全的,但您可以使用 JedisPool 获取每个线程的连接并确保您从池中获得有效的连接。

    lettuce client 是线程安全的,可以为您管理自动重新连接。由于重新连接/连接重置,您不会在代码中看到任何异常。 lettuce 也适用于 Redis Sentinel。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 2019-01-20
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多