可以使用来自不同多个线程的不同作业参数同时调用相同的弹簧批

时间:2015-07-18 16:11:48

标签: spring-batch

我们的要求是同时编写多个文件。我们正在使用spring批处理来编写文件,我们正在从不同的线程中进行春季批处理。每个线程都有自己的应用程序上下文。因此,我们可以确保单线程bean不会在多个线程中共享。以下是我的代码段。

Spring batch config。

<bean id="reportDataReader" class="com.test.ist.batch2.rrm.batch.readers.RRMItmeReader"
    scope="step">
    <property name="verifyCursorPosition" value="false" />
    <property name="dataSource" ref="dataSource" />
    <property name="sql" value="#{jobParameters['sqlquery']}" />
    <property name="rowMapper" ref="valueMapper" />
    <property name="fetchSize" value="5000" />
</bean>

<bean id="valueMapper" class="com.test.ist.batch2.rrm.batch.mappers.DBValueMapper" scope="step"></bean>

<bean id="velocityFileWritter"
    class="com.test.ist.batch2.rrm.batch.writers.RRMVelocityFileWriter"
    scope="step">
</bean>

<bean id="velocityEngine"
    class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
        <value>
            resource.loader = class
            class.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            class.resource.loader.cache = true
            class.resource.loader.modificationCheckInterval = 0
        </value>
    </property>
</bean>

<batch:job id="rrmReportGenJob">
    <batch:step id="rrmReportGenStep">
        <batch:tasklet>
            <batch:chunk reader="reportDataReader" writer="velocityFileWritter"
                commit-interval="${reportData.reader.commit-interval}">
            </batch:chunk>
        </batch:tasklet>
    </batch:step>
</batch:job>

我们如何调用弹簧批。

ThreadPoolExecutor tpe = new ThreadPoolExecutor(10,10,1000000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());         PetReportGenerator rrg = new PetReportGenerator(null);         ThreadTest tt = new ThreadTest(new PetReportGenerator(null),&#34; 161&#34;);         ThreadTest tt2 = new ThreadTest(new PetReportGenerator(null),&#34; 162&#34;);         ThreadTest tt3 = new ThreadTest(new PetReportGenerator(null),&#34; 163&#34;);         ThreadTest tt4 = new ThreadTest(new PetReportGenerator(null),&#34; 165&#34;);         tpe.execute(TT);         tpe.execute(TT2);         tpe.execute(TT3);         tpe.execute(TT4);

在PetReportGenerator的构造函数中,我们正在初始化bean配置。 以下是代码段

私有ApplicationContext appContext;

public PetReportGenerator(ApplicationContext reportContext){
    if(null == reportContext){
        //if(null == appContext){
        appContext=new ClassPathXmlApplicationContext("spring-batch-jobs.xml");
        //}
    }else{
        setAppContext(reportContext);;
    }
}

以下是我们如何调用弹簧批次的代码摘录

Job jobToExecute =(Job)SpringUtils.getBean(jobName);             JobParametersBuilder paramsBuilder = new JobParametersBuilder();             //默认情况下添加数据时间。这将有助于使用相同的参数再次完成相同的工作             paramsBuilder.addLong(&#34; JOB_TIME&#34;,System.currentTimeMillis());             如果(!jobParams.isEmpty()){                 //验证输入字段。                 String sqlToUse = validator.validateInput(jobParams);                 for(Map.Entry entry:jobParams.entrySet()){

                    paramsBuilder.addString(entry.getKey(), entry.getValue());

            }

        }else{
            throw new ReportGenerationException("Job input parameter is Empty");
        }

        jobexe=jobLauncher.run(jobToExecute, paramsBuilder.toJobParameters());

如果它在单个线程中运行,它运行正常。 当它被多个线程调用时,我们将收到错误

09:09:26,742 ERROR pool-1-thread-3 job.AbstractJob:329 - 遇到执行作业的致命错误 显示java.lang.NullPointerException     在org.springframework.batch.core.repository.dao.MapJobExecutionDao.synchronizeStatus(MapJobExecutionDao.java:158)     在org.springframework.batch.core.repository.support.SimpleJobRepository.update(SimpleJobRepository.java:161)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:606)     在org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)     在org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)     在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)     at org.springframework.transaction.interceptor.TransactionInterceptor $ 1.proceedWithInvocation(TransactionInterceptor.java:98)     at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)     在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)     在org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)     在com.sun.proxy。$ Proxy14.update(未知来源)     在org.springframework.batch.core.job.AbstractJob.updateStatus(AbstractJob.java:416)     在org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:299)     在org.springframework.batch.core.launch.support.SimpleJobLauncher $ 1.run(SimpleJobLauncher.java:135)     在org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)     在org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)

任何人都可以帮我理解可能出错的地方吗?

2 个答案:

答案 0 :(得分:0)

你确定MapJobExecutionDao在所有方面都是线程安全的吗?我看到,在MapJobExecutionDao中使用了ConcurrentMap,但我不确定这是否足够。我曾经遇到过从不同线程访问的Map中获取NullPointer的问题。问题在于,一个线程导致了重新散列,当第二个线程在那一刻确实访问了地图时,它收到了一个空指针。

您确定,您的识别工作参数的组合是唯一的吗?我看,你在System.currentTimeMillis()中添加了一个参数Job_Time,但是你知道吗,如果它真的在一个唯一的时间戳中解析了吗?

您是否尝试使用基于表格的JobExecutionDao等版本?

答案 1 :(得分:0)

用于生产用途的MapJobRepository NOT 。它是 NOT 线程安全。如果您需要内存作业存储库的性能(失去可重启性等),请使用内存数据库,如HSQLDB。

除此之外,如果您使用线程安全组件,则没有理由不能使用多个线程启动多个作业实例。