Netty codec technology

What is codec technology? Let’s start with a brief description of the word.

Coding is the process by which information is converted from one form or format to another. Also called code in computer programming languages, coding for short. Decoding refers to the reverse process of coding.

From the description we can know that, in fact, codec technology is a general reference. You can think of it as encryption and decryption in cryptography, serialization in Java (object -> binary/binary -> object), and so on. So coding and decoding technology is actually very widely used.

And what we want to talk about this time is, the use of codec technology in Netty. As I mentioned in my article, Netty is widely used as a communication component nested within other frameworks, such as the high-performance services framework Dubbo or messaging middleware Rocket, or more well-known open source frameworks such as Spring. So Netty’s advantages in coding and decoding technology will certainly have a more appropriate technology selection worthy of our study.

Selection requirements for codec

A good coding and decoding technology, consider the aspects or quite a lot. For example,

  1. Cross-platform, cross-language
  2. High performance/high scale storage format
  3. A high performance

First of all, there is no need to be cross-platform, since the server can be deployed on different machines. However, in distributed or microservices today, the problem with RPC(remote calls) is cross-language. To a large extent in the selection of technology, language selection are cross, in order to maximize the advantages of each language. So cross-language is important;

The second is high expansion. High scalability is mainly reflected in the data format of transmission. Our program cannot be limited to a fixed number of parameters forever as it transmits data. However, one problem is that in the process of data encoding and decoding, once new content is added, the result of the encoding and decoding may be inconsistent with the original result, which will cause problems for the receiver after receiving the decoding.

Third, high performance. High performance is nothing more than coding packets as small as possible. The larger the packet, the more bandwidth it consumes, the higher the hardware cost of storage, and the greater the decoding pressure. In high-concurrency scenarios, binary encoding can reduce space overhead, while XML and other highly readable but space-consuming data structures are used in low-frequency systems interaction scenarios.

Codec selection scheme

In Netty codec, there are several schemes

  1. Serialization comes with the JDK
  2. Google Protobuf
  3. Facebook 的 Thrift
  4. JBOSS Marshalling

We’ll use JDK native serialization and Google Protobuf as examples. The integration of Google Protobuf and Netty will be documented in a separate article because the code integration may be too long.

JDK built-in serialization

[First, this example has much of the same code as the previous article, so take a look at the differences.]

Since we need to use the JDK’s own serialization, we still need to develop the server and client separately first. First, we still create an entity class for the client to send the request and the server to respond to.

SubscribeReq. Java request entity class

public class SubscribeReq implements Serializable {    
        /** * Default serial number ID */    
        private static final long serialVersionUID = 1L;   
        private int subReqID;           // Message id
        private String userName;      // User name
        private String productName; // Product name
        private String phoneNumber;// Phone number
        private String address;         / / address
        
        / /... Omit the getter and setter
}
Copy the code

Next comes the server reply class

import java.io.Serializable;

public class SubscribeResp implements Serializable {
    /*** Default sequence ID*/
    private static final long serialVersionUID = 1L;
    private int subReqID;   // Message name
    private int respCode;   // Request the result code
    private String desc;    / / description
   / /... Omit the getter and setter
Copy the code

The service side

Now that we have the reply class, we can use the entity class to interact.

First we write the server subreqServer.java

public class SubReqServer {
    public void bind(int port) throws Exception {
        // Configure the NIO thread group on the server
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // The server starts the configuration class
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 100)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                    public void initChannel(SocketChannel ch) {
                        // This is a Netty object decoder
                        ch.pipeline().addLast(
                                new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this .getClass().getClassLoader())));
                        // This is a Netty object encoder
                        ch.pipeline().addLast(new ObjectEncoder());
                        ch.pipeline().addLast(newSubReqServerHandler()); }});// Bind the port and wait for synchronization successfully
            ChannelFuture f = b.bind(port).sync();
            // Wait for the server listening port to close
            f.channel().closeFuture().sync();
        } finally {
            // Exit gracefully to free up thread pool resourcesbossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }}public static void main(String[] args) throws Exception {
        new SubReqServer().bind(8080); }}Copy the code

Then we write the server’s processing class SubReqServerHandler.

SubReqServerHandler.java

@Sharable
public class SubReqServerHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
	    throws Exception {
	// Since we used the decoder above, this can be decoded directly to become SubscribeReq
	SubscribeReq req = (SubscribeReq) msg;
	// If equal to "JAVA", print the result
	if ("JAVA".equalsIgnoreCase(req.getUserName())) {
	    System.out.println("Service accept client subscrib req : ["
		    + req.toString() + "]");
		//Netty refreshes the reply methodctx.writeAndFlush(resp(req.getSubReqID())); }}// Response method
    private SubscribeResp resp(int subReqID) {
	SubscribeResp resp = new SubscribeResp();
	resp.setSubReqID(subReqID);
	resp.setRespCode(0);
	resp.setDesc("Java Book is exited!!!");
	return resp;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
	cause.printStackTrace();
	   ctx.close();// An exception occurs and the link is closed}}Copy the code

The client

The client, again, starts a new port to access the server.

SubReqClient.java

public class SubReqClient {
    public void connect(int port, String host) throws Exception {
	// Configure the client NIO thread group
	EventLoopGroup group = new NioEventLoopGroup();
	try {
	    Bootstrap b = new Bootstrap();
	    b.group(group).channel(NioSocketChannel.class)
		    .option(ChannelOption.TCP_NODELAY, true)
		    .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			public void initChannel(SocketChannel ch)
				throws Exception {
				// The client still uses Netty's codec
			    ch.pipeline().addLast(
				    new ObjectDecoder(1024, ClassResolvers
					    .cacheDisabled(this.getClass()
						    .getClassLoader())));
			    ch.pipeline().addLast(new ObjectEncoder());
			    ch.pipeline().addLast(newSubReqClientHandler()); }});// Initiate an asynchronous connection
	    ChannelFuture f = b.connect(host, port).sync();

	    // The contemporary client link is down
	    f.channel().closeFuture().sync();
	} finally {
	    // Exit gracefully, freeing the NIO thread groupgroup.shutdownGracefully(); }}public static void main(String[] args) throws Exception {
	new SubReqClient().connect(8080."127.0.0.1"); }}Copy the code

Subreqclienthandler.java is responsible for logical handling

public class SubReqClientHandler extends ChannelHandlerAdapter {

    public SubReqClientHandler(a) {}@Override
    public void channelActive(ChannelHandlerContext ctx) {
	for (int i = 0; i < 10; i++) {
	    ctx.write(subReq(i));
	}
	ctx.flush();
    }

    private SubscribeReq subReq(int i) {
	SubscribeReq req = new SubscribeReq();
	req.setAddress("Address");
	req.setPhoneNumber("110");
	req.setProductName("Java");
	req.setSubReqID(i);
	req.setUserName("Java");
	return req;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
	    throws Exception {
	System.out.println("Receive server response : [" + msg + "]");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
	ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); }}Copy the code

Disadvantages of Java serialization

We have used a simple example above to realize the transmission of data as an object. But although it is very convenient, the problems it brings are also very obvious. As we mentioned above, choosing a codec requires consideration across languages. Java serialization, because it is a proprietary protocol within the Java language, is not supported by other languages at all. Also, Java serializes byte arrays that other languages cannot deserialize. Therefore, it is the main reason why the current popular RPC framework almost does not choose to use Java serialization as the codec framework.

The end

This article focuses on some problems

  1. What is codec
  2. What are the characteristics of a good codec framework
  3. What are the current popular codec frameworks
  4. Use Java serialization as a demonstration example

The next article will use Netty to integrate Protobuf, as Google Protobuf takes a bit more steps to integrate Netty.