Simple Java implementation of Netty for communication

Posted by PlasmaDragon on Sat, 16 May 2020 13:09:58 +0200

Using Java to build a simple example of Netty communication

Students who have seen the dubbo source code should know that the underlying communication using the dubbo protocol is to use netty for interaction. Recently, after reading the netty part of dubbo, they wrote a simple example of netty communication.

Source address of this paper: implement Netty to communicate

get ready
Project screenshot

Module details

rpc-common
RPC common is a module that all modules need to use. In the project, there are some parameters requested and returned during communication, as well as some serialization tools.

rpc-client
Currently, RPC client is only a NettyClient startup class.

rpc-server
Currently, RPC client is only a NettyServer service startup class.

Dependency needed

At present, all dependencies appear under RPC common pom.xml Medium.

<!-- Netty -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.10.Final</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

<!-- Protostuff -->
<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.0.9</version>
</dependency>

<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.0.9</version>
</dependency>

<!-- Objenesis -->
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>2.1</version>
</dependency>

<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.38</version>
</dependency>


realization
First, we define the base class objects of this Request and Response in common.

public class Request {

private String requestId;

private Object parameter;

public String getRequestId() {
    return requestId;
}

public void setRequestId(String requestId) {
    this.requestId = requestId;
}

public Object getParameter() {
    return parameter;
}

public void setParameter(Object parameter) {
    this.parameter = parameter;
}

}

public class Response {

private String requestId;

private Object result;

public String getRequestId() {
    return requestId;
}

public void setRequestId(String requestId) {
    this.requestId = requestId;
}

public Object getResult() {
    return result;
}

public void setResult(Object result) {
    this.result = result;
}

}

Use fastJson for this serialization

The serialization and transformation of Netty objects are easy to understand. As long as ByteToMessageDecoder and messagetobyencoder inherit them respectively, after rewriting the method, get the Object and Byte, and the respective transformation will be OK.

However, if there are students who want to use it in production, it is recommended not to use fastJson, because there are so many bug patches, you can use google's protostuff.

public class RpcDecoder extends ByteToMessageDecoder {

// Target object type for decoding
private Class<?> target;

public RpcDecoder(Class target) {
    this.target = target;
}

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    if (in.readableBytes() < 4) {   // Not long enough to discard
        return;
    }
    in.markReaderIndex();   // Mark the location of the current readIndex
    int dataLength = in.readInt();  // Read the length of the message sent. ByteBuf's readInt() method will increase his readIndex by 4

    if (in.readableBytes() < dataLength) {  // If the length of the message body read is less than the length of the message we sent, resetReaderIndex. This is used with markReaderIndex. Reset readIndex to mark
        in.resetReaderIndex();
        return;
    }
    byte[] data = new byte[dataLength];
    in.readBytes(data);

    Object obj = JSON.parseObject(data, target);    // Convert byte data to the object we need
    out.add(obj);
}

}

public class RpcEncoder extends MessageToByteEncoder {

//Code target object type
private Class<?> target;

public RpcEncoder(Class target) {
    this.target = target;
}

@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
    if (target.isInstance(msg)) {
        byte[] data = JSON.toJSONBytes(msg);    // Using fastJson to convert objects to byte s
        out.writeInt(data.length);  // First write the message length, that is, the message header
        out.writeBytes(data);   // The message body contains the data we want to send
    }
}

}
NetyServer

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    Request request = (Request) msg;

    System.out.println("Client Data:" + JSON.toJSONString(request));

    Response response = new Response();
    response.setRequestId(request.getRequestId());
    response.setResult("Hello Client !");

    // The client actively closes the connection after receiving the information
    ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}

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

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

}

public class NettyServer {

private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

private String ip;
private int port;

public NettyServer(String ip, int port) {
    this.ip = ip;
    this.port = port;
}

public void server() throws Exception {

    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {

        final ServerBootstrap serverBootstrap = new ServerBootstrap();

        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .option(ChannelOption.SO_SNDBUF, 32 * 1024)
                .option(ChannelOption.SO_RCVBUF, 32 * 1024)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new RpcDecoder(Request.class))
                                .addLast(new RpcEncoder(Response.class))
                                .addLast(new NettyServerHandler());
                    }
                });

        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);  // Open long connection

        ChannelFuture future = serverBootstrap.bind(ip, port).sync();

// if (future.isSuccess()) {
//
// new Register().register("/yanzhenyidai/com.yanzhenyidai.server", ip + ":" + port);
// }

        future.channel().closeFuture().sync();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

public static void main(String[] args) throws Exception {
    new NettyServer("127.0.0.1", 20000).server();
}

}

Key terms:

EventLoopGroup
workerGroup
bossGroup
The EventLoopGroup on the Server side is divided into two parts. Generally, the workerGroup processes the request and the bossGroup receives the request.

ChannelOption
SO_BACKLOG
SO_SNDBUF
SO_RCVBUF
SO_KEEPALIVE
The above four constants are properties in a TCP connection.

ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
In NettyServerHandler ChannelFutureListener.CLOSE As the communication between the Server side and the Client side is actively closed, if there is no active Close, the NettyClient will always be blocked and will not get the return information of the NettyClient.

NettyClient

public class NettyClient extends SimpleChannelInboundHandler {

private final String ip;
private final int port;
private Response response;

public NettyClient(String ip, int port) {
    this.ip = ip;
    this.port = port;
}

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

@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Response response) throws Exception {
    this.response = response;
}

public Response client(Request request) throws Exception {
    EventLoopGroup group = new NioEventLoopGroup();

    try {

        // Create and initialize the Netty client Bootstrap object
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel channel) throws Exception {
                ChannelPipeline pipeline = channel.pipeline();

                pipeline.addLast(new RpcDecoder(Response.class));
                pipeline.addLast(new RpcEncoder(Request.class));
                pipeline.addLast(NettyClient.this);
            }
        });
        bootstrap.option(ChannelOption.TCP_NODELAY, true);

// String[] discover = new Discover().discover("/yanzhenyidai/com.yanzhenyidai.server").split(":");

        // Connect to RPC server
        ChannelFuture future = bootstrap.connect(ip, port).sync();

        // Write RPC request data and close connection
        Channel channel = future.channel();

        channel.writeAndFlush(request).sync();
        channel.closeFuture().sync();

        return response;
    } finally {
        group.shutdownGracefully();
    }
}

public static void main(String[] args) throws Exception {
    Request request = new Request();
    request.setRequestId(UUID.randomUUID().toString());
    request.setParameter("Hello Server !");
    System.out.println(JSON.toJSONString(new NettyClient("127.0.0.1", 30000).client(request)));
}

}

test

If all of the above is ready, you can debug.

Start sequence: start NettyServer first, and then start NettyClient.

summary
I remember that when I first came out to work, some colleagues who had worked for many years asked me if I knew Netty. At that time, my work was too short and I heard Putty directly. Now I think it's really humiliating, haha. 😋

As a communication framework, Netty is really practical if you know TCP, and there are similar requirements for information transmission in the project, and you don't want to integrate HTTP or Socket.

reference material:

Dubbo-Netty

Netty.io

Github address of the project: netty RPC

Original address https://www.cnblogs.com/yanzhenyidai/p/12901527.html

Topics: Java Netty Dubbo JSON