《实践与思考》系列连载(5)——问答Hprose,以及关于技术与开源的思考

引子

之前我写过一篇文章,讨论了在XML Web Service或者WCF中,多次发起异步调用可能导致的问题,请参考http://www.cnblogs.com/chenxizhang/archive/2010/05/31/1747812.html

在这一片文章中,我介绍了问题的症状以及发生的原因,及其解决方法。这篇文章收到了一些反馈,其中有朋友介绍到了Hprose这个产品。我后来也实际用了一下,确实在Hprose中是可以避免这种问题的。(有兴趣的朋友,可以参考这里 http://www.cnblogs.com/chenxizhang/archive/2010/05/31/1747812.html#1873774

本着实践和求真的精神,我也专门对Hprose这个产品也做了一些深入的探究,包括和他们的开发团队做了一些交流。同时,因为这个产品是商业开源(与一般的开源还不太一样),所以也引发了一些对技术和开源的思考。今天整理出来,给大家参考参考

 

什么是Hprose?

Hprose (High Performance Remote Object Service Engine) 是一个商业开源的新型轻量级跨语言跨平台的面向对象的高性能远程动态通讯中间件。它支持众多语言,例如 C++, .NET, Java, Delphi, Objective-C, ActionScript, JavaScript, ASP, PHP, Python, Ruby, Perl 等语言,通过 Hprose 可以在这些语言之间实现方便且高效的互通。

Hprose 是商业开源软件,在取得 Hprose 商业使用授权后,您可以将它用于您所拥有所有权的商业项目的开发当中,Hprose 商业使用授权没有开发者人数、服务器CPU数量、授权年限等限制,但对二次分发有一定的特别要求。如果您所开发的项目或产品涉及到对 Hprose 的二次分发,还需要取得相应的二次分发授权。

关于Hprose的详细介绍和有关资源,请访问其官方网站: http://www.hprose.com

 

问答Hprose

下面记录了我和他们的团队几次邮件交流讨论到的几个一些问题。取自邮件的原文,比较直白,但是原汁原味。

问:协议层面,我关心的是主要包括两个层面:Hprose如何支持不同的传输协议(例如Http和Tcp或者还有更多的),以及Hprose的数据序列化格式是否有公开的规范书?

答:协议实现方面我们是这样考虑的,通过uri来区分不同的协议,目前http://、https://开头的是表示Http客户端和Https客户端,他们由HproseClient的子类HproseHttpClient来具体实现。当以后提供tcp、udp等实现后,tcp://、udp://开头的表示TCP客户端和UDP客户端,他们可能会分别由HproseClient的子类HproseTcpClient和HproseUdpClient来分别实现,每个不同类型的客户端肯定会有他们特有的属性,并且内部通讯机制也完全不同,所以由不同的子类实现是必要的,但是它们又都是HproseClient的子类,在不需要设置特殊属性的情况下,可以使用HproseClient来统一访问,另外,当tcp、udp版本实现之后,我们会给HproseClient类提供一个工厂方法,通过不同协议的uri来创建不同类型的HproseClient对象不需要指定使用哪个子类,这样就可以将不同协议的实现进行统一管理了。

【我的评论】这个做法也是可以的,采用类似于.NET中WebRequest那种工厂方法模式的设计,也能保证一定的灵活性。虽然与WCF那样通过配置文件直接就可以改变传输协议(其实是所谓的binding)的做法是有些差别的。


而数据序列化协议部分,目前我们没有公开的数据格式描述文档,这部分我们以后可能会通过标准或者专利的形式进行发布。格式的公开不是一个简单的问题,它有多方面的因素所制约,AMF这个格式从最初制定实施到最后公开也是花了5-6年的时间(2002-2006年制定,2007年公开发布),对于Adobe那样的大厂商尚且如此,对于我们而言也需要慎之又慎啊。

【我的评论】可以理解

 

问:数据传输层面,有没有办法由用户决定是否进行加密或者压缩?

答:可以由用户来决定加密和压缩。目前压缩是通过直接使用http的压缩方式,服务器端有个IsCompressionEnabled开关,当打开这个开关后,客户端只需要添加Accept-Encoding: gzip,deflate这个http头,就可以开始压缩传输了。不过说实话,压缩开启之后,数据量不够大时,数据压缩之后反而会增大,而且数据压缩传输会对CPU消耗相当严重,所以我们是不推荐使用压缩传输的,因此文档中并没有提到IsCompressionEnabled开关,也没有说明客户端如何开启压缩传输的方法,这样可以避免用户在误用这个功能后抱怨占用太多CPU。至于用户自定义加密传输,我们现在还没有提供这个功能,一是因为https本身可以提供加密传输,在tcp上也可以通过tcp+ssl实现加密传输。因为ssl是标准的,所以要比用户自定义的方式可靠一些。另一个原因是,加密传输要保证安全性需要用到密钥交换技术,这个同样占用大量的CPU计算时间,我们在Hprose的前身PHPRPC中提供了这个功能,但是我们发现它带来的这个问题远远大于它所带来的好处(例如黑客可以通过这一点很轻松的发起DoS攻击,导致服务器计算资源耗尽而停止正常服务)。另外,密钥交换本身必须是一个同步调用的过程,在实现远程异步调用时,又要保证同步,会大大增加实现的复杂度,以及降低异步调用的效率。所以对于自定义加密方式传输的功能,我们目前的1.x版本中还不打算加入。当上面所说的几个问题能够找到有效的解决方法之后,我们会在Hprose 2.0或者Hprose 3.0中,以插件的方式来提供。

【我的评语】我们乐见这个设计的完善

 

问:是否有考虑过REST方面的支持?

答:REST相对于RPC是另外一种构架模式,Hprose提供了一种序列化机制,同时在这种序列化机制上构建了一个RPC机制。所以如果在Hprose的序列化机制上使用REST方式来构建系统也是完全可以的,但是这时候就完全不需要Hprose提供的客户端和服务器了,只需要Hprose的IO(序列化反序列化)部分就可以了。但是REST构架最大的问题是,它没有一个统一的标准,不能向RPC那样可以保证接口的互通性。而且应用的范围我认为也不应存在太大的交集,现在很多采用REST方式来提供类似Web服务功能的用法,大多都是对REST的误用,并没有发挥出REST本该发挥的优势。现在的REST已经像当年的XML一样被当成了万能灵药,而实际上,这已经背离了REST的本意。而Hprose既然是为RPC在XML火爆的年代中所犯下的错误做出的纠正,就不应该在REST火爆的年代犯下同样的错误。所以即使Hprose的序列化机制确实可以用于真正的REST应用,我们也不会明确的告诉大家Hprose可以做REST服务,因为在大多数人并不能真正认识REST的年代,这样说就是对用户的误导。

【我的评语】基本同意。对于REST,还是可以多从实践的角度去辩证地看待。

 

问:OnError事件的做法存疑,如果一个client发起了多次请求(方法名也是一样的),那么怎么区分呢

答:OnError事件确实存在您说的问题,这个问题我们在JavaScript、ActionScript、ASP这些版本中已经提供了在调用中直接提供跟调用绑定的错误处理回调的方式来解决了。但是C#、Java等这些版本我们在1.2版本中尚未提供这种解决方案(未提供的主要原因是用户对这个功能没有强烈需求)。但这个解决方案我们会在1.3版本中在所有语言中统一的。

【我的评论】这个机制还是需要完善起来,现在有用户对这个功能有强烈要求了不好意思

 

问:身份验证方面,我看到目前文档的说明是可以直接改写Http的头,添加Authencation这个标头。

  • 我要说的是,这个部分最好有改进。目前这种做法易用性不高,会让人生畏。ADO.NET Data Service早先就遇到这个问题,后来改进了
  • 身份验证和授权历来都是很重要的。身份验证本来就有标准的做法,例如给Client类型添加一个Credentials这个属性。而授权应该可以结合自定义Attribute或者给予Url的做法可能是较为合适的
  • 后续这个方面最好有一些例子

答:对于.NET版本,我们确实提供了如您所说的Credentials属性,这个属性我们没有在手册中列出(因为他在其它语言中不存在,另外,.NET的某些版本中也不支持这个属性,例如SilverLight,这跟.NET本身有关),但是在使用时您会发现他确实存在。在服务器端授权方面,我们提供了 OnBeforeInvoke 事件,在该事件中可以对认证授权进行统一处理。我们不使用 Attribute 的原因是,我们认为 Attribute 不是一种松散耦合的解决方案,它对所发布的服务具有侵入性,我们设计的目标是,任何可以直接用于本地的方法都可以直接发布为远程方法,而不需要对它做任何修改或修饰。这样才可以保证,对于我们不具有源码的类也可以无需包装就作为远程服务发布,并且还可以对授权做集中控制。

【我的评语】这个设计我认为还是要改进,让用户使用起来更加方便一些

 

问:开发工具和服务描述方面的支持

  • 这个部分最好有改进。例如我看到,如果直接在浏览器中输入服务的地址,会看到几个字符。那不是一个友好的设计。你可能主要是从开发者角度看这个问题了,而不是用户角度。
  • 其实这一点可以借鉴WCF或者Web Service的wsdl做法,人家好的东西也可以吸收的

答:WSDL存在的意义在于它是上一代静态远程调用机制所必需的中间语言,就如同CORBA的IDL、ICE的Slice一样。与IDL和Slice不同的是,在.NET中,WSDL是根据服务自动生成的(但是对于PHP、Python这些非.NET上运行的语言,它们无法自动生成WSDL,需要手工编写,所以在PHP、Python等动态语言中发布WebService是相当痛苦的一件事情)。而Hprose是新一代的动态远程调用,所以不需要WSDL这样的中间语言。通过浏览器来查看Hprose的服务地址显示的远程方法列表虽然对用户来说不够友好,但是相比WSDL来说,Hprose的输出还是简单易懂的多。所以,我想您所说的WCF和WebService提供的用户友好的界面应该是指的在线调试界面,而不是WSDL那个页面吧。针对这个用户调试界面,我们提供了忘忧草(在线试用版地址:http://www.hprose.com/nepenthes/)这样的专业调试工具,用户如果需要调试服务,可以直接在这个调试工具中输入服务器地址,这样不管用户的服务是使用何种语言在何种平台上发布的,都可以采用同样的方式进行调试。而且随着这个调试工具的升级,用户以后可以用更加友好的界面来调试之前的服务,而不需要连同服务一起更新后才能使用新的调试界面。这都是我们为什么要采用这种分离式调试工具的原因。而您所说的WCF和WebService服务发布后的调试界面是.NET中特有的,如果用其它语言发布WebService服务,并不会得到这样的调试界面,所以,不同语言下的WebService服务,用户体验会有相当大的差别,而且用.NET老版本发布的WebService服务也不可能使用新版本.NET发布WCF服务的调试界面来调试,当然这并不是微软的错,而恰恰这是微软的策略,因为只有这样才能将用户牢牢的绑定于.NET平台之上,并且让用户始终追求使用最新版本的.NET平台开发工具,只有这样才能保证微软持续盈利。而我们的出发点恰恰相反,我们是要为所有平台所有语言的用户提供统一的用户体验,所以我们采用了截然不同的设计方式。

【我的评语】提供类似于WSDL这样的服务描述(甚至有配套生成客户端那个接口的工具)肯定是对的做法,让用户使用起来更加方便才是目标。统一的用户体验固然是没错的,但统一的用户体验不是说统一的不高的用户体验。

 

问:如果服务器端有两个类,他们拥有同名方法,则它们是无法区分的。看起来是以最后注册的类为准。这个有没有什么可能的问题。也就是说,在Client端Invoke的时候,只是提供了functionName,而没有办法提供服务类的名称。

答:你说的这个问题,我们一开始就已经考虑到了。所以我们提供了一个别名机制,如果以类(或对象)为单位发布服务,则可以为每个类(或对象)指定一个服务名成空间(该名称空间在发布服务时会变为方法前缀),而对于以方法为单位发布服务时,则可以为每一个方法指定一个完整的别名,通过完整的别名,就可以区分在定义时名称相同的两个不同方法了。这部分在文档的后面部分(客户端部分)有比较详细的说明。
例如您的这个例子可以这样改写:

    class Program

    {

        static void Main(string[] args)

        {

            HproseHttpListenerServer svr = new HproseHttpListenerServer("http://localhost:2010/Hprose/");

            svr.Methods.AddInstanceMethods(new MyService(), "s1");

            svr.Methods.AddInstanceMethods(new MyService2(), "s2");

            svr.Start();

            Console.WriteLine("服务器已经准备就绪");

            Console.Read();

        }

    }

【我的评语】这是不错的

 

问:对于开源的理解

答:我对开源的认识,大概开始于10年之前第一次接触Linux吧。那时的Linux发展了已有10年之久,因为其开源,功能又不逊于Unix,因此许多公司都希望藉由Linux操作系统,来取代昂贵的Unix,以便在激烈的竞争中取得有利的地位,这方面的代表是RedHat、SuSE等商业化 Linux 版本。而另外一些个人或组织则为了教育科研的需要而开发了适合自己的Linux版本,这方面的代表是Slackware。还有一部分个人和组织则是为了兴趣和自由的理想而开发他们自己的Linux版本,这方面的代表是Debian、Gentoo等。虽然大家做的都是Linux,但是出发点不同,因此各个产品之间当然会出现很多的差异化,以致于想要开发一个可以在所有版本Linux都能运行的程序都是一件很困难的事情。Linux阵营因此而分裂,形成了诸侯割据的局面。各诸侯之间的争斗一直在继续,一批死去,一批新的又起来,何时才能统一,不得而知。
而Windows自始至今都是不开源的,也正是因为这样,Windows才保持了统一的局面,而没有像Linux那样四分五裂。尽管Linux阵营常常以Windows不开源为借口来打击Windows,但是Windows的霸主地位始终没有丝毫的动摇,相反因为Linux阵营的四分五裂,各个厂商之间各怀鬼胎,虽然势众,但并不能团结一致,甚至一些公司为了自己的私利,还跟微软签署了合作协议,例如Novell(SuSE和Mono的开发公司)。这就像苏洵在六国论中指出的“六国破灭,非兵不利,战不善,弊在赂秦”一样,Linux照这样发展下去最终必会败于Windows系统。
Linux的开源始于对自由理想的追求,但好的理想却常常会被恶的思想所利用,以致善始而不能善终。

 

我理解的开源是这样的:
OpenSource != Free != No Charge
开源的目的一般分为以下几种:
1、以自由作为理想的开源。代表为GPL开源许可。GPL的发起人Stallman是一个理想主义者,他认为软件是自由的,一个软件被开发出来之后,人人都可以修改它,人人都可以维护它,任何人都不可以剥夺别人修改软件的自由。但是他的这个理想常常被人误认为软件必须是要免费的。也就是被误认为 Free == No Charge,但实际上,Stallman并没有这个意思,而且他自己也确实在通过他的自由软件所盈利。
2、以打击竞争对手或者潜在的竞争对手为目的并把开源贡献者作为免费劳动力的开源,代表为Apache基金会、Mozilla基金会。Apache基金会幕后最大的支持者是IBM,IBM通过赞助Apache基金会来开发一些免费的开源项目,这些开源项目最大的特点是商业友好,也就是说,可以免费的拿来商用且不用继续开源,Apache的开源项目作为一个试验田,由众多小公司拿来免费使用,在使用过程中遇到问题并不能得到任何的商业支持,只能通过自己努力来解决,解决之后在反馈给Apache组织(当然也有大部分人并不会提交自己的努力成果)。而IBM则会拿Apache的那些半成品来经过自己的优化之后,做成高端产品,再以高价卖给高端客户。这样,低端客户有免费的Apache开源项目使用,虽然难用但因为不用花钱,所以就不会选择收费的其它低端市场定位的同类商业产品,尽管那些商业产品可能要比Apache的开源项目优秀的多。而高端用户有因为不在乎花钱,所以只买最贵的,因此IBM的产品就成了他们的首选。IBM通过这种方式有效的打击了低端市场的同类产品,使他们无法成长为可以跟IBM叫板的竞争对手。而Mozilla基金会可以说是一个复仇组织,当它的前身Netscape被微软的IE消灭之后,Mozilla就把打败微软的IE作为了首要目标,因此Mozilla的开源免费完全是为了解决掉微软的IE。不过螳螂捕蝉,黄雀在后,Mozilla经过这么多年的发展,虽然终于可以争取到一点IE的市场份额了,没想到Google的Chrome也进入了浏览器竞争市场,面对这两大高手,Mozilla要翻身恐怕是很难了。
3、为了让客户放心使用而开源。代表是微软和Hprose。微软现在有很多产品会对某些要求查看源码的客户开源,比如某些国家的政府,这种开源仅仅是为了让客户放心使用,证明自己没有留下什么后门。Hprose的开源也是类似的想法,对客户开源,让客户放心使用,当然如果客户遇到问题,因为手中有源码,也可以更容易的反馈错误,我们就可以更快的完成错误修正。所以,这种开源方式是一种双赢的做法。
4、盲目开源。代表是Sun。他在IBM的怂恿下,把Java开源了,最后IBM搞出了自己的JVM,赚翻了。而Sun自己一分钱没赚到,最后把自己公司都搭了进去,以致于最后落了个被Oracle收购的下场。

【我的评语】对于开源向来大家理解都不一,上述总结比较中肯。我个人也赞同:开源不等于免费


 

问:那个PHPRPC开源,你怎么评价?


PHPRPC 现在是以GPL形式开源的,也就是说,它也是一个理想主义的产物,任何人都可以得到它,修改它,使用它,却不能独自拥有它,不能将它私有化。实际上PHPRPC也是我们的一个产品,但是它只能用于同样采用GPL发布的项目中,我们对它也提供收费的商业支持,但实际上,你知道的,我们根本不可能在这方面得到一分钱的收入,因为在国内,只有理想是不能养活一个公司的,甚至连个人都不能养活。呵呵。但是既然已经做了,就让它继续下去吧,只要它还活着,就代表我们还有理想,我们还在奋斗!

【我的评语】为理想和奋斗者而鼓掌

 

最后,我还是要表达我对Hprose的整体评价:

  1. 我对Hprose的构想和设计比较赞许,跨平台跨语言支持当然是很多程序员乃至CTO们孜孜以求的目标。而Hprose在实践这样的目标,这最起码是值得我们学习并且为之欢欣鼓舞的。希望这个产品能精益求精,做得更好,并且能得到更多人的了解和使用。

  2. 对于Hprose团队的创造性工作和认真态度,我是颇感欣赏的。希望他们能坚持理想,实现理想。对于开源,经常有人感概说,看似很好,但实践起来却殊为不易,我自己也深以为然。从这方面而言,更应该赢得我们的敬意和支持。

 

 

【备注】以上言论均代表个人意见,如有雷同,纯属巧合鼻血

posted @ 2010-07-18 18:29  陈希章  阅读(5963)  评论(13编辑  收藏  举报