Netty介绍 与第一个Netty实例
官網(wǎng):https://netty.io/
以下內(nèi)容大部分來(lái)自Netty官網(wǎng)內(nèi)容
一、現(xiàn)有問(wèn)題
現(xiàn)在我們使用通用應(yīng)用程序或庫(kù)來(lái)彼此通信。例如,我們經(jīng)常使用HTTP客戶端庫(kù)從web服務(wù)器檢索信息,并通過(guò)web服務(wù)調(diào)用遠(yuǎn)程過(guò)程調(diào)用。然而,一個(gè)通用協(xié)議或它的實(shí)現(xiàn)有時(shí)并不能很好地?cái)U(kuò)展。這就像我們沒(méi)有使用通用的HTTP服務(wù)器來(lái)交換巨大的文件、電子郵件消息和接近實(shí)時(shí)的消息(如財(cái)務(wù)信息和多人游戲數(shù)據(jù))。需要的是一個(gè)高度優(yōu)化的協(xié)議實(shí)現(xiàn),專門(mén)用于特定的目的。例如,您可能想實(shí)現(xiàn)一個(gè)針對(duì)基于ajax的聊天應(yīng)用程序、媒體流或大型文件傳輸進(jìn)行優(yōu)化的HTTP服務(wù)器。您甚至可能想設(shè)計(jì)和實(shí)現(xiàn)一個(gè)全新的協(xié)議,它可以根據(jù)您的需要進(jìn)行精確定制。另一種不可避免的情況是,您必須處理遺留的專有協(xié)議,以確保與舊系統(tǒng)的互操作性。在這種情況下,重要的是在不犧牲應(yīng)用程序的穩(wěn)定性和性能的情況下,我們可以多快地實(shí)現(xiàn)該協(xié)議。
二、解決方案
Netty項(xiàng)目致力于提供一個(gè)異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架和工具,用于快速開(kāi)發(fā)可維護(hù)的高性能和高可伸縮性協(xié)議服務(wù)器和客戶端。
換句話說(shuō),Netty是一個(gè)NIO客戶端服務(wù)器框架,它支持快速而簡(jiǎn)單地開(kāi)發(fā)網(wǎng)絡(luò)應(yīng)用程序,如協(xié)議服務(wù)器和客戶端。它極大地簡(jiǎn)化和簡(jiǎn)化了網(wǎng)絡(luò)編程,如TCP和UDP套接字服務(wù)器開(kāi)發(fā)。
“快速和簡(jiǎn)單”并不意味著結(jié)果應(yīng)用程序?qū)⑹艿娇删S護(hù)性或性能問(wèn)題的影響。Netty是經(jīng)過(guò)精心設(shè)計(jì)的,從許多協(xié)議(如FTP、SMTP、HTTP和各種基于二進(jìn)制和文本的遺留協(xié)議)的實(shí)現(xiàn)中吸取了經(jīng)驗(yàn)。因此,Netty成功地找到了一種方法,可以在不妥協(xié)的情況下實(shí)現(xiàn)開(kāi)發(fā)的易用性、性能、穩(wěn)定性和靈活性。
有些用戶可能已經(jīng)發(fā)現(xiàn)了其他聲稱具有相同優(yōu)勢(shì)的網(wǎng)絡(luò)應(yīng)用程序框架,您可能想知道是什么使Netty與它們?nèi)绱瞬煌4鸢甘撬⒌恼軐W(xué)。Netty從一開(kāi)始就為您提供API和實(shí)現(xiàn)方面最舒適的體驗(yàn)。這不是一些有形的東西,但你會(huì)意識(shí)到,這種哲學(xué)將使你的生活更容易,當(dāng)你閱讀本指南和玩Netty。
三、編寫(xiě)一個(gè)丟棄服務(wù)器
世界上最簡(jiǎn)單的協(xié)議不是“你好,世界!”但丟棄。它是一種丟棄任何接收到的數(shù)據(jù)而不進(jìn)行任何響應(yīng)的協(xié)議。
要實(shí)現(xiàn)丟棄協(xié)議,您需要做的唯一一件事就是忽略所有接收到的數(shù)據(jù)。讓我們直接從處理程序?qū)崿F(xiàn)開(kāi)始,它處理Netty生成的I/O事件。
package io.netty.example.discard;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;/*** Handles a server-side channel.*/ public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)// Discard the received data silently.((ByteBuf) msg).release(); // (3)}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)// Close the connection when an exception is raised.cause.printStackTrace();ctx.close();} }實(shí)現(xiàn)丟棄服務(wù)服務(wù)器:
package io.netty.example.discard;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;/*** Discards any incoming data.*/ public class DiscardServer {private int port;public DiscardServer(int port) {this.port = port;}public void run() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap(); // (2)b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3).childHandler(new ChannelInitializer<SocketChannel>() { // (4)@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new DiscardServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128) // (5).childOption(ChannelOption.SO_KEEPALIVE, true); // (6)// Bind and start to accept incoming connections.ChannelFuture f = b.bind(port).sync(); // (7)// Wait until the server socket is closed.// In this example, this does not happen, but you can do that to gracefully// shut down your server.f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8080;if (args.length > 0) {port = Integer.parseInt(args[0]);}new DiscardServer(port).run();} }上述代碼中數(shù)字分別代表以下內(nèi)容:
1.NioEventLoopGroup是一個(gè)處理I/O操作的多線程事件循環(huán)。Netty為不同類型的傳輸提供了各種EventLoopGroup實(shí)現(xiàn)。在本例中,我們將實(shí)現(xiàn)一個(gè)服務(wù)器端應(yīng)用程序,因此將使用兩個(gè)NioEventLoopGroup。第一個(gè),通常稱為“boss”,接受一個(gè)傳入連接。第二個(gè),通常稱為“worker”,在老板接受連接并向worker注冊(cè)已接受的連接后,處理已接受連接的流量。使用多少線程以及如何將它們映射到創(chuàng)建的通道取決于EventLoopGroup實(shí)現(xiàn),甚至可以通過(guò)構(gòu)造函數(shù)進(jìn)行配置。
2.ServerBootstrap是一個(gè)設(shè)置服務(wù)器的助手類。您可以直接使用通道設(shè)置服務(wù)器。但是,請(qǐng)注意,這是一個(gè)乏味的過(guò)程,而且在大多數(shù)情況下不需要這樣做。
3.這里,我們指定使用NioServerSocketChannel類,該類用于實(shí)例化一個(gè)新通道以接受傳入連接。
4.這里指定的處理程序?qū)⑹冀K由新接受的通道進(jìn)行評(píng)估。ChannelInitializer是一個(gè)特殊的處理程序,用于幫助用戶配置新通道。您很可能希望通過(guò)添加一些處理程序(如DiscardServerHandler)來(lái)配置新通道的ChannelPipeline,以實(shí)現(xiàn)網(wǎng)絡(luò)應(yīng)用程序。隨著應(yīng)用程序變得越來(lái)越復(fù)雜,您可能會(huì)向管道中添加更多的處理程序,并最終將這個(gè)匿名類提取到一個(gè)頂級(jí)類中。
5.您還可以設(shè)置特定于通道實(shí)現(xiàn)的參數(shù)。我們正在編寫(xiě)一個(gè)TCP/IP服務(wù)器,因此允許設(shè)置套接字選項(xiàng),如tcpNoDelay和keepAlive。請(qǐng)參考ChannelOption的apidocs和特定的ChannelConfig實(shí)現(xiàn),以獲得有關(guān)受支持的ChannelOptions的概述。
6.您注意到option()和childOption()了嗎?option()用于接受傳入連接的NioServerSocketChannel。childOption()用于父服務(wù)器通道(在本例中為NioServerSocketChannel)接受的通道。
7.我們現(xiàn)在準(zhǔn)備走了。剩下的工作就是綁定到端口并啟動(dòng)服務(wù)器。在這里,我們綁定到機(jī)器中所有nic(網(wǎng)絡(luò)接口卡)的端口8080。現(xiàn)在可以任意多次調(diào)用bind()方法(使用不同的綁定地址)。
查看接收到的數(shù)據(jù)
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf in = (ByteBuf) msg;try {while (in.isReadable()) { // (1)System.out.print((char) in.readByte());System.out.flush();}} finally {ReferenceCountUtil.release(msg); // (2)} }1、他的低效循環(huán)實(shí)際上可以簡(jiǎn)化為:System.out.println(in.toString(io.netty.uti.charsetutil.us_ascii))
2、也可以在這里直接使用in.release()。
編寫(xiě)回顯服務(wù)器
到目前為止,我們一直在使用數(shù)據(jù)而沒(méi)有響應(yīng)。然而,服務(wù)器通常應(yīng)該響應(yīng)請(qǐng)求。讓我們學(xué)習(xí)如何通過(guò)實(shí)現(xiàn)ECHO協(xié)議向客戶端編寫(xiě)響應(yīng)消息,接收到的任何數(shù)據(jù)都會(huì)被發(fā)回。與我們?cè)谇皫坠?jié)中實(shí)現(xiàn)的丟棄服務(wù)器的惟一區(qū)別是,它將發(fā)送回接收到的數(shù)據(jù),而不是將接收到的數(shù)據(jù)打印到控制臺(tái)。因此,再次修改channelRead()方法就足夠了:
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ctx.write(msg); //Read完直接寫(xiě)給客戶端 (1)ctx.flush(); // (2)可直接使用ctx.writeAndFlush(msg)}1、ChannelHandlerContext對(duì)象提供各種操作,使您能夠觸發(fā)各種I/O事件和操作。在這里,我們調(diào)用write(Object)來(lái)逐字寫(xiě)入接收到的消息。請(qǐng)注意,我們沒(méi)有像在丟棄示例中那樣釋放接收到的消息。這是因?yàn)镹etty在將其寫(xiě)入網(wǎng)絡(luò)時(shí)為您釋放了它。
2、write(Object)不會(huì)將消息寫(xiě)入網(wǎng)絡(luò)。它在內(nèi)部進(jìn)行緩沖,然后通過(guò)ctx.flush()將其刷新到連接上。或者,為了簡(jiǎn)潔,您可以調(diào)用ctx.writeAndFlush(msg)。
待續(xù)。。
?
?
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Netty介绍 与第一个Netty实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 分布式:阿里云HSF转dubbo+zoo
- 下一篇: foreach无法给外部变量赋值(Loc