java —— 异步任务失败后处理

场景:

          实际项目中,程序操作出了问题,后续要怎么处理是个很重要的问题,如果这个问题是同步操作引起的解决起来比较简单,比如用户注册,如果系统注册失败,后台可以马上返回错误信息,用户也可以马上看到错误点是什么。但是如果是异步操作,比如用户注册成功后,系统异步发送短信给用户,发短信的操作出问题了没有发出去,而且这个操作也不是用户主动操作了,所以可能会出现用户不知道你有这个操作,系统也没办法知道自己操作出了问题,或者说知道了操作出问题后,要考虑怎么处理。

解决方式:

          可以用消息队列去处理异步请求,但是如果是一些比较小的项目,请求量不大,用消息队列就有点大材小用了,单独用数据库保存也可以,但是也是有点浪费资源了,所以可以用文件来保存这些错误请求信息,采用定时或者说每次执行异步操作时先进行判断,看之前是否有错误请求消息,有的话和当前请求一起重新发起请求。

 

大致流程图:

代码实例:

          我的代码的情况是,异步上传文件到阿里云OSS服务器,通过文件URL(真实有效的,输入到网页上可以看到对应资源的URL)找到文件,然后把文件上传到OSS服务器,如果上传失败,把文件URL信息保存到txt文件中,同时修改静态变量 isHaveDefailUpLoadFile(表示是否有文件上传失败,false为没有,true为有)为true,这样下次有请求进来时,就会根据这个静态变量了解到之前有上传失败的文件,于是会读取文件的信息,重新发起请求(通过静态变量来标识,可以防止每次请求都去读取文件看是否有错误信息,减少资源浪费)。

 

步骤:

        1:配置文件信息 system.properties

system.emedical.oosurl=https://diaoguihua.oss-cn-shenzhen.aliyuncs.com/
system.emedical.taskdir=H:/Tomcat/task/

            配置文件工具类 SysConfig.java:可参考我另一篇博客 https://blog.csdn.net/DGH2430284817/article/details/86521151

        2:I/O流工具类 FileUtil.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;

public class FileUtil {
 
	/**
	 * 创建目录 
	 * @param path
	 * @throws Exception 
	 */
	public static void CreatFileDir(String path) throws Exception  {
		try {
		File file = new File(path);
		if(file.getParentFile().isDirectory()){//判断上级目录是否是目录
			if(!file.exists()){   //如果文件目录不存在
				file.mkdirs();  //创建文件目录
			}
		}else{
			throw new Exception("传入目录非标准目录名");
		}
		} catch (Exception e) {
			throw new Exception("创建目录失败:" + e.getMessage()); 
		}
	} 
	
	/**
	 * 创建文件
	 * @param path
	 * @throws Exception 
	 */
	public static void CreatFile(String path) throws Exception  {
		try {
		File file = new File(path);
		if(file.getParentFile().isDirectory()){//判断上级目录是否是目录
			if(!file.exists()){   //如果文件不存在
				file.createNewFile();  //创建文件
			}
		}else{
			throw new Exception("传入目录非标准文件名");
		}
		} catch (Exception e) {
			throw new Exception("创建文件失败:" + e.getMessage()); 
		}
	} 
	
	/**读文件
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public static Set<String> readFile(String path) throws Exception {
		FileInputStream fis = null;
		try {
			Set<String> list = new HashSet<String>(); 
			File file = new File(path);
			fis = new FileInputStream(file);
			InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
	        BufferedReader br = new BufferedReader(isr);
	        String line = "";
	        while ((line = br.readLine()) != null) { 
	                list.add(line);
	        }
	        br.close();
	        isr.close();
	        fis.close();
	        return list;
		} catch (Exception e) {
			throw new Exception("读取文件失败:" + e.getMessage()); 
		}finally {
			fis.close();
		} 

	}
	
	/**清空文件
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public static void cleanFile(String path) throws Exception {
		OutputStream fos = null;
		try { 
			File file = new File(path); 
			CreatFile(path); 
			fos = new FileOutputStream(file, false); 
			fos.write("".getBytes("UTF-8"));
		} catch (Exception e) {
			throw new Exception("保存Oss失败信息失败:" + e.getMessage()); 
		}finally {
			fos.close();
		} 
	}
	
	/**写文件 (一行)
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public static void writeFileOneLine(String path , String Msg) throws Exception {
		OutputStream fos = null;
		try { 
				File file = new File(path); 
				CreatFile(path); 
				fos = new FileOutputStream(file, true); 
				fos.write((Msg + "\r\n").getBytes("UTF-8"));
		} catch (Exception e) {
				throw new Exception("写文件 (一行)失败:" + e.getMessage()); 
		}finally {
				fos.close();
		}

	}
}

 

          3:上传文件工具类 OOSUtil.java 

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Set; 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 
import com.aliyun.oss.OSSClient;
import com.ft.emedical.config.SysConfig;

public class OOSUtil extends Thread{
	private static Log log = LogFactory.getLog(OOSUtil.class);
	private static boolean isHaveDefailUpLoadFile = false;//是否之前有上传OSS失败的文件
	private String UrlMsg; 
	
	private OOSUtil() { }
	
	/**
	 * @param UrlMsg 文件url
	 */
	public OOSUtil(String UrlMsg) { this.UrlMsg = UrlMsg; }
	
	@Override
	public void run() { 
		if(isHaveDefailUpLoadFile){//判断之前有没有上传失败的文件
			UpLoadDefailFile();//先把以前的失败文件重新上传
		}
		OSSupLoadFileByUrl(this.UrlMsg);//上传文件到OSS服务器
	}
	
	private static void OSSupLoadFileByUrl(String UrlMsg)   {
		log.info("上传OSS文件:" + UrlMsg); 
    	// Endpoint以杭州为例,其它Region请按实际情况填写。
    	String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
    	// 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
    	String accessKeyId = "your-accessKeyId";
    	String accessKeySecret = "your-accessKeySecret";

    	// 创建OSSClient实例。
    	OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

    	try {
	    	String source = UrlMsg.substring(0,UrlMsg.indexOf("upload/")+16) + URLEncoder.encode(UrlMsg.substring(UrlMsg.indexOf("upload/")+16), "utf-8");
		    URL url = new URL(source);
	    	InputStream inputStream = url.openStream();
	    	ossClient.putObject("xylcdn", UrlMsg.substring(UrlMsg.indexOf("upload/")), inputStream);
		} catch (Exception e) {//保存失败的上传OSS文件信息
			log.error("上传Oss失败:" + e.getMessage());
			isHaveDefailUpLoadFile = true;
			File newfile = new File(SysConfig.TASKDIR + "NotUpLoadFile.txt" );
			try {
				FileUtil.CreatFileDir(SysConfig.TASKDIR);
				FileUtil.writeFileOneLine(SysConfig.TASKDIR+"NotUpLoadFile.txt" , UrlMsg );
			} catch (Exception e1) {
				log.error("保存Oss失败信息失败:" + e1.getMessage()); 
			} 
		} finally {
	    	// 关闭OSSClient。
	    	ossClient.shutdown();
		}
	}


	/**重新上传以前上传OSS失败的文件
	 * 
	 */
	private void UpLoadDefailFile() { 
		Set<String> aa;
		try {
			//读取OSS上传失败文件信息
			aa = FileUtil.readFile(SysConfig.TASKDIR + "NotUpLoadFile.txt");
			//清空上传失败信息文件
			FileUtil.cleanFile(SysConfig.TASKDIR + "NotUpLoadFile.txt");
			for(String value : aa){
				log.info("重新上传OSS文件:" + value);
				OSSupLoadFileByUrl(value);
			}
		} catch (Exception e) {
			log.error("重新上传失败OSS文件失败:" + e.getMessage());
		}

	}
}

      

           5:测试方法

	public static void main(String[] args) throws Exception {
		//执行上传OSS文件
		new OOSUtil("http://127.0.0.1:8080/medical-web-boss/upload/20190628/1561653493197555.jpg").start();
		new OOSUtil("http://127.0.0.1:8080/medical-web-boss/upload/20190628/1561653919242555.jpg").start();  

    }

 

          6:测试结果(看阿里云是否已经上传文件)

        文件已经上传,并且本地保存错误信息文件 NotUpLoadFile.txt 为空:

 

我们也可以重新测试,把其中一个URL改成不存在的文件地址,模拟上传文件失败

	public static void main(String[] args) throws Exception {
		//执行上传OSS文件
		new OOSUtil("http://127.0.0.1:8080/medical-web-boss/upload/20190628/666.jpg").start(); 

    }

执行完后就会上传失败,保存到文件中

日志:

[Thread-0] INFO com.ft.emedical.util.OOSUtil - 上传OSS文件:http://127.0.0.1:8080/medical-web-boss/upload/20190628/666.jpg
[Thread-0] ERROR com.ft.emedical.util.OOSUtil - 上传Oss失败:Connection refused: connect

文件NotUpLoadFile.txt:

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值