Spring Batch:转换为多线程时的问题(混合数据)

时间:2015-02-25 13:07:56

标签: multithreading spring parallel-processing thread-safety spring-batch

也许这是一个反复出现的问题,但我需要根据我的背景进行一些自定义。

我使用的是Spring Batch 3.0.1.RELEASE

我有一些简单的工作,有一些步骤。一步是像这样的块:

    <tasklet transaction-manager="myTransactionManager">
<batch:chunk reader="myReader" processor="myProcessor" writer="myWriter" commit-interval="${commit.interval}">
</batch:chunk>

<bean id="myProcessor" class="org.springframework.batch.item.support.CompositeItemProcessor" scope="step">
<property name="delegates">
    <list>
        <bean class="...MyFirstProcessor">
        </bean>
        <bean class="...MySecondProcessor">
        </bean>
    </list>
</property>

  • 阅读器:JdbcCursorItemReader
  • 处理器:CompositeProcessor与我的代表
  • 作家:CompositeWriter与我的代表

通过这种配置,我的工作完美无缺。

现在,我想将其转换为多线程作业。 在documentation to basic multi-thread jobs之后,我在tasklet中包含了一个SympleAsyncTaskExecutor,但它失败了。

我已经知道JdbcCursorItemReader在多线程执行时无法正常工作(是不是?)。我已经将读者更改为JdbcPagingItemReader,这是一场噩梦:作业不会失败,编写过程没问题,但数据在线程中混合,客户数据不正确且连贯(客户已经<来自其他人的服务,地址等。

那么,为什么会这样呢?我怎样才能更改为多线程作业?

  • 复合处理器和编写器是否适用于多线程?
  • 我如何制作自定义线程安全的复合处理器?
  • 也许它可能是JDBC阅读器:是否有用于多线程的线程安全JDBC阅读器?

我非常关心和困惑,所以任何帮助都会非常感激。 非常感谢。

[编辑已解决]

嗯,我的问题的正确和合适的解决方法是从开始设计多线程和线程安全执行的作业。习惯性地首先使用单线程步骤执行来理解和了解Spring Batch概念;但如果你认为你要离开这个阶段,必须提出诸如不可变对象,线程安全列表,地图等的考虑因素。

当前我的问题状态中的当前修复是我后面描述的下一个。在测试了马丁的建议并考虑到迈克尔的指导方针后,我终于解决了我的问题。接下来的步骤不是很好的做法,但我无法从开始重建我的工作:

  • 将itemReader更改为JdbcPagingItemReader,并将setState更改为false。
  • 通过CopyOnWriteArrayList更改列表。
  • 通过ConcurrentHashMap更改HashMap。
  • 在每个委托处理器中,通过传递上下文(实现ApplicationContextAware)并获取bean的唯一实例(将每个注入的bean配置为scope =),获取每个bean属性的新实例(幸运的是,只有一个注入的bean) &#34;原型&#34;。)

因此,如果委托的bean是:

<bean class="...MyProcessor">
<property name="otherBean"  ref="otherBeanID" />

更改为:

<bean class="...MyProcessor">
<property name="otherBean"  value="otherBeanID" />

并且,在MyProcessor中,从上下文获取 otherBeanID 的单个实例; otherBeanID 必须使用scope =&#34; protoype&#34;进行配置。

正如我之前所说,他们并没有好的风格,但这是我最好的选择,我可以断言每个线程都有自己的不同的项目实例和其他bean实例。

它证明某些类没有很好地设计用于正确的多线程执行。

马丁,迈克尔,感谢您的支持。

我希望对任何人都有帮助。

1 个答案:

答案 0 :(得分:6)

您在问题中提出了很多问题(将来,请将此类问题分解为多个更具体的问题)。但是,逐项:

JdbcCursorItemReader线程安全吗?
作为documentation states,它不是。原因是JdbcCursorItemReader包装了一个非线程安全的ResultSet

复合处理器和编写器是否适用于多线程?
只要委托CompositeItemProcessor实现也是线程安全的,Spring Batch提供的ItemProcessor被认为是线程安全的。您不提供与您的实现或其配置相关的代码,因此我无法验证其线程安全性。但是,鉴于您所描述的症状,我的预感是您的代码中存在某种形式的线程安全问题。

您也没有确定正在使用的ItemWriter实现或其配置,因此也可能存在与线程相关的问题。

如果您使用有关实施和配置的更多信息更新问题,我们可以提供更多信息。

我如何制作自定义线程安全的复合处理器?
实施任何ItemProcessor时需要考虑两件事:

  1. 使其线程安全:遵循基本的线程安全规则(阅读有关该主题的圣经的书籍Java Concurrency In Practice)将允许您通过添加任务执行程序来扩展组件。
  2. 使其具有幂等性:在跳过/重试处理期间,可能会重新处理项目。通过使ItemProcessor实现具有幂等性,可以防止多次通过处理器的副作用。
  3. 也许它可能是JDBC阅读器:多线程是否有任何线程安全的JDBC阅读器?
    如您所知,JdbcPaginingItemReader是线程安全的,并在documentation中注明。当使用多个线程时,每个块都在它自己的线程中执行。如果您已将页面大小配置为与提交间隔匹配,则表示每个页面都在同一个线程中处理。

    缩放单个步骤的其他选项
    当您走上实现单线程,多线程步骤的道路时,可能会有更好的选择。 Spring Batch提供了5个核心扩展选项:

    1. 多线程步骤 - 正如您现在所做的那样。
    2. 并行步骤 - 使用Spring Batch的拆分功能,您可以并行执行多个步骤。鉴于您在同一步骤中使用复合ItemProcessor和复合ItemWriter,这可能需要探索(将当前复合方案分解为多个并行步骤)。
    3. Async ItemProcessor / ItemWriters - 此选项允许您在不同的线程中执行处理器逻辑。处理器将线程关闭并将Future返回到AsyncItemWriterFuture将阻塞,直到ItemProcessor返回写入。
    4. 分区 - 这是将数据划分为称为分区的块,这些块由子步骤并行处理。每个分区都由一个实际的独立步骤处理,因此使用步骤范围的组件可以防止线程安全问题(每个步骤都获得它自己的实例)。可以通过线程在本地执行分区处理,也可以跨多个JVM远程执行分区处理。
    5. 远程分块 - 此选项将处理器逻辑运行到其他JVM进程。只有在{{1}}逻辑是流程的瓶颈时才能使用它。
    6. 您可以在这里阅读Spring Batch文档中的所有这些选项:http://docs.spring.io/spring-batch/trunk/reference/html/scalability.html

      线程安全是一个复杂的问题。只需将多个线程添加到曾经在单线程环境中工作的代码,通常就会发现代码中的问题。