web-dev-qa-db-ja.com

最高のパフォーマンスを実現するSpring Batch非同期プロセッサー構成

Spring Batchでの非同期プロセッサの作成に問題があります。私のプロセッサはIDからreaderを取得し、SOAP呼び出しからの応答に基づいてオブジェクトを作成しています。 1つの入力(ID)の場合、たとえば、 60-100 SOAP呼び出しと1つだけの場合があります。たとえば、一度に50の入力を処理するマルチスレッドステップを作成しようとしましたが、49のスレッドが1秒でジョブを実行し、ブロックされてこのスレッドを待機しているため、役に立たなかった60-100 SOAP呼び出しを行っていました。今、私はAsyncItemProcessor + AsyncItemWriterを使用していますが、この解決策はゆっくりと機能します。私の入力(IDs)が大きいため、DBから読み取った約25kのアイテムは、一度に最大50-100の入力を開始したいと考えています。

これが私の設定です:

@Configuration
public class BatchConfig {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;
    @Autowired
    public StepBuilderFactory stepBuilderFactory;
    @Autowired
    private DatabaseConfig databaseConfig;
    @Value(value = "classpath:Categories.txt")
    private Resource categories;

    @Bean
    public Job processJob() throws Exception {
        return jobBuilderFactory.get("processJob").incrementer(new RunIdIncrementer()).listener(listener()).flow(orderStep1()).end().build();
    }

    @Bean
    public Step orderStep1() throws Exception {
        return stepBuilderFactory.get("orderStep1").<Category, CategoryDailyResult>chunk(1).reader(reader()).processor(asyncItemProcessor()).writer(asyncItemWriter()).taskExecutor(taskExecutor()).build();
    }

    @Bean
    public JobExecutionListener listener() {
        return new JobCompletionListener();
    }


    @Bean
    public ItemWriter asyncItemWriter() {
        AsyncItemWriter<CategoryDailyResult> asyncItemWriter = new AsyncItemWriter<>();
        asyncItemWriter.setDelegate(itemWriter());
        return asyncItemWriter;
    }

    @Bean
    public ItemWriter<CategoryDailyResult> itemWriter(){
        return new Writer();
    }

    @Bean
    public ItemProcessor asyncItemProcessor() {
        AsyncItemProcessor<Category, CategoryDailyResult> asyncItemProcessor = new AsyncItemProcessor<>();
        asyncItemProcessor.setDelegate(itemProcessor());
        asyncItemProcessor.setTaskExecutor(taskExecutor());
        return asyncItemProcessor;
    }

    @Bean
    public ItemProcessor<Category, CategoryDailyResult> itemProcessor(){
        return new Processor();
    }

    @Bean
    public TaskExecutor taskExecutor(){
        SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
        taskExecutor.setConcurrencyLimit(50);
        return taskExecutor;
    }

    @Bean(destroyMethod = "")
    public ItemReader<Category> reader() throws Exception {
        String query = "select c from Category c where not exists elements(c.children)";

        JpaPagingItemReader<Category> reader = new JpaPagingItemReader<>();
        reader.setSaveState(false);
        reader.setQueryString(query);
        reader.setEntityManagerFactory(databaseConfig.entityManagerFactory().getObject());
        reader.setPageSize(1);

        return reader;
    }
}

どうすればアプリケーションを強化できますか?多分私は何か間違ったことをしていますか?どんなフィードバックでも歓迎します;)

@編集:IDの入力の場合:1から100たとえば、プロセッサを実行している50スレッドが必要です。スレッド1が2分間「1」の入力を処理し、この時点で「スレッド2」が数秒で実行される「2」、「8」、「64」の入力を処理するようにしたい。

@ Edit2:私の目標:データベースに25kのIDがあり、JpaPagingItemReaderでそれらを読み取り、すべてのIDがプロセッサによって処理されます。各アイテムは互いに独立しています。各IDについて、ループで0〜100回SOAPを呼び出してから、Writerに渡し、データベースに保存するオブジェクトを作成します。そのようなタスクに最適なパフォーマンスを得るにはどうすればよいですか?

15
crooked

ジョブを分割する必要があります。次のようにパーティション分割されたステップを追加します。

@Bean
public Step partitionedOrderStep1(Step orderStep1) {
    return stepBuilder.get("partitionedOrderStep1")
            .partitioner(orderStep1)
            .partitioner("orderStep1", new SimplePartitioner())
            .taskExecutor(taskExecutor())
            .gridSize(10)  //Number of concurrent partitions
            .build();
}

次に、ジョブ定義でそのステップを使用します。 .gridSize()呼び出しは、同時に実行されるパーティションの数を構成します。 Reader、Processor、またはWriterオブジェクトのいずれかがステートフルである場合は、@ StepScopeで注釈を付ける必要があります。

2
Joe Chiavaroli

@KCrookedHand:私は 同様のシナリオ を扱ってきました、数千を読む必要があり、SOAP Service(これをitemReaderに注入しました)を呼び出す必要があります)一致基準。

私の構成は以下のようになります。基本的に、並列処理を実現するためのいくつかのオプションがあり、そのうちの2つは「パーティショニング」と「クライアントサーバー」アプローチです。データに基づいて必要なパーティションの数をより詳細に制御できるため、パーティションを選択しました。

@MichaelMinellaが言及しているように、ThreadPoolTask​​Executorを実行してください。

<batch:step id="notificationMapper">
            <batch:partition partitioner="partitioner"
                step="readXXXStep" />
        </batch:step>
    </batch:job>


    <batch:step id="readXXXStep">
        <batch:job ref="jobRef" job-launcher="jobLauncher"
            job-parameters-extractor="jobParameterExtractor" />
    </batch:step>

    <batch:job id="jobRef">

        <batch:step id="dummyStep" next="skippedItemsDecision">
            <batch:tasklet ref="dummyTasklet"/>
            <batch:listeners>
                <batch:listener ref="stepListener" />
            </batch:listeners>
        </batch:step>

        <batch:step id="xxx.readItems" next="xxx.then.finish">
            <batch:tasklet>
                <batch:chunk reader="xxxChunkReader" processor="chunkProcessor"
                    writer="itemWriter" commit-interval="100">
                </batch:chunk>
            </batch:tasklet>
            <batch:listeners>
                <batch:listener ref="taskletListener" />
            </batch:listeners>
        </batch:step>

        ...
0
Ashok Gudise