spring 远路接口访问及集群方案
Spring提供类用于集成各种远程访问技术。这种对远程访问的支持可以降低你在用POJO实现支持远程访问业务时的开发难度。目前,Spring 提供对下面四种远程访问技术的支持:
    远程方法调用(RMI)。通过使用RmiProxyFactoryBean和RmiServiceExporter,Spring 支持传统的RMI(使用java.rmi.Remote interfaces 和 java.rmi.RemoteException)和通过RMI调用器(可以使用任何Java接口)的透明远程调用。
    Spring的HTTP调用器。Spring提供一种特殊的远程调用策略支持任何Java接口(象RMI调用器一样),它允许Java序列化能够通过HTTP传送。对应的支持类是HttpInvokerProxyFactoryBean和HttpInvokerServiceExporter。
    Hessian。通过使用HessianProxyFactoryBean和HessianServiceExporter,你可以使用Caucho提供的轻量级基于HTTP的二进制协议透明地提供你的业务。
    Burlap。Burlap是基于XML的,它可以完全代替 Hessian。Spring提供的支持类有BurlapProxyFactoryBean和BurlapServiceExporter。
    JAX RPC (TODO).

当讨论Spring对远程访问的支持时,我们将使用下面的域模型和对应的业务:

// Account domain object 

 public class Account implements Serializable{ 

   private String name; 


   public String getName(); 

   public void setName(String name) { 

     this.name = name; 

   } 

 } 

 // Account service 

 public interface AccountService { 

   public void insertAccount(Account acc); 

   public List getAccounts(String name); 

 } 

 public class AccountServiceImpl implements AccountService { 

   public void insertAccount(Account acc) { 

     // do something 

   } 

    public List getAccounts(String name) { 

     // do something 

   } 

 }


 

我们先演示使用RMI向远程客户提供业务,并且会谈到使用RMI的缺点。然后我们将继续演示一个Hessian的例子。
使用RMI提供业务
使用Spring的RMI支持,你可以透明地通过RMI提供你的业务。在配置好Spring的RMI支持后,你会看到一个和远程EJB类似的配置,除了没有对安全上下文传递和远程事务传递的标准支持。当使用RMI调用器时,Spring对这些额外的调用上下文提供捕获,所以你可以插入你的安全框架或安全信任逻辑。
使用RmiServiceExporter提供业务
使用RmiServiceExporter,我们可以将AccountServer对象作为 RMI对象输出接口。这个接口可以使用RmiProxyFactoryBean访问,或使用简单 RMI把该接口当作传统RMI业务来访问。RmiServiceExporter支持通过RMI调用器提供任何非RMI业务。

当然,我们首先得在Spring的BeanFactory中设置我们的业务:

<bean id="accountService" class="example.AccountServiceImpl"> 

     <!-- any additional properties, maybe a DAO? --> 

 </bean>


接下来,我们使用RmiServiceExporter提供我们的业务:

<bean class="org.springframework.remoting.rmi.RmiServiceExporter"> 

     <!-- does not necessarily have to be the same name as the bean to be exported --> 

     <property name="serviceName"><value>AccountService</value></property> 

     <property name="service"><ref bean="accountService"/></property> 

     <property name="serviceInterface"><value>example.AccountService</value></property> 

     <!-- defaults to 1099 --> 

     <property name="registryPort"><value>1199</value></property> 

 </bean>


正如你看到的,我们更换了RMI注册的端口。通常,你的应用服务器会维护RMI注册,我们最好不要干扰它。业务名被用来绑定业务。所以现在,业务就绑定在rmi://HOST:1199/AccountService上。我们将在客户端使用URL 来连接业务。
注意:我们漏了一个属性,就是servicePort属性,它缺省值为0。这个意味着该业务使用匿名端口通讯。当然你也可以指定一个端口。
客户端连接业务
我们的客户端是一个使用AccountService管理账户的简单对象:

public class SimpleObject { 

   private AccountService accountService; 

   public void setAccountService(AccountService accountService) { 

     this.accountService = accountService; 

   } 

 }


为了在客户端连接业务,我们建立另一个bean工厂,它包含这个简单对象和业务连接的配置信息:

<bean class="example.SimpleObject"> 

     <property name="accountService"><ref bean="accountService"/></bean> 

 </bean> 


 <bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> 

     <property name="serviceUrl"><value>rmi://HOST:1199/AccountService</value></property> 

     <property name="serviceInterface"><value>example.AccountService</value></property> 

 </bean>


这就是我们在客户端访问远程账户业务所需要做的。Spring透明地创建一个调用器,通过RmiServiceExporter远程提供账户业务。在客户端,我们使用RmiProxyFactoryBean来使用该业务。

使用Hessian或Burlap通过HTTP远程调用业务
Hessian提供了一个基于HTTP的二进制远程协议。它由Caucho创建,更多有关Hessian的信息可以访问 http://www.caucho.com。
为Hessian建立DispatcherServlet
Hessian使用一个特定的servlet来通过HTTP通讯。使用Spring的DispatcherServlet概念,你可以很容易地创建这样的servlet来提供你的业务。首先我们必须在你的应用中创建一个新的servlet(下面来自web.xml):

<servlet> 

     <servlet-name>remote</servlet-name> 

     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 

     <load-on-startup>1</load-on-startup> 

 </servlet>


你可能熟悉Spring的DispatcherServlet概念,如果是的话,你得在WEB-INF目录下建立一个应用上下文,remote-servlet.xml 。这个应用上下文会在下一节中使用。
使用HessianServiceExporter提供你的bean
在这个新的应用上下文remote-servlet.xml中,我们将创建一个 HessianServiceExporter来输出你的业务:

<bean id="accountService" class="example.AccountServiceImpl"> 

     <!-- any additional properties, maybe a DAO? --> 

 </bean> 

 <bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter"> 

     <property name="service"><ref bean="accountService"/></property> 

     <property name="serviceInterface"> 

         <value>example.AccountService</value> 

     </property> 

 </bean>


现在我们准备在客户端连接这个业务。我们使用BeanNameUrlHandlerMapping,就不需要指定处理器映射将请求(url)映射到业务上,因此业务提供在http://HOST:8080/AccountService上。
客户端连接业务
我们在客户端使用HessianProxyFactoryBean来连接业务。和RMI例子中的原则一样。我们将创建一个单独的bean工厂或应用上下文,在SimpleObject使用AccountService来管理账户的地方将会提到下列 bean:

<bean class="example.SimpleObject"> 

     <property name="accountService"><ref bean="accountService"/></property> 

 </bean> 

 <bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> 

     <property name="serviceUrl"><value>http://remotehost:8080/AccountService</value></property> 

     <property name="ServiceInterface"><value>example.AccountService</value></property> 

 </bean>

       
使用Burlap
我们不在这里讨论Burlap,它只不过是Hessian的基于XML实现。因为它和上面的Hessian的例子以相同的方式配置。只要你把Hessian替换成Burlap就可以了。
在通过Hessian或Burlap输出的业务中应用HTTP基本认证
Hessian和Burlap的优点之一就是我们能很容易地应用HTTP认证,因为两者都是基于HTTP的协议。例如,普通的HTTP服务器安全机制可以很容易地通过使用web.xml安全功能来应用。通常,你不会为每个用户都建立不同的安全信任,而是在Hessian/Burlap的ProxyFactoryBean中定义可共享的信任(和JDBC DataSource相类似)。

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> 

     <property name="interceptors"> 

         <list> 

             <ref bean="authorizationInterceptor"/> 

         </list> 

     </property> 

 </bean> 


 <bean id="authorizationInterceptor" 

     class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor"> 

     <property name="authorizedRoles"> 

         <list> 

             <value>administrator</value> 

             <value>operator</value> 

         </list> 

     </property>     

 </bean>

            
这个例子中,我们用到了BeanNameUrlHandlerMapping,并设置了一个拦截器,它只允许管理员和操作员调用这个应用上下文中的 bean。
注意:这个例子并没有演示灵活的安全设施。如果考虑更灵活的安全设置,可以去看看 Acegi Security System,http://acegisecurity.sourceforge.net。

使用HTTP调用器输出业务
和Burlap和Hessian使用自身序列化机制的轻量级协议相反,Spring HTTP调用器使用标准Java序列化机制来通过HTTP输出业务。如果你的参数或返回值是复杂类型,并且不能通过Hessian和Burlap的序列化机制序列化,HTTP调用器就很有优势(参阅下一节,选择远程技术时的考虑)。
实际上,Spring可以使用J2SE提供的标准功能或Commons的HttpClient来实现HTTP调用。如果你需要更先进,更好用的功能,就使用后者。你可以参考jakarta.apache.org/commons/httpclient。

输出业务对象
为业务对象设置HTTP调用器和你在Hessian或Burlap中使用的方式类似。就象Hessian提供HessianServiceExporter,Spring的HTTP调用器提供了org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter。为了输出AccountService,使用下面的配置:

<bean name="/AccountService" class="org.sprfr.remoting.httpinvoker.HttpInvokerServiceExporter"> 

     <property name="service"><ref bean="accountService"/></property> 

     <property name="serviceInterface"> 

         <value>example.AccountService</value> 

     </property> 

 </bean>


 
在客户端连接业务
同样,从客户端连接业务与你使用Hessian或Burlap时做的类似。使用代理,Spring可以将你的调用翻译成HTTP 的POST请求到指向输出业务的URL。

<bean id="httpInvokerProxy" class="org.sprfr.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> 

         <property name="serviceUrl"> 

             <value>http://remotehost:8080/AccountService</value> 

         </property> 

         <property name="serviceInterface"> 

             <value>example.AccountService</value> 

         </property> 

     </bean>



就象上面说的一样,你可以选择使用你想使用的HTTP客户端。缺省情况下,HttpInvokerPropxy使用J2SE的HTTP功能,但是你也可以通过设置httpInvokerRequestExecutor属性选择使用Commons HttpClient:

<property name="httpInvokerRequestExecutor"> 

     <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/> 

 </property>


 
在选择这些技术时的一些考虑

这里提到的每种技术都有它的缺点。你在选择这些技术时,应该仔细考虑你的需要,你所输出的业务和你在远程访问时传送的对象。

当使用RMI时,通过HTTP协议访问对象是不可能的,除非你用HTTP包裹RMI流。RMI是一种很重的协议,因为他支持完全的对象序列化,这样的序列化在要求复杂数据结构在远程传输时是非常重要的。然而,RMI-JRMP只能绑定到Java客户端:它是一种Java-to-Java的远程访问的方案。

如果你需要基于HTTP的远程访问而且还要求使用Java序列化,Spring的HTTP调用器是一个很好的选择。它和RMI调用器使用相同的基础设施,仅仅使用HTTP作为传输方式。注意HTTP调用器不仅只能用在Java-to-Java的远程访问,而且在客户端和服务器端都必须使用 Spring。(Spring为非RMI接口提供的RMI调用器也要求客户端和服务器端都使用Spring)

当在异构环境中,Hessian和Burlap就非常有用了。因为它们可以使用在非Java的客户端。然而,对非Java支持仍然是有限制的。已知的问题包括含有延迟初始化的collection对象的Hibernate对象的序列化。如果你有一个这样的数据结构,考虑使用RMI或HTTP调用器,而不是Hessian。

最后但也很重要的一点,EJB优于RMI,因为它支持标准的基于角色的认证和授权,以及远程事务传递。用RMI调用器或HTTP调用器来支持安全上下文的传递是可能的,虽然这不是由核心Spring提供:而是由第三方或在定制的解决方案中插入拦截器来解决的。

spring的集群使用的组件为cluster4spring
spring有比较好的远程服务解决方案,但其静态导入方案,只能使用一个server端,在大型分布式系统中无法很好的解决负载均衡及单点故障问题。某一个server放生故障,client就会受到影响。
为解决该问题,cluster4spring应运而生,提供一个很好的解决方案。一个client端可以同时链接n个服务节点,其中某一个服务节点发生故障不会影响整个系统运行
新建一个客户端的配置文件client-cluster.xml,内容如下:

<?xml verson="1.0" encoding = "uft-8"?> 

 <!DCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> 

 <beans> 

 <bean name = "_RmiEndpointFactory" class="org.softamis.cluster4spring.rmi.support.RmiEndpointFactory"/> 

 <bean name="REMOTE_SERVICE_CLIENT" class="org.softamis.cluster4spring.rmi.RmiUrlListProxyFactoryBean" abstract="true">    

   <property name="refreshEndpointsOnConnectFailure" value="true"/>    

     <property name="refreshEndpointsOnStartup" value="true"/>    

     <property name="registerTraceInterceptor" value="true"/>     

     <property name="switchEndpointOnFailure" value="true"/>    

     <property name="endpointFactory" ref="_RmiEndpointFactory"/>    

     <property name="endpointSelectionPolicy"> 

         <bean class="org.softamis.cluster4spring.support.invocation.DefaultEndpointSelectionPolicy"/> 

 </property> 

 </bean>    


 <bean name="helloWorldServiceProxy" parent="REMOTE_SERVICE_CLIENT">     

     <property name="serviceInterface" value="demo.rmi.server.IHelloWorld"/>    

     <property name="serviceURLs">    

       <list>    

             <value>rmi://server1:1099/helloWorldService</value>    

           <value>rmi://server2:1099/helloWorldService</value>    

             <value>rmi://server3:1099/helloWorldService</value>    

             </list>    

     </property> 

 </bean>



使用此配置文件来运行客户端,一般情况下会提示错误信息,如:RMI lookup for bean [contextService] with interface [my.own.Interface] failed ; nested exception is java.lang.ClassCastException: java.lang.String
如果出现此错误信息,则应修改上述文件中的serviceURLs属性的配置为:

<property name=”serviceURLs”> 

 <list> 

     <bean class=”org.softamis.cluster4spring.support.ServiceMoniker”> 

         <constructor-arg><value>rmi://localhost:1009/helloWorldService</value></constructor-arg> 

     </bean> 

     <bean class=”org.softamis.cluster4spring.support.ServiceMoniker”> 

         <constructor-arg><value>rmi://localhost: 1009/ helloWorldService</value></constructor-arg> 

     </bean> 

     <bean class=”org.softamis.cluster4spring.support.ServiceMoniker”> 

         <constructor-arg><value>rmi://localhost: 1009/ helloWorldService </value></constructor-arg> 

     </bean> 

 </list> 

 </property>



Terracotta for Spring它为Spring 应用提供了透明的高性能集群支持,对应用代码和部署及配置流程影响都很小
这让开发者能够开发与无状态方式不同的单节点有状态Spring应用。这使得在需要扩展的应用开始设计时不考虑集群。而在应用需要扩展或者要保证搞可用性和故障恢复时,他们只需要在Terracotta 配置文件中定义哪些Spring应用上下文中的beans需要进行集群。Terracotta for Spring 使得应用能够被自动和透明的集群,还保证在集群间的语义和单节点一样。

对于Spring Web Flow来说,这实际上更简单。用户为了获得web应用的状态和持续仓库的集群能力, 只需要在Terracotta配置文件中把特定的web应用声明为启用“session-support”。(详细内容见下面的章节“声明式配置”)

从宏观上看,Terracotta for Spring提供了:
HTTP session状态的集群。保证Spring Web Flow中的用户状态和扩展仓库或放入HTTP session的其它状态的高可用性和故障恢复能力。
Spring bean的集群。Springbean的生命周期语义和域在集群间被保存,它们在“逻辑”上相同的ApplicationContext中。目前能被集群的bean类型是singleton和session scoped. 用户可以声明式配置哪个application contexts中的哪个bean需要被集群。
透明集群POJO。不需要修改已有的代码,甚至不需要源代码。应用基于很少的声明式XML 配置文件,在载入期透明的生效。Terracotta for Spring不需要实现Serializable, Externalizable或其它接口的类。能这样实现的原因它并没有使用序列化,而只是将实际的差量和已经改变了的数据传输给当前需要的节点 (lazily)。
虚拟内存管理。它也提供分布式垃圾收集和虚拟堆功能。比如,由于物理内存在需要时被换入换出,它能在一个4G RAM的机器上运行需要200G堆的Web应用。 这也意味着你不需要关心Spring Web Flow 会话数据的大小是否超过了物理堆大小。
Terracotta for Spring 使用aspect-oriented技术来在类载入时适配应用。在这个阶段它扩展了应用以保证Java语义在集群间被正确的维护,包括对象引用,进程调用和垃圾收集等
Terracotta并没有使用serialization。