11, Getting started with Netty to supernatural series - Netty uses Protobuf encoding and decoding

preface

In the process of network transmission, data needs to be serialized or and deserialized, which requires encoders and decoders. This article mainly discusses the codec in Netty and the use of Protobuf.

Encoding and decoding in Netty

When our Netty client communicates with the server, the data needs to be serialized in the process of transmission, such as binary data transmission, then our business data needs to be encoded into binary data by a corresponding encoder. When the server gets the binary data, it needs to be decoded by a corresponding decoder to get the real business data. As shown below:

Some codecs are provided in Netty as follows:

  • StringEncoder: encoder for strings
  • StringDecoder: decoder for Strings
  • ObjectEncoder: an encoder for Java objects
  • ObjectDecoder: decoder for Java objects

For ObjectEncoder and ObjectDecoder, it is only for the encoding and decoding of Java objects. In other words, Java serialization technology is used at the bottom, and Java serialization has the following problems

  • inefficiency
  • Can't support language, can only support java
  • After serialization, the volume is huge, which is much larger than binary coding

So we introduced google's protobuf to solve this problem

Protobuf know

Protobuf is an open source project of google. It is a flexible, efficient and automatic structural data serialization method; It is a cross language and extensible method of serializing structured data. It can be used for (data) communication protocol (RPC), data storage and so on. It has the following characteristics:

  • Language independent and platform independent. That is, ProtoBuf supports Java, C + +, Python and other languages and multiple platforms
  • Efficient. Compared with XML, it is smaller (3 ~ 10x), faster (20 ~ 100x) and simpler than XML
  • Good expansibility and compatibility. You can update the data structure without affecting and destroying the original old program

You can define the data structure, and then use the specially generated source code to easily write and read the structure data in various languages in various data streams. You can even update the data structure without breaking the deployed program compiled by the old data structure

The encoder ProtobufEncoder and decoder ProtobufDecoder for protobuf are provided in Netty

Generating POJO s using Protobuf

Official documents: http://developers.google.com/protocol-buffers/docs/proto

To use protobuf, you need to use the protobuf compiler to generate Java code. The steps are as follows

  1. Define files ending in. proto
  2. Compile the. proto file with protoc.exe to generate java code
  3. Writing the Netty case

Step 1: write a. Proto file, which can be written in IDEA. The file name here is User.proto

//Define version
syntax = "proto3";
//The class name and file name of the generated code
option java_outer_classname = "UserPOJO";
//Manage data with Messenger, and pay attention to the name and java_outer_classname conflict
message User{
  //Define the ID attribute. int32 corresponds to java. Note that 1 here refers to the need rather than the value
  int64 id = 1;
  //Define a name attribute whose type is String. Note that 2 here refers to the required value rather than the value
  string name = 2;
  //Define an array, like
  repeated string   favorite = 3;
}

This table shows the types that can be specified in the. proto file and the relative types generated automatically!

Step 2: install proto.exe, download address: https://github.com/protocolbuffers/protobuf/releases , unzip and enter the bin directory

Step 3: copy the user.proto file to the bin directory, use protoc.exe to produce java files, and execute the command protoc.exe --java_out=. User.proto

The generated files are as follows

Netty uses Protobuf codec

Take the Server/Client mode of Netty as an example, but the data here is encoded and decoded by protobuf.

Step 1: build the project and copy the UserPOJO.java file to the project. At this time, the class will report an error, which requires importing a dependency

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

Step 2: create a Netty server: NettyServer

public class NettyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();

        try {
            bootstrap.group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        //Server: when using protobuf decoder, you need to specify which type of decoder to decode
                        pipeline.addLast("decoder",new ProtobufDecoder(UserPOJO.User.getDefaultInstance()));
                        pipeline.addLast(new ServerHandler());
                    }
                });

            //Start the server
            ChannelFuture sync = bootstrap.bind(2000).sync();
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

Here we add a protobuf decoder through pipeline

Step 3: write the handler of the server

public class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	//Force the message to UserPOJO.User
        UserPOJO.User user = (UserPOJO.User) msg;
        System.out.println("Get the data:"+user.getName());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}

Step 4: write the client: NettyClient

public class NettyClient {
    public static void main(String[] args) {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        //Add an encoder to the client and use the ProtobufEncoder encoder
                        pipeline.addLast("encoder",new ProtobufEncoder());
                        pipeline.addLast(new ClientHandler());
                    }
                });
        try {
            ChannelFuture sync = bootstrap.connect("127.0.0.1", 2000).sync();
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}

The client has added a ProtobufEncoder encoder

Step 5: write the handler of the client

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //When a connection is established, an object is sent to the server
        UserPOJO.User user = UserPOJO.User.newBuilder().setId(1).setName("zs").build();
        ctx.writeAndFlush(user);
        System.out.println("Client send user="+user.getName());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}

The code is written, and the final project structure is as follows

Step 6: test. Start nettyserver and nettyclient successively. The results are as follows

client:

Server:

That's all for the content of protobuf. If you like it, give it a high praise.

Keywords: Java protobuf

Added by bftwofreak on Tue, 07 Dec 2021 16:26:08 +0200