 | 级别: 初级 Yih-Shin Tan (ystan@us.ibm.com), 高级技术人员 Brad Topol (btopol@us.ibm.com), 高级软件工程师 Vivekanand Vellanki (vellanki@us.ibm.com), 顾问软件工程师 Jie Xing (jiexing@us.ibm.com), 顾问软件工程师
2004 年 1 月 27 日 本系列的第 1 部分至第 5 部分描述了一个服务域,该域代表了通过公共服务入口点可比较或者相关的 Web 服务的集合。服务域应用自主计算原则将 Web 服务与网格服务集成在一起。服务域技术提供了一种服务网格,可以自动创建、过滤、发现、群集、组织、选择、路由、恢复和切换 Web 服务和网格服务。通过使用服务域对象,可以方便快捷地实现商业解决方案。这些服务域对象将多个服务实例例示到一个虚拟的服务中,再将多个虚拟的服务例示到一个高层的虚拟服务中,如此等等。服务网格的操作是基于规则的,而其部署则是通过标准的 Web 服务实现的。服务实例的提供者和消费者按照提供的条款(Terms and Conditions)签约。服务域利用已注册服务提供者的池自动处理消费者的请求。在本文中,我们将讨论如何调用并访问服务域来启动操作阶段。
简介
服务域是已注册服务域实例的“虚拟服务”,因此,无需为服务的调用更改单独的客户接口。在公共端口类型接口的定义下,兼容的实例被集成在一起。客户机不是指向单独的服务实例并手工绑定的,而是指向服务域来动态绑定的。因此,客户机的负担被大大减轻了,避免了诸如异常处理、多选项选择、差异管理等工作。结果,商业软件的实现变得更加健壮。
可通过 Web 服务或网格服务接口实现对服务域请求的调用。服务域使用从服务策略、服务映射、使用契约和提供者契约中提取的信息,选择一个服务实例,并将该实例分派给请求。服务分派是可选的;而对于注册库或服务引用域(如资源分配服务),可能无需激活就能分派服务。
调用服务域
因为服务域请求是通过 Web 服务或者网格服务来调用的,所以它可以调用 Java 类方法、JAX-RPC 接口、WSIF 接口,或者直接进行 Java 调用。可以根据自己的喜好使用不同的客户机接口访问服务域。
下面的示例代码段展示了使用 Web 服务或者网格服务接口调用服务域的一般方法。
String sessionid;
// session id obtained from previous access validation
Service service;
Call call;
QName serviceQName;
QName portQName;
String wsdlImplURL =
"the url of a service domain";
String serviceName =
"service name of the service domain";
String nameSpace =
"name space of the service domain";
String portName =
"port name ";
String endPointAddress=
"invoking address of the service domain";
serviceQName = new QName(namespace, serviceName);
portQName = new QName(namespace, portName);
service = new Service(new URL(wsdlImplURL), serviceQName);
call = (org.apache.axis.client.Call)service.createCall(portQName, "operation name");
call.invoke(new Object[] { parameters, ... });
|
下面的示例代码段展示了用 Java 类调用服务域的一般方法:
import com.ibm.edge.webservices.systemservices.servicedesk.*;
ServiceDesk sd;
// previously created servicedesk object
String sessionid;
// previously validated identity
String implWSDLURL;
// URL of the impl WSDL of the service being registered
String namespace;
// name space of the service being registered
String serviceName;
// service name of the service
String portName;
// port name of the service
String slaData;
// slaData for the supplier. can be null
String supplierKey;
// unique key for the supplier...return value
try {
supplierKey = sd.Register(sessionid, implWSDLURL, namespace, serviceName,
portName, slaData);
}
catch (Exception e) {
}
|
JAX-RPC 提供了一种从 Java 程序调用 Web 服务的方法。下面的示例代码段展示了通过 JAX-RPC 调用服务域的一般方法。从服务域的角度来看,客户机之间的差异是次要的:
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
String endpoint=
"invoking address of the service domain";
// endpoint address
String svcName =
"service name of the service domain";
// service name
String portName =
"port name";
// port name
String BODY_NAMESPACE_VALUE =
"name space of the service domain" ;
// name space for the operation
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(
new QName(svcName));
String parameter1 =
"value of parameter";
String parameter2 =
"value of parameter";
QName port =
new QName(portName);
Call call = service.createCall(port);
call.setTargetEndpointAddress(endpoint);
call.setOperationName(
new QName(BODY_NAMESPACE_VALUE,
"operation name"));
String[] params = { parameter1, parameter2 };
String result = (String)call.invoke(params);
|
服务域的访问
服务域的第一次调用必须是其登录操作,除非服务域放弃登录。服务域为该调用建立了一个请求上下文对象,来维护该请求者注销前的状态。客户机可通过指定用户 ID 和口令,或者通过指定身份标记或发行人标记来实现登录。根据服务域的服务策略中指定的安全过程,将会话 ID 返回给代表这个请求上下文对象的调用程序。该服务域的后续调用都必须指定这个会话 ID。
通过随用户服务级别名称保留会话 ID,可以非正式地提供初始身份标记支持或发行人标记支持。为便于示范,客户机可以将服务级别名称作为会话 ID 来调用服务域,从而不必请求登录。然后,服务域将假设该身份已经被清除。
下面是登录到 StockQuoteServiceDesk 对象的示例代码段:
String username;
// user name input from a portal
String password;
// password input from a portal
. . . . . . . . . .
String wsdlImplURL =
"http://localhost/wstk/servicedomain/StockQuoteServiceDesk_Impl.wsdl";
. . . . . . . . . .
Service service = new Service(new URL(wsdlImplURL), serviceQName);
Call call = (Call) service.createCall(portQName,
"Login");
String sessionid = (String)call.invoke(new Object[] { username, password} );
|
下面是从 StockQuoteServiceDesk 注销的示例代码段:
call = (Call) service.createCall(portQName, "
Logout");
call.invoke(new Object[] { sessionid });
|
使用 ServiceDesk 对象
ServiceDesk 和 ServiceHub 是用来证实服务域的概念和体系结构的两个特定对象。ServiceDesk 这个名称由真实世界中面向服务的经验的灵感中得来。不论是银行、汽车中心,还是类似的组织,您都可以走向其前台,给出您的服务订单。然后,该订单被传递到真正的服务器上进行处理。您不必担心谁在执行服务,只需确保结果的质量即可。因为这些服务必须经过前台传递过来,所以它们总是经过良好的定义,具有可预测性,并且使用简单。
您可以使用 ServiceDesk 对象对具有可比性的服务示例进行分组。这是最简单的服务聚集(service aggregation)形式。该操作模式可以通过分配服务实例“执行”请求、将服务实例“引用”到某个请求上,或是将请求“转发”到服务实例上。
下面的示例代码段展示了一个简单的 ServiceDesk 对象,该对象将多个服务实例注册到自己名下(还请参阅本系列前文中的代码示例):
public class StockQuoteServiceDesk extends ServiceDesk {
public StockQuoteServiceDesk()
throws Exception
{
key_instanceA = this.Register(null,
"http://localhost/wstk/servicedomain/StockQuoteA_Impl.wsdl",
"http://StockQuoteA",
"StockQuoteAService",
"StockquoteA",
null,
slaData);
. . . . . . . .
key_instanceB = this.Register(null,
"http://otherhost/wstk/servicedomain/StockQuoteA_Impl.wsdl",
"http://StockQuoteB",
"StockQuoteAService",
"StockquoteB",
null,
slaData);
. . . . . . . .
}
. . . . . . . . .
. . . . . . . . .
}
|
在 ServiceDesk 内部,使用操作方法中的 InvokeOperation 调用来执行请求,如下面的示例代码段所示:
public java.lang.String getQuoteSingle(String sessionid, java.lang.String arg0)
throws Exception
{
Object[] params;
Object obj;
java.lang.String result;
params = new Object[] {
arg0
};
obj = super.InvokeOperation(
Sessionid, null, null,
"getQuoteSingle",
params);
result = (java.lang.String)JavaUtils.convert(obj, java.lang.String.class);
return result;
}
|
客户机将使用标准的 Web 服务接口请求服务,如下面的示例代码段所示:
. . . . . . . .
public static final String implWSDL =
"http://localhost:80/wstk/stockquoteservicedesk/StockQuoteService-impl.wsdl";
public static final String namespace =
"http://StockQuoteServiceDesk";
public static final String serviceName =
"StockQuoteService";
public static final String portName =
"StockQuoteServiceDesk";
. . . . . . .
serviceQN = new QName(namespace, serviceName);
portQN =
new QName(namespace, portName);
service =
new Service(
new URL(implWSDL), serviceQN);
. . . . . . .
Call call = (Call)service.createCall(portQN,
"getQuoteSingle");
return (String)call.invoke(new Object[] { sessionid,
"IBM"});
|
对于操作模式的引用类型而言,设置 ServiceDesk 无需使用特定操作方法。使用 getServiceInfo () 操作可以获得服务实例的列表。(本系列下一篇文章中将对这一话题进行更全面的探讨。)
对于操作模式的转发类型而言,可以使用一般服务将已有的应用程序实例封装成只有一个转发操作的服务实例。可以让转发操作传递最初的调用命令和参数串来开始应用程序实例,然后运行它。
使用 ServiceHub 对象
ServiceHub 是“ServiceDesk 的 ServiceDesk”,该名称同样由真实世界中繁忙服务中心的灵感中得来,在服务中心,收集正处理的站是十分常见的。尽管从体系结构上讲,它们都是 WSDL 服务,但将纯“一切都是服务”的概念传达到实际实现场景中非常重要。使用服务域,您就可以以开始简单并且成长快速的方式建立服务域。首先,要将具有可比性的服务实例集成到 ServiceDesk 中。然后,将相关的 ServiceDesk 集成到 ServiceHub 中,并将这些 ServiceHub 再集成到更深一层的 ServiceHub 中。在进入域时,客户机看到的总是最上层的域;因此,随着 ServiceHub 的发展,客户机的经验应该变得越来越少。通过根据客户机指令添加单独的 ServiceDesk 和服务实例,您还可以按照自上而下的方式逐渐复制成功的 ServiceHub。
ServiceHub 可以用 ServiceDesk 的分组方法将相关的服务端口类型组合在一起。可以通过显式地添加或删除端口类型来执行过滤,同时还可以请求将服务实例注册到特定的端口类型。
下面是将一个股票报价端口类型添加到 ServiceHub 的示例代码段:
. . . . . . . .
String serviceInfo;
// Stock Quote service information string in xml format
. . . . . . . .
Service service = new Service(new URL(wsdlImplURL), serviceQName);
Call call = (org.apache.axis.client.Call)service.createCall(portQName,
"addPortType");
String servicekey = (String)call.invoke(
new Object[] { sessionid, serviceInfo });
|
下面是将一个股票报价服务实例注册到该股票报价端口类型的示例代码段:
. . . . . . . .
String reg_portName =
"sqservice";
String reg_portType =
"StockQuote";
. . . . . . . .
Service service = new Service(new URL(wsdlImplURL), serviceQName);
Call call = (org.apache.axis.client.Call)service.createCall(portQName,
"Register");
String servicekey = (String)call.invoke(
new Object[] { sessionid, reg_wsdlImplWSDL, reg_namespace,
reg_serviceName, reg_portName, reg_portType, slData });
|
使用 RegistryDiscoveryDomain 对象
RegistryDiscoveryDomain 对象被设计成用来实现对非集成(或未组织的)服务和资源进行一般引用的存储库。从其他服务域对服务实例使用发现操作可以让该域知道这些服务实例的存在,还可以使用 getServiceInfo 操作检索信息,获得匹配服务实例的列表。
可以使用 RegistryDiscoveryDomain 增大主服务域。例如,可以用它创建备份域,在主服务域不可用或容量不足的时候提供保留的服务实例。另外一种采用这项技术的方法就是设置中介域(matchmaker domain)。在复杂的应用程序中,要求预先分配系统服务或资源实例,这种情况下可以将 RegistryDiscoveryDomain 对象用作缓冲域。
下面的代码段从 OGSA 注册域中发现服务,并让 RegistryDiscoveryDomain 知道这些服务的存在:
String registryDiscoverySDURL =
"http://localhost/wstk/servicedomain/RegistryDiscoverySD_Impl.wsdl";
QName registryDiscoverySDServiceQN = new QName("
http://RegistryDiscoverySD",
"RegistryDiscoveryDomainService");
QName registryDiscoverySDPortQN = new QName(
"http://RegistryDiscoverySD",
"Registrydiscoveryservicedomain");
String sessionid =
"null";
String SDUrl =
"http://localhost/wstk/servicedomain/OGSARegistry_Impl.wsdl";
String xPath =
"//service";
Service service = new Service(new URL(wsdlImplURL), registryDiscoverySDServiceQN );
Call call=(Call)service.createCall(registryDiscoverySDPortQN,
"DiscoveryService");
String result=(String) call.invoke( new Object[] {sessionid, SDUrl, xPath} );
|
下面是将服务注册到 RegistryDiscoveryDomain 的代码段:
String uri;
// GSH/WSDL uri for the service to be registered
String sdeStr;
// service data element string for this service
Service service = new Service(new URL(wsdlImplURL), registryDiscoverySDServiceQN );
Call call=(Call)service.createCall(registryDiscoverySDPortQN,
"RegisterProvidedService");
String result=(String) call.invoke( new Object[] {sessionid, uri, sdeStr} );
|
下面是从 RegistryDiscoveryDomain 中发现服务实例的示例代码段:
Service service = new Service(new URL(wsdlImplURL), registryDiscoverySDServiceQN );
Call call = (Call)service.createCall(registryDiscoverySDPortQN,
"FindServiceData");
String result = (String) call.invoke( new Object[] {xPath} );
|
用 OGSI 端口类型调用服务域
所有预制的服务域和域服务都是网格服务。它们支持 GridService portType,同时也支持 Registr portType。在必要的时候,可以直接调用 GridService 和 Registry 操作与服务域进行交互。
下面的示例代码段显示了如何将注册服务注册到 OGSA 注册库中:
private static final QName NQqName = new QName(
"http://ServiceEntry-types",
"ServiceEntry");
String wsdlImplURL =
"http://localhost/wstk/servicedomain/OGSARegistry_Impl.wsdl";
QName RegistryServiceQN = new QName(
"http://OGSARegistry",
"OGSARegistryService");
QName RegistryPortQN = new QName(
"http://OGSARegistry",
"ogsaregistry");
String registerURL2 =
"http://example.com/fooservice2";
Service service = new Service(new URL(wsdlImplURL), RegistryServiceQN );
Call call = (Call)service.createCall(RegistryPortQN,
"RegisterService");
call.registerTypeMapping(ServiceEntry.class, NQqName,
new BeanSerializerFactory(ServiceEntry.class, NQqName),
new BeanDeserializerFactory(ServiceEntry.class, NQqName));
String result = (String)
call.invoke( new Object[] { new ServiceEntry(registerURL2, 0)} );
|
下面是从 OGSA 注册库中取消服务注册的示例代码段:
. . . . . . . .
. . . . . . . .
String registerURL1 =
"http://example.com/fooservice1";
Service service = new Service(new URL(wsdlImplURL), RegistryServiceQN );
Call call = (Call)service.createCall(RegistryPortQN,
"UnregisterService");
call.registerTypeMapping(ServiceEntry.class, NQqName,
new BeanSerializerFactory(ServiceEntry.class, NQqName),
new BeanDeserializerFactory(ServiceEntry.class, NQqName));
String result = (String)
call.invoke( new Object[] { new ServiceEntry(registerURL1, 0)} );
|
下面的示例代码段显示了如何从 OGSA 注册库调用 FindSerivceData 操作。您可以修改这个新的服务实例化过程,用服务 WSDL URL 以及域或域服务的端口名称来代替它:
. . . . . . . .
. . . . . . . .
String xPathQuery =
"//service";
Service service = new Service(new URL(wsdlImplURL), RegistryServiceQN );
Call call = (Call)service.createCall(RegistryPortQN,
"FindServiceData");
String result = (String) call.invoke( new Object[] {xPathQuery} );
|
结束语
本文演示了如何调用和访问服务域。ServiceDesk 和 ServiceHub 对象是将单独的 WSDL 服务集成到虚拟服务、hub 和 hub 的 hub 的核心纲领性构件块。我们以一种开始简单并且成长快速的方式创建了一个服务域映像。服务请求的状态是使用提供惟一会话 ID 的登录机制来维护的。可以执行、引用或转发服务域的操作模式。在本系列的下一篇文章中,我们将讨论如何从正运行的服务域中获得信息,这对编写消费者和管理员用户界面以及提供服务实例非常有用。
参考资料
作者简介  | 
|  | Yih-Shin Tan 是 IBM Software Group 的高级技术人员。他的专业领域包括基于 Web 的系统软件体系结构、分析、设计和开发,并且他已经成为新兴技术的前沿开拓者。目前,他是 WebSphere Platform System House Advanced Design and Development 组织的 Web 服务和网格计算集成技术的首席体系结构设计师。他曾经协助开发了作为 IBM alphaWorks 上的技术概念演示的服务域的概念和技术。您可以通过
ystan-at-us.ibm.com 与 Yih-Shin 联系。
|
 | 
|  | Brad Topol 是 IBM 美国北卡罗来纳州 Research Triangle Park 的高级软件工程师,他在 WebSphere Platform System House Advanced Technology 小组任职。他于 1998 年获得佐治亚州理工学院(Georgia Institute of Technology)的计算机科学博士学位。目前,他正积极地参与 Web 服务、网格计算、面向方面编程(Aspect-Oriented Programming)和自主计算等领域的高级技术项目。您可以通过
btopol-at-us.ibm.com与 Brad 联系。
|
 | 
|  | Vivekanand Vellanki 参与了 Web 服务、网格计算和自主计算领域的高级技术项目。他于 2001 年获得佐治亚州理工学院的计算机科学博士学位。他的兴趣在于分布式计算、Web 服务器和对等计算。您可以通过
vellanki-at-us.ibm.com与 Vivek 联系。
|
 | 
|  | Jie Xing 是一名顾问软件工程师,他已经在 IBM 美国北卡罗来纳州的 Research Triangle Park 工作了一年半。目前,他正参与 Web 服务、网格计算和自主计算领域的高级技术项目。他于 2000 年获得北卡罗来纳州立大学(North Carolina State University)计算机科学运筹学博士学位,他的研究兴趣涉及多代理系统、分布式系统和工作流。您可以通过
jiexing-at-us.ibm.com与 Jie 联系。
|
对本文的评价
|  |