Netty入门指南:构建高性能网络应用

345 阅读5分钟

前言

Netty是一个高级的、异步事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络服务器和客户端程序

它是用Java编写的、支持多种协议,并被许多知名的中间件和框架所采用

本文将带你快速入门Netty,了解其基本概念、特点、部分组件以及编写一个HTTP服务器

Netty简介

Netty是一个开源的网络编程框架,它基于Java NIO,提供了对TCP、UDP和HTTP等协议的异步支持

Netty以其高性能、高可靠性和易用性而闻名,被广泛应用于网络通信、即时通讯等

常见的中间件RocketMQ、Nacos、Spring WebFlux框架等都使用Netty进行网络通信

Netty基于Reactor模式,提供了异步的事件处理机制,支持成千上万的并发连接,还使用零拷贝的缓冲池,以此实现高性能网络通信

Netty还提供各种协议的支持,比如HTTP、WebSocket、SSL还很容易扩展新协议,同时也支持压缩、大文件传输、实时视频流等

(如下图)

官方图

Reactor模式

Reactor模式是一种事件驱动的编程模型,广泛应用于网络服务器的并发处理

不仅仅只是Netty使用Reactor模式,其他熟悉的Redis、Nginx都使用这种模式来处理网络

在Netty中,Reactor模式通过**事件循环(EventLoop)通道处理器(ChannelHandler)**来实现高效的I/O操作

事件循环

事件循环通常包括两类:BossEventLoop(也叫ParentEventLoop)和WorkerEventLoop(也叫ChildEventLoop)

BossEventLoop主要负责接受客户端的连接请求(accept),而WorkerEventLoop负责处理已建立连接上的数据读写操作(read、write)

在Netty中,它们以Group的方式形成线程组(EventLoopGroup),可以根据不同的业务场景对Boss、Worker的线程组动态调整线程数量

比如:当处理连接成为系统瓶颈时可以增加Boss线程数量,而处理数据读写成为系统瓶颈时增加Worker线程数量

通过这种方式,可以对处理数据的连接以及数据的读写进行解耦

通道处理器

在Netty框架中,ChannelHandler是处理网络事件和数据的核心组件,它定义很多方法用于处理不同类型的事件

(比如channelRead方法在channel发生读取数据事件时调用、channelWrite方法在数据写入channel时调用)

多个ChannelHandler组成ChannelPipeline(职责链),事件发生时会依次进行处理,允许用户灵活地扩展事件处理逻辑

通常会先对数据包进行解码、业务处理、最后编码写回数据

Reactor

结合Reactor模型来说,Netty中的BossEventLoop就(图中mainReactor),(以TCP为例)负责从三次握手完成的全连接队列(acceptor)中取出连接进行处理,最终交给WorkerEventLoop(图中subReactor)

当连接发生读事件(有数据来了),WorkerEventLoop会触发处理器链,对其进行解码、计算、编码写回(这部分就是ChannelPipeline,可以自定义实现)

Reactor

快速入门

导入maven依赖

<dependencies>
    <!-- Spring Boot 核心 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- Netty -->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.100.Final</version>
    </dependency>

    <!-- 日志 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>

    <!-- 可选:如果你使用 Kafka -->
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
</dependencies>

编写Netty服务器

package com.example.maslog.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;

@Slf4j
@Component
public class NettyServer {

    @Value("${netty.port:8080}")
    private int port;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    @PostConstruct
    public void start() {
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)
                     .childHandler(new NettyServerInitializer());

            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("Netty HTTP server started on port {}", port);

        } catch (InterruptedException e) {
            log.error("Netty 启动异常", e);
            Thread.currentThread().interrupt();
        }
    }

    @PreDestroy
    public void stop() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        log.info("Netty server shutdown.");
    }
}

每个连接被Netty接收后,调用NettyServerInitializer初始化Channel,将HTTP编解码器加入到Channel的Pipeline中

package com.example.maslog.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.*;

public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) {
        ch.pipeline().addLast(new HttpServerCodec());
        ch.pipeline().addLast(new HttpObjectAggregator(65536)); // 聚合 HTTP 消息
        ch.pipeline().addLast(new SimpleHttpHandler());
    }
}

HttpServerCodec用于编解码HTTP协议,会拆分对象,使用HttpObjectAggregator进行聚合

SimpleHttpHandler用于真正的逻辑处理

package com.example.maslog.netty;

import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;

import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;

@Slf4j
public class SimpleHttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
        String uri = request.uri();
        log.info("Received request: {}", uri);

        // 模拟返回处理结果
        String responseJson = "{\"status\":\"ok\"}";

        FullHttpResponse response = new DefaultFullHttpResponse(
                HTTP_1_1,
                OK,
                Unpooled.wrappedBuffer(responseJson.getBytes())
        );
        response.headers().set(CONTENT_TYPE, "application/json");
        response.headers().setInt(CONTENT_LENGTH, responseJson.length());

        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("处理请求失败", cause);
        ctx.close();
    }
}

启动后随便往127.0.0.1:8080发个Http请求,Netty都会接收并响应JSON格式的以下内容

{
    "status": "ok"
}

Netty作为高性能的网络框架,还被用在大量常见中间件以及框架中,如:Dubbo、RockeMQ、Flink、Spring WebFlux

总结

Netty作为一款高性能的网络框架,凭借其强大的异步事件驱动机制和灵活的组件设计,被广泛应用于Dubbo、RocketMQ、Flink、Spring WebFlux等常用中间件以及框架中

通过本文的介绍,相信你已经对Netty有了初步的了解,后续的文章将深入分析Netty的各个组件以及实现原理、运行流程

最后(点赞、收藏、关注求求啦~)

😁我是菜菜,热爱技术交流、分享与写作,喜欢图文并茂、通俗易懂的输出知识

📚在我的博客中,你可以找到Java技术栈的各个专栏:Java并发编程与JVM原理、Spring和MyBatis等常用框架及Tomcat服务器的源码解析,以及MySQL、Redis数据库的进阶知识,同时还提供关于消息中间件和Netty等主题的系列文章,都以通俗易懂的方式探讨这些复杂的技术点

🏆除此之外,我还是掘金优秀创作者、腾讯云年度影响力作者、华为云年度十佳博主....

👫我对技术交流、知识分享以及写作充满热情,如果你愿意,欢迎加我一起交流(vx:CaiCaiJava666),也可以持续关注我的公众号:菜菜的后端私房菜,我会分享更多技术干货,期待与更多志同道合的朋友携手并进,一同在这条充满挑战与惊喜的技术之旅中不断前行

🤝如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

📖本篇文章被收入专栏 Java常用框架、网络基石,感兴趣的朋友可以持续关注~

📝本篇文章、笔记以及案例被收入 Gitee-CaiCaiJava、 Github-CaiCaiJava,除此之外还有更多Java进阶相关知识,感兴趣的朋友可以star持续关注~