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

背景:

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. HttpRunner学习1--Windows&Linux安装httprunner

    最近在学习HttpRunner,这是一款开源的接口测试框架,可用于HTTP(S)协议的接口测试.通过该框架,我们只需维护一份 YAML/JSON 脚本,即可轻松的进行接口自动化. 更多的介绍,大家可以 ...

  2. 死磕 java线程系列之线程池深入解析——构造方法

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 简介 ThreadPoolExecutor的构造方法是创建线程池的入口,虽然比较简单,但是信息量很大,由此也能 ...

  3. 微信小程序的入门

    1.申请账号     官网:https://mp.weixin.qq.com/  2.开发工具      为了帮助开发者简单和高效地开发和调试微信小程序,推出了小程序开发者工具,集成了公众号网页调试和 ...

  4. Ajax异步按下回车提交表单

    作者:故事我忘了¢个人微信公众号:程序猿的月光宝盒 html <form id="findInvis"> 帖子标题: <input title="请输入 ...

  5. 线程优先级,设置,setPriority()方法

    package seday08.thread;/** * @author xingsir * 线程优先级 * 线程启动后纳入到线程调度,线程时刻处于被动获取CPU时间片而无法主动获取.我们可以通过调整 ...

  6. 一文解读Redis (转)

    本文由葡萄城技术团队编撰并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 引言 在Web应用发展的初期,那时关系型数据库受到了较为广泛的关注和应用,原 ...

  7. VUE组件 之 Drawer 抽屉

    注:因为项目中用的是 element-ui 框架,而这个框架并没有抽屉组件,所以自己实现一个. 一.源码地址 https://github.com/imxiaoer/DrawerForVue 二.效果 ...

  8. Node.js Error简介以及捕获方式

    error的类型nodejs 的error 一般分为四种类型: 1.标准的 JavaScript 错误,例如 EvalError.SyntaxError.RangeError.ReferenceErr ...

  9. Linux系统学习 二十、SAMBA服务—介绍、安装、端口

    1.简介 网络数据文件共享服务器 可以和Windows中的网上邻居通用 数据共享的方法: Windows中最常用的是“网上邻居”.网上邻居使用的文件系统是CIFS(通用互联网文件系统)协议进行数据共享 ...

  10. Logstash filter 插件之 date

    使用 date 插件解析字段中的日期,然后使用该日期或时间戳作为事件的 logstash 时间戳.对于排序事件和导入旧数据,日期过滤器尤其重要.如果您在事件中没有得到正确的日期,那么稍后搜索它们可能会 ...