Netty 是什么
我个人对于 Netty 的理解是:Netty 封装了 JDK 的 NIO,让使用者用的更爽,不用再写一大段复杂的代码了。
官方介绍是:
Netty 是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。
使用 Netty 的好处在于:
- JDK 自带的 NIO 需要了解的概念田铎,编程复杂;
- Netty 底层 IO 模型随意切换,只需要修改参数,Netty 可以直接从 NIO 模型变身为 IO 模型;
- Netty 自带的拆包解包、异常检测等机制可以让使用者从 NIO 繁重的细节中脱离出来,程序员只需要关注业务逻辑;
- Netty 解决了 JDK 的很多空轮询在内的 Bug;
- Netty 底层对线程、selector 做了很多细小的优化,精心设计的 reactor 线程模型可以做到非常高效的并发处理;
- 自带各种协议栈;
- 社区活跃;
- Netty 已经经历各大 RPC 框架、消息中间件、分布式通信中间件线上的广泛验证,健壮性无比强大。
简单应用
实现一个客户端每隔一段时间,发送一条消息给服务端,服务端把消息打印出来。
服务端代码
NettyServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
public class NettyServer {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
serverBootstrap
.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
.bind(8000);
}
}
客户端代码
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Date;
/**
* @author: zhoudongliang
* @date: 2019/5/21 15:24
* @description:
*/
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder());
}
});
Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
while (true) {
channel.writeAndFlush(new Date() + ": hello world!");
Thread.sleep(2000);
}
}
}
如果你了解过 JDK 原生的 NIO 编程模型的话你会发现 Netty 简直是太简单了,剩下了一大段代码出来。
咋地,你还不信?
哪我就给你来一段使用 JDK 原生 NIO 模型写出来的服务端代码:
NIOServer .java
public class NIOServer {
public static void main(String[] args) throws IOException {
Selector serverSelector = Selector.open();
Selector clientSelector = Selector.open();
new Thread(() -> {
try {
// 对应IO编程中服务端启动
ServerSocketChannel listenerChannel = ServerSocketChannel.open();
listenerChannel.socket().bind(new InetSocketAddress(8000));
listenerChannel.configureBlocking(false);
listenerChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
while (true) {
// 监测是否有新的连接,这里的1指的是阻塞的时间为 1ms
if (serverSelector.select(1) > 0) {
Set<SelectionKey> set = serverSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = set.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
try {
// (1) 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(clientSelector, SelectionKey.OP_READ);
} finally {
keyIterator.remove();
}
}
}
}
}
} catch (IOException ignored) {
}
}).start();
new Thread(() -> {
try {
while (true) {
// (2) 批量轮询是否有哪些连接有数据可读,这里的1指的是阻塞的时间为 1ms
if (clientSelector.select(1) > 0) {
Set<SelectionKey> set = clientSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = set.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isReadable()) {
try {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// (3) 面向 Buffer
clientChannel.read(byteBuffer);
byteBuffer.flip();
System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
.toString());
} finally {
keyIterator.remove();
key.interestOps(SelectionKey.OP_READ);
}
}
}
}
}
} catch (IOException ignored) {
}
}).start();
}
}
本系列文章参考自 闪电侠的 掘金小册 感兴趣的读者可以点击如下链接进行购买
https://juejin.im/book/5b4bc28bf265da0f60130116/section/5b4daf9ee51d4518f543f130
关注 公众号 【程序猿杂货铺】 获取更多推送!
本系列文章参考自 闪电侠的 掘金小册 感兴趣的读者可以点击如下链接进行购买
Netty入门与实战