实现数据存储和文件下载异步

实现数据存储和文件下载异步

1.线程池配置

@Configuration
@EnableAsync
public class ThreadPoolConfig {
    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;

    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;

    @Value("${async.executor.thread.queue_capacity}")
    private int queueSize;

    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor(){
        logger.info("=======ThreadPool Started========");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueSize);
        executor.setThreadNamePrefix(namePrefix);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}


yml配置文件的配置

#yml的配置
# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size: 10
# 配置最大线程数
async.executor.thread.max_pool_size: 20
# 配置队列大小
async.executor.thread.queue_capacity: 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix: async-service-

2.@Async异步不生效

在解决bean的注入问题后,开始执行异步流程,在逻辑上按道理是没有问题的,使用@Async注解,让线程池开启线程去异步执行,但此时发现,方法并没有生效,仍然是先打印"11111",文件下载完写入磁盘后才开始输出“文件下载了没有”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oQVczoo0-1625223944116)(image-20210702114605275.png)]在这里插入图片描述

于是查询资料才知道异步不生效的原因是不能在同一个class里去调用异步方法,这是为什么呢?

先看一下@EnableAsync注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;
	
    //一下两个方法都涉及到了代理
    boolean proxyTargetClass() default false;
	
    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

那么在什么情况下会用到代理呢?根据面向切面的编程来理解,只有当我不知道调哪个的时候,aop通过动态代理反射来获取相应bean的class然后再实例化。那么也就是说,当你把异步和需要调用异步的方法定义在了同一个类的时候,这个时候不需要借助动态代理去生成相关的bean实例,也就是绕过了动态代理直接执行本类中的方法调用,因此异步就不生效了。

3.单独定义类编写异步任务方法

因此我又重新定义了一个类来单独实现异步方法。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tfpIRd49-1625223944121)(image-20210702120149262.png)]
这个时候启动后,这里可以看到一个io线程,一个异步线程, io线程在执行数据解析生成table然后进行插入操作,异步线程在下载图片写入磁盘,可以看到“文件开始下载了吗?”在提示“异步了吗“之前,表示没有阻塞在downLoad方法里,可以看到io线程在执行数据库的插入操作,async-service-xx的线程在执行图片文件下载操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tXZPCcO-1625223944125)(image-20210702120324417.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASxQHAQB-1625223944127)(image-20210701180859617.png)]

至此异步成功。

再来看看逻辑是否正确:

未下载前,state为0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c9Yg28gd-1625223944129)(image-20210702190025807.png)]
下载后,state为1,相应的表字段存储了url + 文件路径
在这里插入图片描述
那么整个数据存储和文件下载异步的设计成功。

4.需要考虑的问题

1.任务在阻塞队列里,没有执行,宕机了怎么办?

​ 这个问题延续了任务重试参数持久化的思想,先将需要下载的任务持久化到数据库,state置为0,表示任务还没有执行,那么再开启异步执行任务,当任务执行完之后将相应的参数state置为1,或者删除,这里采用了改变state的策略,方便对比。

2.将文件路径更新到数据库字段过长导致更新失败的问题

​ 先是考虑了两种方案,一种是让异步线程去更改字段长度,二是让url先预留一定的长度来防止字段长度过短导致下次拼接url和文件路径更新数据库失败的问题。但是如果让异步线程执行更改,这样可能会执行多次同样的操作,这样的话效率就比较低了。然后经过思考还是决定用第一种方案,预留大概100长度左右的字符串长度,以空格区分开,然后通过split方法截断参数得到url去请求文件下载。

3.数据插入失败,文件路径无法更新怎么办?

​ 数据插入失败比较多的情况有两种,一是列字段超过了65535的限制,二是编码格式不对utf8mb4无法存到utf8的数据库,在目前看来那边的平台只有第二种情况比较多,需要提前预防。目前的考虑是因为即使文件多次下载重复其实也不影响什么(如果一定要去重那就需要读文件MD5码与下载的MD5码进行对比了),所以最终还是以整个任务流程成功为准,即使文件下载成功,数据插入失败了也不算成功,state变量不更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值