背景:

在大型分布式java应用中,为了方便开发者,通常底层的rpc框架都会做一些调用的封装,让应用层开发人员在开发服务的时候只用编写简单的pojo对象就可以了,如流行的springremoting,jbo***emoting等等,都有这样的效果。

随着业务的需要,可能上层应用希望采用非java技术,如php,rubyonrails,而由于javagc和内存模型的限制,可能有的底层服务又需要采用更高性能和更加灵活的技术,如果c++,python等。

这时候就会考虑跨语言的问题了,在如何不改动原有pojo实现的rpc框架,而让系统实现跨语言,这个难题摆在了中间件开发者的头上。

问题:

现在我们不妨把上面说涉及的问题提取出来:

1)不能改变原有的javarpc服务的发布方式,仍然采用pojo。

2)上层非java应用可以调用到由server端pojo形式发布的服务。

3)底层非java应用,如c++,python等可以发布格式和pojoservice一样的服务

4)提供优雅的借口给应用开发者。

业界考察:

好在我们并不是第一个遇到这个问题的人,那我们来看看在我们业界的前辈们都给我们留下了哪些宝贵的财富(主要是互联网行业)。

GoogleprotocolbuffersGoogle大神总是早人一步,在google架构的初期就意识到了跨语言的重要性,在构建bigtable,GFS的同一时期就是定制出了一套跨语言方案。那就是googleprotocolbuffers,不过直到08年,googleprotoclbuffers才开源出来,正所谓国之利器不可以示人,我们所看到的,googleprotoclbuffers其实是阉割版,如没有map的支持(根据一些资料表明,google内部是有这个东西的),python的nativec性能优化,不包括rpcservice,虽然后面补了一个,但是可用性差强人意,不能多参,不能抛异常。不过在这方面我们确实不应该报太大的希望,因为google自己都说了protocolbuffers–alanguage-neutral,platform-neutral,extensiblewayofserializingstructureddata,好吧,他只是一个序列化格式,而和hessian,java序列化有所不同的是,protocolbuffers可以用通过定义好数据结构的proto(IDL)文件产生目标语言代码,大大了减少了开发量,不过遗憾的是生成的代码有很强的侵入性,并不能产生我们需要的pojojava对象。

不过即使是这样,我们也从googleprotocolbuffers身上学到了很多东西。

  1. 编码的压缩,采用Base128Varints序列化数字,减少网络传输开销。

  2. 非自描述数据,protocolbuffers将每个数据结构的描述信息嵌入到代码中,因此只需要传输数据过来,就可以反序列化出来该数据结构的实例了。

  3. Immutableobject,protocolbuffers在生成的java代码中采用builder&message模式,message是一个不能变的对象,即只有getter,没有setter,而每一个message的生成由一个对应的builder来完成,从这点可以看出,google已经用上了函数式编程。

  4. Rpc异步话,虽然protocolbuffers的rpc很简陋,但是一开始就只提供异步callback调用形式,可见google已经实现异步话,如果在互联网行业的人会知道,这点是相当不容易。

Facebookthrift4月1号,呵呵,没错,thrift是Facebook于07年愚人节开源出来的,有点google的作风。Thrift是facebook自己的一套跨语言实现。有人会问这个和protocolbuffers有啥区别。Ok,先看看它的定义吧。

Thriftisasoftwareframeworkforscalablecross-languageservicesdevelopment.ItcombinesasoftwarestackwithacodegenerationenginetobuildservicesthatworkefficientlyandseamlesslybetweenC++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,Smalltalk,andOCaml.

说得很清楚是一个跨语言的服务开发框架。包括的功能有codegeneration(代码生成,protocolbuffers也有),cross-language(跨语言,protocolbuffers也有),servicedevelopment(好吧,这个protocolbuffers也有)。晕倒,这样看起来,它和googleprotocolbuffers完全是同一个领域的东西,而其有点重复发明轮子的味道。

刚开始,我们也有这样一个疑惑,好吧,接着往下看,herewego。其实除了这些共同性以外(都是解决跨语言问题嘛),thrift还是和protocolbuffers有很大不同的。不同点如下:

1)提供一个完整的servicestack,定义了一整套的rpc服务框架栈,这个protocolbuffers是没有,这个绝对是thrift的利器,如果你想要开发一个服务,thrift甚至有个栈层的实现,我靠,爽。

2)Ok,在thrift论文有这样一句话。Thriftenforcesacertainmessagingstructurewhentransportingdata,butitisagnostictotheprotocolencodinginuse.嗯哼,我懂了,它是不会管,你到底采用哪种序列化方式的,hessian,xml甚至是protocolbuffers。Oh,mygod。

3)接下来不得不膜拜一下thrift的service接口的强大了,多参,异常,同步,异步调用的支持,这正是我们想要的,瞬间给protocolbuffers比下去了。

4)多集合的支持map,set都有,让你爽歪歪。Protocolbuffers颤抖吧。

这时候我们亲爱的读者就会问了,那我们的问题不就解决了吗,就是thrift。我笑而不语,虽然thrift是如此的强大,但是它仍然不是我们想要的,thrift生成的代码也是强侵入性的,这样pojo的对象是无法发布服务的。还有一个硬伤是虽然thrift的stack很强大,当时这和我们原有系统的stack肯定是不兼容的,如jbo***emoting,springremoting,它们都会加一些header信息,而thrift已有实现的传输中式没有header信息的。值得一提的是现有的thriftservice实现中,不是线程安全的,考虑到有些语言没有对线程很好的支持,尤其是Facebook最常用的PHP语言,所以现有的实现中没有线程安全Client的实现。这样就会造成client端connection不能复用的问题,相当于短连接了。(ps:其实短连接就真的比长连接性能差吗?这是个问题。)

总结一下从Facebookthrift学到的东西:

1)同步,异步都支持,这个很强悍,一般的做法是对性能要求高的服务器端采用异步方式开发,对易用性有要求的客户端采用同步方式调用,是比较完美的。

2)从现有的非线程安全的实现看,Facebook很有可能自己有一套更高效的线程安全的实现,估计考虑到和thrift关系不到,或者是核心技术,所以没有放出来,其实想自己做,也不是太难。

3)Thrift对很多脚本语言都进行了nativec的性能优化,如python端,采用nativec以后性能提高20倍。Protocolbuffers一直在做这方面的优化,打算在2.4中加入,不过protocolbuffers就像jdk7一样难产,跟让人崩溃的是,前不久在论坛爆出做这块优化的哥们已经离开了google,不再负责了,好吧,我关心的是他去哪儿了,泪奔。

ApacheHadoopavroAvroisadataserializationsystem.AvroprovidesfunctionalitysimilartosystemssuchasThrift,ProtocolBuffers,etc.好吧它自己都承认了,我们就不去纠结了。

简单介绍一下,avo是hadoop项目下面用来传输数据的一个架构。也是一个跨语言解决方案。不过avro有自己的亮点。1,Dynamictyping,2,Untaggeddata,3,.Nomanually-assignedfieldIds

眼前一亮,Dynamictyping,oh,mygod。没错,avro通过将metadata放在一个叫schema的对象里面,然后可以序列化对应的pojo兑现。这个正是我想要的,至于其他的特性,的确没有咋仔细看avro,感觉上比thrift,和protocolbuffers跟难学习,有熟悉的读者可以给我科普一下。

解决方案:

好了,到了这里,读者大概心里也有数了,protocolbuffers,thrift,avro都有我们想要的和我们不想要的。要解决我们的问题,我们只需要扬长避短就可以了。揉揉就是我们的东西了。方案如下:

1)采用protocolbuffers的message序列化格式和代码生成。

2)采用thrift的service生成格式,以及实现兼容jbo***emoting或者springremoting的thrift(jbo***emoting)stack。

3)原有的pojo对象采用avro的schema方式序列化和反序列化该对象。

Ok了,一切看起来是那样的完美。呵呵,不要被迷惑,还有很多detail的事情需要解决,时候不早,吃碗泡面,洗洗睡了,有时间,再把具体实现detail分享给大家。

10/24/20101:22AM小丑鱼