基于Netty搭建简单得Http服务器,只需要创建服务启动类和业务逻辑处理类即可
public class HttpServer {
public void start(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast("codec", new HttpServerCodec()) // HTTP 编解码
.addLast("compressor", new HttpContentCompressor()) // HttpContent 压缩
.addLast("aggregator", new HttpObjectAggregator(65536)) // HTTP 消息聚合
.addLast("handler", new HttpServerHandler()); // 自定义业务逻辑处理器
}
})
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind().sync();
System.out.println("Http Server started, Listening on " + port);
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new HttpServer().start(8088);
}
}
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) {
String content = String.format("Receive http request, uri: %s, method: %s, content: %s%n", msg.uri(), msg.method(), msg.content().toString(CharsetUtil.UTF_8));
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(content.getBytes()));
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
$ curl http://localhost:8088/abc
$ Receive http request, uri: /abc, method: GET, content:
Netty服务端的启动过程大致分为:
通过参数设置,可以支持三种Reactor模式:单线程模型、多线程模型、主从多线程模型
EventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(group)
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(group)
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
NIO 模型是 Netty 中最成熟且被广泛使用的模型。因此,推荐 Netty 服务端采用NioServerSocketChannel 作为 Channel 的类型,客户端采用 NioSocketChannel。
通过childHandler()方法注册ChannelHandler。ChannelInitializer是实现了 ChannelHandler接口的匿名类,通过实例化 ChannelInitializer 作为 ServerBootstrap 的参数。
ChannelInitializer的initChannel()绑定提个PipeLine,用于服务编排。向PipeLine中按顺序添加多个ChannelHandler。
通过option()和childOption()方法设置Channel属性,option 主要负责设置 Boss 线程组,而 childOption 对应的是 Worker 线程组。Netty 提供了默认参数设置ChannelOption,具体参数参考:
https://www.cnblogs.com/googlemeoften/p/6082785.html
完成Netty配置后,bind() 方法会真正触发启动,sync() 方法则会阻塞,直至整个启动过程完成。
public class HttpClient {
public void connect(String host, int port) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new HttpResponseDecoder());
ch.pipeline().addLast(new HttpRequestEncoder());
ch.pipeline().addLast(new HttpClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
URI uri = new URI("http://127.0.0.1:8088");
String content = "hello world";
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
uri.toASCIIString(), Unpooled.wrappedBuffer(content.getBytes(StandardCharsets.UTF_8)));
request.headers().set(HttpHeaderNames.HOST, host);
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
f.channel().write(request);
f.channel().flush();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
HttpClient client = new HttpClient();
client.connect("127.0.0.1", 8088);
}
}
public class HttpClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
ByteBuf buf = content.content();
System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
buf.release();
}
}
}