Netty整合ProtoBuf開發

2020-08-12 14:48:00

Netty整合ProtoBuf開發

Netty簡介

Netty是業界最流行的NIO框架之,它的健壯性、功能、效能、可定製性和可延伸性在同類框架中都是首屈-指的,它已經得到成百上千的商用專案驗證,例如Hadoop的RPC框架Avro就使用了Netty作爲底層通訊框架,其他還有業界主流的RPC框架,也使
用Netty來構建商效能的非同步通訊能力。通過對Netty的分析,我們將它的優點總結如下:
1、API 使用簡單,開發門檻低,功能強大,預置了多種編解碼功能,支援多種主流協定
2、定製能力強, 可以通過ChannelHandler對通訊框架進行靈活地擴充套件
3、效能高,通過與其他業界主流的NIO框架對比,Netty的綜合效能最優
4、成熟、穩定,Netty修復了已經發現的所有JDK NIO BUG, 業務開發人員不需要
再爲NIO的BUG而煩惱:
5、社羣活躍,版本迭代週期短,發現的BUG可以被及時修復,同時,更多的新功
能會加入;
6、經歷 了大規模的商業應用考驗,品質得到驗證。Netty 在網際網路、大數據、網路
遊戲、企業應用、電信軟體等衆多行業已經得到了成功商用,證明它已經完全能
夠滿足不同行業的商業應用了.
正是因爲這些優點,Netty 逐漸成爲了Java NIO程式設計的首選框架。

ProtoBuf簡介

protocolbuffer(以下簡稱PB)是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。google 提供了多種語言的實現:java、c#、c++、go 和python,每一種實現都包含了相應語言的編譯器以及庫檔案。由於它是一種二進制的格式,比使用 xml進行數據交換快許多。可以把它用於分佈式應用之間的數據通訊或者異構環境下的數據交換。作爲一種效率和相容性都很優秀的二進制數據傳輸格式,可以用於諸如網路傳輸、組態檔、數據儲存等諸多領域。

官方地址:https://github.com/google/protobuf

開發步驟

使用protobuf生成序列化檔案

1、下載protobuf執行檔案,檔案地址:https://github.com/protocolbuffers/protobuf/releases/download/v4.0.0-rc2/protoc-4.0.0-rc-2-win64.zip

2、在D槽新建資料夾:protobuf

3、進入下載檔案bin資料夾拷貝protoc.exe到D槽的protobuf

4、新建檔案User.proto

syntax = "proto3";
//生成的包名,此處根據實際來修改
option java_package = "com.fy.protobuf";
//類名
option java_outer_classname="UserInfo";

message UserMsg{
  int32 id=1;
  string name=2;
  int32 age=3;
  int32 state=4;
}

1️⃣proto檔案和生成的Java檔名稱不能一致!

2️⃣檔案編碼爲ANSI

5、cmd視窗執行命令:protoc.exe --java_out=D:\protobuf User.proto

6、將生成的檔案拷貝到專案中

程式碼範例

// 按照定義的數據結構,建立一個物件
UserInfo.UserMsg.Builder userInfo = UserInfo.UserMsg.newBuilder();
userInfo.setId(1);
userInfo.setName("fangyan");
userInfo.setAge(18);
UserInfo.UserMsg userMsg = userInfo.build();
// 將數據寫到輸出流
ByteArrayOutputStream output = new ByteArrayOutputStream();
userMsg.writeTo(output);
// 將數據序列化後發送
byte[] byteArray = output.toByteArray();
// 接收到流並讀取
ByteArrayInputStream input = new ByteArrayInputStream(byteArray);
// 反序列化
UserInfo.UserMsg userInfo2 = UserInfo.UserMsg.parseFrom(input);
System.out.println("id:" + userInfo2.getId());
System.out.println("name:" + userInfo2.getName());
System.out.println("age:" + userInfo2.getAge());

輸出:
id:1
name:fangyan
age:18

開始Coding~

1、新建maven專案,引入依賴

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.51.Final</version>
</dependency>
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.11.0</version>
</dependency>

2、建立伺服器端啓動程式碼

public class EchoServer {
    public void bind(int port) {
        //我們建立了兩個NioEventLoopGroup範例。NioEventLoopGroup是個執行緒組,
        //它包含了一組NIO執行緒,專門用於網路事件的處理,實際上它們就是Reactor執行緒組。
        //這裏建立兩個的原因是一個用於伺服器端接受用戶端的連線,另一個用於進行SocketChannel的網路讀寫。
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //建立ServerBootstrap物件,它是Netty用於啓動NIO伺服器端的輔助啓動類,目的是降低伺服器端的開發複雜度。
            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap
                    //呼叫ServerBootstrap的group方法,將兩個NIO執行緒組當作入參傳遞到ServerBootstrap中。
                    .group(bossGroup, workerGroup)
                    //接着設定建立的Channel爲NioServerSocketChannel,它的功能對應於JDK NIO類庫中的ServerSocketChannel類。
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    //最後系結I/O事件的處理類ChannelInitializer,它的作用類似於Reactor模式中的Handler類,
                    //主要用於處理網路I/O事件,例如記錄日誌、對訊息進行編解碼等。
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    //建立訊息解碼器,獲取訊息長度
                                    .addLast(new ProtobufVarint32FrameDecoder())
                                    //指定訊息型別
                                    .addLast(new ProtobufDecoder(UserInfo.UserMsg.getDefaultInstance()))
                                    //訊息頭置長度
                                    .addLast(new ProtobufVarint32LengthFieldPrepender())
                                    //建立訊息編碼器
                                    .addLast(new ProtobufEncoder())
                                    //訊息的處理
                                    .addLast(new EchoServerHandler());
                        }
                    });
            //伺服器端啓動輔助類設定完成之後,呼叫它的bind 方法系結監聽埠,
            //隨後,呼叫它的同步阻塞方法sync等待系結操作完成。
            //完成之後Netty會返回一個ChannelFuture, 它的功能類似於JDK的java.util.concurrent.Future,主要用於非同步操作的通知回撥。
            //使用f.channel.closeFuture().syncO方法進行阻塞,等待伺服器端鏈路關閉之後main函數才退出。
            ChannelFuture sync = bootstrap.bind(port).sync();

            sync.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //呼叫NIO執行緒組的shutdownGracefully 進行優雅退出,它會釋放跟shutdownGracefully相關聯的資源。
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

tips