从我的应用程序中,我需要配置需要连接到单个服务器的多个客户端连接。为此,我根据已配置的客户端数量,使用ApplicationContext Beanfactory创建可变数量的bean。以下是2个客户的代码:
//setup beans;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("pkg");
ConnectionFactory factory = new ConnectionFactory();
int clients = 2; //TODO read this value from file
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
for (int count = 1; count <= clients; count++) {
TcpNetClientConnectionFactory connectionFactory = factory.createClientConnectionFactory("127.0.0.1", 6680);
//connection factory
beanFactory.registerSingleton("connectionFactory_" + String.valueOf(count), connectionFactory);
//inbound gateway
MessageChannel input = new DirectChannel();
MessageChannel output = new DirectChannel();
TcpInboundGateway gateway = factory.createInboundGateway(connectionFactory, beanFactory, input, output, 10000, 20000);
beanFactory.registerSingleton("gateway_" + String.valueOf(count), gateway);
//message transformation and handling
IntegrationFlow flow = factory.createFlow(input);
beanFactory.registerSingleton("flow_" + String.valueOf(count), flow);
}
ctx.refresh();
//open connections
for(int count = 1; count <= clients; count++) {
TcpInboundGateway gateway = ctx.getBean("gateway_" + count, TcpInboundGateway.class);
//necessary for the client to connect
gateway.retryConnection();
}
这是我的工厂方法:
@EnableIntegration
@IntegrationComponentScan
@Configuration
public class ConnectionFactory {
public TcpNetClientConnectionFactory createClientConnectionFactory(String ip, int port) {
TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory(ip, port);
factory.setSingleUse(false);
factory.setSoTimeout(10000);
factory.setSerializer(new ByteArrayLfSerializer());
factory.setDeserializer(new ByteArrayLfSerializer());
return factory;
}
public TcpInboundGateway createInboundGateway(
AbstractConnectionFactory factory,
BeanFactory beanFactory,
MessageChannel input,
int replyTimeout,
int retryInterval) {
TcpInboundGateway gateway = new TcpInboundGateway();
gateway.setRequestChannel(input);
gateway.setConnectionFactory(factory);
gateway.setClientMode(true);
gateway.setReplyTimeout(replyTimeout);
gateway.setRetryInterval(retryInterval);
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.initialize();
gateway.setTaskScheduler(scheduler);
gateway.setBeanFactory(beanFactory);
return gateway;
}
public IntegrationFlow createFlow(MessageChannel input) {
IntegrationFlowBuilder builder = IntegrationFlows.from(input);
builder.transform(Transformers.objectToString()).handle(System.out::println);
return builder.get();
}
}
当我运行程序时,两个客户端都连接到我的服务器。但是,只要服务器将其第一个有效负载发送到每个客户端,我就会得到以下异常(每个客户端一个):
Exception sending message: GenericMessage [payload=byte[5], headers={ip_tcp_remotePort=6680, ip_connectionId=localhost:6680:33372:e26b9973-a32e-4c28-b808-1f2556576d01, ip_localInetAddress=/127.0.0.1, ip_address=127.0.0.1, id=4443ca34-fb53-a753-7603-53f6d7d82e11, ip_hostname=localhost, timestamp=1464098102462}]
org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'unknown.channel.name'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:81) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:150) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:45) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:42) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.integration.core.MessagingTemplate.sendAndReceive(MessagingTemplate.java:97) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:422) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:390) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.ip.tcp.TcpInboundGateway.doOnMessage(TcpInboundGateway.java:119) ~[spring-integration-ip-4.2.5.RELEASE.jar:na]
at org.springframework.integration.ip.tcp.TcpInboundGateway.onMessage(TcpInboundGateway.java:97) ~[spring-integration-ip-4.2.5.RELEASE.jar:na]
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.run(TcpNetConnection.java:182) ~[spring-integration-ip-4.2.5.RELEASE.jar:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_31]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_31]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_31]
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:153) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
... 14 common frames omitted
我们的想法是,数据将被读取,通过我为InboundGateway配置的通道发送到变换器,然后变换器将数据转换为字符串,之后将打印出来。
为什么框架不知道将数据放入哪个渠道?据我所知,我确实为入站网关工厂方法中的每个客户端创建了一个唯一的通道。有人可以看看我的配置,让我知道我错过了什么,因为我对这一点感到非常难过。
答案 0 :(得分:1)
没有人会从您的gateway.setReplyChannel(output);
消费消息。
至少我们看不到:
之后它将被打印出来。
在大多数情况下,如果某些Dispatcher has no subscribers
没有任何订阅者,我们会SubscribableChannel
:未配置或停止。
修改强>
忘掉我以前的表情。它适用于outbound
案例。
你的TcpInboundGateway
很好。虽然您不需要setReplyChannel()
,因为您始终可以依赖默认的内置TemporaryReplyChannel
来等待下游流的某些结果。
您的IntegrationFlow
看起来也不错。这是正确的.transform()
不向任何其他渠道发送任何内容。它只依赖于标题中的TemporaryReplyChannel
。
我认为您的问题是您没有为@EnableIntegraiton
课程指定@Configuration
:http://docs.spring.io/spring-integration/reference/html/overview.html#_configuration
编辑2
关于此事,请参阅GH issue。
因此,除了代码之外,您还需要:
beanFactory.initializeBean();
的 registerSingleton()
。因为看到最后一个JavaDocs:
* <p>The given instance is supposed to be fully initialized; the registry
* will not perform any initialization callbacks (in particular, it won't
* call InitializingBean's {@code afterPropertiesSet} method).
在ctx.refresh()
之后执行该操作,以便注册所有必需的BeanPostProcessor
,包括一个用于Spring Integration Java DSL解析的文件。
调用ctx.start()
启动所有Lifecycle
。因为常规ctx.refresh()
流程无法看到这些新手动添加的内容。
答案 1 :(得分:0)
以下是工作简化的解决方案:
<强> Beans.java 强>
package beanconfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegration;
@Configuration
@EnableIntegration
public class Beans {
//Beans can be configured here
}
<强> IntegrationTest.java 强>
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.ip.tcp.TcpInboundGateway;
import org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory;
import org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer;
import org.springframework.integration.transformer.ObjectToStringTransformer;
import org.springframework.messaging.MessageChannel;
public class IntegrationTest {
private String generateComponentName(String baseName, int instanceCount) {
return baseName + "_" + instanceCount;
}
@Test
public void integrationTest1() throws Exception {
try(AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) {
ctx.scan("beanconfig");
ctx.refresh();
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
int numberOfClients = 2; //TODO configure from file
for (int count = 0; count < numberOfClients; count++) {
//connection factory
TcpNetClientConnectionFactory connectionFactory = new TcpNetClientConnectionFactory("127.0.0.1", 6680);
connectionFactory.setSingleUse(false);
connectionFactory.setSoTimeout(10000);
connectionFactory.setSerializer(new ByteArrayLfSerializer());
connectionFactory.setDeserializer(new ByteArrayLfSerializer());
//inbound gateway
TcpInboundGateway inboundGateway = new TcpInboundGateway();
inboundGateway.setRequestChannel(new DirectChannel());
inboundGateway.setConnectionFactory(connectionFactory);
inboundGateway.setClientMode(true);
inboundGateway.setReplyTimeout(10000);
inboundGateway.setRetryInterval(20000);
//message transformation and flow
String flowName = generateComponentName("flow", count);
IntegrationFlow flow = IntegrationFlows.from(inboundGateway)
.transform(new ObjectToStringTransformer())
.handle(h -> System.out.println("Message received: " + h.getPayload()))
.get();
beanFactory.registerSingleton(flowName, flow);
beanFactory.initializeBean(flow, flowName);
}
ctx.start();
//TODO do proper validation here
Thread.sleep(10000);
}
}
}
基本上我的初步尝试有一些问题。以下是我改变它的工作原理:
1)创建AnnotationConfigApplicationContext时,必须使用配置类作为参数创建它,该参数用@EnableIntegration注释标记。如果不是,则必须通过包含此批注的上下文扫描组件。我在第一次尝试时这样做了,但是刷新太晚了,应该在ctx.scan之后直接调用。因为我的ctx.refresh()是在我的beanfactory注册之后,所以在创建集成bean时实际上没有设置@EnableIntegration。直接在ctx.scan()下面移动ctx.refresh()可以解决问题。
2)注册到上下文中的每个bean也必须由beanfactory初始化。这是为了确保运行BeanPostProcessors(这不是由registerSingleton自动完成的。)
3)然后需要调用ctx.start()来启用在ctx.refresh()之后创建的bean。