什麼是Netty?Netty是一個框架。或者說是一個工具集。封裝了網路程式設計方面java的API。
Netty有哪些核心元件?
-
Channel:java nio的基本構造,代表一個實體(硬體裝置、檔案、網路套接字等)的開放連線。用作傳入(入站)或者傳出(出站)資料
-
回撥:封裝操作完成後需要做的事情的方法
-
Future: 提供非同步操作的結果訪問
-
事件和ChannelHandler:程式執行過程中發生的事情抽象(事件)交給ChannerHandler來處理,channelHandler類似於為了響應特定事件而執行的回撥
Netty的Hello world
server
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[]args)throws Exception{
new EchoServer(8888).start();
}
public void start() throws Exception{
final EchoServerHandler handler = new EchoServerHandler();
EventLoopGroup group = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(handler);
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
}
複製程式碼
serverHandler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.printf("Server get:"+in.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//將目前暫存於ChannelOutboundBuffer中的訊息在下一次flush或者writeAndFlush的時候沖刷到遠端並關閉這個channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
複製程式碼
client
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host,int port){
this.host=host;
this.port=port;
}
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)//指定NIO的傳輸方式
.remoteAddress(new InetSocketAddress(host,port))//指定遠端地址
.handler(new ChannelInitializer<SocketChannel>() {//向channel的pipeline新增handler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());//channelHander交給pipeline
}
});
ChannelFuture f = b.connect().sync();//連線到遠端節點,阻塞直到連線完成
System.out.println("wait");
f.channel().closeFuture().sync();//阻塞直到連線關閉
System.out.println("over");
}finally {
System.out.println("shutdown");
group.shutdownGracefully().sync();//關閉執行緒池並且釋放資源
}
}
public static void main(String[]args) throws Exception{
new EchoClient("localhost",8888).start();
}
}
複製程式碼
clientHandler
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello world",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("Client get:"+msg.toString(CharsetUtil.UTF_8));
}
}
複製程式碼
Channel、Eventloop 、ChannelFuture 是什麼關係?
它們是netty對網路的抽象元件。
Channel本身用來提供基本的IO操作(bind/connect/read/write),連線建立之後通過EventLoop來處理所發生的事情,它們之間的對應關係是1個channel只能有1個EventLoop,但是一個EventLoop對應多個Channel。EventLoop整個生命週期中只和一個Thread繫結,對應來講一個Channel中的所有IO操作也都是在一個執行緒中執行的。執行的結果則是通過ChannelFuture來獲取
Channel有什麼特徵?
- channel是傳輸API的核心,每一個都會被分配一個ChannelPipeline和ChannelConfig,ChannelConfig包含了Channel的所有配置,並且支援熱更新
- 每個channel都是獨一無二的,channel之間的順序通過Comparable來實現比較
- channel的實現是執行緒安全的
pipeline和handler是什麼關係?
pipeline用來管理資料流,handler(client和server)是用來處理邏輯。
ChannelHandler接收事件,對事件進行邏輯處理,並將資料傳給鏈(多個按照一定順序定義的ChannelHandler)中的下一個ChannelHandler。ChannelPipLine就是ChannelHandler的編排順序(二者建立關係的時機是ChannelInitializer執行initChannel的時候ChannelPipline組裝自定義的channelHandler)。出於方便ChannelHandler會提供一些適配實現類讓使用者專注於處理自己的業務邏輯。
bootstrap是什麼?
有兩種型別,客戶端(簡稱BootStrap)和服務端(簡稱ServerBootStrap)。區別有兩點
- BootStrap用來連線遠端節點,ServerBootStrap則是繫結本地埠監聽連線
- 客戶端需要一個EventLoopGroup(存多個EventLoop的東西),服務端需要兩個(可以是同一個例項),一個用來繫結到本地埠代表已經監聽了(分配一個EventLoop),另一個則是處理客戶端的連線(再分配一個EventLoop)
Netty支援的傳輸型別有哪些?
- NIO:基於java api的selector機制實現。 大致原理是:當channel狀態發生變化的時得到通知,執行狀態變化相應的任務,響應結束後,選擇器重置,再次重複這個過程
- Epoll:只能在linux系統中使用,高負載情況下,效能優於JDK的NIO。 大致原理是:I/O多路複用【通過一個檔案符管理多個檔案描述符(針對epoll可以看做無限制)】,當一個檔案描述符可讀或者可寫的時候,收到通知,立馬執行【邊沿觸發】
- OIO(舊的阻塞IO):基於java.net的阻塞IO。 大致原理是:單執行緒監聽一個socket,任何I/O操作在任意的時間節點上都有可能被阻塞
- Local:同一個JVM上執行的客戶端和服務端之間的通訊
- Embedded:使用channel,但不需要真正意義上的網路傳輸,一般用於測試
附錄
Netty In Action