【Netty源码解读和权威指南】第03篇:手把手搭建Netty开发环境——5分钟从零到HelloWorld
上一篇【第02篇】Java IO进化史——从BIO到NIO再到AIO,一文搞懂网络IO
下一篇【第04篇】Netty核心架构图解——Bootstrap/Channel/Pipeline/Handler一次看懂
摘要
学任何技术,第一步永远是"把环境跑起来"。本文手把手教您搭建Netty开发环境:Maven/Gradle依赖怎么配、IDE插件有哪些好用的、第一个Netty Echo服务器怎么写。
更重要的是:每行代码都给您讲清楚为什么。服务端只用了30行Java代码就实现了一个高性能NIO服务器;客户端20行代码就能连上服务器并收发消息。附带启动调试技巧和5个常见报错的解决方案,让您一次配好、一次跑通。
一、Netty版本选择——用哪个版本不踩坑?
在开始写代码前,先解决一个关键问题:用哪个版本的Netty?
【Netty版本选择指南】
版本系列 状态 推荐指数 说明
──────────────────────────────────────────────────────
Netty 3.x 已废弃 ❌ 不要用,API陈旧
Netty 4.0.x 停止维护 ⚠️ 有项目在用,但不推荐新项目使用
Netty 4.1.x ✅ 推荐使用 ⭐⭐⭐⭐⭐ 当前最稳定版本,所有新项目都用它
Netty 5.x 已废弃 ❌ 官方已放弃,不要踩坑
为什么Netty 5被废弃了? Netty 5引入了新的ForkJoinPool线程模型,但经过长期测试,性能提升有限(某些场景甚至更慢),反而增加了代码复杂度。最终Netty团队决定将Netty 5的优秀特性合并回Netty 4.1.x,然后废弃了Netty 5分支。
结论:新项目直接用 Netty 4.1.100.Final(或更高版本)。
二、Maven依赖配置——复制粘贴就能用
Netty采用模块化设计,按需引入依赖。最核心的依赖是 netty-all,它包含了Netty的所有模块。
2.1 Maven项目依赖配置
<!-- pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>netty-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<netty.version>4.1.100.Final</netty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- ✅ 推荐:引入netty-all(包含所有模块) -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<!-- 或者:按需引入(如果你很清楚需要哪些模块) -->
<!--
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
</dependency>
-->
<!-- 单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.2 Gradle依赖配置
// build.gradle
plugins {
id 'java'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
dependencies {
// ✅ 推荐:引入netty-all
implementation 'io.netty:netty-all:4.1.100.Final'
// 单元测试
testImplementation 'junit:junit:4.13.2'
}
2.3 Netty的模块化结构
虽然推荐用netty-all,但了解Netty的模块结构有助于排查依赖冲突:
【Netty模块化结构】
netty-all(聚合包)
├── netty-buffer (ByteBuf缓冲区)
├── netty-codec (编解码框架)
├── netty-codec-http (HTTP编解码)
├── netty-codec-http2 (HTTP/2编解码)
├── netty-codec-socks (SOCKS代理协议)
├── netty-common (公共工具类)
├── netty-handler (内置Handler)
├── netty-handler-ssl (SSL/TLS支持)
├── netty-resolver (地址解析器)
├── netty-transport (核心传输层)
├── netty-transport-epoll (Linux Epoll原生传输)
├── netty-transport-kqueue(macOS KQueue原生传输)
└── netty-transport-udt (UDT传输协议,已废弃)
三、第一个Netty程序——Echo服务器
Echo服务器是最经典的Netty入门程序:客户端发送什么,服务端就原样返回什么。
3.1 EchoServer——30行代码的高性能NIO服务器
// EchoServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Netty Echo服务器——30行代码实现高性能NIO服务器
*/
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
// 1️⃣ 创建Boss线程组(接收连接)
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 2️⃣ 创建Worker线程组(处理IO读写)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 3️⃣ 创建ServerBootstrap(服务端启动引导类)
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 设置线程组
.channel(NioServerSocketChannel.class) // 设置服务端Channel类型
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
// 4️⃣ 配置ChannelPipeline(责任链)
ChannelPipeline p = ch.pipeline();
// 字符串解码器(将ByteBuf解码为String)
p.addLast(new StringDecoder());
// 字符串编码器(将String编码为ByteBuf)
p.addLast(new StringEncoder());
// 自定义业务Handler
p.addLast(new EchoServerHandler());
}
});
// 5️⃣ 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
System.out.println("Echo服务器启动,监听端口:" + port);
// 6️⃣ 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 7️⃣ 优雅关闭线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoServer(8080).start();
}
}
/**
* EchoServer的业务Handler——处理客户端消息
*/
class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 客户端发来的消息(已经是String类型,因为用了StringDecoder)
String message = (String) msg;
System.out.println("服务端收到:" + message);
// 原样返回给客户端
ctx.writeAndFlush("Echo: " + message + "\n");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 出现异常,关闭连接
cause.printStackTrace();
ctx.close();
}
}
3.2 EchoClient——20行代码的Netty客户端
// EchoClient.java
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Netty Echo客户端
*/
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
// 1️⃣ 创建客户端线程组(只需要一个)
EventLoopGroup group = new NioEventLoopGroup();
try {
// 2️⃣ 创建Bootstrap(客户端启动引导类)
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new StringDecoder());
p.addLast(new StringEncoder());
p.addLast(new EchoClientHandler());
}
});
// 3️⃣ 连接服务器
ChannelFuture f = b.connect(host, port).sync();
System.out.println("客户端连接成功:" + host + ":" + port);
// 4️⃣ 发送消息
f.channel().writeAndFlush("Hello Netty!\n");
// 5️⃣ 等待连接关闭
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("localhost", 8080).start();
}
}
/**
* EchoClient的业务Handler
*/
class EchoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String response = (String) msg;
System.out.println("客户端收到响应:" + response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
四、运行与调试——让程序"跑起来"
4.1 启动步骤
【启动Echo服务器的步骤】
1. 先运行EchoServer(服务端)
└── 控制台输出:"Echo服务器启动,监听端口:8080"
2. 再运行EchoClient(客户端)
└── 服务端控制台输出:"服务端收到:Hello Netty!"
└── 客户端控制台输出:"客户端收到响应:Echo: Hello Netty!"
4.2 用telnet测试服务器
如果您不想写客户端代码,可以用系统自带的telnet命令测试:
# Linux/macOS
$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello Netty! <-- 输入这行,然后按回车
Echo: Hello Netty! <-- 服务端返回(注意:需要客户端Handler支持)
# Windows(需要先开启telnet功能)
> telnet localhost 8080
注意:上面的EchoServer使用了
StringDecoder,所以可以直接用telnet测试。如果您用的是裸ByteBuf,telnet发送的数据需要手动解析。
4.3 IDEA断点调试技巧
在IDEA中调试Netty程序,有几个实用技巧:
【IDEA调试Netty技巧】
1. 在Handler的channelRead()方法设断点
└── 可以查看客户端发来的消息内容
2. 在EventLoop的run()方法设断点(源码调试)
└── 可以看到IO事件的处理流程(需要下载Netty源码)
3. 条件断点:右键断点 → 设置Condition
└── 例如:ctx.channel().remoteAddress().toString().contains("192.168")
(只有特定IP的连接才会触发断点)
4. 观察Expression:在Debug面板中添加Watch
└── 输入:ctx.channel().pipeline().names()
(查看当前Pipeline中有哪些Handler)
五、5个常见报错与解决方案
报错1:java.lang.NoClassDefFoundError: io/netty/bootstrap/ServerBootstrap
原因:没有引入Netty依赖
解决:检查pom.xml或build.gradle,确保netty-all依赖已正确引入
报错2:java.net.BindException: Address already in use
原因:端口被占用
解决:
# Linux/macOS:查看谁占用了8080端口
$ lsof -i :8080
$ kill -9 <PID>
# Windows:
> netstat -ano | findstr 8080
> taskkill /F /PID <PID>
报错3:客户端连接后没有响应
原因:服务端Pipeline中没有配置Decoder/Encoder,导致消息无法解析
解决:确保在initChannel()中添加了正确的编解码器:
// ✅ 正确
p.addLast(new StringDecoder());
p.addLast(new StringEncoder());
// ❌ 错误:没有编解码器,消息无法解析
p.addLast(new EchoServerHandler()); // 直接用这个,消息类型是ByteBuf
报错4:io.netty.util.IllegalReferenceCountException: refCnt: 0
原因:ByteBuf被释放了多次(内存泄漏检测)
解决:检查代码中是否手动调用了buf.release(),而Netty已经自动释放了
报错5:服务端收不到客户端消息
原因:多种可能,最常见的是TCP粘包/拆包导致消息不完整
解决:使用LineBasedFrameDecoder或DelimiterBasedFrameDecoder解决粘包问题:
p.addLast(new LineBasedFrameDecoder(1024)); // 按换行符分割
p.addLast(new StringDecoder());
六、IDE插件推荐——提升开发效率
【Netty开发推荐IDE插件】
插件名称 适用IDE 功能
────────────────────────────────────────────────────────────
Netty Assistant IDEA Netty Pipeline可视化
Grep Console IDEA 高亮Netty日志
Maven Helper IDEA 分析依赖冲突(netty版本冲突很常见!)
Lombok IDEA/ Eclipse 减少样板代码(@Data/@Builder)
总结
- 版本选择:新项目直接用Netty 4.1.100.Final(或更高版本)
- 依赖配置:Maven/Gradle引入
netty-all,复制粘贴就能用 - 第一个程序:Echo服务器只需要30行代码,客户端20行
- 核心API:
ServerBootstrap/Bootstrap(启动引导)、NioEventLoopGroup(线程组)、ChannelInitializer(初始化Pipeline) - 下一步:理解Netty的核心架构(Bootstrap/Channel/Pipeline/Handler),这是第004篇的内容
上一篇【第02篇】Java IO进化史——从BIO到NIO再到AIO,一文搞懂网络IO
下一篇【第04篇】Netty核心架构图解——Bootstrap/Channel/Pipeline/Handler一次看懂
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)