Spring Integration:邮件错误和HTTP网关响应

时间:2015-12-23 16:25:47

标签: email spring-integration

我的用例很简单。我想处理因系统无法访问而导致的异常,根据配置的重试策略执行重试,在满足重试阈值时发送电子邮件,并将自定义响应返回给调用者。

我面临的挑战是,我不能同时发送电子邮件并将回复返回给来电者。由于我最初使用int-mail:outbound-channel-adapter,我希望这种行为,因为这是一个单向组件:

    <int:chain input-channel="defaultErrorChannel"> 
        <int:service-activator id="mailMessageActivator" expression="@mailHandler.process(payload)" />
        <int-mail:outbound-channel-adapter mail-sender="mailSender" />
    </int:chain>   

但是,如果我在int-amqp:outbound-gateway前面引入int-mail:outbound-channel-adapter(请参阅下面的Error Handling配置),我希望能够调用int:service-activator来构造并向调用者返回一个响应。

我是否以错误的方式思考这个问题?我看到其他人有一个similar question仍未得到答复。我提到的两个配置都发送电子邮件,但总是阻止调用者在超时时没有收到响应。

以下是我配置的相关部分:

网关

<int:gateway id="customerGateway" service-interface="com.uscs.crm.integration.CustomerGateway" 
        default-request-channel="syncCustomers" default-reply-channel="replySyncCustomers" default-reply-timeout="30000">           
</int:gateway>      

<int:object-to-json-transformer input-channel="syncCustomers" output-channel="outboundRequestChannel" />    

<int-http:outbound-gateway request-channel="outboundRequestChannel" reply-channel="replySyncCustomers"
              url="http://voorhees148.uscold.com:9595/web/customerSync/createCustomer"
              http-method="POST"
              rest-template="restTemplate"
              expected-response-type="com.uscs.crm.model.CustSyncResponseVO" 
              mapped-request-headers="Authorization, HTTP_REQUEST_HEADERS">
    <int-http:request-handler-advice-chain>
        <ref bean="retryWithBackoffAdviceSession" />
    </int-http:request-handler-advice-chain>
</int-http:outbound-gateway>

错误处理

    <int:channel id="defaultErrorChannel"/>
    <int:channel id="errorResponses"/>

    <!-- 
      ExponentialBackOffPolicy.multipler is applied to wait time over each retry attempt
      with a ExponentialBackOffPolicy.maximum configured.        
    -->
    <bean id="retryWithBackoffAdviceSession" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
        <property name="retryTemplate">
            <bean class="org.springframework.retry.support.RetryTemplate">
                <property name="backOffPolicy">
                    <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
                        <property name="initialInterval" value="2000" />    
                        <property name="multiplier" value="2" />            
                        <property name="maxInterval" value="30000"/>        
                    </bean>
                </property>
                <property name="retryPolicy">
                    <bean class="org.springframework.retry.policy.SimpleRetryPolicy">
                        <property name="maxAttempts" value="3"/>
                    </bean>
                </property>
            </bean>
        </property>
        <property name="recoveryCallback">
            <bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
                <constructor-arg ref="defaultErrorChannel"/>
            </bean>
        </property>
    </bean>

    <bean id="custSyncResponseHandler" class="com.uscs.crm.integration.handler.CustSyncResponseHandler"></bean>
    <int:chain input-channel="defaultErrorChannel" output-channel="replySyncCustomers"> 
        <int:service-activator id="mailMessageActivator" expression="@mailHandler.process(payload)" />
        <int:header-enricher>
            <int:header name="ERROR_ID" expression="T(java.lang.System).currentTimeMillis()"/>
        </int:header-enricher>          
        <int-amqp:outbound-gateway
            exchange-name="error-responses-exchange"
            routing-key-expression="'error.response.'+headers.ERROR_ID"
            amqp-template="amqpTemplate" />
        <!-- Will this service-activator return a response to the caller (int:gateway) using channel `replySyncCustomers`? -->  
        <int:service-activator id="custSyncResponseActivator" expression="@custSyncResponseHandler.process(payload)" />             
    </int:chain>        

    <int-amqp:inbound-gateway queue-names="error-responses" request-channel="errorResponses"  
            connection-factory="rabbitConnectionFactory" acknowledge-mode="AUTO" />

    <int-mail:outbound-channel-adapter channel="errorResponses" mail-sender="mailSender" />

    <!-- (Outbound Channel Adapter/Gateway) rabbit exchanges, queues, and bindings used by this app -->
    <rabbit:topic-exchange name="error-responses-exchange" auto-delete="false" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="error-responses" pattern="error.response.*"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>
    <rabbit:queue name="error-responses" auto-delete="false" durable="true"/>          

解决方案 我能够在@Artem的帮助下完成此工作。以下是我所做的更改。

服务激活器实施(处理ErrorMessage

关键是返回重建Message的行,其中包含ErrorMessage的所有标题信息。

@Override
public Message<CustSyncResponseVO> process(Message<MessagingException> errorMessage) {

    MessagingException errorException = errorMessage.getPayload();

    CustSyncResponseVO custSyncResponse = new CustSyncResponseVO();
    custSyncResponse.setResponseMessage(ExceptionUtils
            .convertToString(errorMessage.getPayload()));

    return MessageBuilder.withPayload(custSyncResponse)
        .copyHeaders(errorMessage.getHeaders())
        .copyHeadersIfAbsent(errorException.getFailedMessage().getHeaders()).build();

}

服务激活器配置

使用SpEL引用#root上下文来检索ErrorMessage而不是默认值MessagingExceptionpayload)并将其传递给process <bean id="custSyncResponseHandler" class="com.uscs.crm.integration.handler.CustSyncResponseHandler" /> <int:chain id="errorGatewayResponseChain" input-channel="defaultErrorChannel" output-channel="replySyncCustomers"> <int:service-activator id="custSyncResponseActivator" expression="@custSyncResponseHandler.process(#root)" /> </int:chain> POJO上的方法。

Battery History:
          -9m49s747ms 100 64020043 status=discharging health=good plug=none temp
=317 volt=4145 +wifi +wifi_running +wake_lock +sensor brightness=light signal_st
rength=great
          -9m49s507ms 100 24020043 -wake_lock
          -9m46s968ms 100 24020043
          -9m43s749ms 100 24020043 volt=4107
          -9m38s472ms 100 240a0043 status=full plug=ac temp=318 +plugged

Per-PID Stats:
  PID 1184 wake time: +1s315ms
  PID 5096 wake time: +1s18ms
  PID 1184 wake time: +29ms
  PID 1945 wake time: +188ms
  PID 2125 wake time: +42ms
  PID 3757 wake time: +25ms
  PID 1184 wake time: +11ms
  PID 1184 wake time: +12ms
  PID 4008 wake time: +90ms
  PID 7441 wake time: +28ms

Statistics since last unplugged:
  Time on battery: 11s 275ms (1.9%) realtime, 11s 275ms (1.9%) uptime
  Total run time: 9m 49s 748ms realtime, 9m 49s 748ms uptime,
  Screen on: 0ms (0.0%), Input events: 0, Active phone call: 0ms (0.0%)
  Screen brightnesses: No activity
  Mobile total received: 0B, Total sent: 0B
  Wi-Fi total received: 0B, Total sent: 0B
  Total full wakelock time: 0ms , Total partial wakelock time: 1s 151ms
  Signal levels: great 11s 275ms (100.0%) 0x
  Signal scanning time: 0ms
  Radio types: none 11s 275ms (100.0%) 0x
  Radio data uptime when unplugged: 0 ms
  Wifi on: 11s 275ms (100.0%), Wifi running: 11s 275ms (100.0%), Bluetooth on: 0
ms (0.0%)

  Device is currently plugged into power
    Last discharge cycle start level: 100
    Last discharge cycle end level: 100
    Amount discharged while screen on: 0
    Amount discharged while screen off: 0

  1000:
    User activity: 6 other, 1 touch
    Wake lock SCREEN_FROZEN: 187ms partial (1 times) realtime
    Wake lock StartingDockService: 832ms partial (1 times) realtime
    Wake lock DockObserver: 10ms partial (1 times) realtime
    Wake lock AlarmManager: 56ms partial (1 times) realtime
    TOTAL wake: 1s 85ms partial realtime
    Sensor 21: 11s 275ms realtime (0 times)
    Sensor 33: 11s 275ms realtime (0 times)
    Proc mm-pp-daemon:
      CPU: 0ms usr + 180ms krn ; 0ms fg
    Proc charger_monitor:
      CPU: 0ms usr + 20ms krn ; 0ms fg
    Proc com.honeywell.licenseservice:
      CPU: 0ms usr + 10ms krn ; 0ms fg
    Proc com.qualcomm.qualcommsettings:
      CPU: 0ms usr + 10ms krn ; 0ms fg
    Proc surfaceflinger:
      CPU: 70ms usr + 100ms krn ; 0ms fg
    Proc com.android.settings:
      CPU: 10ms usr + 0ms krn ; 0ms fg
    Proc system_server:
      CPU: 540ms usr + 370ms krn ; 0ms fg
    Proc com.hsm.android.counters:
      CPU: 20ms usr + 0ms krn ; 0ms fg
    Proc com.honeywell.tools.honsystemservice:
      CPU: 10ms usr + 0ms krn ; 0ms fg
    Apk com.android.settings:
      Service com.android.settings.bluetooth.DockService:
        Created for: 1s 8ms uptime
        Starts: 1, launches: 1

1 个答案:

答案 0 :(得分:1)

我认为没有理由在最后发送电子邮件时引入AMQP中间件复杂性。

只有<publish-subscribe-channel id="defaultErrorChannel">才能将端点作为订阅者。

第一个是单向电子邮件发送<chain>,第二个是custSyncResponseActivator来回复您<int-http:outbound-gateway>的内容。

您可以在Spring Integration Reference Manual中找到有关此问题的更多信息。