Java程序员的现代RPC指南
1.前言
1.1 RPC框架简介
最早接触RPC还是初学Java时,直接用Socket API传东西好麻烦。于是发现了JDK直接支持的RMI,然后就用得不亦乐乎,各种大作业里凡是涉及到分布式通信的都用RMI,真是方便。后来用上了Spring,发现Spring提供了好多Exporter,可以无侵入地将一个POJO暴露为RPC服务。
接触了这么多RPC框架后,发现当时公司内部自己实现了一套支持压缩、加密等附加功能的RPC基础框架,于是就读了一下源码,发现原来自己实现个简单的RPC挺简单啊,选好序列化框架后用反射为服务接口生成存根就行了。核心技术就是:序列化和动态反射。
前一阵子又接触到了多语言支持的RPC框架。其实传统的方式也是能支持多种编程语言的,只要序列化框架为多种语言都提供了版本支持,那么序列化后使用相同的网络协议传输就能实现跨语言的RPC了,这也是最轻量级的自制RPC了,灵活但是手动工作量比较大。再就是重量级的SOAP WebService或简单方便的REST,后者一般采用JSON格式携带数据,最典型的场景就是前后台的服务调用,从JS到Java的RPC。
本文要重点介绍的则是另外一套RPC框架。相比一般的RPC框架来说,它能够支持多种语言间的RPC;相比WebService,它却没有SOAP那么重量级,又比轻量级的REST高效。对于组件之间需要频繁通信、又对性能要求较高的分布式系统来说,它是不错的解决方案。
1.2 Protobuf vs. Thrift
Protobuf全名为Protocol Buffer,是Google推出的支持多语言的RPC基础设施。通过自定义语言无关的IDL文件和Protoc代码生成器达到跨语言RPC通信的目的。但也正因为跨语言,与我们仅限于Java的那些RPC框架相比稍显复杂一些。之前研究Protobuf序列化性能时,也正因这一点而采用了Java简化版Protostuff,详情请见《序列化战争:主流序列化框架Benchmark》。
Thrift是Facebook推出的RPC框架,与PB相比提供了内建的RPC支持,而PB开源版里并没有RPC功能(也是后面实践时才发现的)。Thrift的RPC提供了多种网络模式和序列化的选项,可以根据不同场景来灵活搭配使用。
关于Protobuf、Thrift以及本文未涉及的Avro,在《大数据日知录》里有具体的比较,感兴趣的可以参考一下。
1.3 核心技术
前面说了传统RPC框架的核心技术,“现代”RPC为了支持多语言所以稍显复杂一点儿。核心技术主要有:IDL、代码生成器、序列化、RPC:
- 在IDL文件中通过不与具体编程语言相关的语法定义通信类
- 利用代码生成器生成出Java语言对应的代码
- 引入框架的运行时Jar包,获得序列化、RPC等能力
所以前两者决定了框架对不同编程语言的支持能力,而后两者决定了运行时的调用性能。
2.Maven集成
不管使用哪种框架,既然涉及到了代码生成,那就要想法与项目构建的过程结合到一起。这里以Java项目最常用的Maven为例,看一下如何将代码生成器与Maven相结合,并且有哪些注意事项。
2.1 Compile阶段
按照我们的设想,代码生成过程应该在编译阶段,这样生成的代码就能跟已有代码一起被编译、打包、发布,两者没有什么差别。一旦修改了IDL,直接编译就能看到最新生成的代码了,这就是我们想要的效果。
2.2 Ant集成插件
因为有些框架并不提供专门的插件,所以与Maven最简单通用的集成方法就是采用maven-antrun-plugin插件。此插件可以执行任意命令,标准写法如下:
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<tasks>
<exec executable="xxx.exe">