导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
为什么用netty?因为dubbo、sofa-rpc、rocketMQ、jetty等知名的的项目都在用,就是这么简单。平时我们在遇到技术难题的时候,第1个想法是什么呢:看看别人怎么搞的。我们在和同事争论技术方案的时候,最常说的一句话是什么:XXX(某知名框架/某知名公司)也是这么做的。思路没问题,看看别人怎么做的,尤其是知名权威的项目/公司是怎么做的,借鉴一下,这是最快速的解决问题的方式。
1 用netty 写个NIO Hello World
1.1 netty版本的NIO服务端
/**
* 欢迎关注公众号“种代码“,获取博主微信深入交流
*
* @author wangjianxin
*/
public class HelloNettyServer {
public static void main(String[] args) throws InterruptedException {
//相当于com.zhongdaima.netty.analysis.part0.jdk.nio.NioServerConnector中的线程
EventLoopGroup boss = new NioEventLoopGroup(1);
//相当于com.zhongdaima.netty.analysis.part0.jdk.nio.NioServerHandler中的线程
EventLoopGroup worker = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
byte[] bytes = new byte[((ByteBuf) msg).readableBytes()];
((ByteBuf) msg).readBytes(bytes);
String name = new String(bytes);
//打印客户端发来的数据
System.out.println(name);
ctx.writeAndFlush(Unpooled.wrappedBuffer(("hello, " + name).getBytes()));
}
});
}
});
//绑定到8000端口
ChannelFuture future = serverBootstrap.bind(8000).sync();
//等待channel关闭
future.channel().closeFuture().syncUninterruptibly();
}
}
- NioEventLoopGroup:就是一组线程,boss就相当于上一篇中
com.zhongdaima.netty.analysis.part0.jdk.nio.NioServerConnector
中的线程,而worker就相当于上一篇com.zhongdaima.netty.analysis.part0.jdk.nio.NioServerHandler
中的线程,这里我们把线程数量设置为1。
serverBootstrap.bind(8000).sync():绑定到8000端口,相当于上一篇中
com.zhongdaima.netty.analysis.part0.jdk.nio.HelloNioServer
绑定到8000端口。serverBootstrap.childHandler():这里我们添加了一个ChannelInitializer,并在initChannel中添加了一个匿名handler,这个匿名handler就是我们的主要业务逻辑,接收到客户端发来的请求,并添加上“hello, ”发送回去。相当于上一篇中的
com.zhongdaima.netty.analysis.part0.jdk.nio.NioServerHandler
。
1.2 netty版本的NIO客户端
/**
* 欢迎关注公众号“种代码“,获取博主微信深入交流
*
* @author wangjianxin
*/
public class HelloNettyClient {
private static final int CLIENTS = 2;
private static final EventLoopGroup EVENT_LOOP_GROUP = new NioEventLoopGroup(1);
public static void main(String[] args) throws InterruptedException {
//用来保存两个客户端
Channel[] clients = new Channel[CLIENTS];
for (int i = 0; i < CLIENTS; i++) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class)
.group(EVENT_LOOP_GROUP)
.handler(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
byte[] bytes = new byte[((ByteBuf) msg).readableBytes()];
((ByteBuf) msg).readBytes(bytes);
//打印服务端返回数据
System.out.println(new String(bytes));
}
});
//连接8000端口
clients[i] = bootstrap.connect(new InetSocketAddress(8000)).sync().channel();
}
while (true) {
for (int i = 0; i < clients.length; i++) {
//循环向服务端发送“zhongdaima" + 客户端编号
clients[i].writeAndFlush(Unpooled.wrappedBuffer(("zhongdaima" + i).getBytes()));
}
Thread.sleep(1000);
}
}
}
该客户端创建了两条连接,然后循环向服务端发送“zhongdaima” + 客户端编号,接收到服务端数据后打印。比较简单,不再赘述。
2 netty vs jdk
与上一节用jdk原生api写的demo比,大家看了用netty写的demo之后有什么感受?哇,很清爽,很简洁,比用jdk原生api写的那一坨代码友好太多了。这里用“一坨”来形容jdk原生api demo一点都不为过。
而netty也不仅仅是NIO,netty对于BIO、NIO、AIO(异步io,netty5)抽象出了一套共同的api,我们只需要稍微修改代码就可以将我们程序切换为BIO、NIO、AIO中的一种。
2.1 为什么不用jdk原生api
代码复杂:大家已经看到了,代码太多。
学习成本高:使用jdk原生api需要熟练掌握Selector、ByteBuffer等,需要具备非常高超的能力才能写出健壮代码。
api复杂不友好:jdk ByteBuffer类的api不够友好,必须非常小心才不会出错。
没有配套设施:我们知道TCP是基于流的协议,而我们的应用发送数据是基于帧的,也就说我们需要手动从TCP流中拆出我们的“Reqeust”、“Response”等,非常复杂。
2.2 为什么选择netty
代码简洁:大家已经看到了,与jdk原生api相比,代码量如何一看便知。
学习成本低:netty对jdk api封装后,屏蔽了某些概念,如Selector。demo丰富,按照netty文档学习2小时轻松上手。
api更简单友好:netty的ByteBuf类比jdk的ByteBuffer好用太多,用了就知道。无需和Selector打交道,由netty自动完成。
周边配套完善:netty并不是简单地封装了jdk的io api,还提供了丰富的周边配套,例如丰富的内置编解码器(http、redis等等)开箱即用。而且还有Abstract类型的编解码器接口,轻松构建私有协议编解码器。使用内置的IdleStateHandler轻松完成长连接心跳功能。
知名项目/知名大厂都在用:群众的眼睛是雪亮的,历经众多项目的考验,稳定性高。
3 我用netty
我在两年前入职转转,负责公司RPC框架开发,当时公司的RPC框架客户端真的是用jdk原生nio写的。我很佩服前辈能用jdk原生api写代码,这么多年了代码依然有bug,不优雅。终于在入两个月后我忍不住用netty重构了,我删除了差不多20个类,新增了个6个类完成了这次重构工作,约摸估计减少上千行代码。看着清爽的代码,心情很美丽。
4 总结
上面都说了这么多,还用说啥,一起开始学习吧,关注我,及时获取最新文章。
关于作者
王建新,转转架构部资深Java工程师,主要负责服务治理、RPC框架、分布式调用跟踪、监控系统等。爱技术、爱学习,欢迎联系交流。