We are using Spring integration 4.1.3.
Implemented the client using tcp-outbound-gateway.
A tcp rset packet was received from the server during the request and an exception occurred.
What is the reason?
Thank you.
// interface
public interface TcpSendGateway {
public byte[] send(String text);
}
// send
byte[] response = sendGateway.send(request);
<int:gateway id="gw"
service-interface="com.mainpay.service.TcpSendGateway"
default-request-channel="input"
default-reply-channel="reply"/>
<int-tcp:tcp-connection-factory id="client"
type="client"
host="#{prop['app.cultureland.host']}"
port="#{prop['app.cultureland.port']}"
so-timeout="10000"
single-use="false"
so-keep-alive="true"
/>
<int:channel id="input" />
<int-tcp:tcp-outbound-gateway id="outGateway"
request-channel="input"
reply-channel="reply"
connection-factory="client"
request-timeout="10000"
reply-timeout="10000"
/>
<int:channel id="reply" datatype="java.lang.String" />
ERROR LOG
▶ 17.09.29 17:07:37 [pool-2-thread-2] ERROR o.s.i.i.t.c.TcpNetConnection - Read exception 211.59.10.133:7611:51503:d2ec0199-fd15-49c0-bd99-0d864eb2145b SocketException:Connection reset
▶ 17.09.29 17:07:39 [http-nio-19900-exec-5] ERROR o.s.i.ip.tcp.TcpOutboundGateway - Tcp Gateway exception
org.springframework.messaging.MessagingException: Exception while awaiting reply; nested exception is java.net.SocketException: Connection reset
at org.springframework.integration.ip.tcp.TcpOutboundGateway$AsyncReply.getReply(TcpOutboundGateway.java:288)
wireshark log
enter image description here
I think you should be sure that your client and server are agreed with the (de)serialization protocol. See Reference Manual for more info:
TCP is a streaming protocol; this means that some structure has to be provided to data transported over TCP, so the receiver can demarcate the data into discrete messages. Connection factories are configured to use (de)serializers to convert between the message payload and the bits that are sent over TCP.
The default one is the ByteArrayCrLfSerializer which deals with the \r\n message delimiter.
So, your client may receive the package but since it doesn't meet the proper delimiter it fails waiting for the reply.
java.net.SocketException: Connection reset
Means the server forced the socket closed (RST) for some reason. You need to look at the server logs.
Perhaps it's not expecting the default wire format (text terminated by CRLF - 0x0d0a).
Related
We are using Http Client 4.5.x with Spring-Ws and make requests with the webServiceTemplate.marshalSendAndReceive(requestObject) method. We would like to have a reliable connection timeout value, but are currently running into the issue described here in section 8 (DNS round robin) where multiple ip addresses are tried and so the timeout is unpredictable. Are there simple ways to set up a hard time-out after a certain time using just the Spring-ws and Http Client library, or is setting up some sort of custom timeout necessary?
Case: Connection timeout set to 1 second (actual timeout for the method is 4 seconds -- is it possible to set a method timeout of 1 second using the Spring/Http client library?)
Application logs (Http Client logs set to DEBUG):
16:45:02 (org.apache.http.impl.execchain.MainClientExec) Opening connection {}->http://salesforce.com:448
16:45:02 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.149.26:448
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.149.26:448 timed out. Connection will be retried using another IP address
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.145.26:448
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.145.26:448 timed out. Connection will be retried using another IP address
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.144.26:448
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.144.26:448 timed out. Connection will be retried using another IP address
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.148.26:448
16:45:06 (org.apache.http.impl.conn.DefaultManagedHttpClientConnection) http-outgoing-0: Shutdown connection
Http client bean:
<bean id="httpClientBean" class="org.apache.http.client.HttpClient" factory-bean="httpClientFactory" factory-method="getHttpClient" />
Http Factory code (connection timeout value set via Spring dependency injection):
public class HttpFactory {
private int connectionTimeout;
public HttpFactory(int connectionTimeout, ...) {
this.connectionTimeout = connectionTimeout;
...
}
...
public HttpClient getHttpClient() {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
...
RequestConfig.Builder configBuilder = RequestConfig.custom();
configBuilder.setConnectTimeout(this.connectionTimeout);
clientBuilder.setDefaultRequestConfig(configBuilder.build());
...
return clientBuilder.build();
}
}
Web service template bean:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
...
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<constructor-arg index="0">
<ref bean="httpClientBean" />
</constructor-arg>
</bean>
</property>
</bean>
Service code (we want this method call to take X seconds, not 2x or 3x seconds):
// we want this method call to take ~1 second, not ~4 seconds (i.e. similar to the connection timeout value, not a multiplier)
Object obj = webServiceTemplate.marshalSendAndReceive(requestDocument);
There are two options
Building a custom ClientConnectionOperator
Building a custom DnsResolver. This option is much simpler.
CloseableHttpClient client = HttpClients.custom()
.setDnsResolver(host -> new InetAddress[] { InetAddress.getByName(host) })
.build();
I have an issue with one of my AMQ producers, I have networks issues with it, consequently it fails to send messages to the Q.
However, the server opens a connections to the Q machine and maintain them intact. The number of connections raises, the Q machine is suffocated and cannot serve the rest of its producers and consumers.
Here is the exception I catch in my "bad" producer:
Caused by: javax.jms.JMSException: Wire format negotiation timeout: peer did not send his wire format.
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:72)
at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1413)
at org.apache.activemq.ActiveMQConnection.ensureConnectionInfoSent(ActiveMQConnection.java:1478)
at org.apache.activemq.ActiveMQConnection.start(ActiveMQConnection.java:527)
at com.queue.service.ActiveMQ.ActiveMQWriter.initSession(ActiveMQWriter.java:163)
... 4 more
Caused by: java.io.IOException: Wire format negotiation timeout: peer did not send his wire format.
at org.apache.activemq.transport.WireFormatNegotiator.oneway(WireFormatNegotiator.java:99)
at org.apache.activemq.transport.MutexTransport.oneway(MutexTransport.java:68)
at org.apache.activemq.transport.ResponseCorrelator.asyncRequest(ResponseCorrelator.java:81)
at org.apache.activemq.transport.ResponseCorrelator.request(ResponseCorrelator.java:86)
at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1388)
... 7 more
Running netstat command on my "bad" producer gives me State ESTABLISHED for every failed request.
Running netstat command on my Q server gives me State ESTABLISHED from the "bad" producer, for every failed request.
Question: what parameter I need to modify in the producer and/or Q in order to "break" those connections after a configurable time?
The best solution is to use failover transport, so that your clients can try connecting again if the first attempt fails.
You can also try extending wire format negotiation period (default 10 sec).
You can do that by using wireFormat.maxInactivityDurationInitalDelay property on the connection URL in your client to increase to 30 sec timeout
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("failover:(tcp://localhost:61616?wireFormat.maxInactivityDurationInitalDelay=30000)");
http://activemq.apache.org/javaxjmsjmsexception-wire-format-negociation-timeout-peer-did-not-send-his-wire-format.html
when i'm sending an email to a false domain, say to test#rstjhajh.com, i'm receiving and email to the sender address, with message body:
The following message to <test#rstjhajh.com> was undeliverable.
The reason for the problem:
5.1.2 - Bad destination host 'DNS Hard Error looking up rstjhajh.com (MX): NXDomain'
and an attachment
Reporting-MTA: dns; ironport2-out.teksavvy.com
Final-Recipient: rfc822;test#rstjhajh.com
Action: failed
Status: 5.0.0 (permanent failure)
Diagnostic-Code: smtp; 5.1.2 - Bad destination host 'DNS Hard Error looking up rstjhajh.com (MX): NXDomain' (delivery attempts: 0)
Transport.send() is not throwing any exceptions.
How is that possible??? a naive Q. but burned my time & there it is.
TIA.
//============
EDIT:
rstjhajh.com is not a registered DNS server. There's no email server to accept and hand-shake on it - when i look it up on DNS, i'm getting a NamingException. to whose "door" is Transport.send() delivering it??
This happens because mail is an asynchronous protocol. Transport.send() throws an exception if the e-mail can not be sent to the mail server or if that mail server rejects it (unknown host for example).
What is happening here is that the e-mail is correctly sent to (and accepted by) the mail server, but that server can not deliver it because the e-mail address does not exist.
You can not rely on Transport.send() throwing exceptions to determine if the e-mail you sent was correctly delivered. The only way is to check the address that was used to send it for undelivered messages.
The design of my current stomp client process is as follows:
Open stomp connection (sending CONNECT frame)
Subscribe to a feed (send a SUBSCRIBE frame)
Do a loop to continually receive feed:
while (true) {
connection.begin("txt1");
StompFrame message = connection.receive();
System.out.println("message get header"+message.toString());
LOG.info(message.getBody());
connection.ack(message, "txt1");
connection.commit("txt1");
}
My problem with this process is that I get
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)...
and I think the cause of this is mostly because the feed I am subscribed to gives information slower on certain times (as I normally get this error when the weekend comes, holidays or evenings).
I have been reading up on this here and I think this would help with my problem. However, I'm not so sure how to incorporate it with the current layout of my stomp client. Would I have to send a CONNECT header within Step 3?
I am currently using activemq to create my stomp client if that helps.
In the stomp spec we have:
Regarding the heart-beats themselves, any new data received over the
network connection is an indication that the remote end is alive. In a
given direction, if heart-beats are expected every milliseconds:
the sender MUST send new data over the network connection at least every milliseconds
if the sender has no real STOMP frame to send, it MUST send a single newline byte (0x0A)
if, inside a time window of at least milliseconds, the receiver did not receive any new data, it CAN consider the
connection as dead
because of timing inaccuracies, the receiver SHOULD be tolerant and take into account an error margin
Would that mean my client would need to send a newline bye every n seconds?
The stomp server you are connected to has timed out your connection due to innactivity.
Providing the server supports Stomp version 1.1 or newer, the easiest solution for your client is to include a heart-beat instruction in the header of your CONNECT, such as "0,10000". This tells the server that you cannot send heart-beats, but you want it to send one every 10 seconds. This way you don't need to implement them, and the server will keep the connection active by sending them to you.
Of course the server will have its own requirements of the client. In your comment it responds to your request with "1000,0". This indicates that it will send a heart-beat every 1000 millisecs, and it expects you to send one every 0 millisecs, 0 indicating none at all. So your job will be minimal.
I'm trying to configure Spring on the server side to open a socket, listen for connections and take the incoming stream (converting to String).
But I cannot find any working examples on how to just create such an incoming tcp connection. This is what I have (taken from an incomplete example):
<int-ip:tcp-connection-factory id="cfServer"
type="server"
port="8080"
using-nio="true"
single-use="true" />
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
channel="loop"
connection-factory="server"/>
<int:channel id="loop"/>
Well, but how do I continue? How can I acutally bind these adapters to a backing class? And what do these classes have to look like?
Subscribe something to the loop channel, e.g. a <service-activator/>.
See the tcp-client-server sample