笔者一直维护的稳定基础服务测试环境不稳定了,这能忍!盘他,虽然不一定能完全盘的了。

背景:

hrexternal 基础服务对外提供公司员工获取的多个接口,很多接口访问频率比较高,加了缓存,使用的是redis,但是redis最近2个月测试环境已经出问题了,时不时的报错,之前流程平台也报过错,只不过是随机的,不是必现的。当时也是没有具体原因,只是将底层的redis实例换掉了。然后就好了,这个服务呢由于历史原因还有很多其他服务是用的同一个redis实例,换的话需要好几个服务一起换,保障稳定性。

这次出现的问题更严重,因为每隔几分钟就会报错,get报错,put也会报错。所以就跟进排查了下。

Redis版本:3.0.7

Jedis版本:2.8.0

异常如下:



这俩异常不经常遇到,但是一旦遇到肯定是比较麻烦的。

笔者也是百度了很多,很多,从下面的链接中了解到一些信息:

https://blog.csdn.net/aubdiy/article/details/53511410

也是按照上面的思路进行排查:

1.找DBA帮忙看redis是否有改动配置,没有

2.看超时时间,客户端没有单独设置连接参数,默认超时时间应该是2秒。

3.可能是网络问题。但是实际上不是。

4.根据jedis  github上面的issues讨论内容发现具体原因也没有说出来,但是出现这个问题的人确实挺多的。解决的人基本上都加了Jedis的连接配置了,刚好我们的没有加,还有可能解决。

这里就揭开了针对于Jedis配置的一场探索之路。

首先看这个hrexternal服务的jedis初始化代码:

/**
* 初始化资源池
*/
static {
try {
if (jedisSentinelPool ==null) {
logger.info("init JedisSentinelPool is start....");
logger.info("redis_ip1:"+RedisConfig.redis_ip1+",redis_port1:"+RedisConfig.redis_port1);
logger.info("redis_ip2:"+RedisConfig.redis_ip2+",redis_ip2:"+RedisConfig.redis_port2);
logger.info("redis_ip3:"+RedisConfig.redis_ip3+",redis_ip2:"+RedisConfig.redis_port3);
Set<String> sentinels = new HashSet<String>();
sentinels.add(new HostAndPort(RedisConfig.redis_ip1, Integer.parseInt(RedisConfig.redis_port1)).toString());
sentinels.add(new HostAndPort(RedisConfig.redis_ip2, Integer.parseInt(RedisConfig.redis_port2)).toString());
sentinels.add(new HostAndPort(RedisConfig.redis_ip3, Integer.parseInt(RedisConfig.redis_port3)).toString());
jedisSentinelPool = new JedisSentinelPool(RedisConfig.master, sentinels);
logger.info(" init JedisSentinelPool is end....");
}
}catch(Exception e){
logger.error("---->init JedisSentinelPool was failed,the msg is " + e.getMessage(), e);
}
} /**
* 获取资源
* @return
* @throws Exception
*/
public static synchronized Jedis getJedis() throws Exception {
try {
if(jedisSentinelPool != null) {
Jedis e = jedisSentinelPool.getResource();
return e;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
logger.error(e);
return null;
}
}

使用的是Jedis哨兵模式进行Jedis初始化,同时使用Jedis连接池。出现上面的异常很多原因都跟连接池的连接有关。因此有必要分析一下Jedis的连接池和连接配置参数,如下图是Jedis连接配置参数和Jedis的连接池对象的类图:



其中只有GenericObjectPoolConfig,BaseObjectPoolConfig不是Jedis中的类,其他都是。这俩类是jedis依赖的另一个jar包:

	<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

这个包是不是看着既熟悉又陌生。这个竟然是java对象池池化技术的一个实现,相关文章如下:

https://blog.51cto.com/andrewli/2148179

当然本文的分析内容也包括这个,其中Jedis的一些配置参数也跟这个池化对象配置有关。

下面是我整理的一个配置参数介绍:

  1. maxTotal:程序允许创建资源的最大数量;默认值 -1,-1 代表无数量限制(int类型)
  2. blockWhenExhausted:当资源耗尽时,是否阻塞等待获取资源;默认值 true
  3. maxWaitMillis: 获取资源时的等待时间,单位毫秒。当blockWhenExhausted 配置为 true 时,此值有效。 -1 代表无时间限制,一直阻塞直到有可用的资源。(long类型)
  4. testOnBorrow: 否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个;默认值 false ,当设置为true时,调用 factory.validateObject() 方法
  5. testOnCreate 创建链接的时候进行链接有效性检查; 默认值 false,当设置为true时,调用 factory.validateObject() 方法(备注:如果 testOnBorrow 或者 testOnCreate 中有一个 配置 为 true 时,就调用 factory.validateObject() )
  6. lifo 资源的存取数据结构,默认值 true,true 资源按照栈结构存取,false 资源按照队列结构存取
  7. fairness 当从池中获取资源或者将资源还回池中时 是否使用 java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平锁机制。 默认值 false, true 使用公平锁,false 不使用公平锁,
  8. timeBetweenEvictionRunsMillis 回收资源线程的执行周期,单位毫秒。默认值 -1 ,-1 表示不启用线程回收资源。(long类型)
  9. evictionPolicyClassName 资源回收策略, 默认值org.apache.commons.pool2.impl.DefaultEvictionPolicy(String类型)
  10. minEvictableIdleTimeMillis 连接在池中保持空闲而不被空闲连接回收器线程(如果有)回收的最小时间值; 默认值 1800000,单位 毫秒(long类型 )
  11. softMinEvictableIdleTimeMillis 软资源最小空闲时间, 默认值 -1 ,单位 毫秒,(long类型 )(备注,这个两个参数,在资源回收策略中,会使用到)
  12. maxIdle 最大空闲资源数,默认值 8 (int类型)
  13. minIdle 最小空闲资源数,默认值 0 (int类型 )
  14. testWhileIdle 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除;默认值 false; 设置为 true 时,当回收策略返回false时,则 调用 factory.activateObject()和factory.validateObject()
  15. testOnReturn 默认值 false; 设置为 true 时,当将资源返还个资源池时候,验证资源的有效性,调用 factory.validateObject()方法,如果无效,则调用 factory.destroyObject()方法
  16. numTestsPerEvictionRun 资源回收线程执行一次回收操作,回收资源的数量。默认值 3, (int类型)。

    备注:当 设置为0时,不回收资源。

    设置为 小于0时,回收资源的个数为 (int)Math.ceil( 池中空闲资源个数 / Math.abs(numTestsPerEvictionRun) );设置为 大于0时,回收资源的个数为 Math.min( numTestsPerEvictionRun,池中空闲的资源个数 );

由于上面代码的配置是使用默认的参数,也就是说当链接出现问题的时候你是不知道是客户端出的问题还是服务端出的问题,跟DBA确认了一些服务端的参数:

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 512mb 128mb 60

client-output-buffer-limit pubsub 32mb 8mb 60

timeout 60 配置的60s。

由于服务端没有动配置,客户端没有动配置,也没有动代码。封装Jedis操作的每个API都检查了,最后都有finally代码块保证jedis用完会close.

不存在链接泄露问题。那为啥上面的错会发生?为啥稳定运行了很长时间最近才报错。

当然几个可能的方向

  1. 这个Redis实例被很多服务共享,导致数据错乱或者Redis链接有问题。
  2. Jedis配置问题
  3. 版本问题。

    当我设置了jedis链接池参数之后就不会出现上面的异常了,配置代码如下:
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setTestOnBorrow(true);
jedisPoolConfig.setTestOnReturn(true);
jedisPoolConfig.setTestOnCreate(true);
jedisPoolConfig.setMaxTotal(50);
jedisPoolConfig.setMaxIdle(10);
jedisPoolConfig.setMinIdle(1);
jedisPoolConfig.setMaxWaitMillis(3000);
jedisSentinelPool = new JedisSentinelPool(RedisConfig.master, sentinels,jedisPoolConfig);

部署完之后,发现异常不再出现。

虽然具体原因没有找到但是通过jedis开源代码和issues可以得到一些结论:

https://github.com/xetorthio/jedis/issues/932

https://blog.csdn.net/SakuraInLuoJia/article/details/89874287

也就是说有2点建议

  1. 不建议用Jedis默认的链接池配置,需要根据自己的需要在构造Jedis链接池的时候传入链接池配置。
  2. 将客户端版本与服务端版本尽量保持一致。

    当然如果你遇到这种问题的话,通过上面的方式还是搞不定,说明你没有找到正确的配置。即使有另一份配置放在你面前,它可能也不能解决你的问题,但至少是多了一种尝试。

本文由博客一文多发平台 OpenWrite 发布!

架构设计@工程设计@服务稳定性之路

Jedis Unexpected end of stream & java.net.SocketException: Broken pipe问题解决思路的更多相关文章

  1. redis报错:java.net.SocketException: Broken pipe (Write failed); nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe (Write failed)

    最近写了一个服务通过springboot构建,里面使用了redis作为缓存,发布到服务器运行成功,但是有时候会报redis的错误:org.springframework.data.redis.Redi ...

  2. Caused by: java.net.SocketException: Broken pipe

    异常信息 时间:2017-03-24 17:22:16,719 - 级别:[ WARN] - 消息: [other] The web application [ROOT] appears to hav ...

  3. JedisConnectionException: java.net.SocketException: Broken pipe (Write failed) 问题排查

    问题描述 笔者有2个应用会不定时请求redis,其中一个应用大约每分钟请求一次,可以正常请求,但是另一个大约每小时请求一次的应用,经常出现Broken pipe (Write failed)报错,具体 ...

  4. java.net.SocketException: Broken pipe 异常可能的原因

    org.apache.catalina.connector.ClientAbortException: java.net.SocketException: Broken pipe at org.apa ...

  5. java.net.SocketException: Broken pipe

    java.net.SocketException: Broken pipe 生产上遇到一个问题,socket发生Broken pipe错误,如下 这个问题跟踪了好几个月,始终没有模拟出为什么会发生Br ...

  6. openTSDB ConnectionManager: Unexpected exception from downstream java.io.IOException: Broken pipe

    openTSDB有这种错误: ConnectionManager: Unexpected exception from downstream for [id: 0xf85323a8, /10.65.3 ...

  7. solr报错 ERROR SolrDispatchFilter null:ClientAbortException: java.net.SocketException: Broken pipe 原因是nginx截断了请求

    [root@localhost nginx]# lltotal 36drwx------. 2 www root 4096 Aug 13 13:25 client_body_tempdrwxr-xr- ...

  8. 线上问题!----------org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe

    1.问题出现 昨晚项目在上线的时候因为推广的原因,新增的大量请求.在八点的时候. org.apache.catalina.connector.ClientAbortException: java.io ...

  9. java.io.IOException 断开的管道 解决方法 ClientAbortException: java.io.IOException: Broken pipe

    今天公司技术支持的童鞋报告一个客户的服务不工作了,紧急求助,于是远程登陆上服务器排查问题. 查看采集数据的tomcat日志,习惯性的先翻到日志的最后去查看有没有异常的打印,果然发现了好几种异常信息,但 ...

随机推荐

  1. 老版本nginx存在安全漏洞,不停服务热升级

    1.场景描述 安全部通知:nginx存在"整数溢出漏洞",经测试2017年4月21日之后的版本无问题,将openresty升级到最新版本,Nginx升级到1.13.2之后的版本. ...

  2. webpack4配置学习(一)

    webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency gr ...

  3. C# 读写倍福plc beckhoff , 使用ADS协议实现读取plc

    本文将使用库技术来读写倍福PLC数据,使用的是基于以太网的ADS实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 github地址:https://g ...

  4. Highlight List View Objects 突出显示列表视图对象

    In this lesson, you will learn how to format data that satisfies the specified criteria. For this pu ...

  5. 关于 IIS Express 常用设置

    关于 IIS Express 常用设置 站点绑定 IIS Express Web 服务器默认只绑定了 localhost 的主机名,这就意味着无法通过内网或其他自定义域名进行访问,可通过如下操作添加其 ...

  6. seaborn画出的一些好看的图片

    PYSPARK_DRIVER_PYTHON=/home/zhangyu/anaconda3/bin/jupyter-notebook PYSPARK_DRIVER_PYTHON_OPTS=" ...

  7. JavaScript定时器(Timer)

    版权声明:本文为博主原创文章,未经博主允许不得转载.https://www.cnblogs.com/gaoguowen/p/11119088.html 什么是定时器 简单来说就是在一段时间间隔后执行一 ...

  8. C++ map insert 另一个map的子集

    C++map中 会有insert操作,举个例子 存在map A,我们截取一部分到map B中,void insert (InputIterator first, InputIterator last) ...

  9. Dockerfile编写

    Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建编写命令: 1.FROM作用:声明使用哪个基础镜像格式:FROM IMA ...

  10. Docker 中卷组管理

    一.概念 数据卷是一个可供一个或多个容器使用的特殊目录实现让容器的一个目录和宿主机中的一个文件或者目录进行绑定.数据卷 是被设计用来持久化数据的,对于数据卷你可以理解为NFS中的哪个分享出来的挂载点, ...