zoukankan      html  css  js  c++  java
  • Java中多线程服务中遇到的Redis并发问题?

    背景:

    一个中小型H5游戏

    核心错误信息:

      (1): java.lang.ClassCastException: [B cannot be cast to java.lang.Long

      at redis.clients.jedis.Connection.getIntegerReply(Connection.java:201)
      at redis.clients.jedis.Jedis.del(Jedis.java:129) 

     

      (2):redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket closed

      at redis.clients.jedis.Protocol.process(Protocol.java:131)
      at redis.clients.jedis.Protocol.read(Protocol.java:187)
      at redis.clients.jedis.Connection.getIntegerReply(Connection.java:201)
      at redis.clients.jedis.Jedis.del(Jedis.java:129)

    贴上核心问题代码(Jedis工具类):

    /**
         * 获取连接池.
         *
         * @return 连接池实例
         */
        private static JedisPool getPool() {
            String key = ip + ":" + port;
            JedisPool pool = null;
            //这里为了提供大多数情况下线程池Map里面已经有对应ip的线程池直接返回,提高效率
            if (maps.containsKey(key)) {
                pool = maps.get(key);
                return pool;
            }
            //这里的同步代码块防止多个线程同时产生多个相同的ip线程池
            synchronized (JedisUtil.class) {
                if (!maps.containsKey(key)) {
                    JedisPoolConfig config = new JedisPoolConfig();
                    config.setMaxTotal(maxTotal);
                    config.setMaxIdle(maxIdle);
                    config.setTestOnBorrow(true);
                    config.setTestOnReturn(true);
                    config.setMaxWaitMillis(maxWaitMillis);
                    config.setBlockWhenExhausted(blockWhenExhausted);
                    try {
                        if (password != null && !"".equals(password)) {
                            pool = new JedisPool(config, ip, port, timeout, password);
                        } else {
                            pool = new JedisPool(config, ip, port, timeout);
                        }
                        maps.put(key, pool);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    pool = maps.get(key);
                }
            }
            return pool;
        }
    
        /**
         * 获取Redis实例.
         *
         * @return Redis工具类实例
         */
        public Jedis getJedis() {
            Jedis jedis = null;
            int count = 0;
            while (jedis == null && count < retryNum) {
                try {
                    JedisPool pool = getPool();
                    jedis = pool.getResource();
                } catch (Exception e) {
                    logger.error("get redis master failed!", e);
                } finally {
                    closeJedis(jedis);
                }
                count++;
            }
            return jedis;
        }
    
        /**
         * 释放redis实例到连接池.
         *
         * @param jedis redis实例
         */
        public void closeJedis(Jedis jedis) {
            if (jedis != null) {
                getPool().returnResource(jedis);
            }
        }
    

      

    问题剖析:

      查阅了线上资料,发现是由于多线程使用了同一个Jedis实例导致的并发问题.

    结果:

      一开始,我发现我调用了getJedis()获取了jedis实例并使用后没有关闭.

      于是我把关闭Jedis的操作加上去了

      结果是错误的量少了

      但还是有报错,说明这是其中一个问题.

      最后还是没能使用Jedis连接池搞定这个问题

    解决办法:

      抛弃使用连接池

      每次使用Jedis都生成一个独立的实例

      每次用完以后就close()

      这样也就不存在并发的问题了

      这样做有一个潜在的问题是如果并发量达到很大值,Redis连接数被塞满的话还是会出现问题.

      一般情况下不是非常大的并发,用完就close的话,没那么容易到这个瓶颈

    相关代码:

        /**
         * 获取一个独立的Jedis实例
         * @return jedis
         */
        public Jedis getSingleJedis() {
            Jedis jedis = new Jedis(ip, port, timeout);
            jedis.connect();
            if (StringUtils.isNotBlank(password)) {
                jedis.auth(password);
            }
            return jedis;
        }

      // 关闭Jedis直接调用 jedis.close() 即可

      

  • 相关阅读:
    IOS-网络(大文件下载)
    IOS-网络(小文件下载)
    IOS-网络(监听网络状态)
    IOS-网络(数据安全:MD5加密)
    IOS-网络(发送JSON数据给服务器和多值参数)
    IOS-网络(GET请求和POST请求、HTTP通信过程、请求超时、URL转码)
    IOS-网络(JSON解析数据与XML解析数据)
    IOS-网络(HTTP请求、同步请求、异步请求、JSON解析数据)
    IOS-CoreData(增删改查、表关联、分页和模糊查询、多个数据库)
    App6种常见的数据加载设计
  • 原文地址:https://www.cnblogs.com/imyjy/p/7307635.html
Copyright © 2011-2022 走看看