一、基于Netty搭建简单的Http服务器
基于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() {
@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);
}
}
测试:
- 启动 HttpServer 的 main 函数。
- 终端或浏览器发起 HTTP 请求。
$ curl http://localhost:8088/abc
$ Receive http request, uri: /abc, method: GET, content:
二、引导器实践指南
Netty服务端的启动过程大致分为:
- 配置线程池
- Channel初始化
- 端口绑定
- 单线程模型:所有I/O操作由一个线程完成,只启动一个EventLoopGroup线程池,线程池类只有一个EventLoop线程
EventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(group)
- 多线程模型:与单线程类似,但EventLoopGroup不需要参数,默认启动2倍CPU核数的EventLoop线程,也至此自定义线程数
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(group)
- 主从多线程模型:Boss 是主 Reactor,Worker 是从 Reactor。它们分别使用不同的 NioEventLoopGroup,主 Reactor 负责处理 Accept,然后把 Channel 注册到从 Reactor 上,从 Reactor 主要负责 Channel 生命周期内的所有 I/O 事件。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
2. Channel初始化 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
3. 端口绑定 【Netty学习系列|Netty学习三(Netty框架之引导器)】完成Netty配置后,bind() 方法会真正触发启动,sync() 方法则会阻塞,直至整个启动过程完成。
三、Netty实现HTTP客户端
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();
}
}
}
推荐阅读
- SpringBoot|SpringBoot的参数校验器 - Validator
- 面试|线程池异常如何处理你都了解吗()
- java初阶|Java-异常处理大全(万字宝典)
- 从0到1|牛客网刷题——JAVA
- JAVA|Java 继承详解
- JAVA|七千字带你了解异常处理
- java基础|一文搞懂java中的高大上技术“反射”
- 谷粒商城|谷粒商城--消息队列--高级篇笔记十
- 大数据|滴滴开源了哪些有意思的项目()