簡體   English   中英

用於動態塊大小的Spring Batch自定義完成策略

[英]Spring Batch custom completion policy for dynamic chunk size

上下文

我們有一個批處理作業,可將本地化的國家名稱(即國家名稱翻譯為不同語言)從外部復制到我們的數據庫中。 這個想法是在1個塊中處理單個國家/地區的所有本地化的國家名稱(即第一個塊-安道爾的所有翻譯,下一個塊-阿聯酋的所有翻譯等)。 我們使用JdbcCursorItemReader讀取外部數據+一些oracle解析函數以提供該國家/地區可用的翻譯總數:

select country_code, language_code, localized_name, COUNT(1) OVER(PARTITION BY c_lng.country_code) as lng_count
from EXT_COUNTRY_LNG c_lng
order by c_lng.countty_code, c_lng.language_code

問題

因此,按塊分割此輸入看起來很簡單:在讀取了lng_count指定的確切行數后停止塊,並從下一個讀取的行開始一個新的行,但實際上似乎並不那么簡單:(

首先要嘗試的是自定義完成政策。 但是問題是,它無權訪問由ItemReader讀取的最后一個項目-您應該將其顯式地放置在reader中的上下文中,並將其返回到策略中。 不喜歡它,因為它需要其他閱讀器修改/添加閱讀器偵聽器。 此外,我不喜歡將同一項目來回序列化/反序列化。 而且我覺得JobContext / StepContext不適合存放此類數據。

還有RepeatContext看起來更適合存放此類數據,但我無法輕松訪問它...

所以最后我們得到這樣的解決方案:

@Bean(name = "localizedCountryNamesStep")
@JobScope
public Step insertCountryStep(
        final StepBuilderFactory stepBuilderFactory,
        final MasterdataCountryNameReader countryNameReader,
        final MasterdataCountryNameProcessor countryNameProcessor,
        final MasterdataCountryNameWriter writer) {
    /* Use the same fixed-commit policy, but update it's chunk size dynamically */
    final SimpleCompletionPolicy policy = new SimpleCompletionPolicy();
    return stepBuilderFactory.get("localizedCountryNamesStep")
            .<ExtCountryLng, LocalizedCountryName> chunk(policy)
            .reader(countryNameReader)
            .listener(new ItemReadListener<ExtCountryLng>() {

                @Override
                public void beforeRead() {
                    // do nothing
                }

                @Override
                public void afterRead(final ExtCountryLng item) {
                    /* Update the cunk size after every read: consequent reads 
                    inside the same country = same chunk do nothing since lngCount is always the same there */
                    policy.setChunkSize(item.getLngCount());
                }

                @Override
                public void onReadError(final Exception ex) {
                    // do nothing
                }
            })
            .processor(countryNameProcessor)
            .writer(writer)
            .faultTolerant()
            .skip(RuntimeException.class)
            .skipLimit(Integer.MAX_VALUE) // Batch does not support unlimited skip
            .retryLimit(0) // this solution disables only retry, but not recover
            .build();
}

它正在工作,需要最少的代碼更改,但是對我來說還是有點難看。 所以我想知道,當所有必需的信息都已經在ItemReader可用時,是否還有另一種優雅的方法可以在Spring Batch中進行動態塊大小ItemReader

最簡單的方法是按國家划分您的步驟。 這樣,每個國家都可以邁出自己的一步,並且您還可以跨國家/地區訪問以提高性能。

如果需要一個閱讀器,則可以包裝委托PeekableItemReader並擴展SimpleCompletionPolicy以實現您的目標。

public class CountryPeekingCompletionPolicyReader extends SimpleCompletionPolicy implements ItemReader<CountrySpecificItem> {

    private PeekableItemReader<? extends CountrySpecificItem> delegate;

    private CountrySpecificItem currentReadItem = null;

    @Override
    public CountrySpecificItem read() throws UnexpectedInputException, ParseException, NonTransientResourceException, Exception {
        currentReadItem = delegate.read();
        return currentReadItem;
    }

    @Override
    public RepeatContext start(final RepeatContext context) {
        return new ComparisonPolicyTerminationContext(context);
    }

    protected class ComparisonPolicyTerminationContext extends SimpleTerminationContext {

        public ComparisonPolicyTerminationContext(final RepeatContext context) {
            super(context);
        }

        @Override
        public boolean isComplete() {
            final CountrySpecificItem nextReadItem = delegate.peek();

            // logic to check if same country
            if (currentReadItem.isSameCountry(nextReadItem)) {
                return false;
            }

            return true;
        }
    }
}

然后在您的上下文中定義:

<batch:tasklet>
    <batch:chunk chunk-completion-policy="countrySpecificCompletionPolicy" reader="countrySpecificCompletionPolicy" writer="someWriter" />
</batch:tasklet>

<bean id="countrySpecificCompletionPolicy" class="CountryPeekingCompletionPolicyReader">
     <property name="delegate" ref="peekableReader" />
</bean>


<bean id="peekableReader" class="YourPeekableItemReader" />

編輯:仔細考慮您的問題,分區是最干凈的方法。 使用分區步驟 ,將從步驟執行上下文中為每個ItemReader(確保scope="step" )傳遞一個單個countryName 是的,您將需要一個自定義的Partitioner類來構建您的執行上下文圖(每個國家一個條目)和一個硬編碼的提交間隔,該間隔足夠容納您最大的工作單元,但是此后,所有事情都是非常簡單的,並且由於每個從屬步驟僅是一個單獨的步驟,因此對於可能遇到問題的任何國家,重新啟動應該是一件輕而易舉的事。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM