FastDFS并发上传Read timed out和NullPointerException

场景:图片迁移用到fastdfs。为了防止多次创建服务器链接,使用静态代码块。在服务启动时加载配置,创建与服务器连接。

后来在程序运行过程中偶尔会抛出Read timed out 和NullPointerException。前者是等待服务器返回数据超时,后者是对象为空调用了close方法。

在超时情况下观察nginx日志发现该请求执行了50.39秒,返回错误码为499(client has closed connection)表示此链接已经建立,nginx在想给客户端发送数据时发现客户端已经关闭了链接。查看服务器图片已经存在。那么猜想既然是超时那么就有两种情况:连接超时和数据读写超时。499表示客户端已经主动关闭了链接,那么就考虑代码里设置的超时时间是多少。发现都是50秒,上传一张图片顶多几十毫秒不可能因超时而关闭关闭连接,猜想可能是程序主动关闭了此链接。而关于关闭连接的操作自己不可能写。看fastdfs源码发现,其不支持多线程上传。多图片上传时会使用一个stock,在一张图片上传成功后程序关闭了这个stock,fastdfs服务端想反回数据发现没有stock,所以会返回499,客户端主动关闭了连接。既然关闭了链接那么空指针也可能就是因为关闭这个连接导致的

源码跟进:

第一步:ClientGlobal.initByProperties(CONFIG_FILENAME);

这一行主要作用是读取配置文件。统计所有trackerServer服务器,为以后创建socket做准备。下面是源码主要部分:

第二步:获取调度服务器客户端

源码:new TrackerClient();

第三步:实例化TrackerServer

与其说实例化不如说是与服务器创建socket链接。

源码:

 

 

第四步:创建java存储客户端

// 声明一个StorageServer对象,null

StorageServer storageServer = null;

// 获得StorageClient1对象

new StorageClient1(trackerServer, storageServer);

并发原因:

到此为止,我们已经大致了解了fastdfs初始化以及创建连接情况。这个时候我们调用StorageClient.upload()是可以成功上传的,似乎已经完成了全部工作。但是刚刚报的读取超时和空指针又从何而来?为什么会偶尔发生?

如果细心我们会发现第四步传入的StorageServer是一个null。那么这个对象是什么时候实例化的。我们并没有做任何事情除了upload()方法。似乎这一切都指向了这个方法。

源码:

 

 

看到这三段主要代码一切问题都解开了。

原来在每次调用上传方法时如果没有StorageServer都会创建一个新的。创建方法也很简单,就是根据ip和端口号创建的。这样才使我们每次上传都能成功。

出现空指针和读取超时也正是因为这种做法。注意上边最后一个图,在完成所有操作后需要关闭StorageServer。但是此步操作和创建新socket没有加锁,导致这样一种情况:

两个线程同时同步上传,A先进来,发现StorageServer没有,创建了新连接,但是还没有完成上传时B抢到了资源,B一看本来就有StorageServer,于是没有创建新的连接。接着A执行完上传,并关闭了连接,而B已经连上服务器但是还没有上传完成,一直等待,但是A关闭了迟迟没有返回数据给B,那么B就抛出读取超时异常。如果A传完了,B也传完了,A先把连接关闭了,没事,等到B也要关闭连接时发现StorageServer为null,此时就抛出空指针异常。

解决:

每次都创建一个新的socket。

注意这个new StorageClient1(trackerServer, storageServer);必须使用new 不能使用全局变量。不然使用的还是一个StorageServer。

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/Houz/p/9507121.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值