Java网络编程 — Netty入门
認識Netty
Netty簡介
Netty is?an asynchronous event-driven network application framework?for rapid development of maintainable high performance protocol servers & clients.
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
Netty是一個異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用于快速開發(fā)可維護的高性能協(xié)議服務(wù)器和客戶端。
Netty是一款NIO客戶端服務(wù)器框架,可以快速輕松地開發(fā)協(xié)議服務(wù)器和客戶端等網(wǎng)絡(luò)應(yīng)用程序。它極大地簡化并簡化了TCP和UDP套接字服務(wù)器等網(wǎng)絡(luò)編程。
Netty架構(gòu)圖如下圖所示:
Netty的特性總結(jié)如下表:
為什么選擇Netty
Netty是業(yè)界最流行的NIO框架之一,它的健壯性、功能、性能、可定制性和可擴展性在同類框架中都是首屈一指的,已經(jīng)得到成百上千的商用項目驗證。通過對Netty的分析,將它的優(yōu)點總結(jié)如下:
- API使用簡單,開發(fā)門檻低;
- 功能強大,預(yù)置了多種編解碼功能,支持多種主流協(xié)議;
- 定制能力強,可以通過ChannelHandler對通信框架進行靈活地擴展;
- 性能高,通過與其他業(yè)界主流的NIO框架對比,Netty的綜合性能最優(yōu);
- 成熟、穩(wěn)定,Netty修復(fù)了已經(jīng)發(fā)現(xiàn)的所有JDK NIO BUG,業(yè)務(wù)開發(fā)人員不需要再為NIO的BUG而煩惱;
- 社區(qū)活躍,版本迭代周期短,發(fā)現(xiàn)的BUG可以被及時修復(fù),同時,更多的新功能會加入;
- 經(jīng)歷了大規(guī)模的商業(yè)應(yīng)用考驗,質(zhì)量得到驗證。
核心概念
Netty is a non-blocking framework. This leads to high throughput compared to blocking IO.?Understanding non-blocking IO is crucial to understanding Netty’s core components and their relationships.
Channel
Channel?is the base of Java NIO. It represents an open connection which is capable of IO operations such as reading and writing.
Channel是Java NIO的基礎(chǔ)。它表示一個開放的連接,能夠進行IO操作,例如讀寫。
Future
Netty中的每個IO操作都是非阻塞的,這意味著每次操作都會在通話結(jié)束后立即返回。
標準Java庫中有一個Future接口,但對Netty而言不方便 - 我們只能向Future詢問完成操作或阻止當(dāng)前線程,直到完成操作。這就是為什么Netty有自己的ChannelFuture接口,我們可以將回調(diào)傳遞給ChannelFuture,其將在操作完成時被調(diào)用。
Events and Handlers
Netty使用事件驅(qū)動的應(yīng)用程序范例,因此數(shù)據(jù)處理的管道是通過處理程序的一系列事件。事件和處理程序可以與入站(inbound)和出站(outbound)數(shù)據(jù)流相關(guān)聯(lián)。?Inbound events(入站事件)可以如下所示:
- Channel activation and deactivation
- Read operation events
- Exception events
- User events
Outbound events(出站事件)更簡單,通常與打開/關(guān)閉連接(?opening/closing a connection)和寫入/清空數(shù)據(jù)(writing/flushing data)有關(guān)。
Netty應(yīng)用程序由幾個網(wǎng)絡(luò)和應(yīng)用程序邏輯事件及其處理程序組成。通道事件處理程序的基礎(chǔ)接口是ChannelHandler及其子接口ChannelOutboundHandler``ChannelInboundHandler。
Netty提供了大量ChannelHandler實現(xiàn)的類。值得注意的是適配器只是空的實現(xiàn),例如ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter。
Encoders and Decoders
當(dāng)我們使用網(wǎng)絡(luò)協(xié)議時,我們需要執(zhí)行數(shù)據(jù)序列化和反序列化。為此,Netty為了能夠解碼傳入數(shù)據(jù)引入了ChannelInboundHandler的擴展解碼器,大多數(shù)解碼器的基類ByteToMessageDecoder。對于編碼輸出數(shù)據(jù),Netty同樣提供了ChannelOutboundHandler的擴展編碼器, MessageToByteEncoder是大多數(shù)編碼器實現(xiàn)的基礎(chǔ)。
應(yīng)用示例
Server Application
創(chuàng)建一個簡單協(xié)議服務(wù)器的項目,它接收請求,執(zhí)行計算并發(fā)送響應(yīng)。
Dependencies
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.10.Final</version> </dependency> 復(fù)制代碼Data Model
public class RequestData {private int intValue;private String stringValue;// standard getters and setters } 復(fù)制代碼public class ResponseData {private int intValue;// standard getters and setters } 復(fù)制代碼Request Decoder
It should be noted that Netty works with socket receive buffer, which is represented not as a queue but just as a bunch of bytes. This means that our inbound handler can be called when the full message is not received by a server.
We must make sure that we have received the full message before processing. The decoder for RequestData is shown next:
public class RequestDecoder extends ReplayingDecoder<RequestData> {private final Charset charset = Charset.forName("UTF-8");protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {RequestData data = new RequestData();data.setIntValue(in.readInt());int strLen = in.readInt();data.setStringValue(in.readCharSequence(strLen, charset).toString());out.add(data);} } 復(fù)制代碼ReplayingDecoder It uses an implementation of ByteBuf which throws an exception when there is not enough data in the buffer for the reading operation.
When the exception is caught the buffer is rewound to the beginning and the decoder waits for a new portion of data. Decoding stops when the out list is not empty after decode execution.
Response Encoder
public class ResponseDataEncoder extends MessageToByteEncoder<ResponseData> {protected void encode(ChannelHandlerContext ctx, ResponseData msg, ByteBuf out) throws Exception {out.writeInt(msg.getIntValue());} } 復(fù)制代碼Request Processing
public class ProcessingHandler extends ChannelInboundHandlerAdapter {public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {RequestData requestData = (RequestData) msg;ResponseData responseData = new ResponseData();responseData.setIntValue(requestData.getIntValue() * 2);ChannelFuture future = ctx.writeAndFlush(responseData);future.addListener(ChannelFutureListener.CLOSE);System.out.println(requestData);} } 復(fù)制代碼Server Bootstrap
public class NettyServer {private int port;// constructorpublic static void main(String[] args) throws Exception {int port = args.length > 0? Integer.parseInt(args[0]);: 8080;new NettyServer(port).run();}public void run() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {public void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new RequestDecoder(), new ResponseDataEncoder(), new ProcessingHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}} } 復(fù)制代碼Client Application
The client should perform reverse encoding and decoding, so we need to have a?RequestDataEncoder?and?ResponseDataDecoder:
public class RequestDataEncoder extends MessageToByteEncoder<RequestData> {private final Charset charset = Charset.forName("UTF-8");protected void encode(ChannelHandlerContext ctx, RequestData msg, ByteBuf out) throws Exception {out.writeInt(msg.getIntValue());out.writeInt(msg.getStringValue().length());out.writeCharSequence(msg.getStringValue(), charset);} } 復(fù)制代碼public class ResponseDataDecoder extends ReplayingDecoder<ResponseData> {protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {ResponseData data = new ResponseData();data.setIntValue(in.readInt());out.add(data);} } 復(fù)制代碼Also, we need to define a?ClientHandler?which will send the request and receive the response from server:
public class ClientHandler extends ChannelInboundHandlerAdapter {public void channelActive(ChannelHandlerContext ctx) throws Exception {RequestData msg = new RequestData();msg.setIntValue(123);msg.setStringValue("all work and no play makes jack a dull boy");ChannelFuture future = ctx.writeAndFlush(msg);}public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println((ResponseData)msg);ctx.close();} } 復(fù)制代碼Now let’s bootstrap the client:
public class NettyClient {public static void main(String[] args) throws Exception {String host = "localhost";int port = 8080;EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(workerGroup);b.channel(NioSocketChannel.class);b.option(ChannelOption.SO_KEEPALIVE, true);b.handler(new ChannelInitializer<SocketChannel>() {public void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(), new ClientHandler());}});ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}} } 復(fù)制代碼Now we can run the client’s main method and take a look at the console output. As expected, we got ResponseData with intValue equal to 246.
參考資源
-
Netty權(quán)威指南(第2版)-李林鋒
-
Introduction to Netty
總結(jié)
以上是生活随笔為你收集整理的Java网络编程 — Netty入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 开发使用 Gradle
- 下一篇: Java基础-基本数据类型