Spring integration 调用高并发性http调用时丢失消息

Spring integration 调用高并发性http调用时丢失消息,spring-integration,spring-xd,Spring Integration,Spring Xd,嗨,我有如下的流定义。我从s3中提取文件,逐行分割,调用http客户端并放入命名通道。我的传输是rabbit,预取是10,http的并发性是100,在3个容器和1个管理员上运行 stream aws-s3|custom processor| custom-http-client --url1=https://test1.com --url2=https://test1.com --filterAttribute=messageAttribute --httpMethod=POST --nonRe

嗨,我有如下的流定义。我从s3中提取文件,逐行分割,调用http客户端并放入命名通道。我的传输是rabbit,预取是10,http的并发性是100,在3个容器和1个管理员上运行

stream aws-s3|custom processor| custom-http-client --url1=https://test1.com --url2=https://test1.com --filterAttribute=messageAttribute --httpMethod=POST --nonRetryErrorCodes=400,401,404,500 --charset=UTF-8 --replyTimeout=30000 --mapHeaders=Api-Key,Content-Type --requestTimeOut=30000  |processor> queue:testQueue
我的http配置如下所示,使用apache http客户端进行连接池和多线程,我将所有错误(如套接字超时)放回DLQ并重试。所有非重试错误50x我将传递到下一个模块并写入错误队列。但在调用外部rest API后,我丢失了消息。我正在发送大约220条消息k条消息有时我收到200k条消息有时我收到所有220k条消息,有时210k条消息是随机的。不确定我是否做错了什么。我试图增加请求超时套接字超时。直到我的处理器在HTTP之前我收到了所有消息,但在HTTP客户端之后,我在命名通道队列中看到的消息较少,在错误队列中没有看到任何消息。但我敢肯定,在调用http客户端后,消息会丢失。这种情况发生在数据负载很高的情况下,比如百万和200k+记录,而负载较低的情况下,比如500到1000条记录,我看不到这个问题

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

    <!--    <context:property-placeholder location="${xd.module.config.location}\processor\${xd.module.name}\batch-http.properties"
         ignore-resource-not-found="true" local-override="true"/> -->

    <context:property-placeholder />

   <!-- logger changes start -->
    <channel-interceptor pattern="*" order="3">
        <beans:bean class="org.springframework.integration.channel.interceptor.WireTap">
            <beans:constructor-arg ref="loggingChannel" />
        </beans:bean>
    </channel-interceptor>

    <logging-channel-adapter id="loggingChannel" log-full-message="true" level="ERROR"/>

<!-- logger changes end -->


    <header-filter input-channel="input"
                   output-channel="inputX" header-names="x-death"/>

    <service-activator input-channel="inputX" ref="gw" />

    <gateway id="gw" default-request-channel="toHttp" default-reply-timeout="0"  error-channel="errors" />

    <beans:bean id="inputfields" class="test.HTTPInputProperties">
        <beans:property name="nonRetryErrorCodes" value="${nonRetryErrorCodes}"/>
    </beans:bean>
    <beans:bean id="responseInterceptor" class="test.ResponseInterceptor">
        <beans:property name="inputProperties" ref="inputfields" />
    </beans:bean>

    <chain input-channel="errors" output-channel="output">
        <!-- examine payload.cause (http status code etc) and decide whether
             to throw an exception or return the status code for sending to output -->
        <header-filter header-names="replyChannel, errorChannel" />
        <transformer ref="responseInterceptor"  />
    </chain>


    <int-http:outbound-gateway id='batch-http'  header-mapper="headerMapper"
                               request-channel='toHttp'
                               rest-template="batchRestTemplate"
                               url-expression="payload.contains('${filterAttribute}') ? '${url1}' : '${url2}'"  http-method="${httpMethod}"
                               expected-response-type='java.lang.String' charset='${charset}'
                               reply-timeout='${replyTimeout}' reply-channel='output'>
    </int-http:outbound-gateway>

    <beans:bean  id="batchHTTPConverter" class="org.springframework.http.converter.StringHttpMessageConverter" >
        <beans:constructor-arg index="0"  value="${charset}"/>
        <beans:property name="supportedMediaTypes" value = "application/json;UTF-8" />

    </beans:bean>

     <beans:bean  id="batchRestTemplate" class="testBatchRestTemplate" >
       <beans:constructor-arg name="requestTimeOut" value="${requestTimeOut}"/>
        <beans:constructor-arg name="maxConnectionPerRoute" value="${maxConnectionPerRoute}"/>
        <beans:constructor-arg name="totalMaxConnections" ref="${totalMaxConnections}"/>
</beans:bean>





<beans:bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper"
            factory-method="outboundMapper">
<beans:property name="outboundHeaderNames" value="${mapHeaders}"/>
<beans:property name="userDefinedHeaderPrefix" value=""/>
</beans:bean>

<channel id="output" />
<channel id="input" />
<channel id="inputX" />
<channel id="toHttp" />

        </beans:beans>



public class BatchRestTemplate  extends RestTemplate{

    private static final Logger LOGGER = LoggerFactory
                .getLogger(BatchRestTemplate.class);

    private  static Integer requestTimeOut;

    private  static Integer totalMaxConnections;
    private  static Integer maxConnectionPerRoute;






    public BatchRestTemplate(Integer requestTimeOut,Integer totalMaxConnections,Integer maxConnectionPerRoute) throws NoSuchAlgorithmException  {
          super(createBatchHttpRequestFactory());
        List<HttpMessageConverter<?>> messageConverters= new ArrayList<HttpMessageConverter<?>>();
        messageConverters.addAll(getMessageConverters());
        messageConverters.add(0,new StringHttpMessageConverter(Charset.forName("UTF-8")));
        setMessageConverters(messageConverters);

    }

        private static ClientHttpRequestFactory createBatchHttpRequestFactory() throws NoSuchAlgorithmException  {

            CloseableHttpClient httpClient;
            HttpComponentsClientHttpRequestFactory httpRequestFactory;

            SSLConnectionSocketFactory socketFactory;

                socketFactory = new SSLConnectionSocketFactory(
                        SSLContext.getDefault(),
                        new String[] {"TLSv1"},
                        null,
                        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", socketFactory)
                    .build();
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            cm.setMaxTotal(250);
            cm.setDefaultMaxPerRoute(100);
            cm.closeExpiredConnections();



            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000)
                    .setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();



            httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(cm).build();


            httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
            return httpRequestFactory; 
    }


    }

公共类BatchRestTemplate扩展了RestTemplate{
专用静态最终记录器记录器=记录器工厂
.getLogger(BatchRestTemplate.class);
私有静态整数请求超时;
私有静态整数totalMaxConnections;
私有静态整数maxConnectionPerRoute;
公共BatchRestTemplate(Integer requestTimeOut、Integer totalMaxConnections、Integer maxConnectionPerRoute)抛出NoSuchAlgorithmException{
super(createBatchHttpRequestFactory());
列表>();
addAll(getMessageConverters());
添加(0,新的StringHttpMessageConverter(Charset.forName(“UTF-8”)));
设置消息转换器(messageConverters);
}
私有静态ClientHttpRequestFactory createBatchHttpRequestFactory()抛出NoSuchAlgorithmException{
可关闭的httpClient httpClient;
Http组件客户端Http请求工厂Http请求工厂;
SSLConnectionSocketFactory socketFactory;
socketFactory=新SSLConnectionSocketFactory(
SSLContext.getDefault(),
新字符串[]{“TLSv1”},
无效的
SSLConnectionSocketFactory。允许\u所有\u主机名\u验证程序);
注册表socketFactoryRegistry=RegistryBuilder.create()
.register(“http”,PlainConnectionSocketFactory.getSocketFactory())
.注册(“https”,socketFactory)
.build();
PoolightPClientConnectionManager cm=新的PoolightPClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(250);
cm.setDefaultMaxPerRoute(100);
cm.closeExpiredConnections();
RequestConfig RequestConfig=RequestConfig.custom().setConnectTimeout(30000)
.setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpClient=HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(cm.build();
httpRequestFactory=新的HttpComponents客户端httpRequestFactory(httpClient);
返回httpRequestFactory;
}
}
响应拦截器

public class ResponseInterceptor {

    private HTTPInputProperties inputProperties;
    private static final Logger LOGGER = LoggerFactory.getLogger(ResponseInterceptor.class);

    /**
     * Intercepts the errorMessage from the API response and sends appropriate
     * information to the Output channel.
     * 
     * @param errorMessage
     * @return Message
     */
    public Message<String> transform(Message<MessagingException> errorMessage) {

        LOGGER.error("Inside Response Interceptor !");
        Message<String> responseMessage = null;
        try {
            if (null != errorMessage && null != errorMessage.getPayload()
                    && null != errorMessage.getPayload().getCause()) {
                LOGGER.error("Cause is - " + errorMessage.getPayload().getCause().getMessage());
                if (errorMessage.getPayload().getCause() instanceof HttpClientErrorException) {

                    HttpClientErrorException clientError = (HttpClientErrorException) errorMessage.getPayload()
                            .getCause();
                    LOGGER.error("Error in ResponseInceptor", clientError);
                    List<String> errorCodeList = getErrorCodes(inputProperties.getNonRetryErrorCodes());
                    // intercept Only those errors that are defined as
                    // nonRetryErrorCodes options in stream definition
                    if (null != clientError.getStatusCode()
                            && errorCodeList.contains(clientError.getStatusCode().toString())) {

                        LOGGER.error("Error in Response Body", clientError.getResponseBodyAsString());
                        LOGGER.debug("Non retry message found. Sending to output channel without retrying");

                        responseMessage = MessageBuilder.withPayload((null == clientError.getResponseBodyAsString() || clientError.getResponseBodyAsString().isEmpty()) 
                                ? getDefaultPayload(clientError.getStatusCode().toString()) : clientError.getResponseBodyAsString())
                                .setHeader(BatchHttpClientConstants.HTTP_STATUS, clientError.getStatusCode().toString())
                                .setHeader(BatchHttpClientConstants.REQUEST_OBJECT,
                                        getFailedMessagePayload(errorMessage))
                                .copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
                                .setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();

                    } else {
                        LOGGER.debug("Status code from API is not present in the nonRetryCodes");
                    }
                } else if (errorMessage.getPayload().getCause() instanceof HttpServerErrorException) {

                    LOGGER.error("Error is Instance of HttpServerErrorException");
                    HttpServerErrorException serverError = (HttpServerErrorException) errorMessage.getPayload()
                            .getCause();

                    responseMessage = MessageBuilder
                            .withPayload((null == serverError.getResponseBodyAsString()
                            || serverError.getResponseBodyAsString().isEmpty())
                            ? getDefaultPayload(serverError.getStatusCode().toString())
                            : serverError.getResponseBodyAsString())
                            .setHeader(BatchHttpClientConstants.HTTP_STATUS, serverError.getStatusCode().toString())
                            .setHeader(BatchHttpClientConstants.REQUEST_OBJECT, getFailedMessagePayload(errorMessage))
                            .copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
                            .setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();


                }

            }
        } catch (Exception exception) {
            LOGGER.error("Exception occured while transforming errorResponse", exception);
        }

        // returning null will send the message back to previous module
        return responseMessage;
    }

    private String getDefaultPayload(String httpStatusCode) {

        JSONObject jsonResponse = new JSONObject();
        if (BatchHttpClientConstants.INTERNAL_SERVER_ERROR.equalsIgnoreCase(httpStatusCode)) {
            jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.INTERNAL_SERVER_ERROR_SUBCODE);
            jsonResponse.put(BatchHttpClientConstants.TEXT, "Internal Server Error");
        } else if (BatchHttpClientConstants.RESOURCE_NOT_FOUND.equalsIgnoreCase(httpStatusCode)) {
            jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.RESOURCE_NOT_FOUND_SUBCODE);
            jsonResponse.put(BatchHttpClientConstants.TEXT, "Empty Response From the API");
        }else{
            jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.GENERIC_ERROR_SUBCODE);
            jsonResponse.put(BatchHttpClientConstants.TEXT, "Generic Error Occured.");
        }

        return jsonResponse.toString();

    }

    /**
     * Get Individual error codes using delimiter
     * 
     * @param nonRetryErrorCodes
     * @return List of Error Codes as string
     */
    private List<String> getErrorCodes(String nonRetryErrorCodes) {

        List<String> errorCodeList = new ArrayList<String>();
        StringTokenizer st = new StringTokenizer(nonRetryErrorCodes, BatchHttpClientConstants.DELIMITER);
        while (st.hasMoreElements()) {
            errorCodeList.add(st.nextToken());
        }
        return errorCodeList;
    }

    /**
     * returns failed Message Payload
     * 
     * @param errorMessage
     * @return String
     * @throws UnsupportedEncodingException
     */
    private byte[] getFailedMessagePayload(Message<MessagingException> errorMessage)
            throws UnsupportedEncodingException {

        if (null != errorMessage.getPayload().getFailedMessage()
                && null != errorMessage.getPayload().getFailedMessage().getPayload()) {
            return errorMessage.getPayload().getFailedMessage().getPayload().toString()
                    .getBytes(BatchHttpClientConstants.UTF_8);
        }

        return "".getBytes(BatchHttpClientConstants.UTF_8);
    }

    public HTTPInputProperties getInputProperties() {
        return inputProperties;
    }

    public void setInputProperties(HTTPInputProperties inputProperties) {
        this.inputProperties = inputProperties;
    }

}
公共类响应interceptor{
私有HTTPInputProperties-inputProperties;
私有静态最终记录器Logger=LoggerFactory.getLogger(ResponseInterceptor.class);
/**
*截取API响应中的errorMessage并发送适当的
*将信息发送到输出通道。
* 
*@param errorMessage
*@返回消息
*/
公共消息转换(消息错误消息){
错误(“内部响应拦截器!”);
消息响应消息=null;
试一试{
if(null!=errorMessage&&null!=errorMessage.getPayload()
&&null!=errorMessage.getPayload().getCause()){
LOGGER.error(“原因是-”+errorMessage.getPayload().getCause().getMessage());
if(HttpClientErrorException的errorMessage.getPayload().getCause()实例){
HttpClientErrorException clientError=(HttpClientErrorException)errorMessage.getPayload()
.getCause();
LOGGER.error(“响应接收器错误”,clientError);
List errorCodeList=getErrorCodes(inputProperties.getNonRetryErrorCodes());
//仅截取定义为
//流定义中的非TRYERRORCODES选项
if(null!=clientError.getStatusCode()
&&errorCodeList.contains(clientError.getStatusCode().toString()){
LOGGER.error(“响应正文中的错误”,clientError.getResponseBodyAsString());
debug(“找到非重试消息。不重试发送到输出通道”);
responseMessage=MessageBuilder.withPayload((null==clientError.getResponseBodyAsString()| | clientError.getResponseBodyAsString().isEmpty())
?getDefaultPayload(clientError.getStatusCode().toString()):clientError.getResponseBodyAsString())
.setHeader(BatchHttpClientConstants.HTTP_状态,clientError.getStatusCode().toString())
.setHeader(BatchHttpClientConstants.REQUEST_对象,
getFailedMessagePayload(errorMessage))