| |||
|
作者:Sebastian Lik-Keung Ma
演示在 Oracle SOA Suite 11g 中如何使用 OSB 和 EDN 实现极端分离。
2014 年 1 月
下载
Oracle SOA Suite
Oracle JDeveloper
在实际的组织中,针对不同平台用不同的编程语言(如 Java J2EE、C# .NET、C++ 等)编写软件程序。面向服务的架构 (SOA) 允许这些应用程序共存,同时根据业务变化的需要逐步提供新服务。
本文通过一个完整的、端到端的工作示例演示 C++ 应用程序如何在 SOA 基础架构中生成一个事件以及 JDeveloper 应用程序如何使用该事件。
虚构的制造公司 SM Corp. 公司使用外部公司 HR Contracts Private Limited 的 HR 外包服务。HR Contracts 每天将新合同工的批处理文件发送给 SM Corp.。但现在 SM Corp. 需要有关这些新合同工的更实时的信息,因为他们经常在被 HR Contracts 招聘的当天就到公司工作。在 SM Corp. 内部,许多其他应用程序还需要为这些新合同工创建记录,这样他们才能开始工作,包括人力资源系统、安全访问控制系统和医疗保险系统。
SM Corp. 已经安装了 Oracle SOA Suite。除了现有的批处理,还应向感兴趣的订阅方发布实时信息。此外,两家公司都希望在开发、部署和事务处理方面尽量不要影响现有系统和流程。下面图 1 所示架构显示了具体实现方式:
OSB 在安全性、物理位置业务术语以及数据结构和通信协议方面提供了企业级的分离。应用程序调用代理服务;OSB 则将这些调用路由到所配置的各个业务服务。
不同应用程序彼此独立运行。我们不希望一个应用程序的事务在另一个应用程序出现故障、降级或关闭时发生堵塞或失败。单向调用、异步 Web 服务调用以及 JMS(Java 消息传递服务)和 AQ (Advanced Queuing) 等消息队列都可以提供异步功能。图 1 中的架构使用 EDN,这是一种更高级、更抽象的业务事件发布和订阅方式,不涉及 JMS 的底层连接细节。请注意,这还映射到事件驱动式架构 (EDA)。
该架构的事件生成部分独立于事件使用部分。控制流模型如下所示(参见图 1):
事件生成流程到此结束。
事件使用流程到此结束。
您很快可以看到,Web 服务、OSB 和 EDN 构成核心 SOA 要素,让这种极其松散耦合的架构成为可能。最终结果是事件生成部分完全独立于事件使用部分。
首先介绍事件生成部分。
在本文中,事件生成部分的三个主要参与者包括:
通过 Web 服务调用时,此应用程序将向 EDN 发布 NewEmployee 事件。控制将立即无阻塞返回 Web 服务调用方。
此 SOA 组合应用程序只包含一个具有以下事件定义的 BPEL 组合(参见图 2):
NewEmployee 事件的负载包含 3 个字符串:din、lastname 和 firstname(din 指员工 ID)。
BPEL 组合也只公开一个名为 process 的 Web 服务方法(参见图 4)。调用 process 方法时,此组合发布 NewEmployeeEvent:
放大 BPEL,可以看到名为 Assign_ReceivedInput 的 Assign 活动和名为 Invoke1 的 Invoke 活动(参见图 5)。
Assign_ReceiveInput 活动仅包含以下简单映射(参见图 6):
Invoke 活动发布 NewEmployeeEvent(参见图 7):
总而言之,HelloPublisher 的 Web 服务方法 process 只发布 NewEmployeeEvent 并立即返回一个字符串值。
要发挥 SOA 的优势,最佳做法是通过中介或代理提供 Web 服务访问。因此这些服务的使用方将不受实际 Web 服务的位置和实现变更的影响。
通常,我们使用 OSB 控制台按以下顺序将 OSB 的配置定义为资源:WSDL、业务服务和代理服务。图 8 给出了结果示例:
WSDL 资源包含实际目标应用程序的 Web 服务的位置。例如,HelloPublisher Web 服务的 URL/路径为:
http://<主机名>:<端口>/soa-infra/services/default/HelloPublisher/bpelpublisher_client_ep?WSDL
WSDL 资源还导入包含用于 HelloPublisher 事件定义的类型的 XSD 文件。
业务服务使用前面定义的 WSDL 资源。这将绑定到实际 Web 服务方法的端点 URI。
最后,OSB 代理服务将包含到前面定义的业务服务资源的路由。
HttpHelloOsbClient 是一个 Windows C++ 控制台应用程序,它只知道和调用 OSB 提供的代理服务。它触发将要发布的 NewEmployEvent。
此 C++ 程序使用轻量级本机代码 Windows Web 服务 API (WWSAPI) 来使用 Web 服务。下面提供了有关如何使用 WWSAPI 的详细信息:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd430435(v=vs.85).aspx
从外部应用程序的角度来看,下面是验证 OSB 代理 Web 服务 URL 路径的步骤:
http://<主机名>:<端口>/sbconsole
/service/test/YourService
)http://<主机名>:<端口>/service/test/YourService?WSDL
)例如,如果 OSB 运行在 localhost 上,端口为 7001,则代理服务的 WSDL 为:
http://localhost:7001/HelloEDN/ProxyService/HelloEDNProxyService?WSDL
图 13 显示调用 OSB 代理服务的 C++ 代码片段:
提示:下面图 14 中的代码注释包含如何让 WWSAPI 发挥作用的提示。
下面图 14 显示内部代码片段(含代码注释)。
#include <string> #include <iostream> #include <sstream> #include <WebServices.h> #include "WsHelpers.h" #include "HelloPublisherURL.wsdl.h" // produced by wsutil.exe #include "XMLSchema_-1547443801.xsd.h" #include "XMLSchema_583194075.xsd.h" using namespace std; HRESULT InvokeOsbWebService(__in wstring url, __in wstring din, __in wstring lastname, __in wstring firstname, __out wstring& result, __in_opt WS_ERROR* error) { HRESULT hr = S_OK; WsHeap heap; HR(heap.Create(2048, // max size 0, // trim size 0, // properties 0, // property count error)); // Required settings for SOAP WS_CHANNEL_PROPERTY channelProperties[2]; WS_ADDRESSING_VERSION addressingVersion = WS_ADDRESSING_VERSION_TRANSPORT; channelProperties[0].id = WS_CHANNEL_PROPERTY_ADDRESSING_VERSION; channelProperties[0].value = &addressingVersion; channelProperties[0].valueSize = sizeof(addressingVersion); WS_ENVELOPE_VERSION envelopeVersion = WS_ENVELOPE_VERSION_SOAP_1_1; channelProperties[1].id = WS_CHANNEL_PROPERTY_ENVELOPE_VERSION; channelProperties[1].value = &envelopeVersion; channelProperties[1].valueSize = sizeof(envelopeVersion); WsServiceProxy serviceProxy; HR(WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL, 0, channelProperties, WsCountOf(channelProperties), &serviceProxy, error)); // C++ std::string... c_str() returns a readonly const pointer to a const object // We will not modify any string contents here, just get the first character and let // WS_STRING retrieve the rest of the string value based on its length WS_STRING wsstrURL; wsstrURL.length = url.length(); wsstrURL.chars = &url[0]; WS_ENDPOINT_ADDRESS address = { wsstrURL }; HR(serviceProxy.Open(&address, 0, // async context error)); // Initialize web service input and output args _NewEmployee employee; employee.din.length = din.length(); employee.din.chars = &din[0]; employee.firstname.length = firstname.length(); employee.firstname.chars = &firstname[0]; employee.lastname.length = lastname.length(); employee.lastname.chars = &lastname[0]; _processResponse* pResponse = NULL; // response will be allocated by callee // Invoke the web service call HR(BPELPublisherBinding_process( serviceProxy, &employee, &pResponse, heap, NULL, 0, NULL, error)); // the response as result if (0 != pResponse && pResponse->result.length > 0) { result.assign(pResponse->result.chars, 0, pResponse->result.length); } HR(serviceProxy.Close(0, // async context error)); return S_OK; }
向 EDN 发布业务事件之后,现在我们讨论事件使用。
事件使用部分的主要参与者包括:
此 SOA 组合应用程序监听 NewEmployeeEvent 并调用代理 Web 服务(图 15)。
为了订阅 NewEmployeeEvent,HelloSubscriber 使用了类似 HelloPublisher 中的事件定义(参见图 16 和 17)。
NewEmployee 事件的负载:
可以有多个应用程序订阅 NewEmployeeEvent。在本例中,名为 SampleHRApp 的自定义 Oracle ADF Web 应用程序希望接收 NewEmployeeEvent 通知,这样如果该员工记录不存在,它可以在数据库中自动创建新员工。
事实上,HelloSubscriber 可以利用 ADF,直接使用 SampleHRApp 的 ADF 服务数据对象 (SDO)。但为了与最佳实践保持一致,HelloSubscriber 还是使用 OSB 代理服务。
OSB ProxyService、BusinessService 和 WSDL 的步骤与本文事件生成一节中介绍的 OSB 部分类似。
为简单起见,我们将 SampleHRApp 的代理服务的 WSDL 设定(同样,采用类似于“事件生成”一节中介绍的步骤)为:
http://localhost:7001/SampleHR/ProxyService/SampleHR?WSDL
收到 NewEmployeeEvent 后,BPEL 将调用图 18 中所示的 Web 服务。
提示:在此将 Transaction Participation 属性保留为 NEVER 非常重要,因为我们不准备参与任何全局事务协作(即目标 Web 服务实现将管理自己的事务)。
HelloSubscriber BPEL 只包含两项活动:Assign 活动和 Invoke 活动(图 19):
AssignToInvoke 活动只是将来自 NewEmployeeEvent 负载的输入变量(din、lastname 和 firstname)传递到被调用的 Web 服务方法 createEmployee 的各相应输出变量(暂时忽略字符串和整型之间的类型不匹配,参见图 20 和 21)。
本文介绍采用 SOA 和 EDA 原则在不同应用程序之间进行端到端集成的工作示例。重点介绍开发、部署和运行时的松散耦合。图 22 展示了实现概览,具体的已部署模块(可执行文件、jar 文件等)以橙色框突出显示。
图 23 是最终结果的概览图。自定义的 C++ 程序 HttpHelloOsbClient.exe 调用内部方法 InvokeOsbWebService(…)。这触发发送了一个新的员工事件。自定义的 Web 应用程序 SampleHRApp 负责自动新建员工记录。
Sebastian Lik-Keung Ma 目前是 Sembcorp Marine Limited 的软件应用程序开发经理。他曾在新加坡和德国做过 20 年的软件开发工程师和架构师,专攻 C++、Java 和 C#,提供分布式计算软件应用程序。他拥有英国谢菲尔德大学的理学硕士学位。