`
tjsy4050
  • 浏览: 21212 次
  • 来自: ...
社区版块
存档分类
最新评论

我眼中的SOA,以及在实际项目中的应用经验(转)

soa 
阅读更多

我眼中的SOA,以及在实际项目中的应用经验

    包包所在的Group从事的是为欧美航空公司量身打造客机管控平台的项目,有以下几个特点:

      1).按照每个Team十几人的样子,整个开发Group大约几百人的样子;

      2).项目投入的人力和财力都很大;

      3).扩展性和重用性要求都很高;

      4).根据Function的不同,各个Team的开发周期也不一样,有的甚至要几年。

      5).跨平台可移植能力。之前已经写好的很多项目是基于J2EE的,现在要把它们转换为.NET平台。

    基于以上多方面考虑,整个Group采用了SOA的架构。我们没有像IBM那样设计一整套 企业级服务总线 (ESB),而是针对具体业务具体实现,来一个需求就暴露一个接口方法。其实,说白了,就是用JavaBean封装企业逻辑,然后通过WebSevice的方式来暴露,只是.NET没有JavaBean,而是用WF工作流来代替。

    说了这么多,就是为了告诉大家,SOA这东西,如果不是大规模的项目,比如说开发人数(用户人数倒在其次)、需求变更(这个倒是用户care的)、跨平台(比如说.NET的WebService如何给Java程序使用),那还是算了吧,有点杀鸡用牛刀的感觉。因为SOA中有太多的地方需要你去关注,不是说加了几个WebService把对数据库的访问放到WebMethod中就是SOA了。 

    SOA的概念我就不说了,反正都是书本上的东西,多少有些教条,对于入门的朋友,还是更希望看到它在项目中是怎么实现的。下面就详细谈一谈.NET项目中的企业级实施:

    工欲善其事,必先利其器。我们使用的是微软提供的Service Factory。也就是Web Service Software Factory。我记得在张逸的博客上看到过只言片语的介绍:

http://www.cnblogs.com/wayfarer/archive/2007/01/30/634899.html。它是作为一个插件安装到Visual Studio.NET中的,于是,作为开发者,在拿到UseCase后,只要定制一下Request和Response消息中的实体,然后在类图设计器中把契约、方法以及参数(Request)、返回值(Response)摆放并连接正确,右键点击“生成代码”菜单,就会基于SOA架构自动为我们生成6个project,截两个图给大家瞅瞅吧(这里没敢用公司的项目截图,于是顺手写了一个ServiceFactory出来):

    1.Schema

    假设有一个GetUserName方法,签名如下:

        string GetUserName(string userId)

    那么,我们就要在Request.xsd中定义方法的参数:

        <xs:element name="UserId" type="xs:string"/>      

    而在Response.xsd中定义方法的返回值:

        <xs:element name="UserName" type="xs:string"/>

    说白了,这里就是定义xml元素和.NET的实体类型之间的对应关系。所有的学问都在这里,不熟悉xsd的要回去补课了。 

    很遗憾,ServiceFactory中的Schema图只有在VS2005中才是可视化的,在vs2008下只能是手动编辑,所以我是最讨厌做定义shcema的事情了,上面的代码只是string类型的,如果是自定义实体、数组、字典……

    在SOA中,我们遵循为每个方法定义一对Request和Response的原则(而不是一个契约的所有方法共享同一套Request和Response)。只有这样,在传来传去的Message中,才不会有太多的信息冗余。比如说,我有2个方法,分别是:string GetUserName(string userId)和UserInfo GetUserInfo(string userId),如果2个方法的Response定义在一起,那么不管调用那个方法,每次返回的Response,不是多带了一个string类型,就是多带了一个UseInfo类型,相应的消息(也就是xml文件)体积就会变大。

    此外,Java程序是不认识.NET WebServce中的List<T>的,它只认识数组,于是为了遵守SOA的跨平台兼容性,我们在Schema中只能定义数组,即使是coding,也要刻意对集合执行ToArray方法。于是,很多人都会抱怨,在调用WebService的一方,仍然是用.NET写的,于是,我们又要把接收到的数组费老大劲转换回List<T>。——这也是我认为小型项目没必要用SOA架构的一个原因,你会被折腾疯的。

    此外,虽然大多数SOA系统要求平台兼容,但实际应用中却发现做来做去竟然全都是.NET平台,而极少甚至是没有Java平台。这时候发现数组和集合的转来转去是白做了,性能受到很大影响。这种情况其实也是有解决方案的,就是先全都做成.NET平台的集合,然后哪些方法需要暴露给J2EE使用,再包一层面向数组的WebService就好了。但是,这个方案也只适用于项目刚起步一穷二白啥都没有的情况,对于从Java平台升级为.NET平台,或者两套系统都有残余物(破烂很多又不舍得扔),那么牺牲性能就再所难免了。

 

    2.Contract图

image

    注意到,基本上每一个方法都对应Request和Response两个Message,当然如果一个方法不带参数,那就不需要Request这个参数了。但是一个void方法呢,比如说Save?是不是也可以省略Response了呢?——NO。这就涉及到SOA架构的另一个原则:方法一律返回object类型的数组,其中数组中第一个值返回成功与否,如果成功,数组的第二个值才返回相应的结果,当然如果是Save,只需要第一个参数判断是否Save成功就够了,不用设置数组第二个值。

 

    3.Project中的WF和Service

image

    WF我就不多说了,Group里面好几个Team一大帮人专门就是写这个的,那个WF图中的框框线线画得不要太复杂哦,居然还打印出来比美感。我看了两眼就烦了,毕竟我是做Silverlight的,只负责调用这些service。但是包包有时候也要写一些Service Factory。为什么呢?因为Silverlight是跨域的,所以需要在service添加跨域文件。但这是SOA啊?写WebService的人可能和你不是一个Team的,他不可能修改他的project来满足你的额外要求,或者,这个WebService是用Java写的……没辙了么?还有最后一招,就是自己再写一层WebService,并添加这个异步文件,并在其中调用原有的WebService,然后我的Silverlight再调用自己写的这个WebService。

    嗯,如果说什么是SOA,那上面这个就是活生生的例子了——松耦合,使得A不必直接调用S,A可以先调用B,然后让B调用S。

    可这也暴露出SOA的另一个缺点——部署麻烦。比如说,还是上面的那个例子,由原先要部署一个Service,变成了要部署两个Service。就是说,我要在ReleaseNotes里面添加这一笔配置信息。如果这个Service被部署在其他Server上,那么我就要准备好几份ReleaseNotes。要知道,部属的事情不一定是开发这套code的人去做的,所以WebService的多少和出错的几率是成正比的。

    SOA中部署的复杂,导致了一旦其中有一个环节出错,就摸不着头脑找不到是那一层的问题。于是我们要添加异常处理机制,把异常从底层到高层逐层往上冒。所以,对于一个Service方法,返回的不是成功执行后得到的string或int,而是一个object数组。第一个参数表示成功与否,如果是否,那么第二个参数就要传递捕获到的异常信息。

    另一个排查Service异常的方法就是借助于SoapUI这个工具了。这是用Java开发的unity,我们指定WebService的地址和要调用的方法,然后把一个消息的XML文本填进Request中,就可以获取到Response了。当然,如果获取不到,要么是Request写错了,要么就是这个Service有问题。这个是需要足够的耐心的,哈哈,我对面的老兄就是做这个的,反正我是没有这个耐心的。

 

    最后说一说SOA的坏话:

    刚才字里行间提到了一些SOA的缺点,一是部署麻烦,二是为了平台兼容而牺牲的性能。

    此外,在SOA的每个Project中,都存在两套实体,一套是调用上一层WebService时产生的代理,另一套是这project自己的实体逻辑。这就会导致在同一个类中同时面对两套类似的实体,不仅仅是类似,有时候连类的名称和成员都一样。但是由于它们属于不同的namespace,所以我们在调用WebMethod后,立即要做的就是进行二者间的转换。在接受数据时,把上一层实体转化为本层实体,在发送数据时,又把本层实体转换回上一层实体。为了区别类的名称不同,我们在调用的时候直接在类前面加上很长很出的namespace前缀。要知道,这样的代码,可读性是很差的。

    难道就没有解决办法么?

    我见过有同事直接使用上一层的代理实体,而不再定义适用于本层的新实体。这样代码确实简单了不少,但是耦合性呢?完全没有了。一旦上层实体变动,那么下层也要跟着大规模变动,会导致很多不可预知的bug。——这样做是不对的。

    实话实说,无解。做SOA就要面临这样的痛苦。

 

    各位,看到以上若干文字,你还敢轻易就谈SOA么?SOA不是时尚,不是说你多写几个WebService就是SOA了。SOA是一种企业级架构,当你的项目繁芜到一定规模,当你的人力到达一定规模(一两个人多少有点扯淡),自然而然要使用SOA的架构了。这时候,把开发人员按照WebService层来划分Team,两头是UI和DB。Team直接靠Service通信,而不再是接口。而各个Team之间的边界也不在是exe或者AppDomain,而是URI(之所以不说是Server,是因为两个Service可能部署在同一台Server上,只是端口不同)。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics