问题现象: 应用跑在tomcat上,近期遇到个问题,运行一段时间后就拒绝服务, 其他程序连接不上应用, 出问题后,tomcat和应用不记录任何日志,重启tomcat后,服务恢复。
第一次出现问题时, 根据经验判断是tomcat的线程数满了, 在服务器上查看链接数的确很多, 就修改了tomcat的最大连接数,从 400 改成 800,修改后应用持续到当天晚上21点,又挂了。
为了不影响业务,将最大连接数改成2000, 重启。
重启后,跟踪链接使用情况,发现从该应用到JOS平台的连接居多,都是ESTABLISHED 状态,并且持续增加,由此可判断问题出在这里。这种问题的第一反应就是,程序里有没有关闭链接的地方,于是开始翻代码,结果发现应用到JOS的连接都是JOS提供的SDK处理的,看了SDK中处理链接的地方,没看出什么问题(此时的关注点还在链接是否被关闭的问题上),代码如下:
public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout) throws IOException { HttpURLConnection conn = null; OutputStream out = null; String rsp = null; try { conn = getConnection(new URL(url), "POST", ctype); conn.setConnectTimeout(connectTimeout); conn.setReadTimeout(readTimeout); out = conn.getOutputStream(); out.write(content); rsp = getResponseAsString(conn); } finally { if(out != null) { out.close(); } if(conn != null) { conn.disconnect(); } } return rsp; }
又看了N遍代码,终于发现一个线索,应用程序中初始化jos client方法是这样的:
JdClient client=new DefaultJdClient(Constant.JDURL,sessionkey,appkey,secret);
JOS sdk里的代码是这样的:
public class DefaultJdClient implements JdClient { ... ... public DefaultJdClient(String serverUrl, String accessToken, String appKey, String appSecret) { this.connectTimeout = 0; this.readTimeout = 0; this.serverUrl = serverUrl; this.accessToken = accessToken; this.appKey = appKey; this.appSecret = appSecret; } public DefaultJdClient(String serverUrl, String accessToken, String appKey, String appSecret, int connectTimeout, int readTimeout) { this(serverUrl, accessToken, appKey, appSecret); this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; } ... ... }
问题来了,如果程序初始化client时不指定超时时间, SDK中默认成了0, 即无穷大,虽然不能确定是不是当前遇到的问题,但这样肯定是不合理的,改了试试再说,
JdClient client=new DefaultJdClient(Constant.JDURL,sessionkey,appkey,secret, 10000, 10000);
修改后,链接数正常了, 不再持续增加, 问题解决了,但是还有两个疑问:
1、出现链接未关闭的根本原因还不清楚;
2、这个应用已经正常运行了2年了, 为嘛现在才出现问题;
看了下应用日志,发现加上超时时间后,日志里有很多这种错误:
11:31:08,352 ERROR JingDongUpdateSkuStockCmd:62 - IO异常:
com.jd.open.api.sdk.JdException: 服务器连接超时,请重试
at com.jd.open.api.sdk.DefaultJdClient.execute(DefaultJdClient.java:103)
到此,问题就很清晰了:
近期,网络问题或者是JOS平台问题(嫌疑比较大),导致我们这边应用发出的请求得不到响应,而连接没有设置超时时间,导致程序一直走等待,占用线程数,等到线程数满了, tomcat就拒绝服务了。
最后甩个锅, 没有设置链接超时时间,是按照JOS平台给的代码示例写的,示例代码这么搞,坑人啊!