1. 程式人生 > 其它 >Protocol Buffer整合netty

Protocol Buffer整合netty

技術標籤:Netty

Protocol Buffer整合netty

1-1 前言

我們是在學習netty的背景下,學些protocol Buffer的,所以我們在我們的實踐文件,也是在這樣的背景下整理出來的。

1-2 編輯簡單的Proco檔案

1-2-1 Student .proto

syntax = "proto2";

package com.zt.proto;

option optimize_for = SPEED;
option java_package = "com.zt.proto";
option java_outer_classname =
"MyDataInfo"; message Person { required string name =1; optional int32 age=2; optional string address=3; }
  • syntax :指定使用的proto的版本 我們這裡使用的是proto2

  • package :指定生成的java的類的包路徑,如果 java_package不指定,就是這個目錄

  • optimize_for:檔案級別的選項,Protocol Buffer定義三種優化級別SPEED/CODE_SIZE/LITE_RUNTIME。預設情況下是SPEED

  • SPEED:表示生成的程式碼執行效率高,但是由此生成的程式碼編譯後會佔用更多的空間。

  • CODE_SIZE: 和SPEED恰恰相反,程式碼執行效率較低,但是由此生成的程式碼編譯後會佔用更少的空間,通常用於資源有限的平臺,如Mobile。

  • LITE_RUNTIME: 生成的程式碼執行效率高,同時生成程式碼編譯後的所佔用的空間也是非常少。這是以犧牲Protocol Buffer提供的反射功能為代價的。因此我們在C++中連結Protocol Buffer庫時僅需連結libprotobuf-lite,而非libprotobuf。在Java中僅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。

  • java_package :表示生成的java類的包路徑,官方建議,我們在編輯proto檔案到額時候,最好還是指定。

  • java_outer_classname :表示生成的Java類的類名

  • message :指定一個訊息協議

  • required:表示在傳輸資料的的時候這個欄位為必須傳輸的資料。

  • optional:對應required,可選輸出資料。

  • string 和int32 :還有其他的型別,指定的資料的型別

      可以在程式除錯階段使用 SPEED模式,而上線以後使用提升效能使用 LITE_RUNTIME 模式優化。
    

1-2-2 生成Java類和小測試

  • 生成的命令
protoc  --java_out=src\main\java src\protobuf\Student.proto 
  • 生成的java類

      生成的Java類有點大   這裡就不貼出來。
    
  • 測試小程式

package com.zt.proto;

import com.google.protobuf.InvalidProtocolBufferException;

/**
 * @Description:
 * @Date: 2020/12/15 10:37
 * @author: zt
 */
public class ProtoBufTest {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        MyDataInfo.Person person = MyDataInfo.Person.newBuilder()
                .setName("張山")
                .setAge(1111)
                .setAddress("成都")
                .build();

        byte[] bytes = person.toByteArray();
        // 位元組陣列可以網路傳輸  實現不同機子的遠端過程呼叫
        MyDataInfo.Person person1 = MyDataInfo.Person.parseFrom(bytes);
        System.out.println(person1.getAddress());
        System.out.println(person1.getAge());
        System.out.println(person1.getName());
    }
}
  • 測試小程式的執行結果
成都
1111
張山

Process finished with exit code 0

1-3 整合netty和proco

1-3-1 proco檔案

syntax = "proto2";

package com.zt.proto;

option optimize_for = SPEED;
option java_package = "com.zt.proto";
option java_outer_classname = "MyDataInfo";

message Person {
  required string name =1;
  optional int32 age=2;
  optional string address=3;
}

1-3-2 netty相關

1-3-2-1 TestServer

package com.zt.proto;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * @Description:
 * @Date: 2020/12/15 10:53
 * @author: zt
 */
public class TestServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workGroup).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new TestInitializer());

            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

1-3-2-2 TestInitializer

package com.zt.proto;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

/**
 * @Description:
 * @Date: 2020/12/15 10:59
 * @author: zt
 */
public class TestInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(MyDataInfo.Person.getDefaultInstance()));
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        // 第一次沒出現Bf shu'xua是ProtobufEncoder
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new TestServerHandler());
    }
}

1-3-2-3 TestServerHandler

package com.zt.proto;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @Description:
 * @Date: 2020/12/15 14:27
 * @author: zt
 */
public class TestServerHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.Person person) throws Exception {
        System.out.println(person.getAddress());
        System.out.println(person.getAge());
        System.out.println(person.getName());
    }
}

1-3-2-4 TestClient

package com.zt.proto;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @Description:
 * @Date: 2020/12/15 14:28
 * @author: zt
 */
public class TestClient {
    public static void main(String[] args) {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new TestClientInitializer());

            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            eventLoopGroup.shutdownGracefully();
        }
    }

}

1-3-2-5 TestClientInitializer

package com.zt.proto;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

/**
 * @Description:
 * @Date: 2020/12/15 14:31
 * @author: zt
 */
public class TestClientInitializerextends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(MyDataInfo.Person.getDefaultInstance()));
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new TestClientHandler());
    }
}

1-3-2-6 TestClientHandler

package com.zt.proto;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @Description:
 * @Date: 2020/12/15 14:37
 * @author: zt
 */
public class TestClientHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.Person person) throws Exception {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        MyDataInfo.Person person = MyDataInfo.Person.newBuilder()
                .setName("張山")
                .setAge(1111)
                .setAddress("成都")
                .build();

        ctx.channel().writeAndFlush(person);
        System.out.println("TestClientHandler:channelActive");
    }
}

1-3-2-7 pom檔案補充

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ProtocolDemo2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!--設定java的編譯的jdk版本-->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <protobufJavaVersion>3.11.0</protobufJavaVersion>
        <protobufJavaUtilVersion>3.11.0</protobufJavaUtilVersion>
        <nettyAllVersion>4.1.40.Final</nettyAllVersion>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobufJavaVersion}</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <version>${protobufJavaUtilVersion}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>${nettyAllVersion}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

1-3-2-8 簡單說明下

netty相關的點,我這裡就不贅述,前面的文件都有相關的說明。

1-3-3 測試和總結

1-3-3-1 server的日誌

十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler channelRegistered
資訊: [id: 0x916b2ebf] REGISTERED
十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler bind
資訊: [id: 0x916b2ebf] BIND: 0.0.0.0/0.0.0.0:8899
十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler channelActive
資訊: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] ACTIVE
十二月 17, 2020 10:34:04 上午 io.netty.handler.logging.LoggingHandler channelRead
資訊: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] READ: [id: 0x07922e1b, L:/127.0.0.1:8899 - R:/127.0.0.1:59693]
十二月 17, 2020 10:34:04 上午 io.netty.handler.logging.LoggingHandler channelReadComplete
資訊: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] READ COMPLETE
成都
1111
張山

1-3-3-2 client的日誌

TestClientHandler:channelActive

1-3-3-3 總結

  • 基本上程式是成功的運行了,我這裡簡單的說明下,在1-2小章節中我們寫了小程式測試下,哪裡我
    們是自己編碼和解碼,我們這個整合netty的時候,發現我們沒有解碼和編碼,其實交給了netty幫我
    們 處理了。
  • 整合netty的大部分還是netty的部分,proco的部分編碼還是沒幾行程式碼。
  • 我們這裡是最的資料通訊協議,要是我們存在多種資料通訊協議,我們怎末處理?我會在後面整理出來。