網站首頁 編程語言 正文
文章目錄
- 前言
- 一、需求分析
- 二、使用步驟
- 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
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-11-20 spring?boot集成redis基礎入門實例詳解_Redis
- 2022-11-05 Golang操作命令行的幾種方式總結_Golang
- 2022-07-26 Python使用psutil獲取系統信息_python
- 2022-06-24 C#利用itext實現PDF頁面處理與切分_C#教程
- 2022-12-22 Go語言學習之接口使用的示例詳解_Golang
- 2024-03-15 Spring Framework對DAO(Data Access Object)的支持
- 2022-07-22 常見的哈希算法總結
- 2022-04-23 window.open打開新窗口設置顯示位置及大小
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支