多語言跨平臺遠程過程調用【Avro】

##開始javascript

Avro是Apache的Hadoop家族的項目之一。具備性能高、基本代碼少和產出數據量精簡等特色。不過這是他們宣傳廣告,我最近也分別研究了Avro和Protobuf。基本的測試代碼,不吐不快。html

##安裝java

###Javapython

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>

###Pythonweb

熟悉Python模塊安裝應該很簡單。avro的Python模塊能夠在 [https://pypi.python.org/pypi Python][https://pypi.python.org/pypi] 下載。下載<code>tar.gz</code>或者<code>zip</code>解壓到硬盤任意目錄。apache

打開終端,並用cd切換到avro解壓的目錄,執行:json

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。

相關文章
相關標籤/搜索