springbatch 入门学习以及完整实例代码

2 篇文章 0 订阅
1 篇文章 0 订阅

本文的目的是让未使用过spring batch的朋友快速上手应用spring batch,以及避免掉一下可能遇到的雷坑。

先推荐一篇关于spring batch学习的文章,这里介绍一下基础知识https://www.jianshu.com/p/6f038c1f6037。

好接下来不多废话

为什么要用spring batch?

spring batch是一个轻量级的,完全面向spring的批处理框架,可以应用于企业级大量的数据处理系统。与spring boot脚手架一同使用,省去了复杂的配置和麻烦的部署,注解化调用方式轻松上手。spring batch相较于其他几种批处理框架,优势就在于轻量级,面向spring,以对象的形式来完成数据的读解写三步操作。

这里我们说一下 job、step、reader、processor、writer在实际使用中的表现以及方式。

up测试了一下spring batch在实际应用中的表现如何,经过完整的验证,db类操作支撑几百万级的数据批处理操作轻轻松松,文件io操作,也足以完成4g左右大小的excel数据导入解析。因此在应对中小型项目的数据批处理工作上,spring batch的表现足以让你眼前一亮。

1.job

spring batch最核心的就是job,job内可以分为很多个step,比如我要对订单商品数据进行导入解析,我的step1导入商品数据,step2导入订单数据,step3结合比对统计分析,step4完成输入。

package com.winhc.flysky.batch;

import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.winhc.flysky.batch.listener.RadarAfterDayJobListener;
import com.winhc.flysky.batch.listener.RadarAfterLimitJobListener;
import com.winhc.flysky.batch.processer.EciDetailProcessor;


/**
 * 任务builder类,统一管理任务
 * @author donnie
 *
 */
@Configuration
@EnableBatchProcessing
public class RadarBatchBuilder {
	
	Log log = LogFactory.getLog(RadarBatchBuilder.class);
	@Autowired
	public JobLauncher jobLauncher;

	@Autowired
	public JobBuilderFactory jobBuilderFactory;
	
	@Autowired
	public RadarBatchStep radarBatchStep;
	
	@Autowired
	public EciDetailProcessor eciDetailProcessor;
	@Autowired
	public RadarAfterDayJobListener radarAfterDayJobListener;
	@Autowired
	public RadarAfterLimitJobListener radarAfterLimitJobListener;

	public void dayRun() {
		try {
			//这里需要注意的是dateParam参数必须传入到JobParameters内,这是spring batch的一个后绑定技术,
			//就是在生成Step的时候,才去创建bean,因 为这个时候jobparameter才传过来。
			//如果加载配置信息的时候就创建bean,这个时候jobparameter的值还没有产生,会抛出异常。
			String dateParam = new Date().toString();
			JobParameters param =new JobParametersBuilder()
										.addString("jobName", "radarMessageDayJob")
										.addString("date", dateParam)
										.toJobParameters();
			JobExecution dayExecution = jobLauncher.run(radarMessageDayJob(), param);//执行job
			log.info("dayExecution  dateParam : " + dateParam);
			log.info("dayExecution  Exit Status : " + dayExecution.getStatus());
		} catch (Exception e) {
			log.error("dayRun :", e );
		}
	}
	public void limitRun() {
		try {
			String dateParam = new Date().toString();
			JobParameters param =new JobParametersBuilder()
										.addString("jobName", "radarMessageLimitJob")
										.addString("date", dateParam)
										.toJobParameters();
			JobExecution limitExecution = jobLauncher.run(radarMessageLimitJob(), param);//执行job
			log.info("limitExecution  dateParam : " + dateParam);
			log.info("limitExecution  Exit Status : " + limitExecution.getStatus());
		} catch (Exception e) {
			log.error("limitRun :", e );
		}
	}
	@Bean
	public Job radarMessageLimitJob() {
		return jobBuilderFactory.get("radarMessageLimitJob")
				.incrementer(new RunIdIncrementer())
				.start(radarBatchStep.eciChattelProcessorStep())//启动第一个执行的step,这里是逐步运行每个step
				.next(radarBatchStep.eciDetailProcessorStep())//next  下一个执行的step
				.next(radarBatchStep.eciExceptionProcessorStep())
				.next(radarBatchStep.eciInvestProcessorStep())
				.next(radarBatchStep.eciShixinZhixingProcessorStep())
				.next(radarBatchStep.eciZSCQProcessorStep())
				.next(radarBatchStep.gudongShixinProcessorStep())
				.next(radarBatchStep.judicialProcessorStep())
				.next(radarBatchStep.recruitProcessorStep())
				.next(radarBatchStep.tenderProcessorStep())
				.listener(radarAfterLimitJobListener)
				.build();
	}
	
	@Bean
	public Job radarMessageDayJob() {
		return jobBuilderFactory.get("radarMessageDayJob")
				.incrementer(new RunIdIncrementer())
				.flow(radarBatchStep.landInfoProcessorStep())//使用flow,这个step可以与其他step同时执行
				.next(radarBatchStep.courtAnnouncementProcessorStep())//next  下一个执行的step
				.end()
				.listener(radarAfterDayJobListener)//job运行结束后的监听器,监听器内可以完成一些消息发送任务完结的代码
				.build();
	}
	
	
}

以上相关重要参数的说明都加好注释。

2.step

每个job分一个或多个step,step内分reader、processor、writer。我们一般的运行机制是先读取reader数据,在解析processor数据,这里可以完成你对数据的业务变更,最后将解析processor的数据根据具体需求可以写入writer到db或者file等

每个step内可以有多个reader、可以有多个processor、多个writer

需要注意的是,假设我定义了3个processor,分别是A,B,C,调用的顺序是A---->B---->C,那么A的返回参数必须是B的输入参数,B的返回参数必须是C的输入参数。

同时,reader的返回参数必须是A的输入参数,C的返回参数必须是writer的输入参数。

可能有点绕,没关系,仔细看一眼再看下面的图,就一目了然了。

画图画的有点潦草了,望大家见谅。

package com.winhc.flysky.batch.reader;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.winhc.flysky.db.mybatis.dataobject.RadarRta;
import com.winhc.flysky.service.radar.RadarRtaService;
import com.winhc.flysky.util.DateUtil;

/**
 * 读取所有公司,进行每日任务
 * @author donnie
 *
 */
@Service
public class RadarDayJobReader {
	
	Log log = LogFactory.getLog(RadarDayJobReader.class);
	
	private static Map<String, List<RadarRta>> finalMap=new HashMap<String, List<RadarRta>>();
	
	@Autowired
	private RadarRtaService radarRtaService;
    
	public ListItemReader<RadarRta> geListRaderReader() {
		log.info("雷达监控开始初始化dayjob  reader......");
		String now=DateUtil.getCurrDate_YYYYMMDD();
		if(finalMap.get(now) == null){
			finalMap.clear();
			List<RadarRta>  list =radarRtaService.queryDayJob();
			finalMap.put(now, list);
		}
		List<RadarRta>  list =finalMap.get(now);	
		ListItemReader<RadarRta> reader = new ListItemReader<RadarRta>(list);
		log.info("雷达监控初始化dayjob  reader完成......");
		log.info("dayjob  reader :"+JSON.toJSONString(list));
		return reader;
	}

	
}
package com.winhc.flysky.batch.processer;


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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.winhc.flysky.batch.ParserHelper;
import com.winhc.flysky.batch.bean.CourtInfoAnalysisBean;
import com.winhc.flysky.batch.parser.CourtInfoParser;
import com.winhc.flysky.common.exception.UnresolveRadarException;
import com.winhc.flysky.config.dict.Dict.InfoType;
import com.winhc.flysky.db.mybatis.dataobject.RadarRta;
import com.winhc.flysky.db.mybatis.dataobject.RadarRtaResultWithBLOBs;
import com.winhc.flysky.db.prism.mybatis.dataobject.CourtAnnouncementWithBLOBs;
import com.winhc.flysky.service.enterprise.EnterpriseDBService;

/**
 * 法院公告
 * @author donnie
 *
 */
@Service
public class CourtAnnouncementProcessor implements ItemProcessor<RadarRta, RadarRtaResultWithBLOBs> {

	Log log = LogFactory.getLog(CourtAnnouncementProcessor.class);
	
	@Autowired
	private EnterpriseDBService enterpriseDBService;

	@Override
	public RadarRtaResultWithBLOBs process(RadarRta radarRta) throws UnresolveRadarException {
		String compName=radarRta.getCompName();
		log.info("开始执行法院公告:"+compName);
		
		try {
			Date now=new Date();
			List<CourtAnnouncementWithBLOBs> courtInfoList = enterpriseDBService.queryCourtAnnouncement(compName, now);
			
			CourtInfoAnalysisBean courtInfoParser =new CourtInfoParser()
					.setCourtInfoList(courtInfoList)
					.parser();
			
			if(ParserHelper.isEmpty(courtInfoParser.getMessage())){
				return null;
			}
			RadarRtaResultWithBLOBs radarRtaResult = new RadarRtaResultWithBLOBs();
			radarRtaResult.setRtaId(radarRta.getRtaId());
			radarRtaResult.setCompName(compName);
			radarRtaResult.setInfoType(InfoType.法院公告.getCode());
			radarRtaResult.setChangeContent(JSON.toJSONString(courtInfoParser.getCourtInfoList()));
			radarRtaResult.setRtaDesc(JSON.toJSONString(courtInfoParser.getMessage()));
		} catch (Exception e) {
			// TODO: handle exception
			log.error("" ,e);
		}
		return null;
	}
	
	
}

 

package com.winhc.flysky.batch.writer;

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

import org.apache.commons.lang.StringUtils;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.winhc.flysky.config.dict.Dict.StatusEnum;
import com.winhc.flysky.db.mybatis.dataobject.RadarRta;
import com.winhc.flysky.db.mybatis.dataobject.RadarRtaResultWithBLOBs;
import com.winhc.flysky.service.radar.dao.RadarRtaDao;
import com.winhc.flysky.service.radar.dao.RadarRtaResultDao;
import com.winhc.flysky.service.radar.dto.RadarRtaDTO;

/**
 * 数据写入类,判断changeContent以及rtaDesc是否为空
 * @author donnie
 *
 */
@Service
public class RadarDBWriter implements ItemWriter<RadarRtaResultWithBLOBs> {
	
	@Autowired
	private RadarRtaResultDao radarRtaResultDao;
	@Autowired
	private RadarRtaDao radarRtaDao;

	@Override
	public void write(List<? extends RadarRtaResultWithBLOBs> list) throws Exception {
		Date now=new Date();
		RadarRtaDTO radarRtaDTO =null;
		for(RadarRtaResultWithBLOBs radar : list){
		
		}
	}
}
	@Bean
	public Step eciChattelProcessorStep() {
		return stepBuilderFactory.get("eciChattelProcessorStep")
				.<RadarRta, RadarRtaResultWithBLOBs> chunk(10)
				.reader(radarLimitJobReader.geListRaderReader())//读取
				.processor(eciChattelProcessor)//解析
				.writer(radarDBWriter)//写入
				.listener(radarSkipListener)//读取异常监听器,用来监听读写解析过程中的异常记录
		        .faultTolerant()//异常重试
		        .skipLimit(10)//跳过
		        .skip(UnresolveRadarException.class)
		        .retryLimit(3)//重试次数
		        .retry(RadarRuntimeException.class)//重试异常控制
		        .backOffPolicy(new FixedBackOffPolicy())
		        .taskExecutor(taskExecutor())//多线程同时完成步骤
                .throttleLimit(5)
				.build();
	}
package com.winhc.flysky.batch.listener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.SkipListener;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import com.winhc.flysky.common.AssetsException;
import com.winhc.flysky.config.dict.Dict;
import com.winhc.flysky.db.mybatis.dataobject.RadarRta;

/**
 * 跳过监听器
 * @author donnie
 *
 */
@Service
public class RadarSkipListener implements SkipListener<RadarRta, RadarRta> {
	Log log = LogFactory.getLog(RadarSkipListener.class);

	@Override
	public void onSkipInProcess(RadarRta radar, Throwable throwable) {
		if (throwable instanceof NullPointerException) {
			 throw new NullPointerException("异常状态提醒   :雷达监控任务在process过程中跳过: "+radar);
        }
	
	}
	@Override
	public void onSkipInRead(Throwable throwable) {
		log.info("异常状态提醒   :雷达监控任务在Read过程中跳过 : "+throwable.getMessage());
	}
	@Override
	public void onSkipInWrite(RadarRta radar, Throwable throwable) {
		log.info("异常状态提醒   :雷达监控任务在Write过程中跳过  : "+radar);
	}

}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值