多語言跨平臺序列化框架Google Protobuf-with Netty

##protoc安裝html

下載Protobuf [Protobuf][https://code.google.com/p/protobuf/]java

我下載的是Protobuf 2.5.0版本. 若是是Windows系統,可直接下載win32, 解壓出protoc.exe到任意目錄.Linux系統下載後還須要自行編譯python

tar zxvf protobuf-2.5.0.tar.gz
cd protobuf-2.5.0
./configure --prefix=/usr/local/protobuf
make
make check
sudo make install

若是編譯出錯,自行解決,直到安裝成功.shell

$protoc --version
libprotoc 2.5.0

##Protobuf消息定義,數據類型bootstrap

完整的Protobuf數據類型見 [text][http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html]數組

##protoc的簡單使用服務器

當定義好了一個消息的協議,以下協議,命名爲hello.prototcp

<pre class="prettyprint"> option java_package = "net.test.protobuf"; option java_outer_classname = "HelloMessage"; message Hello { required string message = 1; optional string name = 3; } </pre>測試

###Java 定義好了協議,就能夠用protoc生成各類語言的消息實體.如Java:ui

protoc --java_out=./ hello.proto

生成代碼以下:

<pre class="prettyprint"> $ tree ./ ./ ├── net │   └── test │   └── protobuf │   └── HelloMessage.java ├── hello.proto </pre>

如上的目錄結構能夠清楚的看到它同時生成了Java須要package層次結構,而類名就是咱們指定的HelloMessage.代碼結構讀者能夠自行分析.

###Python

protoc --python_out=./ hello.proto

會生成一個名爲<code>hello_pb2.py</code> Python類.

固然也能夠用<code>--java_out</code>和<code>--python_out</code>同時生成多種語言的協議

protoc --java_out=./ --python_out=./  hello.proto

如你所見,我這裏生成了Java和Python兩種語言的代碼,你還能夠生成更多語言.如C/C#/Ruby等,只要你須要.

##Test Protobuf with Netty

爲了演示Protobuf序列化以及支持多語言通訊,我直接使用熟悉的Netty作一個簡單的遠程執行簡單命令的例子.

先說說我爲何要作這麼一個例子?大多數的系統都須要經過客戶端發送一些簡單的命令改變服務器程序的運行狀態,查看運行狀態等,固然這個功能有不少種方法能夠實現.如Linux shell命令都能完成不少.可是我熱衷於使用Python.

###Protobuf 協議定義

<pre class="prettyprint"> option java_package = "net.davro.protobuf"; option java_outer_classname = "SocketCommand"; message RequestCommand { required string command = 1; //要執行的命令 optional string auth = 3; //認證信息,如token等 optional bytes payload = 4; //執行命令須要的消息 repeated KVStore params = 2; //附帶的參數,是鍵值對的數組 } message KVStore { required string key = 1; //鍵值對的鍵(key) optional bytes value = 2; //鍵值對的值(value) } message ResponseCommand { required bool success = 1; //執行狀態. true OR false optional string message = 2; //返回的消息提示 optional string error = 3; //執行失敗的error repeated KVStore params = 5; //返回攜帶的參數提示等 } </pre>

如上面的協議, 命名爲command.proto. 經過註釋也許你能明白我要作什麼了.

###Netty Server

Netty Server PipelineFactory:

<pre class="prettyprint lang-java"> public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", new ProtobufDecoder(SocketCommand.RequestCommand.getDefaultInstance())); pipeline.addLast("encoder", new ProtobufEncoder()); pipeline.addLast("handler", new ProtobufChannelHandler()); return pipeline; } </pre>

ChannelHandler代碼服務端和客戶端共用, Netty Server/Client ChannelHandler:

<pre class="prettyprint lang-java"> public class ProtobufChannelHandler extends SimpleChannelHandler { /** * 當接受到消息, 輸出消息 * @param ctx * @param e */ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { Object message = e.getMessage(); //爲了節省代碼量,這裏這裏判斷是request仍是response. if(message instanceof SocketCommand.RequestCommand) { SocketCommand.RequestCommand c = (SocketCommand.RequestCommand)message ; log.info(c.getAuth() + ", " + c.getCommand()); SocketCommand.ResponseCommand.Builder builder = SocketCommand.ResponseCommand.newBuilder(); builder.setSuccess(true); builder.setMessage("OK"); SocketCommand.ResponseCommand response = builder.build(); ctx.getChannel().write(response); } else if(message instanceof SocketCommand.ResponseCommand) { SocketCommand.ResponseCommand responseCommand = (SocketCommand.ResponseCommand)message ; log.info(responseCommand.getSuccess() + ", " + responseCommand.getMessage()); } } } </pre>

啓動Netty Server:

<pre class="prettyprint lang-java"> public static void main(String[] args) { ServerBootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(channelPipelineFactory); bootstrap.setOption("child.tcpNoDelay", true); //注意child前綴 bootstrap.setOption("child.keepAlive", true); //注意child前綴 //啓動端口 8080 bootstrap.bind(new InetSocketAddress(port)); System.out.println("listening port: " + port + " server is starting……"); } </pre>

###Netty Client

Netty Client PipelineFactory:

<pre class="prettyprint lang-java"> public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", new ProtobufDecoder(SocketCommand.ResponseCommand.getDefaultInstance())); pipeline.addLast("encoder", new ProtobufEncoder()); pipeline.addLast("handler", new ProtobufChannelHandler()); return pipeline; } </pre>

ChannelHandler代碼服務端和客戶端共用, 如上.

<pre class="prettyprint lang-java"> //建立無鏈接傳輸channel的輔助類(UDP),包括client和server ChannelFuture future = bootstrap.connect(new InetSocketAddress( "zhenqin-k45vm", 8080)); /* * 給足夠的時間, 讓創建鏈接 */ try { future.await(500); } catch (InterruptedException e) { e.printStackTrace(); } /* 鏈接成功否? */ if(future.isSuccess()){ Channel w = future.getChannel(); SocketCommand.RequestCommand.Builder builder = SocketCommand.RequestCommand.newBuilder(); builder.setAuth("kkk@email.com"); builder.setCommand("shutdown"); builder.setAuthBytes(ByteString.copyFromUtf8("Hello")); SocketCommand.RequestCommand command = builder.build(); ChannelFuture f = w.write(command); //當即同步, 不等緩衝區滿了. 不然還得等待緩衝區滿了纔會發送給遠程 f.syncUninterruptibly(); } /* 關閉鏈接 */ future.getChannel().getCloseFuture().awaitUninterruptibly(); bootstrap.releaseExternalResources(); System.out.println("====================end========================="); </pre>

爲了減小篇幅,我刪減了不少代碼, 若是你熟悉Netty,我相信你看懂代碼不成問題. ###測試結果 Server:

08-26 11:01:43 [INFO] [handler.ProtobufChannelHandler(44)] Hello, shutdown

Client:

08-26 11:01:11 [INFO] [handler.ProtobufChannelHandler(57)] true, OK
相關文章
相關標籤/搜索