日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

使用線程池異步執行定時任務

作者:鑄鍵為犁 更新時間: 2023-07-25 編程語言

文章目錄

  • 前言
  • 一、需求分析
  • 二、使用步驟
    • 1.配置線程池
    • 2.編寫能量過期方法
    • 3.編寫異步執行方法


前言

最近項目中需要做一個定時任務在某個固定的時間去執行一個任務,由于該定時任務的代碼量已經超出可讀性規范,并且處于性能的考慮,故使用線程池來做一個異步執行的方法


一、需求分析

產品給到的需求是,讓用戶獲得的能量根據有效期的規則配置,去實現一個能量過期的效果,因為是springboot單體項目,使用XXLJOB就過于繁瑣,而且項目中沒有其它的定時任務,私以為使用Scheduled執行比較簡單,易用。然后編寫過程中發現,代碼行數太多,按照不能超過80行的規范,故將一部分業務拆分出去,并使用線程池異步執行。

二、使用步驟

1.配置線程池

代碼如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;


@Configuration
@EnableAsync//開啟異步
public class ThreadPoolTaskConfig {
    @Bean("threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(16);
        executor.setQueueCapacity(1024);
        executor.setMaxPoolSize(64);
        executor.setKeepAliveSeconds(30);
        executor.setThreadNamePrefix("thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }
}

猶豫還要考慮性能安全的問題,所以使用Redisson,做一個鎖


import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.port}")
    private Integer port;

    @Bean
    public RedissonClient getRedisson() throws Exception {
        RedissonClient redisson = null;

        Config config = new Config();
        //.setPassword(password)
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        redisson = Redisson.create(config);
        System.out.println(redisson.getConfig());

        return redisson;
    }
}

需要注意的是,其中的@Value已經在yml配置文件中配置好了,這里不再記錄

2.編寫能量過期方法

代碼如下:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hsbc.component.AddOutEnergyComponent;
import com.hsbc.enums.EnergyType;
import com.hsbc.mapper.EnergyRecordMapper;
import com.hsbc.model.EnergyRecord;
import com.hsbc.model.EnergyRule;
import com.hsbc.service.IEnergyRecordsService;
import com.hsbc.service.IEnergyRuleService;
import com.hsbc.enums.DelFlagEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Configuration
@EnableScheduling//開啟定時任務
@Slf4j
public class EneryOutTask {

    @Autowired
    private EnergyRecordMapper energyRecordMapper;

    @Resource
    private RedissonClient redissonClient;
    @Autowired
    private AddOutEnergyComponent addOutEnergyComponent;

    public static final String ENERGY_OUT_LOCK_KEY = "energy:out:lock:%s";

    /**
     * 定時任務
     * 每月月初00:00:00執行
     * 已過期的能量處理
     */
    @Scheduled(cron = "0 0 0 1 * ?")
    public void outEnergy() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String format = sdf.format(date);
        String accessLock = String.format(ENERGY_OUT_LOCK_KEY,format);
        try {
            RLock lock = redissonClient.getLock(accessLock);
            boolean isLock = lock.tryLock(-1, 100, TimeUnit.SECONDS);
            if (isLock) {
                try {
                    /*獲取到本月的最后時間*/
                    Calendar calendar = Calendar.getInstance();
                    calendar.add(Calendar.MONTH, -1);
                    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
                    calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY));
                    calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE));
                    calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND));
                    calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND));
                    QueryWrapper<EnergyRecord> queryWrapper = new QueryWrapper<>();
                    queryWrapper.le("validity_time", calendar.getTime());
                    queryWrapper.eq("del_flag", DelFlagEnum.NO_DEL.getCode());
                    /*過期和代扣的能量不統計*/
                    queryWrapper.ne("type", EnergyType.REDUCE_ENERY.getCode());
                    queryWrapper.ne("type", EnergyType.EXPIRE.getCode());
                    /*所有到期的能量*/
                    List<EnergyRecord> energyRecords = energyRecordMapper.selectList(queryWrapper);
                    Map<String, Integer> outEnergyMap = new HashMap<>(energyRecords.size()); //會員即將到期的能量map
                    Map<String, List<String>> outEnergyIdsMap = new HashMap<>(); //全部到期準備刪除的能量id
                    for (EnergyRecord energyRecord : energyRecords) {

                        Integer outEnergy = outEnergyMap.get(energyRecord.getMemberId());
                        if (outEnergy == null) {
                            outEnergy = 0;
                        }
                        outEnergy += energyRecord.getResidualEnergy();
                        outEnergyMap.put(energyRecord.getMemberId(), outEnergy);
                        List<String> list = outEnergyIdsMap.get(energyRecord.getMemberId());
                        if (list == null) {
                            list = new ArrayList<>();
                        }
                        list.add(energyRecord.getId());
                        outEnergyIdsMap.put(energyRecord.getMemberId(), list);
                    }
                    for (Map.Entry<String, Integer> entry : outEnergyMap.entrySet()) {
                        /*異步添加*/
                        addOutEnergyComponent.add(entry.getKey(), entry.getValue(), outEnergyIdsMap.get(entry.getKey()));
                    }
                } finally {
                    log.info("操作業務完畢,開始釋放鎖");
                    lock.unlock();
                }
            } else {
                log.info("當前過期能量定時任務已被執行!!");
            }
        } catch (Exception e) {
            log.error("已過期能量定時任務報錯", e);
        }
    }
}


3.編寫異步執行方法

代碼如下:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hsbc.enums.DelFlagEnum;
import com.hsbc.enums.EnergyReadEnum;
import com.hsbc.enums.EnergyType;
import com.hsbc.mapper.EnergyRecordMapper;
import com.hsbc.model.EnergyRecord;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

@Component
@Slf4j
public class AddOutEnergyComponent {

    @Autowired
    private EnergyRecordMapper energyRecordMapper;
    //配置線程池
    @Async("threadPoolTaskExecutor")
    public void add(String memberId, Integer energy, List<String> outIds) {
        QueryWrapper<EnergyRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_id", memberId);
        queryWrapper.orderByDesc("id");
        Page<EnergyRecord> page = new Page<>(1, 1);
        Page<EnergyRecord> energyRecordPage = energyRecordMapper.selectPage(page, queryWrapper);
        int lastEnergy = 0;
        if (CollectionUtils.isNotEmpty(energyRecordPage.getRecords())) {
            EnergyRecord energyRecord = energyRecordPage.getRecords().get(0);
            lastEnergy = energyRecord.getBeforeEnergy() + energyRecord.getEnergyNumber();
        }
        EnergyRecord energyRecord = new EnergyRecord();
        energyRecord.setMemberId(memberId);
        energyRecord.setBeforeEnergy(lastEnergy);
        energyRecord.setType(EnergyType.EXPIRE.getCode());
        energyRecord.setEnergyNumber(-energy);
        energyRecord.setCreateTime(new Date());
        energyRecord.setAdditional("能量到期");
        energyRecord.setStatus(EnergyReadEnum.READ.getCode());
        energyRecord.setDelFlag(DelFlagEnum.DEL.getCode());
        energyRecordMapper.insert(energyRecord);
        //修改過期能量為已刪除
        energyRecordMapper.update(null,new UpdateWrapper<EnergyRecord>().in("id",outIds).set("del_flag",DelFlagEnum.DEL));
    }
}

其中該需求是在能量過期以后,在能量記錄表中添加數據,所以涉及另外一張表的數據插入操作,至此已經完全實現需求的功能

原文鏈接:https://blog.csdn.net/l_zl2021/article/details/130863017

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新