多语言跨平台远程过程调用【Avro】

不想你离开。 提交于 2019-12-04 03:44:52

##开始

Avro是Apache的Hadoop家族的项目之一。具有性能高、基本代码少和产出数据量精简等特点。不过这是他们宣传广告,我最近也分别研究了Avro和Protobuf。基本的测试代码,不吐不快。

##安装

###Java

Avro是应运Hadoop而生的,因此主要也是以Java写就。 Java的安装比较简单,往项目中放入Avro及Avro-rpc的jar包便可。我喜欢使用Maven,因此Maven坐标如下:

<dependency>
    <groupId>org.apache.avro</groupId>
    <artifactId>avro</artifactId>
    <version>1.7.2</version>
</dependency>

<dependency>
    <groupId>org.apache.avro</groupId>
    <artifactId>avro-ipc</artifactId>
    <version>1.7.2</version>
</dependency>

###Python

熟悉Python模块安装应该很简单。avro的Python模块可以在 [https://pypi.python.org/pypi Python][https://pypi.python.org/pypi] 下载。下载<code>tar.gz</code>或者<code>zip</code>解压到硬盘任意目录。

打开终端,并用cd切换到avro解压的目录,执行:

python setup.py install

Ubuntu系统需要Root权限:

sudo python setup.py install

如果没有看到特殊的错误,一般都能顺利安装上。

##协议[Protocol]

Avro协议是以JSON结构性描述文本。协议定义了基本的通信的数据类型,名称。并且还包含可调用的方法等。如下我的示例是一个实现简单的HelloWord程序。

<pre class="prettyprint lang-javascript"> { "namespace":"avro", "doc":"This is a message.", "protocol":"messageProtocol", "name":"HelloWorld", "types":[ { "name":"nameMessage", "type":"record", "fields":[ {"name":"name", "type":"string"} ] } ], "messages":{ "sayHello":{ "doc":"say Hello to manbers", "request":[ { "name":"name", "type":"string" } ], "response":"nameMessage" } } } </pre>

我把它命名为helloward.json。之所以我命名为json后缀的文件是因为很多编辑器都能认识,并且还能格式化。<<Hadoop权威指南>>中命名为avro后缀的文件,这点在编辑器中支持不是很好。并且Avro协议是不论协议文件的,它只认内容。

上面的协议相当于我新定义了一种复杂数据类型,名字为nameMessage,并且包含一个属性name,类型为string。 用Java语言通俗的说,我新定义了一个Bean, 这个Bean中只有一个<code>String name</code>的属性。因此她也是包涵在types下面的,相当与新定义了数据类型。

record是复杂类型,也就是可以通过简单类型合成的组装类型。关于支持的简单数据类型见:

http://avro.apache.org/docs/current/spec.html

messages在协议中包含特殊的含义。相当于曝露给外部可以调用的接口。如上面的协议,相当与包含了sayHello的方法,方法入口参数为String name,返回值为一个自定义的类型nameMessage, 其实也是一个String类型的键值对。

##服务端[Java]

<pre class="prettyprint lang-java"> public class AvroHttpServer extends GenericResponder { private static Log log = LogFactory.getLog(AvroHttpServer.class); public AvroHttpServer(Protocol protocol) { super(protocol); } public Object respond(Message message, Object request) throws Exception { GenericRecord req = (GenericRecord) request; GenericRecord reMessage = null; if (message.getName().equals("sayHello")) { Object name = req.get("name"); // do something... //取得返回值的类型 reMessage = new GenericData.Record(super.getLocal().getType("nameMessage")); //直接构造回复 reMessage.put("name", "Hello, " + name.toString()); log.info(reMessage); } return reMessage; } public static void main(String[] args) throws Exception { int port = 8088; try { Server server = new HttpServer( new AvroHttpServer(Protocol.parse( new File("helloword.json"))), port); server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } } </pre>

如上边的Java代码,Avro默认可以支持的Server有很多,如NettyServer,SocketServer等。目前为止我之发现了Avro Python客户端只支持的Http的方式的。

上面的代码能正常运行,并且运行后进程不会退出,一直等待客户端链接。

##客户端

###Java

我是Java程序员, 因此每个例子我都会用Java首先实现一遍。

<pre class="prettyprint lang-java"> private Protocol protocol; private GenericRequestor requestor = null; @Before public void setUp() throws Exception { protocol = Protocol.parse(new File("src/main/resources/helloword.json")); Transceiver t = new HttpTransceiver(new URL("http://localhost:8088")); requestor = new GenericRequestor(protocol, t); } @Test public void testSendMessage() throws Exception { GenericRecord requestData = new GenericData.Record(protocol.getType("nameMessage")); // initiate the request data requestData.put("name", "zhenqin"); System.out.println(requestData); Object result = requestor.request("sayHello", requestData); if (result instanceof GenericData.Record) { GenericData.Record record = (GenericData.Record) result; System.out.println(record.get("name")); } System.out.println(result); } </pre>

上面的是一个JUnit的测试用例,可以直接用来测试。

###Python

<pre class="prettyprint lang-python"> #!/usr/bin/env python #!encoding:utf-8 import json import avro.protocol as proto import avro.ipc as ipc import avro.io as avroio import avro.schema as schema __author__ = 'zhenqin' PROTOCOL = proto.parse(open("helloword.json", "r").read()) def testPro(): client = ipc.HTTPTransceiver("zhenqin-k45vm", 8088) requestor = ipc.Requestor(PROTOCOL, client) message = dict() message["name"] = "ZhenQin" v = requestor.request('sayHello', message) print("Result: " + str(v)) # cleanup client.close() if __name__ == '__main__': testPro() </pre>

Python如上边的代码,helloword.json是HelloWord的协议。运行无论是Java或者Python,都会成功的输出:

Result: {u'name': u'Hello, ZhenQin'}

上面提到,Python目前仅仅支持Http的方式调用,因为avro python只提供了一个HTTPTransceiver, 我查看HTTPTransceiver的代码, 都很简单,但是以我的Python造诣还不能够给写一个SocketTransceiver。我相信Python高人能轻易实现一个支持Socket通信的SocketTransceiver不是难事。

在说一遍,Python目前只有一个HTTPTransceiver实现, 因此服务端Java还必须使用HttpServer,它是一个嵌入的Jetty的Http服务器。

##写在后边的话

测试了Avro, 我怎么觉得它更像是以json的webservice呢? 开发是如此的罗嗦? 并且丝毫体现不了便捷。难道多语言跨平台的RPC调用真的有这么难麽?当然也有简单的方式是把服务端的Responder换成ReflectResponder就可以直接注册接口和实现类的对象,但是这样会失去对多语言的支持。

希望广大同行能给以指正。

Protobuf, Next。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!