at java.net.SocketInputStream.socketRead0(Native Method)

1.背景

现在的Java EE 系统通常都依赖于远程服务,需要通过网络请求获取远程服务。不管是使用什么协议(HTTP,HTTPS,JDBC 等)请求远程服务,Java VM最终都将委托给Socket去实现。首先会通过connect方法与远程服务建立连接,连接建立成功后就可以 write/read 与远程服务实现数据通信。

Socket.connect()、Socket.write()、Socket.read() 方法调用都被称为阻塞IO调用。这些调用由于网络延时、服务器响应缓慢等问题往往会导致线程挂起。

2.问题

从远程并发读取多张图片到服务器,之后转成字节流输送到三方服务。
读取图片代码
使用图片上传
线程池
项目初始启动后,能上传一些图片。也有日志打印,之后不打印日志,也不上传图片。
为了分析原因,只有到生产查看日志

3.排查

登陆服务器查询java应用的pid,ps -ef|grep "java",找到pid
使用命令jstack pid|grep "picSubmit" -A 20,找到该线程池的所有线程当前状态的日志。如下
在这里插入图片描述
线程池中的所有线程,都是这个状态。一直运行着,并未造成阻塞。之后找到我们自己的代码,有一个readRemoteFile,1948行。
定位到改代码是while ((n = fis.read(b)) != -1) { 这一行。结合上下文发现,如果n不是-1,该循环会一直开着。因此造成线程一直运行着,也不报错。
从日志我们还发现,fis.read。是调用了sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3066)的实现。而我们的HttpURLConnection没有设置读超时和链接超时。所以线程一直被占用着无法释放。

4.原因

经过排查,定位到问题后,知道是网络没有超时,数据没拿到。为啥会出现这样的原因呢,是因为生产环境没有外网,服务要网文外网全都走的同一个代理通道。这样就会造成网络很差,而我们的图片又大。再带宽一定下,使用的变的多,就造成了个别线程阻塞。这就是为啥,一开始有的能传,有的不能传。最后由于积少成多,所有线程池都攒满了,就不会有线程来运行任务了,又由于使用了阻塞并且没有阻塞超时。因此就一直卡着。但是由于tomcat框架设置了超时,一定时间后,主线程任然会关闭,不会造成影响。而内部线程池则一直在等待中。

5.解决方案

1.修改打断,设置超时时间latch.await(20, TimeUnit.SECONDS);
2.设置http超时时间

 private static byte[] readRemoteFile(String filePath) {
        byte[] buffer = null;
        try {
            URL url = new URL(filePath); // 创建URL
            URLConnection conn = url.openConnection(); // 试图连接并取得返回状态码
            conn.connect();
            HttpURLConnection http = (HttpURLConnection) conn;
            http.setReadTimeout(5000);
            http.setConnectTimeout(3000);
            if (http.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream fis = new DataInputStream(conn.getInputStream());
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] b = new byte[1024];
                int n;
                while ((n = fis.read(b)) != -1) {
                    bos.write(b, 0, n);
                }
                fis.close();
                bos.close();
                buffer = bos.toByteArray();
            }
        } catch (Exception e) {
            logger.error("获取远程图片失败:"+e.getCause());
            return null;
        }
        return buffer;
    }
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值