一、概述
Netty是一個異步的,基於事件驅動的,網絡應用框架;
目前大多使用netty 4,netty 5被廢棄了
二、大綱
- Netty入門
二、入門示例
1. 示例
開發一個簡單的服務器,綁定端口8899,服務啓動後每次訪問都返回helloworld字符串
- Server:入口
package com.learner.netty.ch1;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @Desc:
* @author: [email protected]
* @date: 2019-11-28
*/
public class Server {
public static void main(String[] args) throws InterruptedException {
// boosGroup線程組負責獲取連接,轉接給workGroup,workGroup處理連接
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
// ServerBootstrap用於簡化啓動netty服務端
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 將boosGroup和workGroup進行綁定,利用反射創建通道,綁定子處理器
serverBootstrap.group(boosGroup, workGroup)
.channel(NioServerSocketChannel.class) // 反射創建實例
.childHandler(new ServerInitializer()); // 子處理器,自己的請求處理服務器,用於處理請求
// 綁定端口
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
} finally {
boosGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
- ServerInitializer:服務初始化
package com.learner.netty.ch1;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http2.Http2Codec;
/**
* @Desc: 初始化管道
* @author: [email protected]
* @date: 2019-11-28
*/
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// pipeline是一個管道,其中有很多的channelHandle(相當於攔截器),用於處理不同的事務
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("serverCodec", new HttpServerCodec());
pipeline.addLast("channelHandle", new ChannelHandle());
}
}
- ChannelHandle:自定義處理器
package com.learner.netty.ch1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* @Desc: 自定義的handle,用於處理自己的業務,需要繼承SimpleChannelInboundHandler
* @author: [email protected]
* @date: 2019-11-28
*/
public class ChannelHandle extends SimpleChannelInboundHandler<HttpObject> {
/**
* 用於獲取請求,並進行處理,最後返回響應
* @author: [email protected]
* @date: 2019/11/28 9:36 下午
* @param ctx
* @param msg
* @return void
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
System.out.println("請求處理");
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
// 獲取請求類型
System.out.println("請求類型:"+request.method().name());
// 獲取訪問地址
System.out.println("請求地址:"+request.uri());
// 1. 構建ByteBuf
ByteBuf content = Unpooled.copiedBuffer("Hello, Netty 學習!", CharsetUtil.UTF_8);
// 2. 根據ByteBuf構建Response
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK, content);
// 3. 設置響應頭
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
// 4. 返回
ctx.writeAndFlush(response);
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
System.out.println("1. handle added");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
System.out.println("2. channel registered");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("3. channel active");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
System.out.println("4. channel inactive");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
System.out.println("5. channel unregistered");
}
}
- 請求示例
curl 'http://localhost:8899'
- 返回示例:
Hello, Netty 學習!
三、開發流程
上面的入門示例的開發流程總結如下:
- 創建兩個EventLoopGroup線程組:boos,work
// boosGroup線程組負責獲取連接,轉接給workGroup,workGroup處理連接
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
boos負責獲取連接,work負責處理連接
- 創建ServerBootStrap實例,用於綁定boss和work,創建channel,綁定handle,綁定端口,啓動服務,最後優雅的關閉服務
// 將boosGroup和workGroup進行綁定,利用反射創建通道,綁定子處理器
serverBootstrap.group(boosGroup, workGroup)
.channel(NioServerSocketChannel.class) // 反射創建實例
.childHandler(new ServerInitializer()); // 子處理器,自己的請求處理服務器,用於處理請求
// 綁定端口
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
boosGroup.shutdownGracefully();
workGroup.shutdownGracefully();
3.繼承ChannelInitializer創建Initializer,重寫initChannel
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// pipeline是一個管道,其中有很多的channelHandle(相當於攔截器),用於處理不同的事務
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("serverCodec", new HttpServerCodec());
pipeline.addLast("channelHandle", new ChannelHandle());
}
ChannelPipeline是一個管道,裏邊可以添加很多的handle,每個handle可以處理不同的事務
- 繼承SimpleChannelInboundHandler,創建自己的ChannelHandle,重寫channelRead0()用於處理自己的業務
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
System.out.println("請求處理");
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
// 獲取請求類型
System.out.println("請求類型:"+request.method().name());
// 獲取訪問地址
System.out.println("請求地址:"+request.uri());
// 1. 構建ByteBuf
ByteBuf content = Unpooled.copiedBuffer("Hello, Netty 學習!", CharsetUtil.UTF_8);
// 2. 根據ByteBuf構建Response
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK, content);
// 3. 設置響應頭
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
// 4. 返回
ctx.writeAndFlush(response);
}
}
四、Netty執行流程
1. 總覽
在自定義的handle中可以重寫handlerAdded,channelRegistered,channelActive,channelInactive,channelUnregistered這五個方法,並進行打印,用來分析Netty的執行流程
package com.learner.netty.ch1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* @Desc: 自定義的handle,用於處理自己的業務,需要繼承SimpleChannelInboundHandler
* @author: [email protected]
* @date: 2019-11-28
*/
public class ChannelHandle extends SimpleChannelInboundHandler<HttpObject> {
/**
* 用於獲取請求,並進行處理,最後返回響應
* @author: [email protected]
* @date: 2019/11/28 9:36 下午
* @param ctx
* @param msg
* @return void
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
System.out.println("請求處理");
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
// 獲取請求類型
System.out.println("請求類型:"+request.method().name());
// 獲取訪問地址
System.out.println("請求地址:"+request.uri());
// 1. 構建ByteBuf
ByteBuf content = Unpooled.copiedBuffer("Hello, Netty 學習!", CharsetUtil.UTF_8);
// 2. 根據ByteBuf構建Response
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK, content);
// 3. 設置響應頭
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
// 4. 返回
ctx.writeAndFlush(response);
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
System.out.println("1. handle added");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
System.out.println("2. channel registered");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("3. channel active");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
System.out.println("4. channel inactive");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
System.out.println("5. channel unregistered");
}
}
啓動服務,查看執行結果:
> Task :Server.main()
1. handle added
2. channel registered
3. channel active
請求處理
請求類型:GET
請求地址:/
請求處理
4. channel inactive
5. channel unregistered
可以看到其執行流程是:
- handle added
- channel registered
- channel active
- 處理業務邏輯
- channel inactive
- channel unregistered