使用绑定实现灵活通信

阅读数:2435 2009 年 8 月 25 日

话题:SOAREST架构

这篇文章节选自《Apache Tuscany in Action》,它介绍了如何使用绑定进行组件服务连接的配置。

SCA 最重要的特征之一是对交互协议的广泛支持。如果你的服务要同 Web 服务、JMS、CORBA 或者 REST 交互的话,通过 SCA 和 Tuscany 就可以轻松做到。如果因为特定应用的需要,服务使用特殊的或私有的协议进行通讯的话,SCA 也没有问题。甚至,你的业务逻辑根本不需要知道交互协议是什么,(没错,你已经猜对了)协议的选择是通过对组件的配置实现的。这太酷了,不是吗?而 SCA 绑定就是让这一切成为可能的“魔力”。

在这篇文章中,我们将了解如何在服务以及引用上使用绑定;此外,如果没有配置绑定话,意味着什么;最后,我们还要去看看 SCA 的域,去了解如何在 SCA 域内和域外运用绑定。

为服务和引用配置绑定

你可以在服务以及引用上配置绑定。为服务设定一个绑定意味着人们可以通过由该绑定所指定的交互协议来访问这个服务。而服务的实现不需要做任何特别的改变来促成这个目标,你要做的仅仅是为服务增加一条绑定配置。

例如,如果要让 Bookings 服务以 Web 服务的方式暴露出来,那么组件的定义看起来就应该是这样的:

<component name="TripBooking">
  <implementation.java class="com.tuscanyscatours.TripBooking" />
  <service name="Bookings">
    <binding.ws uri="http://tuscanyscatours.com:8085/Bookings" />
  </service>
</component>

我们通过增加 <binding.ws> 元素告诉 SCA 运行时,Bookings 服务要以 Web 服务的形式暴露出来,使用 SOAP/HTTP 的 方式,并且以 uri 属性中指定的值作为服务的端点(endpoint)。这就是要创建 Web 服务我们要做的全部事情,无需学习 JAX-WS 或者在 Bookings 服务的实现上做文章。

可以为一个服务配置多个绑定。如果既要通过 JMS 访问又要通过 Web 服务方式来访问 Bookings 服务,则只需要增加另一条绑定。请看下面的例子:

<component name="TripBooking">
  <implementation.java class="com.tuscanyscatours.TripBooking" />
  <service name="Bookings">
    <binding.ws uri="http://tuscanyscatours.com:8085/Bookings" />
    <binding.jms />
  </service>
</component>

这个定义让 Bookings 服务同时以 JMS 的方式暴露出来,并且使用了缺省配置。同样,我们不需要去学习 JMS 的 API 或修改服务的实现代码。

绑定还将 SCA 和 Tuscany 与更广阔的外部世界联系起来!Bookings 服务是以 SCA 实现,运行在 Tuscany 中的。由于使用绑定对这个服务进行了配置,它可以被以非 SCA 方式实现的客户端或运行在 Tuscany 中的客户端调用。对于 Bookings 的 Web 服务调用,客户端可以是任何语言编写的,运行在任何遵循 WS-I 的 Web 服务运行时上的程序;同样,对于 JMS 方式的调用,客户端可以直接使用任何 JMS 提供者所提供的 JMS API,只要该 JMS 提供者与 Tuscany 中配置的 JMS 绑定对应的 JMS 提供者兼容即可。

现在我们已经看到如何通过在 SCA 的服务上配置绑定让服务以标准的交互协议向外提供服务。同样,绑定也可以用于 SCA 引用上,让其通过标准的交互协议去调 用外部服务。这种情况下角色正好倒置:客户端由 SCA 实现,而服务端使用标准的交互协议。例如,服务提供者可能是一个支持 SOAP/HTTP 的 Web 服务 端点,也有可能是一个以 RMI-IIOP 方式交互的 EJB(session bean)。与 Web 服务交互时,SCA 引用使用 <binding.ws> 绑定;而调用 EJB 时,它使 用 <binding.ejb> 绑定。

图 1 显示了如何在组件的服务以及引用上配置绑定。

图 1:Bookings 服务配置了 Web 服务和 JMS 的绑定,“cars”引用配置了 Web 服务绑定,“flights”引用配置的是 EJB 绑定。

列表 1 中的示例代码显示的是图 1 中描绘的 TripBooking 组件对应的组件定义文件。

列表 1 为组件的服务和引用配置绑定和连线

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://tuscanyscatours.com/"
	name="bookings">
    <component name="TripBooking">
      <implementation.java class="com.tuscanyscatours.TripBooking" />
      <service name="Bookings">
	<binding.ws uri="http://tuscanyscatours.com:8085/Bookings" />
	<binding.jms />
      </service>
      <reference name="cars">
	<binding.ws uri="http://tuscanycars.com:8081/Cars" />
      </reference>
      <reference name="flights">
	<binding.ejb uri="corbaname:rir:#flight/FlightProviderHome" />
      </reference>
      <reference name="hotels" target="HotelProvider" />
    </component>
</composite>

你可能已经发现列表 1 中有两个引用(cars 和 flights)配置了绑定而没有 target 属性,这是因为该绑定元素已经提供了引用的目标端点信息,同时还指定了所使用的交互协议。

至此,我们已经了解如何在服务和引用上使用绑定。在下一节中,我们要看看如果在服务和引用的配置中不指定绑定将会发生什么?

缺省绑定

在列表 1 中我们为“hotels”引用没有设置绑定,这意味着它有一个隐含的.sca 绑定(经常被称为缺省绑定)。缺省绑定用于连接 SCA 服务和 SCA 引用,把交互技术的选择工作交给部署服务和引用的 SCA 运行时,而其他的绑定(如 WS 绑定和 JMS 绑定)则要选择具体的交互协议或 API,从而让 SCA 服务或引用可以与非 SCA 的程序进行交互。正因为如此,非缺省的绑定通常被称为可互操作的绑定。

Tuscany 使用基于 SOAP/HTTP 的 Web 服务实现缺省绑定的远程调用;其他的 SCA 运行时可能使用不同的标准协议,如 RMI/IIOP,它们也 可以使用某种私有协议。将来,Tuscany 的缺省绑定的通讯协议也可以从 Web 服务转向其他的协议,因此,Tuscany 的应用程序不应该想当然地假设 使用缺省绑定就意味着使用 Web 服务。应用程序若要使用 Web 服务在组件之间交互的话,为保险起见,还是应该要指定使 用 <binding.ws> 绑定。

缺省绑定只能用于连接位于同一个 SCA中的服务和引用,而当连接跨越域边界时,应该要为其指定某个可互操作的绑定。域在 SCA 中是一个重要的概念,我们将在第四章详细介绍域的概念,不过在下一个节中我们先简单介绍一下什么是域以及它与绑定和连线的关系。

域,绑定和连线(wire)

SCA 域是一个 SCA 组件的部署和管理边界,比如,一个域可能是单个应用服务器或一个服务器集群,也可以是一组服务器或一组集群。而一个完整的域通常只运行某一家提供商的 SCA 实现。

任何 SCA 组件都是 SCA 域的一部分;同一域中的服务和引用可以通过连线进行连接;对于同一域中的服务和引用之间的连接,可以使用缺省绑定,因为 SCA 保证了缺省绑定的实现在域中的一致性。相反,对于域内到域外的交互,不能使用连线进行连接,而应该使用可互操作的绑定。

在第 2 章我们已经介绍了 TuscanySCATours 公司,下面我们将通过该公司的一个场景来描述域的使用。现在,这个公司已经壮大,并且设立了一个独 立的部门提供专门的 hotel booking(酒店预订)服务。这个服务不仅要服务于 TuscanySCATours 的 trip booking(行程预订)服务,还要直接服务于只需预订酒店的客户。公司的两个部门使用不同的域管理他们所提供的服务:TuscanySCATours 域提供原先的 trip booking(行程预订)服务,而 TuscanySCAHotels 域提供新的 hotel booking(酒店预订)服务,如图二所示:

图 2 中有两个 SCA 域及多个组件,展示了组件是如何连接的,连线(实线)用于连接同一域中的组件,而 绑定(带箭头的虚线)用于连接跨域的组件。

图 2 中用实线表示连接组件的 SCA 连线,而用带箭头的虚线表示用可互操作的绑定配置的连接。我们先来看看 TuscanySCATours 域中 TripBooking 组件的3个引用。首先,flights(航班)引用连到一个实现 flights 预定的 Web 服务,因为这个服务不是 SCA 服务,所以 flights 引用通过可互操作的绑定 <binding.ws> 和 flights 预订服务的 URI 进 行配置。

其次,“hotels”引用指向了 TuscanySCAHotels 域中的由 HotelProvider 这个组件所提供的 Hotels 服务。由于连线不能 跨越域的界限,所以这样的服务和引用都要使用 <binding.ws> 和服务端点 URI 进行配置。最后,“cars”引用连到了 TuscanySCATours 域中的由 CarProvider 组件提供的 Cars 服务,因为这对引用和服务位于同一个域中,所以我们可以使用连线以及缺 省绑定连接它们。“Cars”服务没有对外暴露,也不需要和外部的非 SCA 程序交互,所以没有必要为其配置可互操作绑定。

接下来,看看 TuscanySCAHotels 域中由 HotelProvider 组件周围的连线。除了来自 TripBooking 组件的“hotels” 引用连到它之外,还有来自 HotelBooker(非 SCA 的酒店预订客户端程序)和 TuscanySCAHotels 域内的 HotelOffers 组件 的“hotels”引用。由于“Hotels”服务配置了客户操作的绑定类型——<binding.ws>,所以 HotelBooker 客户 端软件可以使用任何 Web 服务程序调用“Hotels”服务。HotelOffers 和 HotelProvider 之间的连接使用 SCA 连线的原因是这些 组件处于同一域中。这个连线既可以使用缺省绑定也可以使用该服务提供的可互操作 binding.ws 绑定,在本例中,我们使用 binding.ws,目的 是为了说明同一域中的连线也并不一定要使用缺省绑定。

了解图 2 中所描述的场景在组件配置文件中是如何配置是很有必要的。列表 2 给出了位于 TuscanySCATours 域中的组件的定义。

列表 2 TuscanySCATours 域中组件的组件定义文件

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://tuscanyscatours.com/"
	name="toursdomain">
    <component name="TripBooking">
      <implementation.java class="com.tuscanyscatours.TripBooking" />
      <reference name="flights">
	<binding.ws uri="http://flightbookingservice.com:8084/Flights" />
      </reference>
      <reference name="hotels">
    <binding.ws uri="http://tuscanyscahotels.com:8083/Hotels" />
      </reference>
      <reference name="cars" target="CarProvider/Cars" />
    </component>
    <component name="CarProvider">
       <implementation.java class="com.tuscanyscatours.CarProvider" />
    </component>
</composite>

列表 3 给出了 TuscanySCAHotels 域中组件的组件定义文件:

列表 3 TuscanySCAHotels 域的组件定义文件

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
	targetNamespace="http://tuscanyscahotels.com/"
	name="hotelsdomain">
    <component name="HotelProvider">
      <implementation.java
	class="com.tuscanyscahotels.Hotels" />
      <service name="Hotels">
	<binding.ws uri="http://tuscanyscahotels.com:8083/Hotels" />
      </service>
    </component>
    <component name="HotelOffers">
      <implementation.java class="com.tuscanyscahotels.HotelOffers" />
    <reference name="hotels"
	target="HotelProvider/Hotels" >
	<binding.ws/> 1
      </reference>
    </component>
</composite>
1 此处的引用使用 binding.ws 进行配置

为 HotelOffers 的“hotels”引用指定引用绑定是有必要的,如果没有,它就可能被设定成默认的 binding.sca 绑定,而这样会导致错误,因为它(“hotels”引用)的目标服务的配置中没有提供 binding.sca 方式的绑定。

图 2 中的例子展示了引用以及服务的大多数连接方式。不过,对于同一个域中的 SCA 服务和 SCA 引用,除了使用 SCA 连线之外,也可以使用一对匹配的绑定进行连接。很多 Tuscany 的例子就是这么做的,这样很容易展示如何为客户端和服务端配置匹配的绑定。

我们已经描述了 SCA 服务和引用连接的若干种选择,这里简要做一个总结。在同一个域中连接引用和服务,你可以:

  • 使用带缺省绑定的 SCA 连线
  • 使用 SCA 连线,并为其配置一对匹配的可互操作的绑定
  • 为引用和服务配置匹配的绑定

连接不在同一域中的 SCA 引用和服务,你可以:

  • 为引用和服务配置匹配的可互操作的绑定。

将引用或服务连接到非 SCA 程序,你可以:

  • 为应用或服务配置一种可互操作绑定

可互操作绑定使 SCA 程序能够与非 SCA 程序交互,也支持不同提供商的 SCA 程序跨域的交互。而缺省绑定的好处是在互操作不是必须的情况下,提供商可以提供更优质的交互能力。

若了解源代码,示例章节,作者的论坛以及其他资源,请访问http://www.manning.com/laws


这篇文章节选自"Apache Tuscany in Action",它介绍了如何使用绑定进行组件服务连接的配置

若从manning.com购买这本书,InfoQ 的读者可以获得25% 的折扣,请使用这个折扣码:"infoq25"

查看英文原文:Communication Flexibility Using Bindings

感谢胡键对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。