网络应用框架Netty快速入门
一 初遇Netty
<font color="#33CC33" size="4"> Netty是什么?</font><font color="#6666CC">Netty 是一個提供 asynchronous event-driven (異步事件驅動)的網(wǎng)絡應用框架,是一個用以快速開發(fā)高性能、可擴展協(xié)議的服務器和客戶端。</font>
<font color="#33CC33" size="4"> Netty能做什么?</font><font color="#6666CC">Netty 是一個 NIO 客戶端服務器框架,使用它可以快速簡單地開發(fā)網(wǎng)絡應用程序,比如服務器(HTTP服務器,FTP服務器,WebSocket服務器,Redis的Proxy服務器等等)和客戶端的協(xié)議。Netty 大大簡化了網(wǎng)絡程序的開發(fā)過程比如 TCP 和 UDP 的 socket 服務的開發(fā)。</font>
<font color="#33CC33" size="4"> Netty為什么好?</font><font color="#6666CC">Netty是建立在NIO基礎之上,Netty在NIO之上又提供了更高層次的抽象,使用它你可以更容易利用Java NIO提高服務端和客戶端的性能。</font>
Netty的特性:
<font color="#99CCCC"> 1. 設計</font>
1.1 統(tǒng)一的API,適用于不同的協(xié)議(阻塞和非阻塞)1.2 基于可擴展和靈活的事件驅動模型1.3高度可定制的線程模型 - 單線程,一個或多個線程池,如SEDA1.4真正的無連接數(shù)據(jù)報套接字支持(自3.1以來)<font color="#99CCCC">2. 性能</font>
2.1更好的吞吐量,低延遲2.2更省資源2.3盡量減少不必要的內存拷貝<font color="#99CCCC">3. 安全</font>
完整的SSL / TLS和StartTLS協(xié)議的支持<font color="#99CCCC">4. 易用性</font>
4.1 官方有詳細的使用指南4.2 對環(huán)境要求很低 <font color="#33CC33" size="4"> NIO和IO的區(qū)別是什么?</font><font color="#99CCCC">1. 一個面向字節(jié)一個面向緩沖;</font>
IO面向流意味著每次從流中讀一個或多個字節(jié),直至讀取所有字節(jié),它們沒有被緩存在任何地方。此外,它不能前后移動流中的數(shù)據(jù)。如果需要前后移動從流中讀取的數(shù)據(jù),需要先將它緩存到一個緩沖區(qū)。 Java NIO的緩沖導向方法略有不同。數(shù)據(jù)讀取到一個它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數(shù)據(jù)。而且,需確保當更多的數(shù)據(jù)讀入緩沖區(qū)時,不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。
<font color="#99CCCC">2. NIO是非阻塞IO,IO是阻塞IO</font>
阻塞意味著當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入,該線程在此期間不能再干任何事情了。而非阻塞不會這樣。
二 Netty使用
環(huán)境要求:
- JDK 7+
- Maven 3.2.x
- Netty 4.x
Maven依賴:
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.0.32.Final</version></dependency>以下Netty examples來源: 官方文檔
2.1 寫個拋棄服務器
<font color="#99CCCC">DiscardServerHandler.java</font>
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;/*** handler 是由 Netty 生成用來處理 I/O 事件的。*/ public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)/*** 這里我們覆蓋了 chanelRead() 事件處理方法。* 每當從客戶端收到新的數(shù)據(jù)時,這個方法會在收到消息時被調用。*((ByteBuf) msg).release():丟棄數(shù)據(jù)*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)// 默默地丟棄收到的數(shù)據(jù)((ByteBuf) msg).release(); // (3)}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)// 當出現(xiàn)異常就關閉連接cause.printStackTrace();ctx.close();} }目前我們已經(jīng)實現(xiàn)了 DISCARD 服務器的一半功能,剩下的需要編寫一個 main() 方法來啟動服務端的 DiscardServerHandler。
<font color="#99CCCC">DiscardServer.java</font>
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;/*** 啟動服務端的 DiscardServerHandler*/ public class DiscardServer {private int port;public DiscardServer(int port) {this.port = port;}public void run() throws Exception {//在這個例子中我們實現(xiàn)了一個服務端的應用,因此會有2個 NioEventLoopGroup 會被使用。//第一個經(jīng)常被叫做‘boss’,用來接收進來的連接。//第二個經(jīng)常被叫做‘worker’,用來處理已經(jīng)被接收的連接,一旦‘boss’接收到連接,就會把連接信息注冊到‘worker’上。EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();try {//啟動 NIO 服務的輔助啟動類ServerBootstrap serverBootstrap = new ServerBootstrap(); //用于處理ServerChannel和Channel的所有事件和IO。serverBootstrap.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)// 綁定端口,開始接收進來的連接ChannelFuture f = serverBootstrap.bind(port).sync(); // (7)// 等待服務器 socket 關閉 。// 在這個例子中,這不會發(fā)生,但你可以優(yōu)雅地關閉你的服務器。f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port;if (args.length > 0) {port = Integer.parseInt(args[0]);} else {port = 8080;}new DiscardServer(port).run();} }2.2 查看收到的數(shù)據(jù)
我們剛剛已經(jīng)編寫出我們第一個服務端,我們需要測試一下他是否真的可以運行。最簡單的測試方法是用 telnet 命令。例如,你可以在命令行上輸入telnet localhost 8080 或者其他類型參數(shù)。
然而我們能說這個服務端是正常運行了嗎?事實上我們也不知道,因為他是一個 discard 服務,你根本不可能得到任何的響應。為了證明他仍然是在正常工作的,讓我們修改服務端的程序來打印出他到底接收到了什么。
我們已經(jīng)知道 channelRead() 方法是在數(shù)據(jù)被接收的時候調用。讓我們放一些代碼到 DiscardServerHandler 類的 channelRead() 方法。
<font color="#99CCCC">修改DiscardServerHandler類的channelRead(ChannelHandlerContext ctx, Object msg)方法如下:</font>
@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)} }再次驗證,cmd下輸入:telnet localhost 8080。你將會看到服務端打印出了他所接收到的消息。
如下:
你在dos界面輸入的消息會被顯示出來
2.3 寫個應答服務器
到目前為止,我們雖然接收到了數(shù)據(jù),但沒有做任何的響應。然而一個服務端通常會對一個請求作出響應。讓我們學習怎樣在 ECHO 協(xié)議的實現(xiàn)下編寫一個響應消息給客戶端,這個協(xié)議針對任何接收的數(shù)據(jù)都會返回一個響應。
和 discard server 唯一不同的是把在此之前我們實現(xiàn)的 channelRead() 方法,返回所有的數(shù)據(jù)替代打印接收數(shù)據(jù)到控制臺上的邏輯。因此,需要把 channelRead() 方法修改如下:
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ctx.write(msg); ctx.flush(); }再次驗證,cmd下輸入:telnet localhost 8080。你會看到服務端會發(fā)回一個你已經(jīng)發(fā)送的消息。
如下:
下一篇我們會學習如何用Netty實現(xiàn)聊天功能。
歡迎關注我的微信公眾號:"Java 面試通關手冊"(堅持原創(chuàng),分享美文,分享各種Java學習資源,面試題,以及企業(yè)級Java實戰(zhàn)項目回復關鍵字免費領取):
總結
以上是生活随笔為你收集整理的网络应用框架Netty快速入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux rsyslog 转存至日志服
- 下一篇: Swift解决【闭包引起的循环强引用】