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