异常描述

使用 Fastdfs 作为文件存储系统,Java 客户端单个上传文件没什么问题,在并发300的时候就会出现异常,部分上传失败。

客户端是自己封装的 spring-boot-starter-fastdfs,引用的核心库是 happyfish100大神的 java 版客户端,该库的 github 地址是 https://github.com/happyfish100/fastdfs-client-javaSpringBoot 的版本是 2.0.3.RELEASE

项目中引入编译好的 fastdfs-client-java jar lib。

1
2
3
4
5
6
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
</dependency>

fastdfs-client-java 没有实现连接池,故此,参考 Thirft 的连接池,本人写了一个连接池,提升性能。

spring-boot-starter-fastdfs 的仓库地址: https://github.com/FataliBud/spring-boot-starter-fastdfs

由于 fastdfs-client-java 加载的配置文件是 fastdfs.conf(也可以自己指定),在实际开发过程中,我们其实是根据不同的 spring.profile 区别环境的,整合在 spring 的配置文件则更方便,因此该项目只需要在 spring 的配置文件中配置 fastdfs 即可。

配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 连接超时时间,单位秒
fastdfs.connect-timeout-in-seconds=10
# socket读取超时时间,单位秒
fastdfs.network-timeout-in-seconds=30
# 字符编码
fastdfs.charset=UTF-8
# token 防盗链功能
fastdfs.http-anti-steal-token=false
# 密钥
fastdfs.http-secret-key=FastDFS123456789
# TrackerServer port
fastdfs.http-tracker-http-port=80
# tracker server服务地址列表
fastdfs.tracker-server-list[0]=10.22.10.22:22122

java调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Main.class})
@Slf4j
public class Test {

@Autowired
private FastDFSClient fastDFSClient;

@org.junit.Test
public void test() {
for (int i = 0; i &lt; 500; i++) {
new Thread(() -&gt; {
String path = "/Users/zhoujunwen/Desktop/20140102095833296.png";
String fileId = fastDFSClient.upload(path, null);
}).start();
}
try {
TimeUnit.MINUTES.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

单个测试没发现问题,但是当多线程模拟上传时,发现有问题:

java.io.IOException: recv package size -1 != 10
        at org.csource.fastdfs.ProtoCommon.recvHeader(ProtoCommon.java:206)
        at org.csource.fastdfs.ProtoCommon.recvPackage(ProtoCommon.java:242)
        at org.csource.fastdfs.TrackerClient.getStoreStorage(TrackerClient.java:143)
        at org.csource.fastdfs.StorageClient.newWritableStorageConnection(StorageClient.java:1912)
        at org.csource.fastdfs.StorageClient.do_upload_file(StorageClient.java:702)
        at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:207)
        at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:225)
        at org.csource.fastdfs.StorageClient1.upload_file1(StorageClient1.java:112)

解决方法

该异常在网络超时之后,重新调用 upload 方法上传文件时,由于 socket 未连接造成的,我们可以在连接池的配置中开启 TestOnBorrow:

1
2
// 集成BaseObjectPoolConfig类,设置true即可
setTestOnBorrow(true);

同时,从连接池中 borrow TrackerServer 的时候,需要做校验,测试是否连接。

1
2
3
4
5
6
7
8
9
10
11
public TrackerServer borrowObject() {
TrackerServer trackerServer = null;
try {
trackerServer = trackerServerPool.borrowObject();
// 下面这段就是为了测试SOCKET是否已经连接
ProtoCommon.activeTest(trackerServer.getSocket());
} catch (Exception e) {
log.warn("borrowObject出现异常", e);
}
return trackerServer;
}

默认情况下,fastdfs 仅仅支持最大连接数为 256, 所以在测试阶段发现只有 255 个成功,可以通过修改 tracker.conf 中的 max_connetion 即可增加连接数。同时记得修改 Linux 修通的 TCP 连接限制。