SpringBoot实现异步导⼊导出任务(实现思路+超详细流程)
前⾔:
在系统⽤户量较⼤,且⽤户较为依赖导⼊导出报表,⽽数据量⼜⽐较⼤的情况下,这时再采⽤即时下载Excel的⽅式⽤户体验就不太理想了。
所以退⽽求其次,需要另外的实现模式在不影响⽤户使⽤的前提下,降低我们服务器压⼒,同时⼜能满⾜⽤户需求的解决⽅案。
本⽂介绍⼀下我们实际项⽬中使⽤的:异步导⼊导出模式。
主要分为⼏个步骤:
⼀、 ⽤户操作导出时保存操作⾄异步任务表中,记录⽤户信息及请求时参数,同步响应给⽤户 “操作成功,请去任务中⼼查看”。
⼆、 ⽤户操作导⼊时将导⼊⽂件暂时保存⾄服务器中,同时保存导⼊操作⾄异步任务表中,记录⽤户信息,同步响应给⽤户 “操作成功,请去任务中⼼查看”。
三、 需要⼀个"我的任务"菜单来展⽰⽤户导⼊/导出任务的状态及操作。
四、 需要定时任务或任务调度Quartz来实现我们异步任务表中的任务执⾏,任务执⾏可采⽤多线程执⾏来提⾼效率。
五、 例如导出操作:⾸先在服务器创建要导出的⽂件,由于导出数据可能过多,Excel写⼊效率低下,可采⽤.csv格式导出数据。
防⽌出现数据库⼀次查询数据过多导致内存溢出的情况,可以采⽤分批查询以流的⽅式写⼊csv⽂件,最终操作成功后更新异步任务表该任务的状态。
六、 在"我的任务"菜单中提供下载按钮,⽤户的导出任务若状态为:处理完成(成功)的状态时可进⾏下载操作,此时将服务器⽣成的scv ⽂件下载到本地。
七、 由于该模式会⽣成很多临时⽂件,建议将临时⽂件保存⾄oss服务器,如果资源充⾜的情况可以将该模块作为异步服务,后续继续优化的空间还是⾮常⼤的。
主要流程:
1. 需要⼀张异步任务表,这⾥贴出建表sql
CREATE TABLE`async_import_export_task`(
`task_id`bigint(20)NOT NULL COMMENT'任务id',
`task_name`varchar(255)DEFAULT NULL COMMENT'任务名称',
`company_id`bigint(20)DEFAULT NULL COMMENT'公司id',
`user_id`bigint(20)DEFAULT NULL COMMENT'⽤户id',
`priority`tinyint(255)DEFAULT NULL COMMENT'优先级0-10',
`req_param`varchar(8000)DEFAULT NULL COMMENT'请求参数',
`task_status`tinyint(255)DEFAULT NULL COMMENT'任务状态',
`created_time`datetime DEFAULT NULL COMMENT'创建时间',
`deal_time`datetime DEFAULT NULL COMMENT'处理时间',
`finish_time`datetime DEFAULT NULL COMMENT'完成时间',
`result`mediumtext COMMENT'处理结果',
`tag`varchar(255)DEFAULT NULL COMMENT'⽂件路径:\n导⼊任务时,路径为⽤户⽂件上传 oss 的路径\n导出任务时,路径为⽣成的⽂件在 oss 的路径', `task_code`varchar(255)DEFAULT NULL COMMENT'任务编码\r\n * D-A-01 下载当天呼叫记录\r\n',
`task_type`tinyint(4)DEFAULT NULL COMMENT' * 任务类型\n * 1:上传(导⼊)\n * 2:下载(导出)',
PRIMARY KEY(`task_id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 实现流程可能需要某些maven依赖,这⾥我将我⾃⼰的都贴出来了,需要的话可以从中引⼊
<properties>
<java.version>1.8</java.version>
<lombok.version>1.16.18</lombok.version>
<fastjson.version>1.2.39</fastjson.version>
<commons.io.version>2.5</commons.io.version>
<commons.lang3.version>3.5</commons.lang3.version>
&tor.version>8.0.16</tor.version>
<mybatis-plus-version>3.1.0</mybatis-plus-version>
<druid-version>1.0.29</druid-version>
<velocity-version>2.0</velocity-version>
<swagger2-version>2.7.0</swagger2-version>
</properties>
<dependencies>
<dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引⼊aop相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>${lombok.version}</version>
</dependency>
<!--springboot配置元数据-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional>
</dependency>
<!--Json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--io常⽤⼯具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!--commons-lang3 ⼯具类 -->
<dependency>
<groupId>org.apachemons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
<!--使⽤Mysql数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${tor.version}</version>
</dependency>
<!--mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity-version}</version>
</dependency>
<!--swagger2 集成-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2-version}</version>
springboot aop</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2-version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.10</version>
</dependency>
</dependencies>
3. 我们按上⽅的流程来贴我的实现代码
指定临时⽂件保存路径,在l中配置
temp:
file:
path: D:\\
会需要⼏个⼯具类及AsyncImportExportTask表的实体类,直接copy到项⽬中即可package ity;
import batisplus.annotation.IdType;
import batisplus.annotation.TableField;
import batisplus.annotation.TableId;
import batisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
perimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 异步导⼊导出任务表
*
* @author LiWT
* @date 2021-06-27
*/
@Data
@EqualsAndHashCode(callSuper =false)
@Accessors(chain =true)
@TableName("async_import_export_task")
public class AsyncImportExportTask {
/**
* 任务id
*/
@TableId(value ="task_id", type = IdType.ID_WORKER)
private Long taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 公司 id
*/
private Long companyId;
/**
* ⽤户 id
*/
private Long userId;
/**
* 任务优先级
* 只针对当前⽤户的任务起作⽤
*/
private Integer priority;
/**
* 请求参数
*/
private String reqParam;
/**
* D-A-01 下载当天呼叫记录
* D-A-02 下载历史呼叫记录
* U-C-01 导⼊客户数据
* D-C-01 导出客户数据
* U-U-01 导⼊⽤户数据
* D-U-01 导出⽤户数据
*/
private String taskCode;
/**
* 任务状态
* 1:init
* 2: running
* 3: finish(success)
* 4: finish(fail)
*/
private Integer taskStatus;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8") @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss")
private Date createdTime;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8") @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss")
private Date dealTime;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8") @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss")
private Date finishTime;
private String tag;
/**
* 处理结果
*/
private String result;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8") @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss")
@TableField(exist =false)
private Date beginTime;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8") @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss")
@TableField(exist =false)
private Date endTime;
/**
* 任务类型
* 1:上传(导⼊)
* 2:下载(导出)
*/
private Integer taskType;
}
系统统⼀返回类型R
package com.blog.utils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
perimental.Accessors;
import org.apachemons.lang3.StringUtils;
import java.io.Serializable;
/**
* @author LiWT
* @date 2020-12-04
*/
@Builder
@ToString
@NoArgsConstructor
@Accessors(chain =true)
@ApiModel(value="R对象", description="返回信息的封装对象")
public class R<T>implements Serializable {
private static final long serialVersionUID =1L;
/**
* 成功标记
*/
private static final Integer SUCCESS =200;
/**
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论