1.簡介
Netty是爲了快速開發可維護的高性能、高可擴展、網絡服務器和客戶端程序而提供的異步事件驅動基礎框架和工具。換句話說,Netty是一個Java NIO客戶端/服務器框架
2.Netty目標
- 使開發可以做到“快速和輕鬆”
- 做到高性能和高擴展
3.創建第一個Netty項目
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
public class NettyDiscardServer {
private final int serverPort;
ServerBootstrap b = new ServerBootstrap();
public NettyDiscardServer(int port) {
this.serverPort = port;
}
public void runServer() {
//創建reactor 線程組
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
try {
//1 設置reactor 線程組
b.group(bossLoopGroup, workerLoopGroup);
//2 設置nio類型的channel
b.channel(NioServerSocketChannel.class);
//3 設置監聽端口
b.localAddress(serverPort);
//4 設置通道的參數
b.option(ChannelOption.SO_KEEPALIVE, true);
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//5 裝配子通道流水線
b.childHandler(new ChannelInitializer<SocketChannel>() {
//有連接到達時會創建一個channel
protected void initChannel(SocketChannel ch) throws Exception {
// pipeline管理子通道channel中的Handler
// 向子channel流水線添加一個handler處理器
ch.pipeline().addLast(new NettyDiscardHandler());
}
});
// 6 開始綁定server
// 通過調用sync同步方法阻塞直到綁定成功
ChannelFuture channelFuture = b.bind().sync();
Logger.info(" 服務器啓動成功,監聽端口: " +
channelFuture.channel().localAddress());
// 7 等待通道關閉的異步任務結束
// 服務監聽通道會一直等待通道關閉的異步任務結束
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8 優雅關閉EventLoopGroup,
// 釋放掉所有資源包括創建的線程
workerLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
int port = NettyDemoConfig.SOCKET_SERVER_PORT;
new NettyDiscardServer(port).runServer();
}
}
- 客戶端代碼:
public class EchoClient {
public void start() throws IOException {
InetSocketAddress address =
new InetSocketAddress(NioDemoConfig.SOCKET_SERVER_IP,
NioDemoConfig.SOCKET_SERVER_PORT);
// 1、獲取通道(channel)
SocketChannel socketChannel = SocketChannel.open(address);
// 2、切換成非阻塞模式
socketChannel.configureBlocking(false);
//不斷的自旋、等待連接完成,或者做一些其他的事情
while (!socketChannel.finishConnect()) {
}
Print.tcfo("客戶端啓動成功!");
//啓動接受線程
Processer processer = new Processer(socketChannel);
new Thread(processer).start();
}
static class Processer implements Runnable {
final Selector selector;
final SocketChannel channel;
Processer(SocketChannel channel) throws IOException {
//Reactor初始化
selector = Selector.open();
this.channel = channel;
channel.register(selector,
SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
SelectionKey sk = it.next();
if (sk.isWritable()) {
ByteBuffer buffer = ByteBuffer.allocate(NioDemoConfig.SEND_BUFFER_SIZE);
Scanner scanner = new Scanner(System.in);
Print.tcfo("請輸入發送內容:");
if (scanner.hasNext()) {
SocketChannel socketChannel = (SocketChannel) sk.channel();
String next = scanner.next();
buffer.put((Dateutil.getNow() + " >>" + next).getBytes());
buffer.flip();
// 操作三:通過DatagramChannel數據報通道發送數據
socketChannel.write(buffer);
buffer.clear();
}
}
if (sk.isReadable()) {
// 若選擇鍵的IO事件是“可讀”事件,讀取數據
SocketChannel socketChannel = (SocketChannel) sk.channel();
//讀取數據
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int length = 0;
while ((length = socketChannel.read(byteBuffer)) > 0) {
byteBuffer.flip();
Logger.info("server echo:" + new String(byteBuffer.array(), 0, length));
byteBuffer.clear();
}
}
//處理結束了, 這裏不能關閉select key,需要重複使用
//selectionKey.cancel();
}
selected.clear();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
new EchoClient().start();
}
}
4.Reactor反應器模式
4.1Reactor反應器模式中IO事件的處理流程
- 1.通道註冊
- 2.查詢選擇
- 3.事件分發
- 4.io操作和業務處理
4.2Netty中的Channel通道組件
- 反應器模式和通道緊密相關,反應器的查詢和分發的IO事件都來自於Channel通道組件。
- Netty中不直接使用Java NIO的Channel通道組件,對Channel通道組件進行了自己的封裝。
- Netty還能處理Java的面向流的OIO(Old-IO,即傳統的阻塞式IO)
-
Netty中的每一種協議的通道,都有NIO(異步IO)和OIO(阻塞式IO)兩個版本
4.3Netty中的Reactor反應器
- Netty中的反應器有多個實現類,與Channel通道類有關係。對應於NioSocketChannel通道,Netty的反應器類爲:NioEventLoop。
- NioEventLoop類綁定了兩個重要的Java成員屬性:一個是Thread線程類的成員,一個是Java NIO選擇器的成員屬性。
4.4 Netty中的Handler處理器
Netty的Handler處理器分爲兩大類:第一類是ChannelInboundHandler通道入站處理器;第二類是ChannelOutboundHandler通道出站處理器。二者都繼承了ChannelHandler處理器接口
4.5Netty的流水性模式
- (1)反應器(或者SubReactor子反應器)和通道之間是一對多的關係:一個反應器可以查詢很多個通道的IO事件。
- (2)通道和Handler處理器實例之間,是多對多的關係:一個通道的IO事件被多個的Handler實例處理;一個Handler處理器實例也能綁定到很多的通道,處理多個通道的IO事件。
- Netty設計了一個特殊的組件,叫作ChannelPipeline(通道流水線),它像一條管道,將綁定到一個通道的多個Handler處理器實例,串在一起,形成一條流水線。