Netty(4.x)中文文檔

??????本文由筆者翻譯于Netty官方文檔,如有任何翻譯上的錯誤,請聯(lián)系筆者冈欢,感謝城须!

前言

問題

??????現(xiàn)在我們使用通用應(yīng)用程序或庫來彼此通信。例如势决,我們經(jīng)常使用HTTP客戶端庫從web服務(wù)器檢索信息,并通過web服務(wù)調(diào)用遠程過程調(diào)用。然而筑累,一個通用協(xié)議或它的實現(xiàn)有時并不能很好地擴展。這就像我們沒有使用通用的HTTP服務(wù)器來交換巨大的文件丝蹭、電子郵件消息和接近實時的消息(如財務(wù)信息和多人游戲數(shù)據(jù))慢宗。需要的是一個高度優(yōu)化的協(xié)議實現(xiàn),專門用于特定的目的奔穿。例如镜沽,您可能想實現(xiàn)一個針對基于ajax的聊天應(yīng)用程序、媒體流或大型文件傳輸進行優(yōu)化的HTTP服務(wù)器贱田。您甚至可能想設(shè)計和實現(xiàn)一個全新的協(xié)議缅茉,它可以根據(jù)您的需要進行精確定制。另一種不可避免的情況是男摧,您必須處理遺留的專有協(xié)議蔬墩,以確保與舊系統(tǒng)的互操作性。在這種情況下耗拓,重要的是在不犧牲應(yīng)用程序的穩(wěn)定性和性能的情況下拇颅,我們可以多快地實現(xiàn)該協(xié)議。

解決方案

??????Netty項目致力于提供一個異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架和工具乔询,用于快速開發(fā)可維護的高性能和高可伸縮性協(xié)議服務(wù)器和客戶端樟插。
??????換句話說,Netty是一個NIO客戶端服務(wù)器框架竿刁,它支持快速而簡單地開發(fā)網(wǎng)絡(luò)應(yīng)用程序黄锤,如協(xié)議服務(wù)器和客戶端。它極大地簡化和簡化了網(wǎng)絡(luò)編程食拜,如TCP和UDP套接字服務(wù)器開發(fā)鸵熟。“快速和簡單”并不意味著結(jié)果應(yīng)用程序?qū)⑹艿娇删S護性或性能問題的影響监婶。
??????Netty是經(jīng)過精心設(shè)計的旅赢,從許多協(xié)議(如FTP、SMTP惑惶、HTTP和各種基于二進制和文本的遺留協(xié)議)的實現(xiàn)中吸取了經(jīng)驗煮盼。因此,Netty成功地找到了一種方法带污,可以在不妥協(xié)的情況下實現(xiàn)開發(fā)的易用性僵控、性能、穩(wěn)定性和靈活性鱼冀。
有些用戶可能已經(jīng)發(fā)現(xiàn)了其他聲稱具有相同優(yōu)勢的網(wǎng)絡(luò)應(yīng)用程序框架报破,您可能想知道是什么使Netty與它們?nèi)绱瞬煌凭汀4鸢甘撬⒌恼軐W。
??????Netty從一開始就為您提供API和實現(xiàn)方面最舒適的體驗充易。這不是一些有形的東西梗脾,但你會意識到,這種哲學將使你的生活更容易盹靴,當你閱讀本指南和玩Netty炸茧。

開始

??????本章通過簡單的例子介紹了Netty的核心構(gòu)造,讓你快速入門稿静。當你在本章結(jié)束的時候梭冠,你將能夠?qū)懸粋€客戶端和一個服務(wù)器在Netty之上。如果您喜歡使用自頂向下的方法來學習改备,您可能希望從第2章“架構(gòu)概述”開始控漠,然后再回到這里。

開始之前

??????運行本章示例的最低要求只有兩個;最新版本的Netty和JDK 1.6或以上悬钳。最新版本的Netty可以在項目下載頁面中找到盐捷。要下載正確版本的JDK,請參考您首選的JDK供應(yīng)商的網(wǎng)站他去。

??????在閱讀的過程中毙驯,你可能會對本章介紹的課程有更多的疑問倒堕。如果您想了解更多有關(guān)它們的信息灾测,請參閱API參考。為了您的方便垦巴,本文檔中的所有類名都鏈接到在線API參考媳搪。此外,如果有任何不正確的信息骤宣、語法錯誤或打字錯誤秦爆,請不要猶豫聯(lián)系Netty項目社區(qū),并讓我們知道憔披,如果您有任何好的想法來幫助改進文檔等限。

寫一個丟棄服務(wù)

??????世界上最簡單的協(xié)議不是“你好,世界!”但丟棄芬膝。它是一種丟棄任何接收到的數(shù)據(jù)而不進行任何響應(yīng)的協(xié)議望门。
??????要實現(xiàn)丟棄協(xié)議,您需要做的唯一一件事就是忽略所有接收到的數(shù)據(jù)锰霜。讓我們直接從處理程序?qū)崿F(xiàn)開始筹误,它處理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)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // Discard the received data silently.
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

??????1.DiscardServerHandler擴展了ChannelInboundHandlerAdapter癣缅,它是ChannelInboundHandler的實現(xiàn)厨剪。ChannelInboundHandler提供了各種可以覆蓋的事件處理程序方法『逶停現(xiàn)在,擴展ChannelInboundHandlerAdapter就足夠了祷膳,而不需要您自己實現(xiàn)處理程序接口陶衅。
??????2.我們在這里重寫channelRead()事件處理程序方法。每當從客戶端接收到新數(shù)據(jù)時直晨,就會隨著接收到的消息調(diào)用此方法万哪。在本例中,接收到的消息的類型是ByteBuf抡秆。
??????3.要實現(xiàn)丟棄協(xié)議奕巍,處理程序必須忽略接收到的消息。ByteBuf是一個引用計數(shù)對象儒士,必須通過release()方法顯式地釋放它的止。請記住,釋放傳遞給處理程序的引用計數(shù)對象是處理程序的責任着撩。通常诅福,channelRead()處理程序方法的實現(xiàn)如下所示:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}

??????4.當Netty由于I/O錯誤或處理程序?qū)崿F(xiàn)由于處理事件時拋出異常而引發(fā)異常時,使用Throwable調(diào)用exceptionCaught()事件處理程序方法拖叙。在大多數(shù)情況下氓润,應(yīng)該記錄捕獲的異常,并在這里關(guān)閉與之關(guān)聯(lián)的通道薯鳍,盡管此方法的實現(xiàn)可能因您想要處理異常情況而有所不同咖气。例如,您可能希望在關(guān)閉連接之前發(fā)送帶有錯誤代碼的響應(yīng)消息挖滤。

??????到目前為止一切順利崩溪。我們已經(jīng)實現(xiàn)了丟棄服務(wù)器的前半部分。現(xiàn)在剩下的工作是編寫main()方法斩松,該方法使用DiscardServerHandler啟動服務(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)
                 @Override
                 public 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();
    }
}

??????1.NioEventLoopGroup是一個處理I/O操作的多線程事件循環(huán)。Netty為不同類型的傳輸提供了各種EventLoopGroup實現(xiàn)惧盹。在本例中乳幸,我們將實現(xiàn)一個服務(wù)器端應(yīng)用程序,因此將使用兩個NioEventLoopGroup钧椰。第一個粹断,通常稱為“boss”,接受一個傳入連接演侯。第二個姿染,通常稱為“worker”,在老板接受連接并向worker注冊已接受的連接后,處理已接受連接的流量悬赏。使用多少線程以及如何將它們映射到創(chuàng)建的通道取決于EventLoopGroup實現(xiàn)狡汉,甚至可以通過構(gòu)造函數(shù)進行配置。
??????2.ServerBootstrap是一個設(shè)置服務(wù)器的助手類闽颇。您可以直接使用通道設(shè)置服務(wù)器盾戴。但是,請注意兵多,這是一個乏味的過程尖啡,而且在大多數(shù)情況下不需要這樣做。
??????3.這里剩膘,我們指定使用NioServerSocketChannel類衅斩,該類用于實例化一個新通道以接受傳入連接
??????4.這里指定的處理程序?qū)⑹冀K由新接受的通道進行評估。ChannelInitializer是一個特殊的處理程序怠褐,用于幫助用戶配置新通道畏梆。您很可能希望通過添加一些處理程序(如DiscardServerHandler)來配置新通道的ChannelPipeline,以實現(xiàn)網(wǎng)絡(luò)應(yīng)用程序奈懒。隨著應(yīng)用程序變得越來越復(fù)雜奠涌,您可能會向管道中添加更多的處理程序,并最終將這個匿名類提取到一個頂級類中磷杏。
??????5.您還可以設(shè)置特定于通道實現(xiàn)的參數(shù)溜畅。我們正在編寫一個TCP/IP服務(wù)器,因此允許設(shè)置套接字選項极祸,如tcpNoDelay和keepAlive慈格。請參考ChannelOption的apidocs和特定的ChannelConfig實現(xiàn),以獲得有關(guān)受支持的ChannelOptions的概述贿肩。
??????6.您注意到option()和childOption()了嗎?option()用于接受傳入連接的NioServerSocketChannel峦椰。childOption()用于父服務(wù)器通道(在本例中為NioServerSocketChannel)接受的通道龄寞。
??????7.我們現(xiàn)在準備走了汰规。剩下的工作就是綁定到端口并啟動服務(wù)器。在這里物邑,我們綁定到機器中所有nic(網(wǎng)絡(luò)接口卡)的端口8080×锵現(xiàn)在可以任意多次調(diào)用bind()方法(使用不同的綁定地址)。

??????恭喜你!您剛剛完成了在Netty上的第一個服務(wù)器色解。

查看收到的數(shù)據(jù)

??????現(xiàn)在我們已經(jīng)編寫了我們的第一個服務(wù)器茂嗓,我們需要測試它是否真的工作。最簡單的測試方法是使用telnet命令科阎。例如述吸,您可以在命令行中輸入telnet localhost 8080并鍵入一些內(nèi)容。
??????但是,我們能說服務(wù)器工作正常嗎?我們不能真正知道這一點蝌矛,因為它是一個廢棄服務(wù)器道批。你不會得到任何回應(yīng)。為了證明它確實在工作入撒,讓我們修改服務(wù)器來打印它收到的內(nèi)容隆豹。
??????我們已經(jīng)知道,只要接收到數(shù)據(jù)茅逮,就會調(diào)用channelRead()方法璃赡。讓我們在DiscardServerHandler的channelRead()方法中放入一些代碼:

@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)實際上可以簡化為:System.out.println(in.toString(io.net . uti.charsetutil.us_ascii))
??????2.您也可以在這里使用in.release()。

??????如果您再次運行telnet命令献雅,您將看到服務(wù)器打印它接收到的內(nèi)容碉考。
??????丟棄服務(wù)器的完整源代碼位于該發(fā)行版的io.net .example.丟棄包中。

寫一個應(yīng)答服務(wù)器

??????到目前為止挺身,我們一直在使用數(shù)據(jù)而沒有響應(yīng)豆励。然而,服務(wù)器通常應(yīng)該響應(yīng)請求瞒渠。讓我們學習如何通過實現(xiàn)ECHO協(xié)議向客戶端編寫響應(yīng)消息良蒸,接收到的任何數(shù)據(jù)都會被發(fā)回。
??????與我們在前幾節(jié)中實現(xiàn)的丟棄服務(wù)器的惟一區(qū)別是伍玖,它將發(fā)送回接收到的數(shù)據(jù)嫩痰,而不是將接收到的數(shù)據(jù)打印到控制臺。因此窍箍,修改channelRead()方法就足夠了

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg); // (1)
        ctx.flush(); // (2)
    }

??????1.ChannelHandlerContext對象提供各種操作串纺,使您能夠觸發(fā)各種I/O事件和操作。在這里椰棘,我們調(diào)用write(Object)來逐字寫入接收到的消息纺棺。請注意,我們沒有像在丟棄示例中那樣釋放接收到的消息邪狞。這是因為Netty在將其寫入網(wǎng)絡(luò)時為您釋放了它祷蝌。write(Object)不會將消息寫入網(wǎng)絡(luò)。它在內(nèi)部進行緩沖帆卓,然后通過ctx.flush()將其刷新到連接上巨朦。或者剑令,為了簡潔糊啡,您可以調(diào)用ctx.writeAndFlush(msg)。
??????2.write(Object)不會將消息寫入網(wǎng)絡(luò)吁津。它在內(nèi)部進行緩沖棚蓄,然后通過ctx.flush()將其刷新到連接上。或者梭依,為了簡潔挣柬,您可以調(diào)用ctx.writeAndFlush(msg)。

??????如果您再次運行telnet命令睛挚,您將看到服務(wù)器發(fā)回您發(fā)送給它的任何內(nèi)容邪蛔。
??????echo服務(wù)器的完整源代碼位于該發(fā)行版的io.net .example.echo包中。

寫一個時間服務(wù)器

?
?


??????未完扎狱,待續(xù)侧到!

????????????最近更新于2020.11.04


??????
??????

??????

??????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淤击,隨后出現(xiàn)的幾起案子匠抗,更是在濱河造成了極大的恐慌,老刑警劉巖污抬,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汞贸,死亡現(xiàn)場離奇詭異,居然都是意外死亡印机,警方通過查閱死者的電腦和手機矢腻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來射赛,“玉大人多柑,你說我怎么就攤上這事¢乖穑” “怎么了竣灌?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秆麸。 經(jīng)常有香客問我初嘹,道長,這世上最難降的妖魔是什么沮趣? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任屯烦,我火速辦了婚禮,結(jié)果婚禮上兔毒,老公的妹妹穿的比我還像新娘漫贞。我一直安慰自己,他們只是感情好育叁,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芍殖,像睡著了一般豪嗽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天龟梦,我揣著相機與錄音隐锭,去河邊找鬼。 笑死计贰,一個胖子當著我的面吹牛钦睡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躁倒,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荞怒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秧秉?” 一聲冷哼從身側(cè)響起褐桌,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎象迎,沒想到半個月后荧嵌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡砾淌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年啦撮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汪厨。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逻族,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骄崩,到底是詐尸還是另有隱情聘鳞,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布要拂,位于F島的核電站抠璃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脱惰。R本人自食惡果不足惜搏嗡,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拉一。 院中可真熱鬧采盒,春花似錦、人聲如沸蔚润。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫡纠。三九已至烦租,卻和暖如春延赌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叉橱。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工挫以, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窃祝。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓掐松,卻偏偏與公主長得像,于是被迫代替她去往敵國和親粪小。 傳聞我的和親對象是個殘疾皇子大磺,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354