从s3轮询文件时处理重复的消息

时间:2016-02-08 17:18:50

标签: spring-integration spring-xd

我正在使用s3模块从s3轮询文件。它将文件下载到本地系统并开始处理它。我在3节点集群上运行它,模块计数为1.现在假设文件已下载到本地系统从s3和xd处理它。如果xd节点发生故障,它将处理一半的消息。当服务器启动时,它将再次开始处理文件,因此我将获得重复的消息。我正在尝试使用消息存储库更改为幂等模式将模块计数更改为3,但仍然存在重复的消息问题。

  <int:poller fixed-delay="${fixedDelay}" default="true">
        <int:advice-chain>
                <ref bean="pollAdvise"/>

                </int:advice-chain>
        </int:poller>


        <bean id="pollAdvise" class="org.springframework.integration.scheduling.PollSkipAdvice">
            <constructor-arg ref="healthCheckStrategy"/>

        </bean>

        <bean id="healthCheckStrategy" class="ServiceHealthCheckPollSkipStrategy">
                     <property name="url" value="${url}"/>
                      <property name="doHealthCheck" value="${doHealthCheck}"/>
         </bean>



        <bean id="credentials" class="org.springframework.integration.aws.core.BasicAWSCredentials">
            <property name="accessKey" value="${accessKey}"/>
            <property name="secretKey" value="${secretKey}"/>
        </bean>



        <bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
            <property name="proxyHost" value="${proxyHost}"/>
            <property name="proxyPort" value="${proxyPort}"/>
        <property name="preemptiveBasicProxyAuth" value="false"/> 
        </bean>


        <bean id="s3Operations" class="org.springframework.integration.aws.s3.core.CustomC1AmazonS3Operations">
            <constructor-arg index="0" ref="credentials"/>
            <constructor-arg index="1" ref="clientConfiguration"/>
            <property name="awsEndpoint" value="s3.amazonaws.com"/>
            <property name="temporaryDirectory" value="${temporaryDirectory}"/>
            <property name="awsSecurityKey"  value=""/>
        </bean>



        <!-- aws-endpoint="https://s3.amazonaws.com"  -->
        <int-aws:s3-inbound-channel-adapter aws-endpoint="s3.amazonaws.com"
                                            bucket="${bucket}"
                                            s3-operations="s3Operations"
                                            credentials-ref="credentials"
                                            file-name-wildcard="${fileNameWildcard}"
                                            remote-directory="${remoteDirectory}"
                                            channel="splitChannel"
                                            local-directory="${localDirectory}"
                                            accept-sub-folders="false"
                                            delete-source-files="true"
                                            archive-bucket="${archiveBucket}"
                                            archive-directory="${archiveDirectory}">
        </int-aws:s3-inbound-channel-adapter>

      <int-file:splitter input-channel="splitChannel" output-channel="output" markers="false" charset="UTF-8">

            <int-file:request-handler-advice-chain>
                <bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
                    <property name="onSuccessExpression" value="payload.delete()"/>
                </bean>
            </int-file:request-handler-advice-chain>

    </int-file:splitter>

<int:idempotent-receiver id="expressionInterceptor" endpoint="output"
                              metadata-store="redisMessageStore"
                             discard-channel="nullChannel"
                             throw-exception-on-rejection="false"
                              key-expression="payload"/>

   <bean id="redisMessageStore" class="o.s.i.redis.store.RedisChannelMessageStore">
    <constructor-arg ref="redisConnectionFactory"/>
</bean>
<bean id="redisConnectionFactory"
    class="o.s.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="port" value="7379" />
</bean>

        <int:channel id="output"/>

更新2 这个配置对我有用感谢您的帮助。

<int:idempotent-receiver id="s3Interceptor" endpoint="s3splitter"
                             metadata-store="redisMessageStore"
                             discard-channel="nullChannel"
                             throw-exception-on-rejection="false"

                             key-expression="payload.name"/>

     <bean id="redisMessageStore" class="org.springframework.integration.redis.metadata.RedisMetadataStore">
        <constructor-arg ref="redisConnectionFactory"/>
    </bean>

    <bean id="redisConnectionFactory"
          class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="port" value="6379" />
    </bean>

    <int:bridge id="batchBridge"  input-channel="bridge" output-channel="output">

</int:bridge>


    <int:idempotent-receiver id="splitterInterceptor" endpoint="batchBridge"
                             metadata-store="redisMessageStore"
                             discard-channel="nullChannel"
                             throw-exception-on-rejection="false"

                             key-expression="payload"/>


    <int:channel id="output"/>

我很少有人怀疑如果我做对了。

1)如您所见,我有ExpressionEvaluatingRequestHandlerAdvice来删除文件。在将文件读入redis或读取最后一条记录后,文件是否会被删除?

2)我使用桌面管理器探索了redis我看到我有一个MetaData作为man Key enter image description here

两个(文件和有效负载)metadatastore密钥和值是否会转到同一个表是这样的?还是应该是不同的metadatastore?

我可以使用有效载荷的散列而不是有效载荷作为密钥吗?有没有像payload.hash这样的东西!

1 个答案:

答案 0 :(得分:1)

看起来它是Multiple message processed的延续,但遗憾的是我们在您的情况下看不到<idempotent-receiver>配置。

根据您的评论,您似乎会继续使用SimpleMetadataStore或经常清理共享的(Redis / Mongo)。

您应该分享更多信息在哪里挖掘。一些日志和DEBUG调查也会很好。

<强>更新

幂等接收器完全适用于endpoint。在您的配置中,它适用于MessageChannel。这就是为什么你没有做任何正确的工作,因为MessageChannel只是忽略IdempotentReceiverInterceptor

您应为id添加<int-file:splitter>,并使用id属性中的endpoint。如果将File用作key用于幂等性,那么不应该这样做。 name听起来更好。

更新2

  

如果一个节点出现故障,并假设文件被下载(文件大小有百万条记录可能是gb)到xd节点,我会处理一半的记录和节点崩溃。当服务器出现时我想我们会处理相同的记录了吗?

行。我终于得到了你的观点!你的文件中的分割线已经存在问题。

此外,我还为<splitter>使用Idempotent Receiver,以避免来自S3的重复文件。

要修复您的用例,您应该在<splitter>output之间添加另一个端点 - <bridge>以跳过与幂等接收器重复的行。