前言
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,可以自定义实现)
快速入门
导入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持续关注~