轮询和内存泄漏

时间:2015-04-07 02:58:03

标签: spring-integration

我在spring集成参考文档中看到了以下内容

  

receiveTimeout属性指定轮询器在调用接收操作时没有可用消息时应等待的时间。例如,考虑表面上看起来相似但实际上完全不同的两个选项:第一个具有5秒的间隔触发器和50毫秒的接收超时,而第二个具有50毫秒的间隔触发器和5的接收超时秒。第一个消息可能会比它到达通道之前多达4950毫秒收到一条消息(如果该消息在其中一个轮询调用返回后立即到达)。另一方面,第二种配置永远不会错过超过50毫秒的消息。不同之处在于第二个选项需要线程等待,但结果是它能够更快地响应到达的消息。这种称为长轮询的技术可用于模拟轮询源上的事件驱动行为。

根据我的经验,第二个选项可能会导致问题,因为50毫秒的间隔将使轮询器每50毫秒运行一次,但是如果没有消息要提取,则每个创建的线程将等待5秒要显示一条消息。在那5秒内,轮询器将再次执行100次,可能会创建另外100个线程等等。

这很快就会消失。

我的问题是我误解了这一切的运作方式吗?因为如果我是正确的,我认为应该更改参考文档,或者至少添加一个警告。

e<bean id="store" class="org.springframework.integration.jdbc.store.JdbcChannelMessageStore">
    <property name="dataSource" ref="channelServerDataSource"/>
    <property name="channelMessageStoreQueryProvider" ref="queryProvider"/>
    <property name="region" value="${user.name}_${channel.queue.region:default}"/>
    <property name="usingIdCache" value="false"/>
</bean> 

<int:transaction-synchronization-factory id="syncFactory">
    <int:after-commit expression="@store.removeFromIdCache(headers.id.toString())" />
    <int:after-rollback expression="@store.removeFromIdCache(headers.id.toString())"/> 
</int:transaction-synchronization-factory>

<int:channel id="transacitonAsyncServiceQueue">
    <int:queue message-store="store"/> 
    <!--  <int:queue/>  --> 
</int:channel>

<bean id="rxPollingTrigger" class="org.springframework.scheduling.support.PeriodicTrigger">
    <constructor-arg value="500"/>
    <constructor-arg value="MILLISECONDS"/>
    <property name = "initialDelay" value = "30000"/> 
    <!-- initialDelay important to ensure channel doesnt start processing before the datasources have been initialised becuase we
         now persist transactions in the queue, at startup (restart) there maybe some ready to go which get processed before the
         connection pools have been created which happens when the servlet is first hit -->
</bean> 

<int:service-activator ref="asyncChannelReceiver" method="processMessage" input-channel="transacitonAsyncServiceQueue">
    <int:poller trigger="rxPollingTrigger" max-messages-per-poll="20"  task-executor="taskExecutor" receive-timeout="400">
        <int:transactional transaction-manager="transactionManagerAsyncChannel" /> 
    </int:poller>
    <int:request-handler-advice-chain>
        <ref bean="databaseSessionContext" />
    </int:request-handler-advice-chain>
</int:service-activator>

<task:executor id="taskExecutor" pool-size="100-200" queue-capacity="200" keep-alive="1" rejection-policy="CALLER_RUNS" />

1 个答案:

答案 0 :(得分:0)

  

我的问题是我误解了这一切的运作方式吗?

是的,你误解了。

只有在当前轮询退出时计算下一个轮询时间,才会查询触发器(在本例中为PeriodicTrigger,间隔为50毫秒)。

只有一个轮询器线程并发运行。如果没有消息,则轮询线程将暂停5秒;然后查询触发器(t.nextExecutionTime()),并为+50ms安排下一轮询;所以,如果没有数据,单个线程将每5.05秒运行一次。

当存在消息,并且您希望并发大于1时,您将使用任务执行程序允许轮询器线程切换到另一个线程,以便在下一个轮询时间立即查询触发器。 / p>

  

根据我的经验

请澄清&#34;您的经历&#34;并显示配置,证据等。

如果您有可疑的线程泄漏,第一步通常是采用线程转储来弄清楚它们正在做什么。

编辑 :(回复您的评论)。

在这种情况下,CALLER_RUNS并没有真正的缺点,因为尽管当前的线程已经超前了#34;对于排队的任务,它不像这个民意调查有比排队任务更新的数据,它毕竟只是一个民意调查。但是,轮询器线程是一种有限的资源(尽管可以更改限制),因此通常不鼓励在轮询器线程上执行长时间运行的任务。

ABORT可能会在日志中产生一些噪音;另一种方法是配置PollSkipAdvice,其中建议可以查看任务队列并静默忽略当前轮询。在4.2中,我们added even more flexibility to the poller

你会发现互联网上有很多文章说使用RDBMS作为队列不是最好的主意;您可能想要考虑使用JMS或rabbitmq支持的频道。如果您与JDBC绑定,则应确保使用JdbcChannelMessageStore而不是JdbcMessageStore。前者是支持频道的首选,因为它只使用1个表格;由于在消息组表上存在争用,后者在用于支持通道时会出现一些性能问题。有关详细信息,请参阅Backing Message Channels in the JDBC Support chapter