您的位置:首页 > 其它

远程服务调用框架设计与实现

2013-04-24 16:10 751 查看
目的 ...
2


实现技术 ...
2


接口定义和实现规范 ...
2


设计实现 ...
3


基本类图 ...
3


基本流程图 ...
4


具体的配置和使用 ...
4


目的

为远程服务调用提供统一的框架,该框架集中解决远程调用过程中的三方面问题:

a. 应用透明性:应用的接口和实现不依赖于框架的实现。框架可以透明的切换各种远程调用技术,而上层应用的接口和实现不用做任何调整。

b. 安全性:安全性主要包括两个方面:身份及签名验证 ( 防篡改伪造 ) ;数据传输保密性 ( 防监听 ) ; IP 认证。

c. 调用频度控制:为保证服务可用,需要对于调用频度根据一定的规则进行控制。

实现技术

由于调用双方都是基于 Java 的应用,实现技术上建议采用基于 Spring 的 Remoting 框架,这样可以实现应用透明性,接口开发人员不用考虑远程调用等与业务无关的技术细节。基于 Spring 框架并进行扩展,我们可以在框架层次实现安全性和调用频度限制。

由于调用双方不在一个局域网环境内,因此在具体通讯协议上,最佳选择即为 Http 。因此我们推荐的实现技术包括: Spring Remoting + Spring HttpInvoker ,以及 Spring Remoting + Hessian 。

安全性包括身份验证和数据传输安全两个方面,身份验证可以根据调用双方的信任程度以及性能要求确定采用对称加密或者非对称加密,当前提供了三种验证措施,用户名加密认证, IP 认证,以及消息数字摘要加密验证,该验证可以在 Spring Remoting 基础上进行扩展。数据传输安全则主要是担心数据在传输过程中被截获,对于基于 Http 的传输,使用 Https 即可 ( 无需在框架或者应用层支持 ) 。

调用频度控制,则可以应用 AOP 技术,对于调用进行截获和统计,根据一定的规则,判断调用是否符合控制策略。

接口定义和实现规范

接口定义和实现为简单的 POJI 和 POJO 即可,不过为了满足远程调用的需要,需要保证所有参数和返回值都是可序列化的,另外,鉴于部分远程调用技术的序列化机制的特殊性 ( 例如 Hessian) ,数据类型应尽可能简单。此外,基于性能考虑,远程接口调用方式适用于中低频度的小数据量的调用,对于大批量数据同步或者相当高频度的调用,远程接口调用方式并不合适。

设计实现

基本类图





图 1 远程服务发布类结构图

针对 Hessian 和 HttpInvoker 两种远程服务调用的方式封装了对于安全控制的两个安全发布类,具体的安全配置以及安全操作都在 RemoteContractTemplate 中,这样可以方便扩展任何安全的需求变更,并且对原有任何的 Exporter 做了安全切面处理,防止过度耦合。



图 2 远程服务调用类结构图

远程服务调用对于不同的方法调用需要不同的定制,这里针对 Hessian 和 HttpInvoker 采用了替换植入内部处理类的方式, Hessian 植入了新的 HessianProxyFactory 用来生成新的 HessianProxy 来植入安全机制, HttpInvokerFactoryBean 植入了新的 HttpInvokerRequestExecutor 来植入安全机制,同样安全配置以及操作都封装在 RemoteContractTemplate 中,集中控制和配置,方便扩展和管理。

基本流程图





图 3 基本流程图

如上图所示,用户发起请求调用远程服务,首先是创建远程服务代理,然后通过植入安全信息将请求发送到远程服务发布处理类中,首先检查安全信息,如果通过安全检测就进入方法调用拦截器中检验类似于频率之类的限制过程中,通过拦截器的检测就可以调用真正的远程服务,并且获得结果,将结果返回并封装安全信息返回给服务调用代理,代理首先检测是否有合法的安全信息,如果通过安全信息认证,将结果返回给客户端。

具体的配置和使用

这里通过一个 Demo 来说明如何使用这个远程服务调用框架。

假定一个售票管理服务要发布,售票管理服务结构图如下:



图 4 售票管理服务结构图

服务类接口为 TicketManage ,实现类是 TicketManageImpl 。测试调用类为 TicketManageClient 。接口和接口的实现类就是按照普通的 Java 规范来实现即可, TicketManageClient 根据你选择不同的服务调用方式来编写代码,这里用到了 Hessian 和 HttpInvoker 两种方式,代码如下:

public
static void

main(String[] args)

{

ApplicationContext ctx = new
ClassPathXmlApplicationContext(
"ticket.xml" );

TicketManage ticketManage = (TicketManage)ctx.getBean(
" ticketService
" );
//hession 调用的配置

Ticket ticket = ticketManage.buyTicket(20);

System. out
.println( "ticket seat: "
+ ticket.getSeat());

int
returncost = ticketManage.returnTicket(ticket);

System. out
.println( "return ticket, get back cost :"
+ returncost);

TicketManage httpTicketManage = (TicketManage)ctx.getBean(
"ticketHttpService"
); //HttpInvoke 调用的配置

ticket = httpTicketManage.buyTicket(30);

System. out
.println( "ticket seat: "
+ ticket.getSeat());

returncost = httpTicketManage.returnTicket(ticket);

System. out
.println( "return ticket, get back cost :"
+ returncost);

}

ticket.xml 是客户端的 spring 配置文件,具体的内容如下:

<?xml version="1.0" encoding="GB2312"?>

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

<beans default-autowire="byName">

<!—- 安全模版配置类,参数在后面会有详细解释
-->

<bean id="remoteContractTemplate" class="com.alibaba.common.remoting.util.RemoteContractTemplate" init-method="init">

<property name="encryptKeyPath" value="file:c:\\key.ky" />

<property name="decryptKeyPath" value="file:c:\\key.ky" />

<property name="algonrithm" value="RSA"/>

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

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

<property name="user">

<list>

<value>taobao</value>

<value>zhifubao</value>

<value>b2b</value>

</list>

</property>

<property name="owner" value="alisoft"/>

<property name="connectTimeout" value="6"/>

<property name="readTimeout" value="0"/>

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

<property name="ipList">

<list>

<value>10.0.26.23</value>

<value>10.0.0.42</value>

</list>

</property>

</bean>

<!—- 需要植入到
HttpInvokerProxyFactoryBean
的安全请求处理类 -->

<bean id="securityHttpInvokerRequestExecutor" class="com.alibaba.common.remoting.http.SecurityHttpInvokerRequestExecutor">

<property name="remoteContractTemplate" ref="remoteContractTemplate" />

</bean>

<!—- 发布服务的
Bean -->

<bean id="ticketHttpService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">

<property name="serviceUrl" value="http://localhost:80/remote-examples/remote/TicketHttpService"/>

<property name="serviceInterface" value="com.alibaba.common.remoting.test.TicketManage"/>

<property name="httpInvokerRequestExecutor" ref="securityHttpInvokerRequestExecutor"/>

</bean>

<!—- Hessian 的安全代理工厂类
Bean -->

<bean id="securityHessianProxyFactory" class="com.alibaba.common.remoting.hessian.SecurityHessianProxyFactory">

<property name="remoteContractTemplate" ref="remoteContractTemplate" />

</bean>

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

<property name="serviceUrl" value="http://localhost:80/remote-examples/remote/TicketService"/>

<property name="serviceInterface" value="com.alibaba.common.remoting.test.TicketManage"/>

<property name="proxyFactory" ref="securityHessianProxyFactory"/>

</bean>

</beans>

上面的 xml 中蓝色的内容需要根据具体的情况作修改,黑色的内容则不需要修改。

安全模版配置类参数具体说明:下面是代码中的说明,大家一看就应该明白了

private String
encryptKeyPath
; //encryptKey
的路径

private
String decryptKeyPath
; //decryptKey
的路径

private
Key encryptKey
; //
根据 encryptKey
的路径生成的 key

private
Key decryptKey
; //
根据 decryptKey
的路径生成的 key

private
String algonrithm
; //
算法

private
String needMD
; //
是否需要 MD 校验

private
String needUserAuth
; //
是否需要加密

private
List<String> user
; //
允许对方访问或者返回结果的用户名

private
String owner
; //
自己的签名

private
byte
[]
encrypted ;
// 加密后的用户签名,一次加密,多次使用,增加性能提升

private
String encoding
= "GB2312"
; // 编码方式,加密和
md 的内容的编码方式

private
String needIPAuth
; //
是否需要 IP 认证

private
List<String> ipList
; //
允许访问的 ip 列表

private
int
readTimeout
= 0; // 读取数据超时时间设置,单位秒

private
int
connectTimeout
= 0; // 连接超时时间设置,单位秒

客户端设置好以后,就需要设置服务端了:

服务端很简单首先是类似于 Spring 的 Hessian 和 HttpInvoker 调用的配置一样,在 WEB-INF/lib 下面放入 toolkit-sandbox-remoting.jar 以及其它以来的 jar ,然后配置 web.xml ,增加如下内容(这都是 spring mvc 框架的配置,大家可以参考 spring 来配置):

<servlet>

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

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

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

</servlet>

<servlet-mapping>

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

<url-pattern>/remote/*</url-pattern>

</servlet-mapping>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: