初识Netty系列——Netty实战

433 阅读2分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

今天来搭建一个Netty Http Server,闲话少说,开战吧!

本篇使用IDEA + Maven搭建,

第一步,修改pom文件

    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.67.Final</version>
        </dependency>
    </dependencies>

第二步:创建HttpServer

  • 在这一步中,我们构造了两个线程组BossGroup和WorkerGroup,作用是什么呢?
    • bossGroup与workGroup都是一个独立的Reactor
    • bossGroup是负责接收请求,Reactor的优势是一个线程能处理多个请求,核心是通过for循环实现
    • workGroup负责不同连接的读写请求,使用轮询的方式,一旦有接收到数据,则创建线程执行 所以,这两者是分工明确的运行着,bossGroup负责连接,workGroup负责处理I/O请求
/**
 * @version 1.0
 * @date 2021/8/29 20:43
 */
public class HttpServer {
    //定义服务端的端口号
    int port = 8080;
    public void start() throws Exception{
        //构造两个线程组:BossGroup和WorkerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //服务端启动辅助类,配置NioEventLoopGroup
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new HttpServerInitializer());
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println(" server start up on port : " + port);
            //等待服务端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

第三步:定义HttpServerInitializer

自定义一个类 HttpServerInitializer 继承 ChannelInitializer 并实现其中的 initChannel方法, 当一个新的连接被接受时, 一个新的 Channel 将被创建,同时它会被自动地分配到它专属的 ChannelPipeline 自定义的ChannelHandler主要作用是处理业务逻辑


/**
 * @version 1.0
 * @date 2021/8/29 20:44
 */
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel sc) throws Exception {
        ChannelPipeline pipeline = sc.pipeline();
        //处理http消息的编解码
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        //添加自定义的ChannelHandler
        pipeline.addLast("httpServerHandler", new HttpServerChannelHandler());
    }
}

第四部:自定义ChannelHandler(关键点)

/**
 * @version 1.0
 * @date 2021/8/29 20:53
 */
public class HttpServerChannelHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    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());
        System.out.println("HTTP协议版本:" + request.getProtocolVersion().text());
        ByteBuf buf = request.content();
        System.out.print(buf.toString(CharsetUtil.UTF_8));


        ByteBuf byteBuf = Unpooled.copiedBuffer("Received message successfully", 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);
    }
}

HTTP请求发送中,Netty HTTP Server收到的请求数据 image.png 其中,FullHttpRequest f有以下的方法:

  • f.getMethod.name()//获取请求方法
  • f.getMethod.getUri()//获取请求的url
  • f.getProtocolVersion().text()//获取
  • f.headers().get("Content-Type");// 获取header中的数据,这里是map格式

image.png

最后,在执行HttpServer的start方法

image.png

客户端测试

使用 Postman postman这个工具模拟客户端来测试, 发起调用GET请求:http://localhost:8080 以及成功收到反馈的消息:Received message successfully

image.png

今日小结 今天搭建的Http服务器算是入门级别的,主要目的是能够直观的感受到Netty的使用