Netty 创建 HTTP 服务
引入 Netty 依赖
Maven
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.77.Final</version>
</dependency>
Gradle(Groovy)
implementation 'io.netty:netty-all:4.1.77.Final'
Gradle(Kotlin)
implementation("io.netty:netty-all:4.1.77.Final")
启动 Netty 服务
public final class HttpHelloWorldServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new HttpHelloWorldServerInitializer(sslCtx));
Channel ch = b.bind(PORT).sync().channel();
System.err.println("Open your web browser and navigate to " +
(SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
上方代码,使用 ServerBootstrap 创建了一个 NIO TCP 服务器。数据经过一系列 handlers 被解码、处理、构造响应、然后编码回 TCP 包。
这里的关键类是 HttpHelloWorldServerInitializer
。负责将 TCP 数据解码成 HTTP 数据,并执行后续处理。
创建 HttpHelloWorldServerInitializer
public class HttpHelloWorldServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public HttpHelloWorldServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
p.addLast(new HttpServerCodec());
p.addLast(new HttpServerExpectContinueHandler());
p.addLast(new HttpHelloWorldServerHandler());
}
}
HttpHelloWorldServerInitializer
非常简单。它构造了一条 ChannelPipeline。数据经过被该 Pipeline 处理,依次经过 HttpServerCodec、HttpServerExpectContinueHandler、HttpHelloWorldServerHandler。
HttpServerCodec 负责把 TCP 解码成 HTTP 层数据。
HttpServerExpectContinueHandler 见名知意,处理 HTTP 的 Continue 响应。
HttpHelloWorldServerHandler 则负责最后处理 HTTP 请求。
处理 HTTP 请求
public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler<HttpObject> {
private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest) msg;
boolean keepAlive = HttpUtil.isKeepAlive(req);
FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
Unpooled.wrappedBuffer(CONTENT));
response.headers()
.set(CONTENT_TYPE, TEXT_PLAIN)
.setInt(CONTENT_LENGTH, response.content().readableBytes());
if (keepAlive) {
if (!req.protocolVersion().isKeepAliveDefault()) {
response.headers().set(CONNECTION, KEEP_ALIVE);
}
} else {
// Tell the client we're going to close the connection.
response.headers().set(CONNECTION, CLOSE);
}
ChannelFuture f = ctx.write(response);
if (!keepAlive) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
channelRead0()
方法接受 HTTP 请求 HttpObject msg
, 构造 FullHttpResponse response
HTTP 响应,并返回 HTTP 结果 ctx.write(response)
。
开源项目应用
上述的例子只是一个粗浅的 HTTP Server。一个成熟的 HTTP 服务需要同时支持 HTTPS、HTTP/2、HTTP/2 Cleartext 和 WebSocket等其他情况。
需要的朋友可以参考其他开源项目的配置。这里列出两个比较流行的 Netty-based 的 HTTP 服务框架。