之前做异步的时候通常都是用消息队列来做,今天使用spring的异步任务

首先异步任务需要定义线程池,spring异步提供了默认线程池,但是线程池通常都是自己定义,这样更能符合业务需求。

将自定义线程池注入到sprin中,并设置异步任务异常捕获处理器

package com.imooc.ecommerce.config;import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** Created By 丛梓祺 on 2021/11/9* Write this code and change the world* 自定义异步任务线程池 异步任务异常捕获处理器*/
@Slf4j
@EnableAsync  //开启spring 异步任务支持
@Configuration
public class AsyncPoolConfig implements AsyncConfigurer {/*** 将自定义线程池注入到spring容器中** @return*/@Bean@Overridepublic Executor getAsyncExecutor() {//spring提供的自定义线程池ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(20);executor.setKeepAliveSeconds(60);//给这个线程池里的线程起一个名字,这个很重要executor.setThreadNamePrefix("czq-async-");//等待所有任务结果后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);//线程池的等待时间executor.setAwaitTerminationSeconds(60);// 定于拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 指定系统中的异步任务在出现异常的时候使用到的处理器** @return*/@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new AsyncExceptionHandler();}/*** 异步任务异常捕获处理器*/class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {ex.printStackTrace();log.error("Async Error:[{}], Method:[{}], Param:[{}]", ex.getMessage(), method.getName(), JSON.toJSONString(params));//todo 发送邮件或者是短信}}
}

商品的异步任务入库,将数据存储到redis和db中,这里主要是面向运营使用,所以入库的时候要保证三点

1.判断商品的属性是否符合正常规范

2.新入库商品中没有重复的数据,如果有重复的数据直接返回,入库不成功,让运营同学进行判断商品去重,这里要保证商品唯一,在数据库中添加唯一键。

实现方式,将保证唯一的属性进行拼接存放到set当中,进行判断是否存在。

3.保证添加的商品在数据库中没有重复

实现方式,只需要每次从数据中根据唯一键去查询,判断是否有重复数据。如果有重复数据就去除重复数据不添加

存入redis中的数据应该是简化的商品详情,如果将商品的全部详情都存入,一是没有必要的,用户访问数据得时候并不是都会查看所有详情,二是,数据量过大占用缓存过多

package com.imooc.ecommerce.service.async;import com.alibaba.fastjson.JSON;
import com.imooc.ecommerce.constant.GoodsConstant;
import com.imooc.ecommerce.dao.EcommerceGoodsDao;
import com.imooc.ecommerce.entity.EcommerceGoods;
import com.imooc.ecommerce.goods.GoodsInfo;
import com.imooc.ecommerce.goods.SimpleGoodsInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/*** Created By 丛梓祺 on 2021/11/9* Write this code and change the world* <>异步服务接口实现</>*/@Service
@Transactional
@Slf4j
public class AsyncServiceImpl implements IAsyncService {@Autowiredprivate EcommerceGoodsDao ecommerceGoodsDao;@Autowiredprivate RedisTemplate redisTemplate;/*** 异步任务处理两件事,将商品信息保存到数据表* 更新商品缓存** @param goodsInfos* @param taskId     异步任务管理器的id*///指定自定义线程池,不指定就是使用spring默认线程池@Async("getAsyncExecutor")@Overridepublic void asyncImportGoods(List<GoodsInfo> goodsInfos, String taskId) {log.info("async task running taskId :[{}]", taskId);StopWatch stopWatch = StopWatch.createStarted();//1.如果是goodsInfo中存在重复商品,不保存,直接返回,记录错误日志//请求数据是否合法的标记boolean isIllegal = false;//将商品信息字段 joint 在一起,用来判断是否存在重复Set<String> goodsJointInfos = new HashSet<>(goodsInfos.size());//过滤出来的可以入库的商品信息 按照自己的业务需求定义List<GoodsInfo> filteredGoodsInfo = new ArrayList<>(goodsInfos.size());//走一遍循环,过滤非法参数与判定当前请求是否合法for (GoodsInfo goods : goodsInfos) {//基本条件不满足的直接过滤if (goods.getPrice() <= 0 || goods.getSupply() <= 0) {log.info("goods info is invalid:[{}]", JSON.toJSONString(goods));continue;}//组合每一个商品信息String jointInfo = String.format("%s,%s,%s", goods.getGoodsCategory(), goods.getBrandCategory(), goods.getGoodsName());if (goodsJointInfos.contains(jointInfo)) {isIllegal = true;}//加入到两个容器中goodsJointInfos.add(jointInfo);filteredGoodsInfo.add(goods);}//如果存在重复商品或者没有需要入库的商品直接返回if (isIllegal || CollectionUtils.isEmpty(filteredGoodsInfo)) {stopWatch.stop();log.warn("import nothing: [{}]", JSON.toJSONString(filteredGoodsInfo));log.info("check and import goods done:[{}]", stopWatch.getTime(TimeUnit.SECONDS));return;}List<EcommerceGoods> ecommerceGoods = filteredGoodsInfo.stream().map(EcommerceGoods::to).collect(Collectors.toList());List<EcommerceGoods> targetGoods = new ArrayList<>(goodsInfos.size());//2. 保存goodsInfo之前先判断下是否有存在重复的商品ecommerceGoods.forEach(//limit 1g -> {if (null != ecommerceGoodsDao.findFirst1ByGoodsCategoryAndBrandCategoryAndGoodsName(g.getGoodsCategory(), g.getBrandCategory(), g.getGoodsName()).orElse(null)) {return;}targetGoods.add(g);});List<EcommerceGoods> savedGoods = IterableUtils.toList(ecommerceGoodsDao.saveAll(targetGoods));saveNewGoodsInfoToRedis(savedGoods);log.info("save goods info to db and redis :[{}]", savedGoods.size());stopWatch.stop();log.info("check and import goods success:[{}ms]", stopWatch.getTime(TimeUnit.MINUTES));}/***将保存呆数据表中的数据保存到redis当中* dict : key -> <id,simpleGoodsInfo(Json)>* @param ecommerceGoods*/private void saveNewGoodsInfoToRedis(List<EcommerceGoods> ecommerceGoods) {//由于redis是内存存储不应该保存大量的商品信息List<SimpleGoodsInfo> simpleGoodsInfos = ecommerceGoods.stream().map(EcommerceGoods::toSimple).collect(Collectors.toList());Map<String,String> id2JsonObject = new HashMap<>(simpleGoodsInfos.size());simpleGoodsInfos.forEach(g->id2JsonObject.put(g.getId().toString(),JSON.toJSONString(g)));//保存到redis中redisTemplate.opsForHash().putAll(GoodsConstant.ECOMMERCE_GOODS_DICT_KEY,id2JsonObject);}
}

商品入库异步任务实现相关推荐

  1. Java案例2-1商品入库

    一.题目 提示:这里可以添加本文要记录的大概内容: 现对华为和小米两种手机产品进行入库,本案例要求编写一个模拟商品入库的教程,可以在控制台输入入库商品的数量,最后打印出仓库中所有商品详细信息,以及所有 ...

  2. [案例2-1]商品入库

    现要对华为和小米两种手机产品进行入库,本案例要求编写一个模拟商品入库的程序,可以在控制台输入入库商品的数量,最后打印出仓库中所有商品详细信息以及所有商品的总库存数和库存商品总金额. 商品信息如下: 品 ...

  3. Java语言基础案例2-1 商品入库

    现要对华为和小米两种手机产品进行入库,本案例要求编写一个模拟商品入库的程序,可以在控制台输入入库商品的数量,最后打印出仓库中所有商品详细信息以及所有商品的总库存数和库存商品总金额. 商品信息如下: 品 ...

  4. 【Java案例】商品入库

    案例介绍: 现要对华为和小米两种手机产品进行入库,本案例要求编写一个模拟商品入库的程序,可以在控制台输入入库商品的数量,最后打印出仓库中的所有商品详细信息以及所有商品的总库存数和库存商品总金额. 商品 ...

  5. 商品入库(Java基础2-1)

    import java.util.Scanner; public class ShangPingRuKu {  //用static全局都可查询         static String[] name ...

  6. java入库_Java实现商品的查找、添加、出库、入库操作完整案例

    本文实例讲述了Java实现商品的查找.添加.出库.入库操作.分享给大家供大家参考,具体如下: package com.jredu.oopch08; public class Goods1 { priv ...

  7. 进销存管理轻松实现入库商品分类管理

    很多销售型企业,在过去传统的管理模式下,商品出入库管理都依靠人工记录,企业不妨通过专业的进销存管理,轻松实现入库商品的分类管理. 前言 很多销售型企业,在过去传统的管理模式下,商品出入库管理都依靠人工 ...

  8. java版出库入库_Java实现商品的查找、添加、出库、入库操作完整案例

    本文实例讲述了Java实现商品的查找.添加.出库.入库操作.分享给大家供大家参考,具体如下: package com.jredu.oopch08; public class Goods1 { priv ...

  9. 如何用百数低代码开发平台实现商品扫码出入库?

    一.传统出入库的问题 使用纸质类记录出入库时,由于货物量大,统计起来很繁琐,而且工作量也特别的大. 使用ERP常规方式记录出入库时,由于物品种类多,下拉选择时,难以找到具体的物件,操作起来也不方便. ...

最新文章

  1. golang 同一个包中函数互相调用报错 undefined 以及在 VSCode 中配置右键执行整个包文件
  2. Quartz2D在项目中的实际使用
  3. 开发编程值得收藏的经典书籍 免费下载
  4. 【IOC 控制反转】Android 视图依赖注入 ( 视图依赖注入步骤 | 视图依赖注入代码示例 )
  5. interrupt 1 using 1
  6. 今日代码(200623)--回厂日期预测(python + R)
  7. MFC中OnCtlColor的用法(改变控件颜色)
  8. Android子线程更新UI的方法总结
  9. padding 后尺寸变化 设置_padding margin border 和元素大小
  10. linux服务器监控zabbix,Linux监控之--使用ZABBIX监控web服务器
  11. 数据结构与算法 完整版双链表
  12. delphi pid判断进程结束_有两个这样的进程:僵尸进程amp;孤儿进程,蓝瘦香菇
  13. 全球著名编程大赛地址
  14. python制作音乐相册_用Python制作音乐海报
  15. PMP-37项目采购管理
  16. 协同软件市场一盘散沙 春种能否秋收心中没底
  17. 牛客网刷题——斩获offer
  18. 二手交易app manifest.xml
  19. 移动端、PC端 网页特效
  20. Linux 编辑器 !强推!

热门文章

  1. java filemonitor 多层_IDEA 创建多级文件夹的操作
  2. 董事长被撤销政协委员资格,这家数据安全公司市值蒸发40亿元
  3. 通常在班班通教室使用的计算机是,班班通使用方法
  4. 关于Linux下进程无故挂掉问题的分析
  5. Mac nginx运行php文件 File not found.
  6. wap2app去除系统自带的导航栏
  7. android invoke 参数,android 使用反射獲取MediaPlayer的Invoke方法
  8. 彻底搞清楚Handler,再也不怕面试官
  9. postgresql 判断是空的_PostgreSQL NULL 值
  10. SQLite 的文艺复兴