Netty 代码-一个简单 Http 服务器

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();
// 处理 http 消息的编解码,等价于下面两句
pipeline.addLast("httpServerCodec", new HttpServerCodec());
// pipeline.addLast("httpResponseEndcoder", new HttpResponseEncoder());
// pipeline.addLast("HttpRequestDecoder", new HttpRequestDecoder());
// 处理 FullHttpReques 时需要
// pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
// 添加自定义的 ChannelHandler
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