基于SCA的ESB分析:
SCA:
为构建基于SOA的应用和解决方案提供了编程模型
致力于为服务构件以及连接各服务构件的访问方式而包容各种广泛的技术的模型
实现了组件与传输协议的解耦(即组件自由绑定协议,并且支持自定义的扩展绑定)Tuscany是SCA的JA V A实现,其基本架构如下:
重点关注tuscany的扩展机制:
Implementation:SCA 组件(Component)的实现方式,一个SCA 组件可以由各种语言或技术平台实现,如:POJO,EJB,Spring Bean,bpel 流程,各种脚本语言等等。
Binding:是SCA 的绑定(Binding)规范的实现,SCA 服务(Service)和引用(Reference)的绑定方式,即一个SCA 服务可以暴露为Web Service,Java RMI 服务,http 资源,jms 消息等等,一个SCA 引用也可以通过Web Service,RMI 调用,http 调用,jms 调用等方式调用远端服务。
Databinding:数据绑定方式,用与在Binding 中定义参数的传输格式,比如Web Service 的Binding 一般用XML 格式,SCA 的Binding 一般用SDO 格式,Jsonrpc 的Binding 一般用Json 格式等等。
Interface:是SCA 的接口(Interface)规范的实现,SCA 服务(Service)和引用(Reference)的接口暴露方式,一般有Java,WSDL 等类型。
这套扩展机制为整合各个平台的服务提供了基础。
ESB:
实现传输协议的转换
实现消息格式的转换
消息路由
数据集成
处理不同来源的业务
Tuscany中,对于传输协议的转换、消息格式的转换以及处理不同来源业务已经有所实现。具体分析如下:
传输协议的转换:通过绑定实现。比如一个SCA组件可以向外部提供一个ws的服务,同时在该服务的实现中加入一个corba服务的引用。这样,用户就可通过调用ws服务的方式,实现对corba服务的调用。且tuscany的扩展机制允许针对新的协议加入自定义的绑定。
实现消息格式的转换:通过数据绑定实现。Tuscany SCA支持的数据类型有XML、SDO、JAXB、DOM、JSON等等,其内部有一个消息转换器来实现不同格式消息之间的转换,这种转换机制并不都是一一对应的,而是传递的。比如SDO要转换成AXIO,但Tuscany并没有实现这两种格式的直接转换,而是将SDO转换成StAX,然后StAX再转换成AXIO。具体的转换路径使用Dijkstra最短路径算法获得。而数据绑定是绑定中的子元素,相当于tuscany已经实现格式转换,并且同样允许自定义的数据绑定加入。
处理不同来源的业务:相当于支持处理同步、异步的事件。SCA在向外界提供服务时,可以选择同步和异步两种方式(异步细分为Async oneway、Deferred response和Call back),用以处理不同来源的业务。
消息路由和数据集成,以及服务的加入(注册),删去(注销),修改(更新)是需要加入的功能。
以SCA架构为基础,或者说以Tuscany为基础实现ESB能节省很多工作。
SCA规范学习:
SCA装配规范
在SCA中,构件(或组件)是最小单元,多个构件可以构成组合构件,组合构件用以完成某个或某些相关的功能。构件有其具体的实现(即代码),而组合构件的创建与配置则需要SCDL文档来实现。
SCA装配规范即是针对构件不同的装配需求而规定了SCDL文档该如何写。
本学习文档针对规范中提到的SCDL文档中一些重要元素,采用结合tuscany中的例子的方式来帮助理解这些概念。
Component
以下为带有component元素的XML schema
autowire="xs:boolean"? requires="list of xs:QName"? policySets="list of xs:QName"? constrainingType="xs:QName"?>* policySets="list of xs:QName"?>* policySets="list of xs:QName"?/>* policySets="list of xs:QName"? wiredByImpl="xs:boolean"? requires="list of xs:QName"?>* policySets="list of xs:QName"?/>* (type="xs:QName" | element="xs:QName")? mustSupply="xs:boolean"? many="xs:boolean"? source="xs:string"? file="xs:anyURI"?>* property-value?
Component元素有如下属性:
?name(必须) –构件的名字。在同一组合构件里的所有构件中名字必须唯一。
?autowire(可选) –指示包含的构件引用是否自动连线,默认是false。
?requires(可选) –策略意图的列表。(策略框架规范)
?policySet(可选) –策略集列表。(策略框架规范)
?constrainingType(可选) –强制类型的名字。当指定时,构件的服务、引用和属性的集合,加之相关的意图将被限定为强制类型定义的集合。
component元素有0个或者1个implementation子元素,该子元素指定构件所使用的实现。Service
component元素有0个或多个service子元素,用于配置构件的服务。可配置的服务是由实现定义的。
service元素有如下属性:
?name(必须) –服务名字。必须匹配由实现定义的服务名。
?requires(可选) --策略意图的列表。
?policySets (可选) --策略集列表。
service元素有0个或者1个interface子元素。如果没有指定接口,那么实现为服务指定的接口有效。如果指定了接口,就必须提供一个与实现提供的接口相兼容的子集。
service元素有0个或多个binding子元素。如没有指定binding,那么实现为服务指定的binding有效。如果指定了,那些绑定会覆盖实现所指定的绑定。
Reference
component元素有0个或多个reference子元素,用于对其他服务的引用。
reference元素有如下属性:
?name (必须) –引用名。必须匹配实现中定义的引用名称。
?autowire(可选) –指示引用是否自动连线,默认是false。
?requires (可选) –策略意图的列表。
?policySet(可选) –策略集列表。
?multiplicity(可选) –定义了能连接到目标服务的引用的连线数目。
1..1, 0..1, 1..n, 0..n
?target (可选) –一个或多个目标服务URI的列表,依赖于multiplicty的设置。
?wiredByImpl(可选)–一个boolean值。默认”false”,指示实现动态地连线该引用。如果设置为“true”,表示这个引用的目标由实现代码在运行时设置。
reference元素有0个或者1个interface子元素。(含义与service中相同)
reference元素有0个或多个binding子元素。(含义与service中相同)
component元素有零个或多个property子元素。该子元素用于配置实现的属性数据值。其值可以通过多种方式获得。(属性元素直接赋值;通过引用包含该component的composite中某个属性来赋值;通过文件赋值)
component的SCDL文档例子
该例子是一个由两个component构成的composite,实现加法计算器功能,结构如下:AddServiceComponent实现加法功能,采用AddService接口,即其它本地类可以以AddService 接口调用它
CalculatorServiceComponent实现CalculatorService接口(其它本地类可以以CalulatorService 接口调用它),且其实现类调用了AddService接口(本地方式)
注:该例子中的连线都是本地方式,通过SCA的装配方式,可以直接完成接口到具体实现的对应,不用从Java代码中新建接口类,指定接口的实现。
例1:(例1作为第一个SCDL文档学习,会比较详细)
定义了一个名为AddServiceComponent的构件,并指定了它的实现类。AddServiceImpl代码如下:
public class AddServiceImpl implements AddService {
public double add(double n1, double n2) {
return n1 + n2;
}
}
现在需要定义一个新的构件,名称为CalculatorServiceComponent。该构件需要引用AddServiceComponent构件。首先,我们定义一个名为AddService的Interface 和一个名为CalculatorService 的Interface
代码如下:
public interface AddService {
double add(double n1, double n2);
}
public interface CalculatorService {
double add(double n1, double n2);
}
相应的,在实现CalculatorServiceComponent构件的java文件CalculatorServiceImpl.java中,应有以下代码:
public class CalculatorServiceImpl implements CalculatorService{
private AddService addService;
@Reference
public void setAddService(AddService addService) {
this.addService = addService;
}
public double add(double n1, double n2) {
return addService.add(n1, n2);
}
}
代码中使用了@Reference注释,表明该方法中含有引用。
而在CalculatorServiceComponent构件的SCDL文档部分,应该有以下内容:
指定了实现类,指明了reference的服务名称是addService(对应代码中,即是方法setAddService中的参数addService。此处的名称一定要一致,不然引用会失败),并
且指明了addService的target是AddServiceComponent。
最后将这两个component装配成一个composite。完整的SCDL文档如下:
targetNamespace="http://sample" xmlns:sample="http://sample" name="Calculator">
测试调用:
编写一个测试类
public class CalculatorClient {
public static void main(String[] args) throws Exception {
SCANodeFactory factory = SCANodeFactory.newInstance();
SCANode node =
factory.createSCANodeFromClassLoader("https://www.doczj.com/doc/9f16096967.html,posite", CalculatorClient.class.getClassLoader());
node.start();
//初始化部分将在SCA规范JAVA实现部分中进一步学习
CalculatorService calculatorService =
((SCAClient)node).getService(CalculatorService.class, "CalculatorServiceComponent");
// Calculate
System.out.println("3 + 2=" + calculatorService.add(3, 2)); node.stop();
}
}
运行后得到结果:
2010-12-20 19:36:30 org.apache.tuscany.sca.node.impl.NodeImpl
信息: Creating node: https://www.doczj.com/doc/9f16096967.html,posite
2010-12-20 19:36:30 org.apache.tuscany.sca.node.impl.NodeImpl configureNode
信息: Loading contribution:
file:/D:/tuscany-sca-1.6.1-src/samples/calculator/target/classes/ 2010-12-20 19:36:31 org.apache.tuscany.sca.node.impl.NodeImpl start 信息: Starting node: https://www.doczj.com/doc/9f16096967.html,posite
3 + 2=5.0
2010-12-20 19:36:31 org.apache.tuscany.sca.node.impl.NodeImpl stop 信息: Stopping node: https://www.doczj.com/doc/9f16096967.html,posite
该例子通过代码中的@Reference以及SCDL中对reference元素name属性和target 属性的定义,将AddService接口的实例addService链接到了接口的实现部分(AddServiceImpl.java)。
例2:
同样是例1中的calculator加法功能,这次我们用ws的方式提供add服务,这需要在接口AddService中添加@Remotable注释:
@Remotable
public interface AddService {
double add(double n1, double n2);
}
同时,在其实现类中添加@Service注释
@Service(AddService.class)
public class AddServiceImpl implements AddService {
public double add(double n1, double n2) {
return n1 + n2;
}
}
调用该服务的CalculatorServiceImpl.java不用做任何改动。
而相应的,SCDL文档改为:
targetNamespace="http://sample" xmlns:sample="http://sample" name="Calculator"> uri="http://localhost:8080/sample-calculator-ws-webapp/AddServ iceComponent"/>
AddServiceComponent构件中声明了服务,该服务的接口名称以及该服务的绑定协议。CalculatorServiceComponent构件中指明了引用服务的接口名称,绑定协议以及该ws服务的具体接入点(uri)
该例子通过@Remotable注释和@Service注释的使用,结合reference元素和service 元素中interface子元素和binding子元素的帮助,完成了对于一个ws服务的调用。
Composite
Composite被用于在逻辑分组中装配SCA元素。其在SCA 域中是组合的基本单元。SCA 组合构件包含一系列的Componet,Service,Reference以及内连它们的wire和像外部暴露它们的promotion,外加上用于配置构件的一系列property。
composite元素有如下属性:
?name (必须) –组合构件的名字。组合构件名字的形式是一个XML QName,在由targetNamespace属性标识的命名空间中。
?targetNamespace (可选) –composite声明所在的目标命名空间标识符
?local (可选) –composite中的所有component是否必须运行于同一个操作系统进程。Local=”true”指示所有的构件运行于同一进程。Local=”false”,是默认值,指示composite 里的不同的构件运行于不同的操作系统进程,这些component可能运行在某个网络的不同节点上。
?autowire (可选) –被包含的构件引用是否自动连线,默认为false。?constrainingType (可选) –constrainingType的名字。当指定的时候,组合构件的服务,引用和属性的集合,再加上相关的意图,都会受限于costrainingType所定义的设置。?requires (可选) –策略意图的列表。
?policySets (可选) –策略集列表。
composite包含0个或多个property,service,component,reference,wire以及included composites。
Composite与Component关系:
Component包含配置化的实现,该实现承载其Composite的业务逻辑。
Component提供服务并引用其他的服务。
Composite定义了Composite所提供的公开服务,这些服务能被Composite的外部访问(采用promotion)。Composite引用描述该Composite依赖于其外部其他地方所提供的服务。wire描述Composite中的Component服务与Component引用之间的连接。
关于promotion:
Composite级别的服务是对该Composite中某一个Component的某一服务的提升。
即Composite服务实际上是由其内部的一个Component所提供的。
Composite级的引用是对一个或多个Component的一个或多个引用的提升。
注:只要所有的Component引用相互兼容,多个Component引用能被提升为同一个Composite 的引用。在多个Component引用被提升为同一个Composite的引用的地方,它们都共享相同的配置,包括相同的目标服务。
Composite中的reference元素:
与component中的reference类似,当多了一个promotion的子元素,且在composite的reference 中,promotion子元素是必须的。
?promote (必须) –标识一个或多个被提升的构件引用。该值是用空格分隔的,形式为
另外,composite的引用可选地指定一个接口(interface),multiplicity,required intents和bindings。只要是没指定的方面,都默认为被提升component的引用配置。
Promotion举例:
要实现的composite结构如下
该composite(MyValueComposite)包含一个component(MyValueServiceComponent),需要提升的有一个Service(MyValueService)和两个Reference(Customer Service和StockQuote Service)。
SCDL文档如下:
targetNamespace="https://www.doczj.com/doc/9f16096967.html," name="MyValueComposite" > class="services.myvalue.MyValueServiceImpl"/> promote="MyValueServiceComponent"> interface="services.myvalue.MyValueService"/> promote="MyValueServiceComponent/customerService"> promote="MyValueServiceComponent/StockQuoteService"> interface="services.stockquote.StockQuoteService"/> Composite中的Wire: 用途:将Composite中源构件引用连接到目标构件服务(引用->服务) 方法: 通过使用reference的target属性来配置一个构件引用(component中例子1中的方法) 通过作为composite元素的wire子元素来定义 wire 元素有如下属性: ?source (必须) –标识源构件引用名. 格式: source所指的地方是一个构件的引用。如果源构件只有一个引用,那么对referencename的指定是可选的。 ?target (必须) –标识目标构件的服务。格式: target所指的地方是一个构件的服务。如果目标构件只有一个带有兼容接口的服务,那么对service-name的指定是可选的。 注:wire仅仅能连接包含于同一组合构件中的源与目标 wire举例: 在promotion举例中MyValueComposite的基础上,增加了一个component (StockQuoteMediatorComponent),该composite的结构如下: SCDL文档: name="MyValueComposite" > promote="MyValueServiceComponent"> interface="services.myvalue.MyValueService"/> class="services.myvalue.MyValueServiceImpl"/> class="services.myvalue.SQMediatorImpl"/> promote="MyValueServiceComponent/customerService"> interface="services.customer.CustomerService"/> promote="StockQuoteMediatorComponent"> interface="services.stockquote.StockQuoteService"/> port="https://www.doczj.com/doc/9f16096967.html,/StockQuoteService# wsdl.endpoint(StockQuoteService/StockQuoteServiceSOAP)"/> target="StockQuoteMediatorComponent"/> 关于自动连线(AutoWire): SCA提供了一个特性叫做自动连线,该特性可以帮助简化组合构件的装配。自动连线可以让构件引用自动连线到满足的构件服务上,而不需要创建引用于服务间显式的连线。当使用自动连线特性时,未被提升同时未被显式连接到组合构件中某个服务的构件引用会自动连线到同一组合构件中的某一目标服务。自动连线通过在组合构件中查找匹配引用接口的服务接口来完成该工作。 autowire属性可以用于组合构件中的如下任意元素: ?reference ?component ?composite 注:默认情况下,autowire的值为false Autowire举例: 仍然以wire举例中的MyValueComposite,但采用autowire的方式。 SCDL文档: name="MyValueComposite" > promote="MyValueServiceComponent"> interface="services.myvalue.MyValueService"/> class="services.myvalue.MyValueServiceImpl"/> class="services.myvalue.SQMediatorImpl"/> promote="MyValueServiceComponent/customerService"> interface="services.customer.CustomerService"/> promote="StockQuoteMediatorComponent"> interface="services.stockquote.StockQuoteService"/> port="https://www.doczj.com/doc/9f16096967.html,/StockQuoteService# wsdl.endpoint(StockQuoteService/StockQuoteServiceSOAP)"/> 从SCDL文档中可以看到,我们没有显示的声明任何wire,或者针对reference指定target 值。只是在声明MyValueServiceComponent构件时,将该component的autowire值设为true。实现了自动连线。 ComponentType与constrainingType: componentType: 概念:针对一个component的implementation,是对某个implementation中的service、reference、property的规定。componentType文件名与implementation文件相同,但后缀不同(.componentType),而componentType文件的位置依赖构件的实现类型。以例子1(java实现)中calculator的扩展(提供了加减乘除功能)为例,下图是该例子的包结构: componentType文件的内容是对service、reference、property的定义,以https://www.doczj.com/doc/9f16096967.html,ponentType为例: SCA在配置一个implementation时分为两步: 1.在实现文件中配置。在上述例子中,即是代码中的注解内容(@remotable、@reference等) 2.第二步用于补充第一步(可选)。即当第一步无法完成实现的配置时,比如代码中没有注解,SCA会查找该implementation对应的componentType文件,从而完成配置。 constrainingType: 概念:SCA允许一个构件及其相关的实现被一个constrainingType限定。在自顶向下的设计应用中,constrainingType提供了辅助功能。 一个constrainingType带有service、reference以及property子元素,并且其子元素上可 以应用意图(intent)。constrainingType与任意的实现无关。因为与实现无关,所以其不能包含任意实现相关的配置信息或默认值。特别地,其不包含绑定(binding)、策略集(policySet)、属性值以及默认的连线信息。 constrainingType相当于给component及其implementation定了一个“形”,。任意指向了一个constrainingType的构件配置都受该“形”的限制。 下面展示了名为"MyValueServiceComponent"的构件的内容,此构件受限于myns:CT的constraingType。 constrainingType="myns:CT> class="services.myvalue.MyValueServiceImpl"/> interface="services.myvalue.MyValueService"/> target="StockQuoteMediatorComponent"/> targetNamespace="https://www.doczj.com/doc/9f16096967.html,"> interface="services.myvalue.MyValueService"/> interface="services.customer.CustomerService"/> interface="services.stockquote.StockQuoteService"/> 注:当一个实现被constrainingType限定,那么此实现必须定义constrainingType中指定的所有服务、引用以及属性。一个实现可能包含附加的服务、附加可选的引用以及附加可选的属性,但不能包含附加却非可选的引用或附加却非可选的属性。对于附加的服务,不可以被上层的composite提升,确保constrainingType不被composite改变。 以composite作为implementation 通过使用component元素的https://www.doczj.com/doc/9f16096967.html,posite子元素,将组合构件用作构件实现。相当于该composite是实现的方式变为一个component。 注:当一个组合构件用作构件实现时,其定义了可见性的边界。此组合构件中的构件不能被使用构件直接引用。使用构件只能连线到被使用组合构件的服务和引用,并可以为此组合构件的任意属性设置值。此组合构件的内部结构对使用它的构件是不可见的。 例子:下图为该SCA设计总体结构 共有3个composite(1个Outer,2个Inner),Outer用以composite作为implementation的方式使用两个Inner。 https://www.doczj.com/doc/9f16096967.html,posite文件: targetNamespace="http://sample" xmlns:sample="http://sample" name="OuterComposite"> target="TargetComponent"/> target="TargetComponent2/InnerTargetService"/> https://www.doczj.com/doc/9f16096967.html,posite文件: targetNamespace="http://sample" xmlns:sample="http://sample" name="InnerComposite"> promote="InnerSourceComponent"> promote="InnerSourceComponent/targetReference"> callbackInterface="composite.SourceCallback"/> promote="InnerSourceComponent/targetReference2"> callbackInterface="composite.SourceCallback"/> https://www.doczj.com/doc/9f16096967.html,posite文件: targetNamespace="http://sample" xmlns:sample="http://sample" name="InnerComposite2"> promote="InnerTargetComponent"> callbackInterface="composite.SourceCallback"/> 以include方式使用composite 组合构件可以通过包含其他的composite文件以接受附加的内容。 被包含的组合构件的语义是:通过在使用它们的组合构件中,将被包含的组合构件内容嵌入到使用它们的组合构件的https://www.doczj.com/doc/9f16096967.html,posite文件中。 注:include是内容的嵌入。即用被包含的组合构件的文本内容替换掉使用它们的组合构 件的include语句。 Include语句: 例子:同wire中的例子,现在我打算将MyValueCompsite划分为3个composite MyValueService:定义MyValueCompsite中所有的Service MyValueComponents:定义MyValueCompsite中所有的Component MyValueReference:定义MyValueCompsite中所有的Reference 包含这3个composite的新组合构件名为MyValueCompsite2,MyValueCompsite以include的方式使用这3个composite 结构体如下: https://www.doczj.com/doc/9f16096967.html,posite文件: targetNamespace="https://www.doczj.com/doc/9f16096967.html," xmlns:foo="https://www.doczj.com/doc/9f16096967.html," name="MyValueComposite2" >