记录:fastdfs上传报错IOException: recv cmd: 0 is not correct, expect cmd: 100解决方案

项目在生产环境部署,出现大批量文件并发上传,运行环境偶发性报错:

java.io.IOException: recv cmd: 0 is not correct, expect cmd: 100
  at org.csource.fastdfs.ProtoCommon.recvHeader(ProtoCommon.java:173)
  at org.csource.fastdfs.ProtoCommon.recvPackage(ProtoCommon.java:201)
  at org.csource.fastdfs.StorageClient.do_upload_file(StorageClient.java:714)
  at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:162)
  at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:180)
  at org.csource.fastdfs.StorageClient1.upload_file1(StorageClient1.java:103)
  at com.innoking.coding.utils.FastDfsClient.uploadFile(FastDfsClient.java:102)
  at com.innoking.coding.utils.FastDfsClient.uploadFile(FastDfsClient.java:111)
  at com.innoking.coding.FileHelper.saveAndUploadImageToFds(FileHelper.java:111)
  at com.innoking.coding.uniview.subscriptioncontroller.RedisMsgSubscriber1.downPhoto(RedisMsgSubscriber1.java:315)
  at com.innoking.coding.uniview.subscriptioncontroller.RedisMsgSubscriber1.onMessage(RedisMsgSubscriber1.java:186)
  at org.springframework.data.redis.connection.lettuce.LettuceMessageListener.message(LettuceMessageListener.java:43)
  at org.springframework.data.redis.connection.lettuce.LettuceMessageListener.message(LettuceMessageListener.java:29)
  at io.lettuce.core.pubsub.PubSubEndpoint.notifyListeners(PubSubEndpoint.java:219)
  at io.lettuce.core.pubsub.PubSubEndpoint.notifyMessage(PubSubEndpoint.java:208)
  at io.lettuce.core.pubsub.PubSubCommandHandler.doNotifyMessage(PubSubCommandHandler.java:292)
  at io.lettuce.core.pubsub.PubSubCommandHandler.decode(PubSubCommandHandler.java:130)
  at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:594)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
  at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
  at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
  at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795)
  at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480)
  at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)
  at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
  at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
  at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
  at java.lang.Thread.run(Thread.java:748)

环境如下:服务器环境fastDFS做了主从备份

  • 操作系统:centos7
  • FastDFS :5.05
  • libfastcommon:1.0.36
  • nginx :1.7.9
  • fastdfs-nginx-module:1.16

fastDFS的tracker服务器日志如下类似:

[2017-09-19 09:13:52] ERROR - file: tracker_nio.c, line: 306, client ip: 192.168.0.1, pkg length: 15150 > max pkg size: 8192
[2017-09-19 10:34:57] ERROR - file: tracker_nio.c, line: 306, client ip: 192.168.0.1, pkg length: 16843 > max pkg size: 8192
[2017-09-19 10:34:57] ERROR - file: tracker_nio.c, line: 306, client ip: 192.168.0.1, pkg length: 16843 > max pkg size: 8192
[2017-09-19 11:31:08] ERROR - file: tracker_nio.c, line: 306, client ip: 192.168.03, pkg length: 23955 > max pkg size: 8192
[2017-09-19 11:42:56] ERROR - file: tracker_nio.c, line: 306, client ip: 192.168.01, pkg length: 12284 > max pkg size: 8192
[2017-09-19 12:10:28] ERROR - file: tracker_service.c, line: 2452, cmd=103, client ip: 192.168.0.3, package size 6258 is too long, exceeds 144
 

storaged的日志如下类似:

[2017-09-25 14:22:38] WARNING - file: storage_service.c, line: 7135, client ip: 192.168.1.11, logic file: M00/D1/04/wKg5ZlnIoKWAAkNRAAAY86__WXA920.jpg-m not exist
[2017-09-25 14:22:39] WARNING - file: storage_service.c, line: 7135, client ip: 192.168.1.11, logic file: M00/D1/04/wKg5ZlnIoKuAUXeVAAAeASIvHGw673.jpg not exist
[2017-09-25 14:22:50] ERROR - file: storage_nio.c, line: 475, client ip: 192.168.1.13, recv failed, errno: 104, error info: Connection reset by peer
[2017-09-25 14:22:56] ERROR - file: tracker_proto.c, line: 48, server: 192.168.1.11:23001, response status 2 != 0
[2017-09-25 14:23:06] ERROR - file: tracker_proto.c, line: 48, server: 192.168.1.11:23001, response status 2 != 0
[2017-09-25 14:23:11] ERROR - file: storage_service.c, line: 3287, client ip:192.168.1.13, group_name: group2 not correct, should be: group1

我们项目中fastdfs在application.properties中配置:

clientConf=ip:22122

项目中获取fastDFS上传方法如下:

 FastDfsClient fastDFSClient = FastDfsClient.getInstance(clientConf);

//获取到返回的fastdfs附件地址(不含服务器ip地址的附件地址)
String fastdfs_url = fastDfsClient.uploadFile(uploadFile.getBytes(), fileSuffix);

项目中引用fastdfs工具类如下:

package com.innoking.coding.utils;

import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;

import java.io.IOException;

/**
 * 
 * @ClassName: FastDfsClient
 * @Description: TODO
 * @author
 * @version
 *
 */
public class FastDfsClient {

	private TrackerClient trackerClient = null;
	private TrackerServer trackerServer = null;
	private StorageServer storageServer = null;
	private StorageClient1 storageClient = null;



	private  static FastDfsClient fastDfsClient=null;
	public static  FastDfsClient  getInstance(String conf) throws Exception {
		if( fastDfsClient==null){
			fastDfsClient=new FastDfsClient(conf);
		}
		return fastDfsClient;
	}

	public FastDfsClient(String conf) throws Exception {
		if (conf.contains("classpath:")) {
			conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
		}
		ClientGlobal.initByTrackers(conf);
		ClientGlobal.init(conf);
		trackerClient = new TrackerClient();
		trackerServer = trackerClient.getConnection();
		storageServer = null;
		storageClient = new StorageClient1(trackerServer, storageServer);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileName 文件全路径
	 * @param extName 文件扩展名,不包含(.)
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
		String result = storageClient.upload_file1(fileName, extName, metas);
		return result;
	}
	
	public String uploadFile(String fileName) throws Exception {
		return uploadFile(fileName, null, null);
	}
	
	public String uploadFile(String fileName, String extName) throws Exception {
		return uploadFile(fileName, extName, null);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileContent 文件的内容,字节数组
	 * @param extName 文件扩展名
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
		
		String result = storageClient.upload_file1(fileContent, extName, metas);
		return result;
	}
	
	public String uploadFile(byte[] fileContent) throws Exception {
		return uploadFile(fileContent, null, null);
	}
	
	public String uploadFile(byte[] fileContent, String extName) throws Exception {
		return uploadFile(fileContent, extName, null);
	}
	
	/**
	 * 删除文件
	 * @param fileName 文件服务器保存的地址
	 * @return
	 * @throws IOException
	 * @throws MyException
	 */
	public int deleteFile(String fileName) {
		try {
			return storageClient.delete_file1(fileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (MyException e) {
			e.printStackTrace();
		}
		return 1;
	}
	
	/**
	 * 下载文件
	 * @param fileName  文件服务器中的文件名
	 * @param local_filename 下载到本地的文件名
	 * @return
	 * @throws IOException
	 * @throws MyException
	 */
	public int downloadFile(String fileName,String local_filename) throws IOException, MyException{
		return storageClient.download_file1(fileName, local_filename);
	}
	/**
	 * 下载文件
	 * @param fileName  文件的路径 如group1/M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
	 * @return
	 * @throws IOException
	 * @throws MyException
	 */
	public byte[] getBytedownloadFile(String fileName) throws IOException, MyException{
		return storageClient.download_file1(fileName);
	}

}

开发环境因并发量并不大未出现问题,上线至生产环境后上传文件速度过快造成了并发量增大。

问题原因:在每次上传时都获取了storageClient,如上传时定义了:

String fastdfs_url = fastDfsClient.uploadFile(uploadFile.getBytes(), fileSuffix);

但是大批量上传时,可以理解为第一次上传时storageClient创建了storageServer,但是第二次上传时,发现已经有storageServer就直接使用了。因此造成第一次上传结束后storageServer关闭,第二次上传借用的storageServer丢失。

排查问题中参考该文章得到解决:一次FastDFS并发问题的排查经历_luckykapok918的博客-CSDN博客

特别感谢该文章博主。

具体修改如下:

对FastDfsClient工具类进行修改,将上传方法uploadFile变更为如下:

public class FastDfsClient {

	private TrackerClient trackerClient = null;
//	private TrackerServer trackerServer = null;
	private StorageServer storageServer = null;
//	private StorageClient1 storageClient = null;



	private  static FastDfsClient fastDfsClient=null;
	public static  FastDfsClient  getInstance(String conf) throws Exception {
		if( fastDfsClient==null){
			fastDfsClient=new FastDfsClient(conf);
		}
		return fastDfsClient;
	}

	public FastDfsClient(String conf) throws Exception {
		if (conf.contains("classpath:")) {
			conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
		}
		ClientGlobal.initByTrackers(conf);
		ClientGlobal.init(conf);
		trackerClient = new TrackerClient();
//		trackerServer = trackerClient.getConnection();
		storageServer = null;
//		storageClient = new StorageClient1(trackerServer, storageServer);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileName 文件全路径
	 * @param extName 文件扩展名,不包含(.)
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
		TrackerServer trackerServer = trackerClient.getConnection();
		StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);
		String result = storageClient.upload_file1(fileName, extName, metas);
		return result;
	}
}

在每次上传时都new新的storageClient和storageServer。

进行一天测试后问题未复现,得到解决

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
org.csource:fastdfs-client-java:1.29-是一个Java语言的FastDFS客户端,用于访问FastDFS分布式文件系统。FastDFS是一个开源的分布式文件系统,具有高性能、高可靠性、可扩展性和易于管理等特点。FastDFS将文件分成许多小块,然后存储在多台服务器上,提供了快速的文件上传和下载功能。 org.csource:fastdfs-client-java:1.29-是FastDFSJava语言实现,通过该客户端,我们可以轻松地在Java项目中使用FastDFS进行文件的上传和下载。它提供了一组简单易用的API,允许我们通过指定文件路径或字节数组来上传文件,并通过文件的标识符来下载文件。同时,我们还可以获取文件的元信息,例如文件大小、创建时间等。 通过该客户端,我们还可以进行文件的删除、修改和查询等操作。它提供了丰富的接口方法,可以满足不同的业务需求。此外,该客户端还支持文件的断点续传功能,当网络中断或上传下载过程中出现异常时,我们可以恢复中断的操作,避免重新上传或下载整个文件。 org.csource:fastdfs-client-java:1.29-是一个成熟稳定的Java组件,被广泛应用于各种基于Java的项目中。它的源代码是开放的,意味着我们可以根据自己的需求进行修改和定制。此外,它还具有良好的文档和社区支持,我们可以在遇到问题时及时获得帮助和解决方案。 总之,org.csource:fastdfs-client-java:1.29-是一个功能强大、易用的Java客户端,提供了丰富的API和功能,帮助我们轻松地在Java项目中使用FastDFS分布式文件系统。它是一个值得信赖和推荐的工具,可以提高文件操作的效率和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值