• RedisPool的TestOnBorrow,TestOnReturn的坑


     今天尝试Redis的分布式锁,因为没有分布式环境,使用多线程来代替,但是在使用多线程的时候,总是会有

    redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket closed或者

     
    Caused by: redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
        at redis.clients.util.Pool.returnResourceObject(Pool.java:69)
        at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:253)
        ... 14 more
    Caused by: java.lang.IllegalStateException: Object has already been returned to this pool or is invalid
        at org.apache.commons.pool2.impl.GenericObjectPool.returnObject(GenericObjectPool.java:538)
        at redis.clients.util.Pool.returnResourceObject(Pool.java:67)
        ... 15 more
     
    redis.clients.jedis.exceptions.JedisDataException: ERR Protocol error: invalid bulk length
    	at redis.clients.jedis.Protocol.processError(Protocol.java:127)
    	at redis.clients.jedis.Protocol.process(Protocol.java:161)
    	at redis.clients.jedis.Protocol.read(Protocol.java:215)
    	at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
    	at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
    	at redis.clients.jedis.Jedis.set(Jedis.java:139)
    	at com.sunxj.redis.DistributeLock.lock(DistributeLock.java:85)
    	at com.sunxj.redis.DistributedLockTest.lambda$createThread$0(DistributedLockTest.java:32)
    	at java.lang.Thread.run(Thread.java:748)
    

      

    Exception in thread "Thread-71" java.lang.ClassCastException: java.lang.Long cannot be cast to [B
        at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:259)
        at redis.clients.jedis.Connection.getBulkReply(Connection.java:248)
        at redis.clients.jedis.Jedis.lpop(Jedis.java:1055)
        at us.codecraft.webmagic.scheduler.RedisScheduler.poll(RedisScheduler.java:77)
        at us.codecraft.webmagic.Spider.run(Spider.java:308)
        at java.lang.Thread.run(Thread.java:748)
    

      

    主要问题就是在于Redis是一个单线程,多线程操作的话就会产生错误。首先看了一下Jconsole,发现大多数的线程都是Block状态

     但是他们运行的特别慢,看了一下Runnable的线程,

    可以发现线程卡在了SocketRead,name可以断定是Redis的问题,redis一直被卡住,所以才非常慢。

    通过查阅论坛和搜索,可以知道是因为set函数被挂起了,导致最后set不成功。前辈的解释:

    查看 Jedis 源码发现它的Connection中对网络输出流做了一个封装(RedisInputStream),其中自建了一个buffer。当发生异常的时候,这个buffer里还残存着上次没有发送或者发送不完整的命令。这个时候没有做处理,直接将该连接返回到连接池,那么重用该连接执行下次命令的时候,就会将上次没有发送的命令一起发送过去,所以才会出现上面的错误“返回值类型不对”。

    而如果我们配置了TestOnBorrow或者TestOnReturn,就会检测这是不是一个活的jedis。但是高并发下就会很多失败,就没有一些所以会被一直卡住,无法从redis获得数据。(不知道解释的对不对)有知道答案的请告诉我。。。

    那我们应该怎么办呢?

    1. 最简单的办法就是为每一个线程建立一个jedis对象,并且lock或者release的的方法传入jedis。

    2. 但是这样总是觉得很别扭,应为这是线程池的问题,因此,我们可以将TestOnBorrow或者TestOnReturn都设置为false,同时如果set或者release失败了,我们直接返回,就不等待了!

    两种方法都可以的。 

     

  • 相关阅读:
    NHibernate使用
    成为专业程序员路上用到的各种优秀资料、神器及框架
    SignalR
    sql 语句 事务
    entity framework 数据加载三种方式的异同(延迟加载,预加载,显示加载)
    获取前一个页面的地址
    给button按钮加回车事件
    单元测试
    教学资源网站整理
    Go资源
  • 原文地址:https://www.cnblogs.com/tjpeng/p/12342478.html
Copyright © 2020-2023  润新知