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,
Cross-platform, cross-language
High performance/high scale storage format
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
Serialization comes with the JDK
Google Protobuf
Facebook 的 Thrift
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
- What is codec
- What are the characteristics of a good codec framework
- What are the current popular codec frameworks
- 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.