 | 级别: 初级 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 年 3 月 23 日 本系列的第 1 至 6 部分描述了一个服务域,该域通过公共服务入口点表示了一组可比较的或相关的 Web 服务。服务域应用自主计算原则将 Web 服务与网格服务集成在一起。服务域技术提供了一种服务网格,可以自动创建、过滤、发现、群集、组织、选择、路由、恢复和切换 Web 服务和网格服务。通过使用服务域对象,可以方便快捷地实现商业解决方案。这些服务域对象将多个服务实例例示到一个虚拟的服务中,再将多个虚拟的服务例示到一个高层的虚拟服务中,如此等等。服务网格的操作是基于规则的,而其部署则是通过标准的 Web 服务实现的。服务实例的提供者和消费者按照提供的条款(Terms and Conditions)签约。服务域会利用已注册服务提供者的池自动处理消费者的请求。然后,我们将讨论如何访问、调用和使用服务域。本文还将讨论如何提供服务域中的可用信息。为什么应该关心这些信息?我们将介绍几种使用这些信息的方式,并讨论了一些优化其管理的方法。
简介
如果您正使用 Web 服务客户机访问服务域,那么本文描述的大多数信息您都不会用到。然而,本文会让您对服务域如何隐藏处理典型客户机中的多个服务实例的复杂性有一个更好的评价。
典型的客户机应该屏蔽这类信息,这类信息包括静态服务描述,以及从正进行的服务聚集和监控中收集的动态信息。服务域在普通的注册过程之上对这些信息进行分类和整理。这些维护着当前运行环境的整体视图。运行人员、管理人员或自动化软件程序可以通过编程的方式获得环境的快照,并对环境进行操纵。例如,ETTK 1.2 中演示的 On Demand Service 虚拟化层就是一个例子(请参阅
参考资料)。通常,这些程序会使用这类信息直接管理实例。
获取和设置服务域信息
处理服务域信息的基本机制是
getServiceInfo 和
setServiceInfo 操作。使用这两个操作可以检索并设置服务域的初始信息。下面是设置 StockQuoteServiceDesk 的服务信息的示例代码段:
String sessionid; // session id obtained from previous access validation
StockQuoteServiceDesk sd; // previously created StockQuoteServiceDesk
String infoStr; // service info string created for the service desk
// call to setServiceInfo
try {
sd.setServiceInfo(sessionid, "byKey", null, infoStr, "self");
}
catch (Exception e) {
}
|
下面是从 StockQuoteServiceDesk 中获得服务信息的示例代码段:
// setServiceInfo()
String sdKey; // key for the service desk obtained from a prior call to
// servicedesk
ServiceDesk sd = new ServiceDesk(sdKey); // re-starting a previously created
// of getServiceInfo() call
String infoStr;; // service info string for the service desk... return value
String[] info = sd.getServiceInfo(sessionid, "byKey", sdKey, "self");
// call to getServiceInfo
infoStr = info[0];
|
设置服务域中服务实例的信息
在注册个别服务实例的服务域中,服务信息不断地在累积。这些实例本身也可以是其他服务域。以前,我们已经使用服务信息字符串和服务关键字显示了 setServiceInfo 和 getServiceInfo 操作的形式,同时,还提供了基于 XML 文档和 xPath 查询字符串格式(byXpath)的其他形式,以及信息的类型(byType),以便提供更灵活的设置和获得一组服务的信息的方法。
对 XML 文档,可以使用 setServiceInfo byXpath 更新服务域中的服务信息。XML 文档使用下面的 ServiceDataElement 字符串模式:
<gsdl:serviceData name = "serviceInfoList">
<serviceInfoList>
<serviceInfo servicekey = ""
ServiceName = "DummyServiceBService"
WsdlSvcIntfURL =
"http://localhost/wstk/servicedomain/DummyServiceB_Interface.wsdl"
WsdlImplURL=
"http://localhost/wstk/servicedomain/DummyServiceB_Impl.wsdl"
namespace = "http://DummyServiceB"
ServicePortName = "dummyservice"
ProviderName = "Provider Name"
ProviderDesc = "Provider Description"
ServiceDeskKey = ""/>
<attributes>
<serviceType value="SOAP-RPC" />
<cloneable value="true"
CloneUUid = "clone instance id"
ClassJarsProperty = "classJar"
DeployFileProperty = "deployfile"
ImplWSDLProperty = "ImplWSDL"/>
<!-- other attribute tags -->
</attributes>
<measurements Availability="100.0" NumErrors="0" NumInvocations="4"
NumOutstanding="0" NumTimedOut="0" Responsiveness="1191.0" />
<sla value="PREMIER" />
</serviceInfoList>
</gsdl:serviceData>
|
每一个服务实例都是通过一个 serviceData 节点来描述的,其中包括 serviceInfoList 节点中的初始服务信息和附加服务属性和特性。上面的示例代码段显示的是一个“cloneable”属性,它包含服务实例克隆所需的信息,ETTK 1.2 的 Deployment Service/Service 克隆演示中演示了这种情况(请参阅
参考资料)。
下面的示例代码段显示了如何通过 setServiceInfo 操作,使用 XML 服务数据元素字符串更新服务实例的服务信息:
// session id
String sessionid;
// service instance GSH or WSDL implementation url
String uri;
// service data elements string per above
String serviceDataElementStr;
................
service = new Service(new URL(wsdlImplURL), serviceQName)
call = (org.apache.axis.client.Call)service.createCall(portQName, "setServiceInfo");
call.invoke(new Object[]{sessionid, "byKey", uri, serviceDataElementStr, "self"});
|
还可以在将服务实例注册到服务域时指定服务信息。下面的示例代码段展示了如何通过注册操作,使用 XML 服务数据元素字符串更新服务实例的服务信息。稍后,可通过 getServiceInfo 操作,可以将用来为该实例生成初始服务信息数据的参数显示在服务信息字符串中:
// session id
String sessionid
// service domain GSH or WSDL implementation url;
String SDuri;
// service instance GSH or WSDL implementation url
String uri;
// service data elements string per above
String serviceDataElementStr;
................
service = new Service(new URL(SDuri), serviceQName)
call = (org.apache.axis.client.Call)service.createCall(portQName, "Register");
call.invoke(new Object[] { sessionid, uri, reg_namespace,
reg_serviceName, reg_portName, reg_portType, slData });
|
使用 xPath 从服务域中查找服务实例
如果您喜欢使用 XML,可以使用带有 xPath 查询字符串的 getServiceInfo 服务域操作,从服务域中检索信息,获得匹配服务实例的列表。
虽然服务信息是通过 setServiceInfo 累积的,但您也可以调用 getServiceInfo 操作(基于 XML xPath),以 XML 文档字符串的形式检索服务信息。
下面是通过登录到 StockQuoteServiceDesk 对象来检索信息的示例代码段:
<services>
XML xPath results
</services>
|
下面的示例代码中举例说明了 xPath 查询字符串:
//serviceInfoList/*[@servicekey=
"1326031c-27e6-4a1e-870e-193409c4091f"]/ancester::service
get a list of service instances with servicekey=
1326031c-27e6-4a1e-870e-193409c4091f
//serviceInfoList/*[@serviceDeskKey=
"111c2a7e-37ae-4391-a1ac-129d200029b4"]/ancester::service
get a list of provided service instance with serviceDeskKey=
111c2a7e-37ae-4391-a1ac-129d200029b4
//serviceInfoList/*[@servicename="
DummyServiceBService"]/ancester::service
get a list of service instances with servicename=
DummyServiceBService
|
下面的示例代码段展示了如何调用带有 xPath 查询的 getServiceInfo 操作,查找服务域中的相关服务实例:
// session id
String sessionid;
// XML xPath query string as constructed above
String xPathStr;
// service domain GSH or WSDL implementation url
String SDuri;
................
service = new Service(new URL(SDuri), serviceQName)
call = (org.apache.axis.client.Call)service.createCall(portQName, "getServiceInfo");
String[] result = call.invoke(new Object[]{sessionid, "byXPath", xPathStr, "self"});
|
下面的示例 XML 代码展示了找到的相关服务实例的结果信息。有关服务实例的信息以相同的 ServiceDateElement 字符串模式显示在 <service> 标签中:
<services>
<service>
...service information for the domain here...
</service>
<service>
<description referencedNamespace = "http://schemas.xmlsoap.org/wsdl/"
Location = "http://localhost/wstk/servicedomain/DummyServiceB_Impl.wsdl">
</description>
<gsdl:serviceData name = "serviceInfoList">
<serviceInfoList>
<serviceInfo servicekey = "1326031c-27e6-4a1e-870e-193409c4091f"
ServiceName = "DummyServiceBService"
WsdlSvcIntfURL =
"http://localhost/wstk/servicedomain/DummyServiceB_Interface.wsdl"
WsdlImplURL=
"http://localhost/wstk/servicedomain/DummyServiceB_Impl.wsdl"
namespace = "http://DummyServiceB"
ServicePortName = "dummyservice"
ProviderName = "Provider Name"
ProviderDesc = "Provider Description"
ServiceDeskKey = "111c2a7e-37ae-4391-a1ac-129d200029b4"/>
<attributes>
<serviceType value="SOAP-RPC" />
<cloneable CloneUUid = "clone instance id"
ClassJarsProperty = "classJar"
DeployFileProperty = "deployfile"
ImplWSDLProperty = "ImplWSDL" />
</attributes>
<measurements Availability="100.0" NumErrors="0" NumInvocations="4"
NumOutstanding="0" NumTimedOut="0" Responsiveness="1191.0" />
<sla value="PREMIER" />
</serviceInfoList>
</gsdl:serviceData>
</service>
<service>
...another service instance information here...
</service>
</services>
|

 |

|
使用服务信息
待发掘的信息浩如烟海。除了直接使用 XML 方法之外,我们还提供了一个直接切入重点的方法,在某个“作用域”(scope)中“通过类型”(byType)访问信息。该方法是一种对象样式查询接口。
byType 选项尝试着对特定任务可能如何使用一组信息进行预测。scope 选项定义了节点在服务域内的范围或深度。两者结合在一起提供了一种限定方式。byType 选项将基本的描述、行为特征、测量数据、服务契约或拓扑等信息指定为信息的类型,而 scope 选项指定了应该只将信息用于服务域对象自身,还是也可以将信息用于该对象的直系子孙、父母、兄弟或者服务域中的整个家族。
这种方法提供的是一种折衷的方法,它高于简单的 XML 层次,同时也避免了应用程序因为特定使用而需要另一个属性对象的无休止可能而被拖垮。
下面是几种使用服务域信息的例子。该代码使用了 Java 接口,并假设应用程序在运行:
/* Getting a snapshot of service domain measurements
*
* This example shows how to collect service domain metrics
* including all its supplier instances.
* The information can be used by an application that displays
* runtime statistics on an administrative console, where "sh"
* is a ServiceHub object and sessionid is the handle of the
* current session returned from a previous logon.
*/
String[ ] results1 = sh.getServiceInfo
(sessionid, "byType", "measurements", "self and immediate");
/* Getting a snapshot of the attributes of all supplier service instances
*
* This example shows how to get an update of the current
* list of supplier instances and their service attributes. The
* information can be used by an application that displays service
* instance configurations on an administrative console, where
* "sh" is a ServiceHub object and sessionid is the handle of the
* current session returned from a previous logon
*/
String[ ] results2 = sh.getServiceInfo
(sessionid, "byType", "serviceInfo and attributes", "immediate");
/* Getting a snapshot of basic service descriptions of entire service domain
*
* This example shows how to get a complete list of supplier
* instances and their basic service descriptions. The information can
* be used by an application at initialization time to prepare an object
* structure to keep track of subsequent changes, where "sh" is a ServiceHub
* object and sessionid is the handle of the current session
* returned from a previous logon
*/
String[ ] results3 = sh.getServiceInfo(sessionid, "byType", "serviceInfo", "all");
/* Setting new information in service domain
*
* This example shows how to update the list of supplier instances
* and their basic service descriptions. The information can be
* used by an application at run time to update service domain
* when changes such as registering new instances are made,
* where "sh" is a ServiceHub object and sessionid is the handle
* of the current session returned from a previous
* logon
*/
String[ ] results4 = sh.setServiceInfo
(sessionid, "byType", "serviceInfo", input service string, "immediate");
|
接下来,让我们看一看如何对从服务域中获得的信息进行优化管理:
优化信息管理
getServiceInfo 操作获得的结果是作为以 XML 格式编码的一组字符串返回的,如前面
使用 xPath 从服务域中查找服务实例 中所示。每个字符串都代表一个节点,它包含用于该节点的查询合格的服务信息。需要将这些信息转换成数据结构对象,这样其他应用程序部件就能使用它们。您可以为您感兴趣的数据类型创建一个主控数据对象、一组服务实例对象或指针对象,如下面的代码例子所示:
/* Create a main control data structure object as the
* single access point of information.
* The object provides methods to navigate to specific
* pieces of information encapsulated in objects of smaller
* granularity. They correspond typically to the tag names
* of the xml information string. Below the "results" object
* is the full service information string returned
* from a getServiceInfo() call. The object class names
* are mostly based on the xml tag names.
*/
ControlDataStructure cds = new ControlDataStructure(results);
services mySelf = cds.getSelf();
// mySelf represents the information of the service domain
// object, ServiceDesk or ServiceHub, that getSer
// has requested
services = cds.getImmediate();
// myImmediate represents the information of
// the service domain objects, ServiceDesk or
// ServiceHub, or service instances that are
// registered to mySelf
service[] supplierList = mySelf.getServices();
// get an array of supplier information in mySelf
// first entry is mySelf, the rest all suppliers
// registered with mySelf
service x = mySelf.getService("name of x");
// get a specific supplier information by name (or key)
measurements m = x.getMeasurements();
// get the measurements information for supplier x
|
下面的代码例子提供了访问您感兴趣的数据类型的快速路径:
/* Create a point control data structure object for fast path
* access to a specific piece of information
*
* The object takes the "results" object of the full service
* information string returned from a getServiceInfo() and
* extracts out just the specific piece of information, where
* "rootDataStructure" is a base class used to provide the
* factory methods.
*/
// get measurements for all suppliers covered by the getServiceInfo()
measurements[] supplierMetrics = rootDataStructure.getMeasurements(results);
// get serviceInfo for all suppliers covered by the getServiceInfo()
serviceInfo[] supplierServiceInfos = rootDataStructure.getServiceInfo(results);
// get attributes for all suppliers covered by the getServiceInfo()
attributes[] supplierAttributes = rootDataStructure.getAttributes(results);
|
应该根据应用程序的目的构造和管理数据对象。要避免经常读取或更新大量您并不需要的数据。一个好的经验法则是,在初始化的时候建立完整的数据结构对象。大多数时间内,程序都是在显示信息。使用指针对象可以周期性地获得和设置新的信息。下面的示例代码段显示了从原始数据结构初始化供应商姓名的列表、将新的供应商姓名添加到列表中、将新的供应商信息添加到数据结构中,以及将新的信息设置到新的供应商的服务域中的例子:
/*initialize control data structure object from service domain's
service information*/
// assume ControlDataStructure and services as previously defined
ControlDataStructure cds = new ControlDataStructure(results);
services mySelf = cds.getSelf();
Vector supplierNames = mySelf.getNames();
// get the list of supplier names
/* add a new supplier */
// assume sh as previously defined, register the new supplier
String servicekey = sh.Register
(sessionid, uri-wsdlImpl, reg_namespace, reg_servicename,
reg_portName, reg_portType, slData);
supplierNames.addElement("new supplier name");
/* update service domain's service information
// assume the services object has provided a method to
// add a new service with a registered key and additional
// descriptive information. The object will use the key
// to make a setServiceInfo call with the new descriptive
// information myself.addService(servicekey, information_string);
|
结束语
本文演示了如何掌握自安装服务域之后,服务域不断累积的那些信息。典型的 Web 服务应用程序客户机并不需要这些信息。然而,运行人员、管理人员和自动化软件程序会发现这类信息很有用。大多数时间内,这些程序需要使用 getter 方法获取这些信息,偶尔也会使用 setter 方法。维护自己的控制数据结构对象可以优化信息的管理。
在本系列的下一篇文章中,我们将讨论一个显而易见的问题:假设服务域已经具备管理以多种端口类型组织的许多服务实例的能力,那么我们该如何利用这种集成环境促进端口类型之间的交互呢?
请保持关注!
参考资料
作者简介  | 
|  | 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 服务、网格计算、OGSA 和自主计算等领域的高级技术项目。您可以通过
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 联系。
|
对本文的评价
|  |