Spring Batch 3600 разделов долгая пауза перед запуском - PullRequest
0 голосов
/ 10 декабря 2018

У меня есть следующее весеннее пакетное задание с разделенным шагом, которое создает 3600 разделов для разделенного шага.Я использую ThreadPoolTaskExecutor с максимальным размером пула 100 и емкостью очереди 100 (хотя, похоже, это не имеет значения для скорости).Я использую Visual VM для мониторинга потоков, и я замечаю, что потоки taskExecutor не запускаются до> 5 минут после начала работы.

Как ни странно, если я ограничу количество разделов до 100, потоки начнутся довольно быстро и завершатся примерно через минуту.

Еще одна проблема, которую я замечаю, заключается в том, что, похоже, больше нетчем одно соединение с базой данных, как показано в VisualVM визуализация потоков

Может кто-нибудь, пожалуйста, просмотрите мою пакетную работу ниже и скажите мне, если я пропускаю что-то, что ограничило бы количество соединений с базой данных до 1?Кроме того, почему добавление большего количества разделов влияет на производительность, если мои параметры ThreadPoolTaskExecutor не меняются?Разве задания не должны просто находиться в очереди, пока не найдется поток, способный их обслуживать?

--- Пакетное задание Spring ---

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:batch="http://www.springframework.org/schema/batch"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns:lang="http://www.springframework.org/schema/lang"
   xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
    http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.0.xsd">


<context:property-placeholder location="${ext.properties.dataManipulation.Properties}"/>

<import resource="${ext.properties.dataManipulation.Connection}"/>

<import resource="flatFileLineProperties.xml"/>

<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository"
      class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>

<bean id="transactionManager"
      class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>

<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository"/>
</bean>



<util:map id="keys" map-class="java.util.HashMap">
    <entry key="SAILING_ID" value="ASCENDING" value-type="org.springframework.batch.item.database.Order"/>
    <entry key="RES_ID" value="ASCENDING" value-type="org.springframework.batch.item.database.Order" />
</util:map>

<!-- Here is my partioned step -->
<bean id="reservationsItemReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
    <property name="dataSource" ref="dataSource" />
    <property name="queryProvider">
        <bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="selectClause">
                <value>
                    <![CDATA[ 
                    GUEST_ID,
                    FIRST_NAME,
                    LAST_NAME,
                    TITLE,
                    HOUSEHOLD_NAME,
                    SAILING_ID,
                    RES_ID
                ]]>
                </value>
            </property>
            <property name="fromClause" value="FROM RESERVATION "/>
            <property name="whereClause" >
                <value>
                    <![CDATA[ AND SAIL_ID = :sailId
                    ]]>
                </value>
            </property>
            <!--<property name="sortKey" value="SAILING_ID" />-->
            <property name="sortKeys" ref="keys"/>
       <!--<property name="sortKeys" ref="sortKeys"/>-->
        </bean>
    </property>
    <property name="parameterValues">
        <map>
            <!--<entry key="shipCode" value="#{stepExecutionContext[shipCode]}" />-->
            <entry key="sailId" value="#{stepExecutionContext[sailId]}" />
        </map>
    </property>
    <!--property name="pageSize" value="500000" /-->

    <property name="pageSize" value="40000" />
    <property name="rowMapper">
        <bean class="com.ncl.endeca.mapper.ColumnToHashMapper" />
    </property>
</bean>

<bean id="sortKeys" class="java.util.HashMap" scope="prototype" >
    <constructor-arg>
        <map key-type="java.lang.String" value-type="org.springframework.batch.item.database.Order">
            <entry key="SAILING_ID" value="ASCENDING" />
            <entry key="RES_ID" value="ASCENDING" />
        </map>
    </constructor-arg>
</bean>

<util:list id="client_fields" value-type="java.lang.String"> 
    <value>FIRST_NAME</value>
    <value>LAST_NAME</value>
    <value>TITLE</value>
    <value>HOUSEHOLD_NAME</value>

</util:list>

<bean id="reservationsItemWriter" class="com.ncl.endeca.writer.ReservationWriter" scope="step">
    <property name="guestFields" ref="client_fields" />
    <property name="outPrefix" value="${file.out.prefix}" />
    <property name="shipCode" value="#{stepExecutionContext[shipCode]}" />
    <property name="sailId" value="#{stepExecutionContext[sailId]}" />
    <property name="soldOutSailings" ref="soldOutSailingsList" />
</bean>

<bean id="yearsAgo" class="java.lang.Integer">
    <constructor-arg>
        <value>${yearsAgo}</value>
    </constructor-arg>
</bean>
<bean id="yearsAhead" class="java.lang.Integer">
    <constructor-arg>
        <value>${yearsAhead}</value>
    </constructor-arg>
</bean>

<bean id="resPartitioner" class="com.ncl.endeca.partition.ReservationPartitioner">
    <property name="yearsAgo" ref="yearsAgo" />
    <property name="yearsAhead" ref="yearsAhead" />
    <property name="batchLimit" value="${batch.limit}" />
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="taskExecutor"
      class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="${batch.corePoolSize}" />
    <property name="maxPoolSize" value="${batch.maxPoolSize}" />
    <property name="queueCapacity" value="${batch.queueCapacity}" />
</bean>


<!--<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>-->



<!-- each thread will run this job, with different stepExecutionContext values. -->
<step id="slave" xmlns="http://www.springframework.org/schema/batch" >
    <flow parent="readReservations"/>
</step>

<!--<bean id="countrySpecificCompletionPolicy" class="org.springframework.batch.core.resource.StepExecutionSimpleCompletionPolicy">-->
    <!--<property name="keyName" value="sailId"/>-->
<!--</bean>-->

<batch:flow id="readReservations">
    <batch:step id="reservations" xmlns="http://www.springframework.org/schema/batch" >
        <tasklet throttle-limit="${batch.corePoolSize}">
            <chunk reader="reservationsItemReader" writer="reservationsItemWriter" commit-interval="50000" />
        </tasklet>
    </batch:step>
</batch:flow>

<!-- Actual Job -->
<batch:job id="dataManipulationJob">

    <batch:step id="masterStep">
        <batch:partition step="slave" partitioner="resPartitioner">
            <batch:handler grid-size="100" task-executor="taskExecutor" />
        </batch:partition>
    </batch:step>
</batch:job>

Я пробовал подключения BasicDataSource и Hikari, но размеры пула не влияют на мониторинг VisualVM

---- connection.xml ----

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
   xmlns:batch="http://www.springframework.org/schema/batch"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns:lang="http://www.springframework.org/schema/lang"
   xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
    http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.0.xsd">

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
    <property name="poolName" value="springHikariCP" />
    <property name="connectionTestQuery" value="SELECT 10 from dual" />
    <property name="dataSourceClassName" value="${hibernate.dataSourceClassName}" />
    <property name="maximumPoolSize" value="${batch.maxPoolSize}" />
    <property name="idleTimeout" value="${hibernate.hikari.idleTimeout}" />

    <property name="dataSourceProperties">
        <props>
            <prop key="url">${dataSource.url}</prop>
            <prop key="user">${dataSource.username}</prop>
            <prop key="password">${dataSource.password}</prop>
        </props>
    </property>
</bean>

<!-- HikariCP configuration -->
<!--<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">-->
    <!--<constructor-arg ref="hikariConfig" />-->
<!--</bean>-->

<!--<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" scope="step">-->
    <!--<property name="driverClassName" value="${hibernate.dataSourceClassName}" />-->
    <!--<property name="url" value="${dataSource.url}" />-->
    <!--<property name="username" value="${dataSource.username}" />-->
    <!--<property name="password" value="${dataSource.password}" />-->
    <!--<property name="testWhileIdle" value="false"/>-->
    <!--<property name="maxActive" value="${batch.corePoolSize}"/>-->
<!--</bean>-->

    <!-- connect to database -->
<bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@******" />
    <property name="username" value="****" />
    <property name="password" value="****" />
</bean>

1 Ответ

0 голосов
/ 10 декабря 2018

Время создания разделов зависит от производительности вашего ReservationPartitioner, а также от количества разделов.Создание 3600 разделов означает создание 3600 StepExecution / ExecutionContext объектов и сохранение их в соответствующих таблицах.Это может занять некоторое время для такого большого количества разделов.

Что касается соединения с базой данных, вы используете MapJobRepositoryFactoryBean с ResourcelessTransactionManager, поэтому нет взаимодействия с базой данных для мета-мета Spring Batch.данные.Единственный компонент, который взаимодействует с базой данных в соответствии с вашей конфигурацией, это JdbcPagingItemReader (я не знаю, какой у вас тип ReservationWriter), поэтому неудивительно видеть одно соединение с базой данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...