多线程实战(一)
多线程实战(一)最近做了一个多线程的业务场景,对多线程不熟悉的可以直接拿来使用。1.业务需求: 使用的微服务架构,在做导出数据的时候,需要对主服务的数据查询出来,然后对主服务中的数据进行遍历,根据主服务数据外键ID去从服务查相关信息,若是数据量大,或者每条数据遍历都要调多个从服务查询关联数据,就会出现后台处理业务接口时间过长,1.5W条需要1s左右,数据达到50W条时,就需要大量时间,...
多线程实战(一)
最近做了一个多线程的业务场景,对多线程不熟悉的可以直接拿来使用。
1.业务需求: 使用的微服务架构,在做导出数据的时候,需要对主服务的数据查询出来,然后对主服务中的数据进行遍历,根据主服务数据外键ID去从服务查相关信息,若是数据量大,或者每条数据遍历都要调多个从服务查询关联数据,就会出现后台处理业务接口时间过长,1.5W条需要1s左右,数据达到50W条时,就需要大量时间,用户导出Excel,等待时间过长。页面容易卡死,用户体验度不好。响应也超出excel的请求时间。(声明:因为是微服务架构,涉及的分服务和分库的情况,不能在一个服务中写sql跨另一个服务的表,所以不能在一个服务中查询所有数据,需要关联服务去查)
2.解决方案:
点击导出的时候,做一个后台的报表生成,生成的excel报表放到图片服务器上,增加一个报表记录表,保存生成报表的记录和下载路径提供一个tab切换页面,做生成的excel报表展示。供用户下载,excel报表生成的时候,用户去浏览其他页面。页面展示如下:
后台的写入和写出使用的是IO操作,为了提高后台CUP使用效率,需要使用多线程。
3.前台代码:
//绑定按钮点击事件
var active = {
exportXls: function () { //导出
var f=$("form").serialize() ;
var fileId;
//先生成info,本次导出excel记录数据,状态生成中,防止重复生成
var infoAjax= $.ajax({
url: '/dubbo/ord/ordLanedetailsManager/createInfo.json?'+f+'&flag=0&num='+pageTotalCount ,
isAysn: false,
success: function (res) {
var result=$.parseJSON(res);
// var data=re
if(result.code==0){
if(result.record.code!=0){
layer.msg("数据生成中...");
}else{
var value=result.record.data.id;
fileId=value;
}
}else{
layer.msg("失败");
}
}
});
//当上一个ajax执行完毕,去发送异步请求进行文件上传到文件服务器,并把路径存到已经生成的记录数据表中,状态更改为已完成
$.when(infoAjax).done(function(){
if(!fileId){
return false;
}
//生成数据
var time=1000*60*3;
var dayAjax = $.ajax({
url: '/dubbo/ord/ordLanedetailsManager/exportOutboundedList.json?'+f+'&flag=0&fileId='+fileId+'&num='+pageTotalCount ,
isAysn: true,
timeout: time,
success: function (result) {
//异步处理完毕,不处理结果值
}
});
});
}
}
4.后台代码:
4.1 第一个ajax对应的后台方法
//第一个ajax对应的后台方法
@Override
@ServiceMapping(trancode = "", caption = "导出", log = false)
public Record createInfo(PubContext pubContext,Map<String, String> file){
Record record= new Record();
//保留两份
Collection<Condition> conditions= new ArrayList<>();
String userId = PubContextUtils.getUserId(pubContext);
conditions.add(ConditionUtils.getCondition("userId", Condition.EQUALS, userId));
conditions.add(ConditionUtils.getCondition("deleteFlag", Condition.EQUALS, "0"));
conditions.add(ConditionUtils.getCondition("type", Condition.EQUALS, OrdExportRecord.TYPE_BHMC));
Collection<Order> orders = new ArrayList<>();
orders.add(new Order("createTime", false));
List<OrdExportRecord> exportRecords = ordExportRecordManager.getExportRecords(conditions, orders);
int i=0;
for (OrdExportRecord ordExportRecord : exportRecords) {
if(i<2){ //判断是否有生成的
if(OrdExportRecord.STATUS_SAVING.equals(ordExportRecord.getStatus())){
record.put("code", "205");
record.put("msg", "正在生成数据!!!");
return record;
}
}else{ //把多余的给删掉
fileStoreService.deleteFile(ordExportRecord.getFilepath());
ordExportRecord.setDeleteFlag("1");
ordExportRecordManager.save(ordExportRecord);
}
i++;
}
//生成新的数据
OrdExportRecord export= new OrdExportRecord();
export.setCreateTime(DateUtils.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
export.setStatus(OrdExportRecord.STATUS_SAVING);
export.setUserId(PubContextUtils.getUserId(pubContext));
// String filePath = file.get("filePath");
// export.setFilepath(filePath);
export.setFilename(TempData.FILE_NAME);
export.setDeleteFlag("0");
export.setType("0");
// export.set
OrdExportRecord save = ordExportRecordManager.save(export);
logger.info("-------生成数据--"+export);
record.put("code", "0");
record.put("data", save);
return record;
}
4.2 第二个ajax对应的后台方法, 此时使用多线程处理,防止多用户同时操作,出现IO阻塞。
4.2.1 先声明线程类
若要获取线程执行后的执行结果的话,用FatureTask接口,不需要返回
值的话,使用常见的继承Thred类或实现Runnable接口就OK
本次异步写出Excel表格,只要继承Thred类即可, OrdExportRecordExcutor.java
package com.gsoft.yoreach.ord.executor;
/***
* BHMC导出报表线程
* @author kaifa-08
*
*/
public class OrdExportRecordExcutor extends Thread {
private Log logger = LogFactory.getLog(OrdExportRecordExcutor.class);
private OrdLanedetailsManager ordLanedetailsManager ;
private OrdExportRecordDao ordExportRecordDao;
private PubContext pubContext;
private Pager pager;
private Map<String, Object> map;
public OrdExportRecordExcutor(OrdLanedetailsManager ordLanedetailsManager, OrdExportRecordDao ordExportRecordDao,
PubContext pubContext, Pager pager, Map<String, Object> map) {
this.ordLanedetailsManager =ordLanedetailsManager;
this.ordExportRecordDao =ordExportRecordDao;
this.pubContext =pubContext;
this.pager=pager;
this.map=map;
logger.info("----接收参数"+this.ordLanedetailsManager);
}
@Override
public void run() {
Map<String, Object> mapdata=new HashMap<String, Object>();
try {
logger.info("----主线程开启");
//1.查询数据并上传到图片服务器
mapdata= ordLanedetailsManager.getDataAndUpload(pubContext, pager, map);
//2.保存记录,把图片服务器上的路径和名字更新到数据记录表
Record createInfo = this.createInfo(mapdata);
logger.info("----主线程结束"+createInfo);
//
} catch (Exception e) {
e.printStackTrace();
logger.info("----BHMC报表生成失败--执行报表记录回退--");
String fileId = MapParamsUtils.getParam(map, "fileId");
OrdExportRecord info = ordExportRecordDao.findOne(fileId);
info.setDeleteFlag("1");
OrdExportRecord save = ordExportRecordDao.save(info);
throw new BusException("BHMC报表生成失败!");
} finally {
}
}
}
4.2.2 在对应的业务层声明一个线程池,用来执行线程
private ExecutorService executorService = Executors.newFixedThreadPool(30);
4.2.3 第二个ajax对应的后台接口
@Override
@ServiceMapping(trancode = "", caption = "导出", log = true)
public void exportOutboundedList
(PubContext pubContext, Pager pager, Map<String, Object> map) {
//1.执行生成报表的线程
executorService.submit(new OrdExportRecordExcutor(this, exportDao, pubContext, pager, map));
}
更多推荐
所有评论(0)