好用易用方便的Java基于Springboot的异步处理框架代码使用及说明

好用易用方便的Java基于Springboot的异步处理框架代码使用及说明

1. 功能和使用场景总结

该代码是一个基于Redis的异步任务处理框架,主要功能是将任务异步化处理,通过多线程从Redis队列中获取任务并执行,支持任务失败重试、异常恢复和多线程并发处理。

使用场景:适用于需要异步处理的业务场景(如日志上报、消息推送、数据异步更新等),尤其适合任务处理耗时较长或需要避免阻塞主流程的场景。

2. 核心模块拆分与解释

1)日志配置模块(log4j.properties

功能:配置日志输出方式,包括控制台输出和文件输出,同时设置第三方框架的日志级别(如SpringHibernate等只输出ERROR级别日志,减少冗余)。

核心配置

  - 日志输出到控制台(`log2Console`)和文件(`log2File`),文件按大小滚动(最大4096KB,保留50个备份)。

  - 日志格式包含时间、类名、行号、日志级别和消息内容,方便问题追踪。

2)异步任务实体类(AsyncData

功能:封装异步任务的基本信息,相当于任务的"快递包裹"

核心属性

  - `businessFlag`:业务标记(类似快递单上的"易碎品"标识,区分不同任务类型)。

  - `data`:任务数据(包裹里的物品,即要处理的具体内容)。

  - `failCount`:失败次数(记录包裹投递失败的次数,超过上限则放弃)。

  - `beginTime`:任务创建时间(包裹生成时间)。

3)异步处理接口(AsyncInterface

功能:定义任务处理的标准方法,相当于"快递处理指南"

核心方法`handle(String businessFlag, Object data)`,返回`boolean`表示处理成功/失败(类似指南中规定"签收成功""拒收"的判断标准)。

使用方式:用户需实现该接口,编写具体的任务处理逻辑(如消息推送的具体代码)。

4)工具类(LashenAsyncUtils

功能:框架的"控制面板",负责初始化配置、管理任务队列和启动线程。

核心功能

  - 配置管理:设置线程数、重试次数、Redis键名等(类似调节快递站的"最大快递员数量""最多派送次数")。

  - 任务添加:`addAsyncData()`方法将任务放入Redis队列(类似市民把快递放进快递柜)。

  - 线程启动:`startAsyncTaskServiceThread()`方法启动处理线程(安排快递员开始工作),同时处理异常退出的线程恢复(快递员突然离岗后,安排其他人接手未完成的快递)。

5)异步处理线程(AsyncHandleThread

功能:实际执行任务的"快递员",负责从Redis队列取任务、处理任务、失败重试和异常恢复。

核心流程

  1. 异常恢复(restoreHandle):启动时检查是否有未完成的任务(快递员上班先看是否有昨天没送完的快递),如有则优先处理。

  2. 正常处理(doHandle):循环从Redis队列取任务(不断从快递柜取件),处理完成后删除备份;失败则重试(派送失败就再试几次)。

  3. 等待机制(waitForRedisData):队列空时休眠等待(快递柜空了就休息一会儿,定期查看),避免无效循环。

 3. 抽象概念类比说明

Redis队列:相当于"快递柜"`addAsyncData`方法是存快递,线程的`fetchAsyncData`是取快递。

线程池(多线程):多个"快递员"同时工作,提高处理效率。

失败重试:类似快递派送失败后,再试几次(最多`maxRetryCount`次)。

异常恢复:快递员突然离职,新快递员接手他未完成的快递(从Redis备份中获取)。

等待机制:快递柜空了,快递员不会一直盯着,而是隔一段时间看一次(`sleepTime`),节省精力。

通过以上模块协作,框架实现了任务的异步化、高可用(失败重试+异常恢复)和可配置(线程数、重试次数等),适合作为通用的异步任务处理组件嵌入业务系统。

代码结构图

异步数据类AsyncData

package com.lashen.async.entity;

import java.io.Serializable;

/*
 * @Description: 异步数据结构
 * @Author: sam
 * @Date: 2023-05-16 18:04:24
 */
public class AsyncData implements Serializable {
    //业务标记;由用户自行随意定义,用于区分不同的异步数据
    private String businessFlag;
    //开始时间
    private Long beginTime;
    //需要异步处理的数据
    private Object data;
    //失败次数
    private Integer failCount = 0;

    public AsyncData(String businessFlag,Object data){
        setBusinessFlag(businessFlag);
        setBeginTime(System.currentTimeMillis());
        setData(data);
    }
    public String getBusinessFlag() {
        return businessFlag;
    }
    public void setBusinessFlag(String businessFlag) {
        this.businessFlag = businessFlag;
    }
    public Long getBeginTime() {
        return beginTime;
    }
    public void setBeginTime(Long beginTime) {
        this.beginTime = beginTime;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public Integer getFailCount() {
        return failCount;
    }
    public void setFailCount(Integer failCount) {
        this.failCount = failCount;
    }

    
}

接口类AsyncInterface

package com.lashen.async.interfaces;


/*
 * @Description: 异步处理接口
 * @Author: sam
 * @Date: 2023-05-16 16:13:49
 */
public interface AsyncInterface {
    
    /**
     * @description: 异步处理接口实现函数
     * @param {String} businessFlag 业务标记,由用户自行随意定义,用于区分不同的数据处理
     * @param {Object} data 要异步处理的数据
     * @return {boolean} true表示处理成功,false表示处理失败(注意:如果处理失败,会进行重试,最大重试次数由配置文件中的ASYNC_MAX_RETRY_COUNT决定,超过最大重试次数这条数据会被抛弃,默认为3次)
     * @author: sam
     * @Date: 2023-05-16 16:21:35
     */    
    public abstract boolean handle(String businessFlag,Object data);

}

异步处理线程类AsyncHandleThread

package com.lashen.async.service.async.thread;

import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;

import com.lashen.async.entity.AsyncData;
import com.lashen.async.interfaces.AsyncInterface;
import com.lashen.async.utils.LashenAsyncUtils;

/*
 * @Description: 异步处理线程
 * @Author: sam
 * @Date: 2023-05-15 13:40:13
 */
public class AsyncHandleThread extends Thread{
    private static final Logger logger = Logger.getLogger(AsyncHandleThread.class);

    private RedisTemplate redisTemplate;
    private AsyncInterface asyncInterface ;
    //最大重试次数
    private int maxRetryCount;
    //是否只是恢复处理
    private boolean isRestore = false;
    


    public AsyncHandleThread(RedisTemplate redisTemplate,String name,AsyncInterface asyncInterface,boolean isRestore){

        this.setName(name);
        this.redisTemplate = redisTemplate;
        this.asyncInterface = asyncInterface;
        this.maxRetryCount = LashenAsyncUtils.getAsyncMaxRetryCount();
        this.isRestore = isRestore;
        
    }
   


    //线程run
    public void run(){

        //1. 异常恢复处理
        restoreHandle();

        //2. 是否是异常恢复线程
        if(isRestore == true){
            //如果只是恢复处理线程,则异常恢复后,直接退出。
            logger.info("thread="+this.getName()+",exception thread data restore finished,exit current thread.");  
            return ;
        }

        //3. 正常处理
        doHandle();        

    }

    //异常恢复处理
    private void restoreHandle(){
        try{
            //从备份hash中获取数据
            AsyncData asyncData =(AsyncData)redisTemplate.opsForHash().get(LashenAsyncUtils.getAsyncRedisListKeyBackup(), this.getName());
            if(asyncData == null){
                return ;
            }

            //进行数据处理
            handleData(asyncData);

            //从备份中删除
            redisTemplate.opsForHash().delete(LashenAsyncUtils.getAsyncRedisListKeyBackup(), this.getName());

        }catch(Exception e){
            logger.error("",e);
        }
    }

    //正常处理
    private void doHandle(){
        try{          
            
            while(true){

                //1. 等待redis list中出现数据
                waitForRedisData();
    
                //2. 从redis list中取数据
                AsyncData asyncData = fetchAsyncData();
                if(asyncData == null){
                    //设置为没有数据标记
                    LashenAsyncUtils.setRedisListExistData(false);
                    logger.info("thread="+this.getName()+",redis list no data.");                    
                    continue;
                }
    
                //3. 处理数据
                handleData(asyncData);
                             
            }//end while

        }catch(Exception e){
            logger.error("",e);
        }
        
    }

    //处理数据
    private void handleData(AsyncData asyncData){
        try{
            logger.info("thread="+this.getName()+",handle data begin-------------------->>>");  
            if(asyncInterface.handle(asyncData.getBusinessFlag(), asyncData.getData()) == false){//处理失败
                logger.info("thread="+this.getName()+",handle data fail,go to retry--------------------<<<"); 
                retryData(asyncData);
            }else{//处理成功
                logger.info("thread="+this.getName()+",handle data ok--------------------<<<"); 
            }  

            //从备份中删除
            redisTemplate.opsForHash().delete(LashenAsyncUtils.getAsyncRedisListKeyBackup(), this.getName());
            
        }catch(Exception ee){
            logger.error("thread="+this.getName()+",handle data exception:",ee);            
            retryData(asyncData);    
        }
    }

    //从redis list中取数据
    private AsyncData fetchAsyncData(){
              
        try{
            //从list中取出
            AsyncData asyncData = (AsyncData)redisTemplate.opsForList().rightPop(LashenAsyncUtils.getAsyncRedisListKey());
            if(asyncData == null){
                //删除备份
                redisTemplate.opsForHash().delete(LashenAsyncUtils.getAsyncRedisListKeyBackup(), this.getName());
                return null;
            }

            //保存到hash中
            redisTemplate.opsForHash().put(LashenAsyncUtils.getAsyncRedisListKeyBackup(), this.getName(), asyncData);

            return asyncData;
            
        }catch(Exception e){            
            logger.error("",e);  
        }

        return null;
    }

    //重试处理
    private void retryData(AsyncData asyncData){
        try{
            //超过最大重试次数就抛弃。
            if(asyncData.getFailCount()>=maxRetryCount){
                return ;
            }

            //把本次处理失败的追加到待处理中,再次进行处理
            asyncData.setFailCount(asyncData.getFailCount()+1);
            redisTemplate.opsForList().leftPush(LashenAsyncUtils.getAsyncRedisListKey(), asyncData);
        }catch(Exception e){
            logger.error("",e);  
        }
    }

    //等待redis list中出现数据
    private void waitForRedisData(){
        long st = System.currentTimeMillis();
        long stReset = st;
        while(true){
            if(LashenAsyncUtils.getRedisListExistData() == true){
                return ;
            }

            //等待几十毫秒
            sleepTime();
            long ms = System.currentTimeMillis() - st;
            if(ms>(LashenAsyncUtils.getAsyncMaxLivingTime() * 1000l)){                
                st = System.currentTimeMillis();
                logger.info("thread="+this.getName()+",idle...");
            }

            //判断是否有设置异常重置(大于0表示设置,其他:表示未设置)
            if(LashenAsyncUtils.getAsyncMaxExceptionResetTime()>0){
                ms = System.currentTimeMillis() - stReset;
                if(ms>(LashenAsyncUtils.getAsyncMaxExceptionResetTime() * 1000l)){
                    stReset = System.currentTimeMillis();
                    //设置为有新添加的数据。
                    LashenAsyncUtils.setRedisListExistData(true);
                }
            }
            
        }//end while
    }

    //等待几十毫秒
    private void sleepTime(){
        try{
            //wait 50 ms
            Thread.sleep(LashenAsyncUtils.getAsyncMaxActionTime());
        }catch(Exception e){
            logger.error("",e);  
        }
    }
}

异步处理工具类LashenAsyncUtils

package com.lashen.async.utils;


import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;

import com.lashen.async.entity.AsyncData;
import com.lashen.async.interfaces.AsyncInterface;
import com.lashen.async.service.async.thread.AsyncHandleThread;

/*
 * @Description: sinocontact异步处理通用框架工具类
 * @Author: sam
 * @Date: 2023-05-15 14:38:37
 */

public class LashenAsyncUtils {
    private static final Logger logger = Logger.getLogger(LashenAsyncUtils.class);

    private static final String REDIS_LIST_KEY="---REDIS_LIST_KEY---";
    private static final String REDIS_LIST_KEY_BACKUP="---REDIS_LIST_KEY_BACKUP---";
    
    //REDIS_LIST_KEY中是否存在数据
    private static boolean redisListExistData = true;
    //最大重试次数
    private static int asyncMaxRetryCount = 3;
    //异步线程数
    private static  int asyncMaxThreadCount = 1;
    //线程活着状态打印间隔时间(秒) 默认是30秒
    private static int asyncMaxLivingTime = 30;
    //线程最大响应时间(毫秒ms),默认50毫秒  0.05秒
    private static long asyncMaxActionTime = 50l;

    //最大异常重置时间(秒),默认为-1;当此值小于等于0时,表示永远不自动重置 redisListExistData = true;否则,超过此值(秒),即自动重置redisListExistData为true。
    private static int asyncMaxExceptionResetTime = -1;

    //最大异常重置时间(秒)
    public static int getAsyncMaxExceptionResetTime(){
        return asyncMaxExceptionResetTime;
    }

    //最大重试次数
    public static int getAsyncMaxRetryCount(){
        return asyncMaxRetryCount;
    }
    //异步线程数
    public static int getAsyncMaxThreadCount(){
        return asyncMaxThreadCount;
    }
    public static int getAsyncMaxLivingTime(){
        return asyncMaxLivingTime;
    }
    //线程活着状态打印间隔时间(秒) 默认是30秒
    public static void SetAsyncMaxLivingTime(int maxLivingTime){
        asyncMaxLivingTime = maxLivingTime;
    }
    public static long getAsyncMaxActionTime(){
        return asyncMaxActionTime;
    }
    public static void setAsyncMaxActionTime(long maxActionTime){
        asyncMaxActionTime = maxActionTime;
    }
    //最大异常重置时间(秒),默认为-1;当此值小于等于0时,表示永远不自动重置 redisListExistData = true;否则,超过此值(秒),即自动重置redisListExistData为true。
    public static void setAsyncMaxExceptionResetTime(int maxExceptionResetTime){
        asyncMaxExceptionResetTime = maxExceptionResetTime;
    }

    
    //设置REDIS_LIST_KEY中是否存在数据
    public synchronized static  void setRedisListExistData(boolean existData){
        LashenAsyncUtils.redisListExistData = existData;
    }
    //得到REDIS_LIST_KEY中是否存在数据
    public synchronized static boolean getRedisListExistData(){
        return LashenAsyncUtils.redisListExistData;
    }


    //redis 中 list的key名称
    public static String getAsyncRedisListKey(){        
        return REDIS_LIST_KEY;
    }
    //redis 中hash的key名称
    public static String getAsyncRedisListKeyBackup(){
        return REDIS_LIST_KEY_BACKUP;
    }


    /**
     * @description: 添加一个要处理的数据
     * @param {RedisTemplate}: springboot中的redis操作对象:RedisTemplate
     * @param {businessFlag} :业务标记;由用户自行随意定义,用于区分不同的异步数据
     * @param {Object} data: 要进行异步处理的数据。特别注意:data需要实现 implements Serializable ,否则无法添加成功。
     * @return {boolean}  true表示添加成功,false表示添加失败
     * @author: sam
     * @Date: 2023-05-15 14:59:45
     */    
    public static boolean addAsyncData(RedisTemplate redisTemplate,String businessFlag,Object data){
        try{
            //1. 构建 要保存的数据结构
            AsyncData asyncData = new AsyncData(businessFlag,data);

            //2. 数据保存到redis list 中
            redisTemplate.opsForList().leftPush(getAsyncRedisListKey(), asyncData);

            //3. 设置为有数据标记
            setRedisListExistData(true);

            return true;
        }catch(Exception e){
            logger.error("",e);
        }
        return false;
    }

    
    /**
     * @description: 启动异步处理线程
     * @param {RedisTemplate} redisTemplate : redis操作对象
     * @param {AsyncInterface} asyncInterface: 异步处理接口实现
     * @return {*}
     * @author: sam
     * @Date: 2023-05-16 17:54:23
     */    
    public static void startAsyncTaskServiceThread(RedisTemplate redisTemplate,AsyncInterface asyncInterface){  
        LashenAsyncUtils.asyncMaxThreadCount = 1;
        startAsyncTaskServiceThread(redisTemplate, asyncInterface);
    }

    /**
     * @description: 启动异步处理线程
     * @param {RedisTemplate} redisTemplate : redis操作对象
     * @param {AsyncInterface} asyncInterface: 异步处理接口实现
     * @param {int} asyncMaxThreadCount : 启动异步线程数     
     * @return {*}
     * @author: sam
     * @Date: 2023-05-16 17:54:23
     */ 
    public static void startAsyncTaskServiceThread(RedisTemplate redisTemplate,AsyncInterface asyncInterface,int asyncMaxThreadCount){
        LashenAsyncUtils.asyncMaxRetryCount = 3;
        startAsyncTaskServiceThread(redisTemplate, asyncInterface,asyncMaxThreadCount,3);
    }

    /**
     * @description: 启动异步处理线程
     * @param {RedisTemplate} redisTemplate : redis操作对象
     * @param {AsyncInterface} asyncInterface: 异步处理接口实现
     * @param {int} asyncMaxThreadCount : 启动异步线程数
     * @param {int} asyncMaxRetryCount: 处理出错重试次数
     * @return {*}
     * @author: sam
     * @Date: 2023-05-16 17:54:23
     */ 
    public static void startAsyncTaskServiceThread(RedisTemplate redisTemplate,AsyncInterface asyncInterface,int asyncMaxThreadCount,int asyncMaxRetryCount){
        try{

            LashenAsyncUtils.asyncMaxThreadCount = asyncMaxThreadCount;
            LashenAsyncUtils.asyncMaxRetryCount = asyncMaxRetryCount;

            //配置的处理最大线程数
            
            //上次退出时的有数据未处理完成的线程数
            Set set = redisTemplate.opsForHash().keys(LashenAsyncUtils.getAsyncRedisListKeyBackup());
            
            int cnt = asyncMaxThreadCount - set.size();
            if(cnt>=0){//需要线程比异常的线程多的情况
                for(Object hashkey:set){//用异常线程的名称
                    AsyncHandleThread baseThread = new AsyncHandleThread(redisTemplate,hashkey.toString(),asyncInterface,false);            
                    baseThread.start();
                }//end for
                
                for(int i=0;i<cnt;i++){
                    String threadName="async_thread_"+System.currentTimeMillis()+"_"+(i+1);
                    AsyncHandleThread baseThread = new AsyncHandleThread(redisTemplate,threadName,asyncInterface,false);            
                    baseThread.start();
                }//end for
            }else{//异常的线程比需要的线程多的情况
                int i=0;
                for(Object hashkey:set){//全部都用异常线程
                    boolean isRestore = false;
                    if(i>=asyncMaxThreadCount){//如果当前配置的线程数,比上次异常退出时的线程数少,则超过当前配置数的线程作为异常恢复处理线程,异常恢复处理完成后就会自动退出。
                        isRestore = true;
                    }
                    AsyncHandleThread baseThread = new AsyncHandleThread(redisTemplate,hashkey.toString(),asyncInterface,isRestore);            
                    baseThread.start();
                    i++;
                }//end for
            }

        }catch(Exception e){
            logger.error("",e);
        }
    }
}

pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>	
	<groupId>com.lashen</groupId>
	<artifactId>async</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>async</name>
	<description>lashen async workframe</description>
	<properties>
		<webVersion>2.5</webVersion>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
			<version>2.7.11</version>
		</dependency>
		
		 <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j</artifactId>
			<version>1.3.8.RELEASE</version>
		</dependency>

		
	</dependencies>

	<build>
		<finalName>async</finalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

二:使用例子

特别注意:本异步处理框架需要使用springboot redisRedisTemplate

  1. 实现异步处理接口

通过实现AsyncInterface接口来处理异步数据。

String businessFlag 表示异步数据业务标记,由用户在添加异步数据时自由定义,主要用来区分异步数据类型。

import com.lashen.async.interfaces.AsyncInterface;

public class AsyncHandleImpl implements AsyncInterface{

   
    public boolean handle(String businessFlag, Object data) {
        
        ResultData resultData = (ResultData)data;
        
        System.out.println("处理数据:"+resultData.getData().toString());
        
        return true;
    }
    
    
}

  1. 启动异步处理线程

通过

LashenAsyncUtils.startAsyncTaskServiceThread(RedisTemplate, AsyncInterface);

启动异步处理线程。

import com.lashen.async.utils. LashenAsyncUtils;

@Component
public class AsyncInit implements ApplicationRunner{

    @Autowired
    private RedisTemplate redisTemplate;

public void run(ApplicationArguments args){
		//异步处理线程数
        int asyncMaxThreadCount = 1;
        //出错重试次数
        int asyncMaxRetryCount = 3;
        System.out.println("启动异步处理------------>>>");
        //LashenAsyncUtils.startAsyncTaskServiceThread(redisTemplate,new AsyncHandleImpl());
        //LashenAsyncUtils.startAsyncTaskServiceThread(redisTemplate, new AsyncHandleImpl(), asyncMaxThreadCount);
       LashenAsyncUtils.startAsyncTaskServiceThread(redisTemplate, new AsyncHandleImpl(), asyncMaxThreadCount,asyncMaxRetryCount);
        System.out.println("启动异步处理完成------------<<<");

    }
}

  1. 添加异步处理数据

通过LashenAsyncUtils.addAsyncData(RedisTemplate,String businessFlag, Object data);添加异步处理数据。


    @Autowired
    private RedisTemplate redisTemplate;  

    @RequestMapping("add")
    @ResponseBody
    public String add(){

        String businessFlag = "--sys--";
        ResultData data = ResultData.success("sino--------ok");
       LashenAsyncUtils.addAsyncData(redisTemplate,businessFlag,data);   

        return "ok";
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyq_sam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值