Google Protobuf結合Netty實踐


1.Win版Protobuf代碼生成工具下載:

https://github.com/protocolbuffers/protobuf/releases

注意下載protoc-3.6.1-win32.zip

2.編寫.proto文件注意:

指定包為外層目錄名稱,指定java包名為你想使用的java工程該序列化類包名,還需要指定生成Java類的名稱。字段賦予的數值或代表次序,而不是字段默認值。示例:

syntax = "proto2";
package Protobuf;
option java_package = "com.xiaobai.codec.protobuf";
option java_outer_classname = "SubscribeReqProto";

message SubscribeReq{
required int32 subReqID = 1;
required string userName = 2;
required string productName = 3;
repeated string address = 4;
}

syntax = "proto2";
package Protobuf;
option java_package = "com.xiaobai.codec.protobuf";
option java_outer_classname = "SubscribeRespProto";

message SubscribeResp{
required int32 subReqID = 1;
required int32 respCode = 2;
required string desc = 3;
}

3.使用工具時需要注意

a.需要指定一個--proto_path路徑,根據錯誤提示:

D:\protoc-3.6.1-win32\bin>protoc.exe --java_out=F:\NewAge\nettydemo\src\main\jav
a F:\NewAge\nettydemo\Protobuf\SubscribeReq.proto
F:\NewAge\nettydemo\Protobuf\SubscribeReq.proto: File does not reside within any
path specified using --proto_path (or -I). You must specify a --proto_path whi
ch encompasses this file. Note that the proto_path must be an exact prefix of t
he .proto file names -- protoc is too dumb to figure out when two paths (e.g. ab
solute and relative) are equivalent (it's harder than you think).

這個路徑需要是.proto文件所在目錄

b.需要在.proto文件中指定它的proto語法版本,這樣生成時不會出現警告,根據提示,這個版本默認是proto2,可以設置為proto3:

D:\protoc-3.6.1-win32\bin>protoc.exe --proto_path=F:\NewAge\nettydemo\Protobuf -
-java_out=F:\NewAge\nettydemo\src\main\java F:\NewAge\nettydemo\Protobuf\Subscri
beReq.proto
[libprotobuf WARNING T:\src\github\protobuf\src\google\protobuf\compiler\parser.
cc:562] No syntax specified for the proto file: SubscribeReq.proto. Please use '
syntax = "proto2";' or 'syntax = "proto3";' to specify a syntax version. (Defaul
ted to proto2 syntax.)

參照:

https://www.jianshu.com/p/42a480a45cd6

https://www.cnblogs.com/gifisan/p/5976208.html?utm_source=itdadao&utm_medium=referral

生成效果如下:

Win版本安裝參考與示例:

https://www.cnblogs.com/tyw66/p/7352033.html

Linux版本安裝參考與示例:

https://www.cnblogs.com/learn21cn/p/6206206.html

https://www.cnblogs.com/luoxn28/p/5303517.html

4.實踐時采用了3.6.1版本代碼生成,所以Java工程引入依賴也選擇了3.6.1版本,且重新用proto3生成了代碼,該語法不支持required字段,所以去掉:

D:\protoc-3.6.1-win32\bin>protoc.exe --proto_path=F:\NewAge\nettydemo\Protobuf -
-java_out=F:\NewAge\nettydemo\src\main\java F:\NewAge\nettydemo\Protobuf\Subscri
beResp.proto
SubscribeResp.proto: Required fields are not allowed in proto3.
SubscribeResp.proto: Required fields are not allowed in proto3.
SubscribeResp.proto: Required fields are not allowed in proto3.

syntax = "proto3";
package Protobuf;
option java_package = "com.xiaobai.codec.protobuf";
option java_outer_classname = "SubscribeReqProto";

message SubscribeReq{
int32 subReqID = 1;
string userName = 2;
string productName = 3;
repeated string address = 4;
}
syntax = "proto3";
package Protobuf;
option java_package = "com.xiaobai.codec.protobuf";
option java_outer_classname = "SubscribeRespProto";

message SubscribeResp{
int32 subReqID = 1;
int32 respCode = 2;
string desc = 3;
}

注意Protobuf里面沒有list,需要指定為repeated + 元素類型!!

未寫元素類型導致的報錯:

D:\protoc-3.6.1-win32\bin>protoc.exe --proto_path=F:\NewAge\nettydemo\Protobuf -
-java_out=F:\NewAge\nettydemo\src\main\java F:\NewAge\nettydemo\Protobuf\Subscri
beReq.proto
SubscribeReq.proto:10:22: Expected field name.

參照(定義復雜對象):

https://blog.csdn.net/hry2015/article/details/70766603

https://blog.csdn.net/qq_33951440/article/details/80599712

5.源碼中的報錯:@Override問題和支持1.6+的Java版本問題

參考https://blog.csdn.net/jdjdndhj/article/details/70256789

是因為IDEA源碼版本設置的原因:

6.編譯報錯

Error:java: Compilation failed: internal java compiler error

這是IDEA編譯版本設置的原因,注意本項目(nettydemo)的編譯版本也要設置成1.8,這里是坑:

參考:https://www.cnblogs.com/comeluder/p/8215317.html

7.序列化測試

package com.xiaobai.codec.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.ArrayList;
import java.util.List;

public class TestSubscribeReqProto {

private static byte[] encode(SubscribeReqProto.SubscribeReq req) {
return req.toByteArray();
}

private static SubscribeReqProto.SubscribeReq decode(byte[] body) throws InvalidProtocolBufferException {
return SubscribeReqProto.SubscribeReq.parseFrom(body);
}

private static SubscribeReqProto.SubscribeReq createSubScribeReq() {
SubscribeReqProto.SubscribeReq.Builder builder = SubscribeReqProto.SubscribeReq.newBuilder();
builder.setSubReqID(1);
builder.setUserName("xiaobai");
builder.setProductName("Java Supior");
List<String> address = new ArrayList<>();
address.add("Shenyang YUEXING");
address.add("Jinzhou HUAGUANG");
address.add("Beijing HAIDIAN");
builder.addAllAddress(address);
return builder.build();
}

public static void main(String[] args) throws InvalidProtocolBufferException {
SubscribeReqProto.SubscribeReq req = createSubScribeReq();
System.out.println("Before encoding: ");
System.out.println(req.toString());
SubscribeReqProto.SubscribeReq req2 = decode(encode(req));
System.out.println("After encoding and decoding: ");
System.out.println(req2.toString());
System.out.println("Assert enqual: --> " + req2.equals(req));
}
}

輸出結果:

Before encoding:
subReqID: 1
userName: "xiaobai"
productName: "Java Supior"
address: "Shenyang YUEXING"
address: "Jinzhou HUAGUANG"
address: "Beijing HAIDIAN"

After encoding and decoding:
subReqID: 1
userName: "xiaobai"
productName: "Java Supior"
address: "Shenyang YUEXING"
address: "Jinzhou HUAGUANG"
address: "Beijing HAIDIAN"

Assert enqual: --> true

8.結合netty(netty版本需要5.0.0.Alpha1

ChannelHandlerAdapter版本問題:

https://my.oschina.net/linwl/blog/1823315

ChannelHandlerAdapter分析:

https://www.cnblogs.com/wade-luffy/p/6222960.html

GitHub:https://github.com/Xiaobai0419/nettydemo

注意:

a.例子中客戶端和服務端可一直保持連接,和雙向互傳數據。如果一方斷開,另一方報異常:

java.io.IOException: 遠程主機強迫關閉了一個現有的連接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at io.netty.buffer.UnpooledUnsafeDirectByteBuf.setBytes(UnpooledUnsafeDirectByteBuf.java:446)
at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:871)
at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:208)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:119)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:452)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:794)
at java.lang.Thread.run(Thread.java:748)

需要進行處理。

客戶端重新連接,仍可與服務端繼續通信。

b.例子中序列化類int類型字段值為0無法傳輸該字段,其他值則可以,待查。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM