前文總結了NIO的內容,有了NIO的一些基礎以後,咱們就能夠來看下Netty。Netty是Java領域的高性能網絡傳輸框架,RPC的技術核心就是網絡傳輸和序列化,因此Netty給予了RPC在網絡傳輸領域巨大的支持。 編程
一個簡單的Netty代碼實現
網絡傳輸基於的是TCP協議,因此會有服務端和客戶端之分,而Netty是網絡傳輸框架,因此一個完整的Netty代碼至少是有服務端和客戶端的。本文代碼基於Netty4.1.15。bootstrap
服務端:服務器
public class DemoServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup). channel(NioServerSocketChannel.class). childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8)); ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8)); ch.pipeline().addLast(new DemoServerHandler()); } }); ChannelFuture future = serverBootstrap.bind(8899).sync(); future.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } public class DemoServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("" + ctx.channel().remoteAddress() + "," + msg); ctx.channel().writeAndFlush("from server" + UUID.randomUUID()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }
}
客戶端:網絡
public class DemoClient { public static void main(String[] args) throws Exception{ EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try{ Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8)); ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8)); ch.pipeline().addLast(new DemoClientHandler()); } }); ChannelFuture channelFuture = bootstrap.connect("localhost",8899).sync(); channelFuture.channel().closeFuture().sync(); }finally { eventLoopGroup.shutdownGracefully(); } }
}
public class DemoClientHandler extends SimpleChannelInboundHandler<String> {br/>@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("" + ctx.channel().remoteAddress());
System.out.println("client output:" + msg);
ctx.writeAndFlush("from client" + LocalDateTime.now());
}併發
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { String msg = "來自客戶端的問候!"; ctx.writeAndFlush(msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }
}
來看下這個簡單的代碼作了什麼:框架
一、服務端啓動後,客戶端先向服務端發起鏈接創建;dom
二、鏈接創建後,觸發客戶端的channelActive方法,該方法向服務端發出了一條信息,這條信息默認在網絡中是會轉成字節的形式來傳輸的,由於TCP的數據傳輸都是基於字節的,這個過程也叫作編碼;異步
三、服務端收到信息後,會被服務端的Handler,其實就是StringDecoder先作處理,從字節變成了字符,這個過程也叫作解碼;ide
四、此時對於DemoServerHandler來講,信息已經變成了符合本身的String類型,因此channelRead0方法會被調用,輸出信息,同時向客戶端發出信息,信息又會轉成字節的信息傳向客戶端;模塊化
五、客戶端收到信息後,會被客戶端解碼成字符,觸發客戶端的channelRead0方法,輸出客戶端地址和收到的信息,再向服務端發送時間戳;
六、循環往復上述3-5,死循環;
模塊化
從上述這個簡單的DEMO中,咱們能夠提取出Netty的核心模塊:
一、Channel、EventLoop、ChannelFuture
Channel接口:基本的IO操做(bind()/connect()/read()/write())依賴於底層網絡傳輸所提供的原語。在咱們這個DEMO中,能看到NioServerSocketChannel和NioSocketChannel,NioServerSocketChannel使用基於NIO選擇器的實現來接受新鏈接,NioSocketChannel使用基於NIO選擇器的實現來創建和處理新鏈接。
EventLoop接口:EventLoop定義了Netty的核心抽象,用於處理鏈接的生命週期中所發生的事件。EventLoop是協調設計的一部分,採用了兩個基本的API:併發和網絡編程。在咱們這個DEMO中,能看到NioEventLoop,NioEventLoop就是一個Reactor,是整個Netty的一個核心。
ChannelFuture接口:Netty中的全部的IO操做都是異步的,由於一個操做可能不會當即返回,因此咱們須要一種用於在以後某個時間點肯定其結果的方法。爲此,Netty提供了ChannelFuture接口,其addListener()方法註冊了一個ChannelFutureListener,以便在某個操做完成是獲得通知。
二、ChannelHandler、ChannelPipeline
ChannelHandler接口:從應用程序開發人員的角度來看,ChannelHandler是Netty的主要組件,它充當了全部處理入站和出站數據的應用程序邏輯的容器,由於ChannelHandler的方法是由事件來觸發的。在咱們這個DEMO中,DemoClientHandler、DemoServerHandler就是兩個自定義的ChannelHandler,DemoClientHandler在鏈接一創建的時候,就觸發了channelActive方法,而後DemoServerHandler在channelRead0方法中讀取了其傳輸的信息。
ChannelPipeline接口:ChannelPipeline爲ChannelHandler鏈提供了容器,並定義了用於該鏈上傳播入站和出站事件流的API。當Channel被建立時,它會被自動的分配到它所專屬的ChannelPipeline。在咱們的DEMO中,一個ChannelInitializer的實例被註冊到ServerBootStrap或者BootStrap,當它的initChannel方法被調用的時候,ChannelInitializer將在ChannelPipeline中安裝一組自定義的ChannelHandler,最後ChannelInitializer將它本身從ChannelPipeline中移除。
三、ByteBuf
網絡數據的基本單位是字節,NIO提供了ByteBuffer做爲網絡數據的字節容器,可是ByteBuffer自己設計並不優雅,使用繁瑣,Netty使用ByteBuf來替代ByteBuffer,在咱們的DEMO中,不能直接看到ByteBuf,可是在Netty提供的內置編解碼器StringDecoder/StringEncoder中,操做的對象就是ByteBuf。
四、ServerBootStrap、BootStrap
ServerBootStrap和BootStrap是一個很是抽象的概念。ServerBootStrap是Netty建立服務器的輔助類,負責封裝服務啓動的一系列操做。和ServerBootStrap同樣,Bootstrap也是封裝客戶端向服務端發送請求的一系列操做。
五、Codec
經過Netty發送和接收一個消息的時候,就會發生一次數據轉換,入站消息會被解碼,也就是從字節轉換爲本來的形式,若是是出站消息,就會從一種形式變成字節,這個就是編碼,編解碼的根本緣由就是由於網絡數據就是一系列的字節。在咱們的DEMO中,StringEncoder和StringDecoder就是編解碼器。
最後總結一下:
一、經過一個簡單的DEMO,介紹了一下Netty,站在應用的角度看了下Netty是如何運行的;
二、從DEMO中提取出Netty的重要的模塊,簡單介紹一下各個模塊,在後續的文章中將詳細介紹每一個模塊組件。