我尝试使用 org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory 创建TCP客户端。当Factory创建TCP连接时,我需要向服务器发送一些数据,就像授权一样。服务器发送了一些响应的数据(例如 salt )。在下一个请求中,需要将 salt 发送到服务器。对于Pool中的所有连接, salt 必须不同,我认为Connection必须在自己中存储 salt 。
所以,我的实施是...... Spring Beans XML:
<int-ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="12345"
interceptor-factory-chain="customInterceptorFactory"
mapper="mapper" />
<bean id="customInterceptorFactory" class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
<property name="interceptors">
<bean class="ru.example.gateway.StatefulTcpConnectionFactory" />
</property>
</bean>
<bean id="cachedClient" class="org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory">
<constructor-arg ref="client" />
<constructor-arg value="5" />
</bean>
<int:channel id="clientRequestChannel"/>
<int-ip:tcp-outbound-gateway id="clientCrLf"
connection-factory="cachedClient"
request-channel="clientRequestChannel"/>
<int:converter ref="byteArrayToStringConverter" />
<bean id="byteArrayToStringConverter" class="ru.example.gateway.ByteArrayToStringConverter">
<property name="charset" value="windows-1251" />
</bean>
<bean id="mapper" class="org.springframework.integration.ip.tcp.connection.TcpMessageMapper">
<property name="charset" value="windows-1251" />
</bean>
TCP连接拦截器:
package ru.example.gateway;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.serializer.Serializer;
import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorSupport;
import org.springframework.integration.ip.tcp.connection.TcpConnectionSupport;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
@Getter
public class StatefulTcpConnection extends TcpConnectionInterceptorSupport {
private volatile String nextSalt = "";
private final AtomicBoolean initialized = new AtomicBoolean(false);
private final CountDownLatch latch = new CountDownLatch(1);
public StatefulTcpConnection() {
}
public StatefulTcpConnection(ApplicationEventPublisher applicationEventPublisher) {
super(applicationEventPublisher);
}
@Override
public void send(Message<?> message) throws Exception {
if (!initialized.get()) {
throw new Exception("Connection not initialized");
}
Message<?> newMessage = message;
Object payload = message.getPayload();
log.debug("Send Payload({})", payload.getClass());
if (payload instanceof String) {
new GenericMessage<String>((String) payload + ";Salt=" + nextSalt + ";", message.getHeaders());
}
super.send(newMessage);
}
/**
* Invoke initialize after connection wrapped in ConnectionFactory4Interceptors
* @see org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory#initializeConnection(TcpConnectionSupport, Socket)
* @param serializer
*/
@Override
public void setSerializer(Serializer<?> serializer) {
super.setSerializer(serializer);
try {
initialize();
} catch (Exception e) {
throw new RuntimeException("Connection couldn't initialize", e);
}
}
private void initialize() throws Exception {
GenericMessage<String> loginMessage = new GenericMessage<>("Login");
super.send(loginMessage);
log.debug("Waiting initializing response");
latch.await(10, TimeUnit.SECONDS);
}
@Override
public boolean onMessage(Message<?> message) {
Object messagePayload = message.getPayload();
log.debug("onMessage Payload({})", messagePayload.getClass());
messagePayload = new String((byte[]) messagePayload);
if (messagePayload instanceof String) {
String payload = (String) messagePayload,
salt;
int saltStart = payload.indexOf(";Salt=");
if (saltStart >= 0) {
int saltEnd = payload.indexOf(";", saltStart + 1);
if (saltEnd >= 0) {
salt = payload.substring(saltStart + 6, saltEnd);
} else {
salt = payload.substring(saltStart + 6);
}
nextSalt = salt;
}
if (!initialized.get()) {
initialized.set(true);
latch.countDown();
log.debug("Initializing complete.");
}
}
return super.onMessage(message);
}
@Override
public String getConnectionId() {
return "Stateful:" + super.getConnectionId();
}
@Override
public String toString() {
return getConnectionId();
}
}
网关:
package ru.example.gateway;
import org.springframework.integration.annotation.MessagingGateway;
@MessagingGateway(name = "clientOutGateway",
defaultRequestChannel = "clientRequestChannel"
)
public interface StringGateway {
String send(String message);
}
Spring MVC的服务:
package ru.example.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.example.gateway.StringGateway;
import ru.example.service.MessageService;
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private StringGateway clientGateway;
public String getMessage(String code) {
return clientGateway.send(code);
}
}
我有一些错误。在来自 MessageServiceImpl 的第一个请求中,我获得了对#34;身份验证请求的响应&#34;并且需要响应丢失,或在日志中抛出异常或错误消息:
ERROR org.springframework.integration.ip.tcp.TcpOutboundGateway - Cannot correlate response - no pending reply for Cached:Stateful:localhost:12345:56060:6993fc83-7e69-4f18-9300-8553e6d74a4f
有人有状态连接的解决方案吗?
谢谢! p.s:抱歉我的英文
答案 0 :(得分:1)
目前没有框架提供的有状态连接管理。您可以为连接工厂编写一个包装器,将连接存储在ThreadLocal
中,但是当您完成连接时,需要一些机制来清理(并释放它)。
如果只是简单的握手,你可以使用连接拦截器;有一个测试用例(见HelloWorldInterceptor),但它有点复杂。
我认为自定义连接工厂包装器会更简单。