wiki
使用 Netty 编写一个 Http 服务器的程序,启动服务,在浏览器输入网址来访问,便会得到服务端的响应
1. 服务启动类
1. 主类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class HttpServer { public static void main(String[] args) { EventLoopGroup bossrGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new HttpServerInitializer()); ChannelFuture future = bootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
|
bossGroup, workerGroup
: 也可以称为 parentGroup, childGroup
,是两个线程池, 它们默认线程数为 CPU 核心数乘以 2,bossGroup 用于接收客户端传过来的请求,接收到请求后将后续操作交由 workerGroup 处理
bootstrap
: 为 Netty 程序的启动组装配置一些必须要组件
channel()
: 用于指定服务器端监听套接字通道 NioServerSocketChannel,其内部管理了一个 Java NIO 中的ServerSocketChannel 实例
channelHandler()
: 用于设置业务职责链,是由一个个的 ChannelHandler 串联而成,形成的链式结构
bind()
: 服务绑定到 8080 端口上
sync()
: 阻塞当前 Thread,一直到端口绑定操作完成
channel().closeFuture()
: 程序将会阻塞等待直到服务器的 Channel 关闭
2. 业务处理类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class HttpServerInitializer extends ChannelInitializer<SocketChannel> { protected void initChannel(SocketChannel sc) throws Exception { ChannelPipeline pipeline = sc.pipeline(); pipeline.addLast("httpServerCodec", new HttpServerCodec()); pipeline.addLast("httpServerChannelHandler", new HttpServerChannelHandler0 ()); } }
|
ChannelInitializer
: 继承 ChannelInboundHandlerAdapter,用于初始化 Channel 的 ChannelPipeline
initChannel()
: 方法参数 sc 得到 ChannelPipeline 的一个实例,当一个新的连接被接受时, 一个新的 Channel 将被创建,同时它会被自动地分配到它专属的 ChannelPipeline
ChannelPipeline
: 提供了 ChannelHandler 链的容器
HttpServerCodec
: HttpRequestDecoder 和 HttpResponseEncoder 的组合,对通信数据进行编解码
addLast()
: 将一个个的 ChannelHandler 添加到责任链上
3. 自定义 ChannelHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class HttpServerChannelHandler extends SimpleChannelInboundHandler<HttpObject> { private HttpRequest request; @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if (msg instanceof HttpRequest) { request = (HttpRequest) msg; request.method(); String uri = request.uri(); System.out.println("Uri:" + uri); } if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; ByteBuf buf = content.content(); System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8)); ByteBuf byteBuf = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf); response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().add(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes()); ctx.writeAndFlush(response); } } }
|
channelRead0()
: 处理自定义的业务逻辑
HttpRequest
: 在 Netty 设计中,包含请求头、请求方法等信息
HttpContent
: 在 Netty 设计中,包含请求体的信息
Unpooled
: 创建未池化的 ByteBuf 实例
ByteBuf
: 字节容器,Netty 的 ByteBuffer 替代品是 ByteBuf
FullHttpResponse
: http 响应体,
writeAndFlush()
: 写回给客户端
Netty 提供了另一个类 FullHttpRequest,包含请求的所有信息,直接或者间接继承了 HttpRequest 和 HttpContent,它的实现类是 DefalutFullHttpRequest,因此,可以改为下面的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class HttpServerChannelHandler extends SimpleChannelInboundHandler<FullHttpRequest> { protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { ctx.channel().remoteAddress(); FullHttpRequest request = msg; System.out.println("请求方法名称:" + request.method().name()); System.out.println("uri:" + request.uri()); ByteBuf buf = request.content(); System.out.print(buf.toString(CharsetUtil.UTF_8)); ByteBuf byteBuf = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf); response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().add(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes()); ctx.writeAndFlush(response); } }
|
此时,对于将请求合并为一个 FullRequest 是需要代码实现的,Netty 提供了一个 HttpObjectAggregator 类,这个 ChannelHandler 作用就是将请求转换为单一的 FullHttpReques,需要在责任链上添加 HttpObjectAggregator