spring-integration-rmi适用于什么样的场景?RMI技术使两个系统通过接口的远程方法调用连接起来,当接口需要返回值时,RMI会同步地阻塞,直到收到远端系统地返回值(或者超时)。但spring-integration-rmi却并不尽然,它基于spring-integration,通讯介质是消息(Message),任何要传递地东西先封装成消息,再传递给一个Channel,然后一个rmi的gateway对接到这个channel,将消息通过RMI传递出去,获得返回值后,将返回的消息(Message)再传给一个Channel,发起方需要额外再写一个独立的outbound-channel-adapter处理这些返回消息。大家注意到没有,这个过程是异步的!不适用于发起调用后根据返回值做后续处理的场景。我有点迷惑了。Google搜索“spring-integration RPC”,第四条是来自springsource官方论坛的帖子“Spring remoting via spring integration - Am I missing some concepts?”解答了我的疑惑,现全文翻译如下:
Matt Di(楼主):
大家好:
在我接触spring integration时,我对它涉及到的概念还有很有把握的,它照例来源于spring。
我以为SI(spring integration的缩写 译者)是基于spring remoting的一个抽象层,比如它既可以处理同步也可以处理异步服务调用。更进一步的,我还假定SI支持消息(message)和服务巴士(service bus)的配置。但是在实际使用中,我遇到了预想不到的困难。
我想要的目标是:
客户端通过JMS远程调用组件异步地调用服务接口(ServiceInterface)(使用队列(Queue),因为那样的话发送和接收的过程可以是事务性的)。服务端接收服务调用(service call),将之委托给具体接口实现类去处理。
客户端暴露的接口如下所示:
public interface SpecificService {
public void foo(SpecificDto dto)
public void foo(SpecificDto dto1, SpecificDto dto1)
public SpecificDto bar(SpecificDto dto)
further methods...
}
payload类型的匹配,gateway模式,服务激活(service activator)都是SI中很美好的概念,但是它们并没有被证明是非常成功的,至少对我来说。
Spring remoting提供了JmsInvokerProxyFactoryBean和JmsInvokerServiceExporter。这些概念在SI中没有吗?
我非常感谢能给我一些关于“我如何才能将客户端暴露出来的接口映射到我的服务端实现上”的点子。
Marten Deinum(论坛高级会员):
它们并非漏掉的概念,它们就不是一回事。Spring Integrationi并不是有关远程调用的,它使用消息(message)使你的系统松耦合(我建议你读一读EIP这本书(我擦,这本书绝对是七企业集成中的龙书,Spring Integration就是这本书的具体代码实现的说 译者))Spring remoting是有关如何实现一个RPC调用的,而RPC并不是松耦合的。
所以你的误解就是你试图用错误的理解一根筋到底。你想要RPC,但是这货并不是Spring Integration构建或者设计的理念。
Matt Di(楼主):
你好Marten,
谢谢你言简意赅的解释。我同意使用象xml这种消息形式(比如通信协议为jms)来进行企业应用集成在SI中是得到完美支持的。但是尽管如此,我希望的是SI能有效地支持对不同系统间暴露地服务的松耦合。
换句话说,最好是在服务端能有一个单独的服务类带有多个方法。你所需要做的就是将进来的消息绑定到可以处理它的相关服务上,现在这个魔法由payload类型匹配完成了。但是在实际调用中,相同的payload可能会被不同的功能块调用,即不同的方法。
现在我正在研究spring rpc的更多的细节。
Mark Fisher(论坛高级会员、Spring团队):
Marten是正确的,Spring Integration的目标并不是取代RPC,对于RPC,客户端和服务端在特定的接口层面紧密的耦合在一起,包括它的方法和它的参数类型。将两个无关的系统耦合在一起并不是个好主意。只有当这两个系统在同一个开发团队和同一个发布日程的控制下它才是一个可选方案。
我们说,对于spring remoting,Spring Integration支持的最接近的就是<gateway/>(内部是一个GatewayProxyFactoryBean),它暴露了一个接口,该接口可能被客户端代码注入(非常象Spring remoting中任何一种ProxyFactoryBean)。That may *or may not* be the same interface that is implemented by the actual POJO being invoked on the other side of a <service-activtor/>(意思我懂,可就是不能通顺的翻译出 =,= 译者),如果是相同的接口,那么就和RPC非常象了,但是在之间额外添加了"channel",因此其他组件可以添加到pipeline中(transformation,routing等等)。事实上根本不需要相同的接口,因为这样可以使你不需要在相同层次上耦合在一起,并在使用相同的非侵入式地方案上有灵活性。
如果你使用JMS作为一个传输手段来做RPC,你实际上并不需要transformation、routing、filtering、等等这些概念,我会说JmsInvokerServiceExporter和JmsProxyFactoryBean已经足够了。
希望能帮助到你。
-Mark
Matt Di(楼主):
你好Mark,
谢谢你的回复,这就是我研究SI的意图。向服务发消息,transform、route最后将它们松耦合的配置到一个特性实现中。
但是<service-activator/>组件是如何将某个服务消息映射到一个能够处理它的POJO的呢?
我的意思是...gateway接口在我的理解下包含带有多个带有多个参数的方法。SI提供一种机制来处理这种场景吗?
万一这个问题太抽象我可以提供一些代码片段。
Mark Fisher(论坛高级会员、Spring团队):
Spring Integration会试图将消息映射到合适的方法(配置在XML的一个方法名字属性或者一个或者多个方法级注解上)。它试图匹配payload类型到参数类型。也许你可以使用你特定的例子创建一个测试用例来看看还有那些限制?
Matt Di(楼主):
好的,这里有一段商业用例的代码片段。目标是将几个企业应用集成到一个中心平台上。这里有一个点对点连接着中央平台和每个外部应用。一些p2p连接,通过使用jms-message-driven-listener、transformer、router等等,我可以完美的使用SI集成。其他p2p集成被证明是困难的。这里就是一例:
使用中央平台提供的服务的一些外部系统是异步的执行的。
这些外部系统有下面这些接口:
public interface Service {
public void log(Dto dto);
public void process(Dto dto);
public void replicate(Long id, Dto dto)
}
客户端相关的SI配置可以是这样的:
<!-- outbound side--> <si:channel id="outbound" /> <jee:jndi-lookup id="exampleQueue" jndi-name="jms/ExampleQueue" /> <si:gateway service-interface="com.example.service.Service" id="serviceGateway"> <si:method name="log" request-channel="outbound" /> <si:method name="process" request-channel="outbound" /> <si:method name="replicate" request-channel="outbound" /> </si:gateway> <jms:outbound-channel-adapter channel="outbound" destination-name="exampleQueue" />
中央平台包含所有的暴露接口的实现。毫不奇怪的,当检测一个通用接口的实现类时,代码看起来象这样:
public class Service implements Service {
public void log(Dto dto) {
// implementation
}
public void process(Dto dto) {
// implementation
}
public void replicate(Long id, Dto dto) {
// implementation
}
}
给特定p2p连接的inbound配置是:
<!-- Inbound side--> <jee:jndi-lookup id="exampleQueue" jndi-name="jms/ExampleQueue" /> <si:channel id="inbound" /> <jms:message-driven-channel-adapter channel="inbound" container="exampleListenerContainer" /> <si:service-activator input-channel="inbound" ref="serviceImpl" /> <beans:bean id="exampleListenerContainer" class="org.springframework.jms.listener.DefaultMes sageListenerContainer"> <beansroperty name="connectionFactory" ref="connectionFactory" /> <beansroperty name="destinationName" value="exampleQueue" /> <beansroperty name="sessionTransacted" value="true" /> </beans:bean> <beans:bean id="serviceImpl" class="com.example.service.impl.ServiceImpl" />
简便起见,这里只有一个队列,它负责逻辑任务(在这有例子中,异步地处理一个特定的服务)。
我如何才能继承服务端的配置确信每个方法如log,process和replication被一个合适的服务实现处理?
SI支持包裹多个参数(@see replication(Long id, Dto dto))到一个消息的payload里吗?
感谢任何高见
oleg.zhurakousky(高级会员、Spring团队):
一个通常被误解的概念就是通常意义上的服务(services)(非SI关联)事实上就是人们趋向将一个Java类作为一个服务。恕我直言(IMHO,有意思的缩写 译者),这是错误的。Java类是一些相互关联的服务的集合,服务呢,就是一个方法。因此在你有一个类拥有多个方法的用例下,当然拥有多个service-activator引用到同一个bean(通过'ref'属性)是一点问题也没有的,这些方法属性映射每个servcie-activator到一个特定服务。
Matt Di(楼主):
Oleg你好,
你的解释我知道了。问题是我如何才能使用多个service activator呢?当我试验我的SI例子时发现了一个奇怪的现象。
当调用log10次,毫无预料的,5次方法log,5次方法process调用,使用如下多service activator配置:
<si:service-activator input-channel="inbound" ref="serviceImpl" method="log" /> <si:service-activator input-channel="inbound" ref="serviceImpl" method="process" /> <beans:bean id="serviceImpl" class="com.example.service.impl.ServiceImpl" />
当看到payload类型匹配时, 这是一个对我来说没有预料到的行为但是是可以理解的。SI试图匹配payload类型到参数类型,就像Mark提到的。为了使配置干净,我仅仅使用了一个jms队列,处理两个系统之间的通讯。
我如何配置service提供者才能确信对于每个外部的gateway接口和它定义的方法来说,队列的pipelined消息会被某个类的合适的方法处理。
Mark Fisher(论坛高级会员、Spring团队):
你可以得到的是这里有有个默认的“round robin”负载均衡。It's actually invoking those 2 consumers each every other time a Message arrives.(Mark啊,您老的意思我懂,翻译不出,咋办? 译者)
如果你想要对单个消费者动态类型匹配,这里有两个选择:1)在<service-activator/>里准备一个method属性,确信所有的处理方法拥有相同的名字或者2)不管方法名,通过返回类型匹配所有public方法。对于fine-tuning的选择#2,假设你拥有类的控制权,你也可以添加@ServiceActivator注解到你想要将之作为后候选者的方法上。
oleg.zhurakousky(论坛高级会员、Spring团队):
然而,看着你的类,Mark所说的动态类型匹配看起来并不像是你的选择,这意味着你不得不将每个service-activator绑定到一个没有依赖的input-channel上同时将一个router方到它前面:
input-channel -> service-activator / Gateway -> request-channel -> router \ input-channel -> service-activator
router可以基于方法名字路由,这个可以在Message History中得到,你可以在Loan Broker的例子里看看如何从Message History中获取gateway的方法名字 - http://blog.springsource.com/2010/03...tation-part-1/