原文博客地址: pjmike的博客java
這篇主要介紹一個Netty 客戶端與服務端的示例代碼,對Netty有一個直觀感覺,看看如何使用Netty,後續文章會對Netty的各個組件進行詳細分析git
Netty是一款異步的事件驅動的網絡應用程序框架,支持快速開發可維護的高性能的面向協議的服務器和客戶端。Netty主要是對java 的 nio包進行的封裝github
上面介紹到 Netty是一款 高性能的網絡通信框架,那麼咱們爲何要使用Netty,換句話說,Netty有哪些優勢讓咱們值得使用它,爲何不使用原生的 Java Socket編程,或者使用 Java 1.4引入的 Java NIO。接下來分析分析 Java Socket編程和 Java NIO。編程
首先來看一個Java 網絡編程的例子:安全
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println("received: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
複製代碼
上面展現了一個簡單的Socket服務端例子,該代碼只能同時處理一個鏈接,要管理多個併發客戶端,須要爲每一個新的客戶端Socket建立一個 新的Thread。這種併發方案對於中小數量的客戶端來講還能夠接受,若是是針對高併發,超過100000的併發鏈接來講該方案並不可取,它所須要的線程資源太多,並且任什麼時候候均可能存在大量線程處於阻塞狀態,等待輸入或者輸出數據就緒,整個方案性能太差。因此,高併發的場景,通常的Java 網絡編程方案是不可取的。服務器
仍是先來看一個 Java NIO的例子:網絡
public class ServerSocketChannelDemo {
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public ServerSocketChannelDemo(int port) throws IOException {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listener() throws IOException {
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
if (selectionKey.isAcceptable()) {
ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = server_channel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
//若是通道處於讀就緒的狀態
//讀操做
//TODO
}
}
}
}
}
複製代碼
NIO的核心部分主要有:併發
選擇器 Selector 是 Java 非阻塞 I/O實現的關鍵,將通道Channel註冊在 Selector上,若是某個通道 Channel發送 讀或寫事件,這個Channel處於就緒狀態,會被Selector輪詢出來,進而進行後續I/O操做。這種I/O多路複用的方式相比上面的阻塞 I/O模型,提供了更好的資源管理:框架
儘管使用 Java NIO可讓咱們使用較少的線程處理不少鏈接,可是在高負載下可靠和高效地處理和調度I/O操做是一項繁瑣並且容易出錯的任務,因此才引出了高性能網絡編程專家——Netty異步
Netty有不少優秀的特性值得讓咱們去使用它(摘自《Netty實戰》):
設計
性能
健壯性
安全性:
易用:
下面是server 和client的示例代碼,先來看看Netty代碼是怎麼樣的,後續文章會詳細分析各個模塊。
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8888).start();
}
public void start() throws InterruptedException {
final EchoServerHandler serverHandler = new EchoServerHandler();
//建立EventLoopGroup,處理事件
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss,worker)
//指定所使用的NIO傳輸 Channel
.channel(NioServerSocketChannel.class)
//使用指定的端口設置套接字地址
.localAddress(new InetSocketAddress(port))
//添加一個EchoServerHandler到子Channel的ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//EchoServerHandler標誌爲@Shareable,因此咱們能夠老是使用一樣的實例
socketChannel.pipeline().addLast(serverHandler);
}
});
//異步的綁定服務器,調用sync()方法阻塞等待直到綁定完成
ChannelFuture future = b.bind().sync();
future.channel().closeFuture().sync();
} finally {
//關閉EventLoopGroup,釋放全部的資源
group.shutdownGracefully().sync();
worker.shutdownGracefully().sync();
}
}
}
複製代碼
EchoServerHandler
@ChannelHandler.Sharable //標識一個 ChannelHandler能夠被多個Channel安全地共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
//將消息記錄到控制檯
System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
//將接受到消息回寫給發送者
ctx.write(buffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//將未消息沖刷到遠程節點,而且關閉該 Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//打印異常棧跟蹤
cause.printStackTrace();
//關閉該Channel
ctx.close();
}
}
複製代碼
代碼要點解讀:
ServerBootStrap
是引導類,幫助服務啓動的輔助類,能夠設置 Socket參數EventLoopGroup
是處理I/O操做的線程池,用來分配 服務於Channel的I/O和事件的 EventLoop
,而NioEventLoopGroup
是EventLoopGroup
的一個實現類。這裏實例化了兩個 NioEventLoopGroup
,一個 boss
,主要用於處理客戶端鏈接,一個 worker
用於處理客戶端的數據讀寫工做EchoServerHandler
實現了業務邏輯ServerBootStrap.bind()
方法以綁定服務器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 InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = b.connect().sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("127.0.0.1", 8888).start();
}
}
複製代碼
EchoClientHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+byteBuf.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
複製代碼
代碼要點解讀:
ServerBootStrap
同樣,也是一個引導類,主要輔助客戶端NioEventLoopGroup
實例,裏面的 EventLoop
,處理鏈接的生命週期中所發生的事件EchoClientHandler
類負責處理業務邏輯,與服務端的EchoSeverHandler
做用類似。