Netty:初识Netty
总结了nio的内容,有了nio的一些基础之后,我们就可以来看下netty。netty是java领域的高性能网络传输框架,rpc的技术核心就是网络传输和序列化,所以netty给予了rpc在网络传输领域巨大的支持。
一个简单的netty代码实现
网络传输基于的是tcp协议,所以会有服务端和客户端之分,而netty是网络传输框架,所以一个完整的netty代码至少是有服务端和客户端的。本文代码基于netty4.1.15。
服务端:
public class demoserver { public static void main(string[] args) throws exception { eventloopgroup bossgroup = new nioeventloopgroup(); eventloopgroup workergroup = new nioeventloopgroup(); try { serverbootstrap serverbootstrap = new serverbootstrap(); serverbootstrap.group(bossgroup,workergroup). channel(nioserversocketchannel.class). childhandler(new channelinitializer<socketchannel>() { @override protected void initchannel(socketchannel ch) throws exception { ch.pipeline().addlast(new stringdecoder(charsetutil.utf_8)); ch.pipeline().addlast(new stringencoder(charsetutil.utf_8)); ch.pipeline().addlast(new demoserverhandler()); } }); channelfuture future = serverbootstrap.bind(8899).sync(); future.channel().closefuture().sync(); }finally { bossgroup.shutdowngracefully(); workergroup.shutdowngracefully(); } } }
public class demoserverhandler extends simplechannelinboundhandler<string> { @override protected void channelread0(channelhandlercontext ctx, string msg) throws exception { system.out.println("" + ctx.channel().remoteaddress() + "," + msg); ctx.channel().writeandflush("from server" + uuid.randomuuid()); } @override public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception { cause.printstacktrace(); ctx.close(); } }
客户端:
public class democlient { public static void main(string[] args) throws exception{ eventloopgroup eventloopgroup = new nioeventloopgroup(); try{ bootstrap bootstrap = new bootstrap(); bootstrap.group(eventloopgroup).channel(niosocketchannel.class).handler(new channelinitializer<socketchannel>() { @override protected void initchannel(socketchannel ch) throws exception { ch.pipeline().addlast(new stringdecoder(charsetutil.utf_8)); ch.pipeline().addlast(new stringencoder(charsetutil.utf_8)); ch.pipeline().addlast(new democlienthandler()); } }); channelfuture channelfuture = bootstrap.connect("localhost",8899).sync(); channelfuture.channel().closefuture().sync(); }finally { eventloopgroup.shutdowngracefully(); } } }
public class democlienthandler extends simplechannelinboundhandler<string> { @override protected void channelread0(channelhandlercontext ctx, string msg) throws exception { system.out.println("" + ctx.channel().remoteaddress()); system.out.println("client output:" + msg); ctx.writeandflush("from client" + localdatetime.now()); } @override public void channelactive(channelhandlercontext ctx) throws exception { string msg = "来自客户端的问候!"; ctx.writeandflush(msg); } @override public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception { cause.printstacktrace(); ctx.close(); } }
来看下这个简单的代码做了什么:
1、服务端启动后,客户端先向服务端发起连接建立;
2、连接建立后,触发客户端的channelactive方法,该方法向服务端发出了一条信息,这条信息默认在网络中是会转成字节的形式来传输的,因为tcp的数据传输都是基于字节的,这个过程也叫做编码;
3、服务端收到信息后,会被服务端的handler,其实就是stringdecoder先做处理,从字节变成了字符,这个过程也叫做解码;
4、此时对于demoserverhandler来说,信息已经变成了符合自己的string类型,所以channelread0方法会被调用,输出信息,同时向客户端发出信息,信息又会转成字节的信息传向客户端;
5、客户端收到信息后,会被客户端解码成字符,触发客户端的channelread0方法,输出客户端地址和收到的信息,再向服务端发送时间戳;
6、循环往复上述3-5,死循环;
模块化
从上述这个简单的demo中,我们可以提取出netty的核心模块:
1、channel、eventloop、channelfuture
channel接口:基本的io操作(bind()/connect()/read()/write())依赖于底层网络传输所提供的原语。在我们这个demo中,能看到nioserversocketchannel和niosocketchannel,nioserversocketchannel使用基于nio选择器的实现来接受新连接,niosocketchannel使用基于nio选择器的实现来建立和处理新连接。
eventloop接口:eventloop定义了netty的核心抽象,用于处理连接的生命周期中所发生的事件。eventloop是协调设计的一部分,采用了两个基本的api:并发和网络编程。在我们这个demo中,能看到nioeventloop,nioeventloop就是一个reactor,是整个netty的一个核心。
channelfuture接口:netty中的所有的io操作都是异步的,因为一个操作可能不会立即返回,所以我们需要一种用于在之后某个时间点确定其结果的方法。为此,netty提供了channelfuture接口,其addlistener()方法注册了一个channelfuturelistener,以便在某个操作完成是得到通知。
2、channelhandler、channelpipeline
channelhandler接口:从应用程序开发人员的角度来看,channelhandler是netty的主要组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器,因为channelhandler的方法是由事件来触发的。在我们这个demo中,democlienthandler、demoserverhandler就是两个自定义的channelhandler,democlienthandler在连接一建立的时候,就触发了channelactive方法,然后demoserverhandler在channelread0方法中读取了其传输的信息。
channelpipeline接口:channelpipeline为channelhandler链提供了容器,并定义了用于该链上传播入站和出站事件流的api。当channel被创建时,它会被自动的分配到它所专属的channelpipeline。在我们的demo中,一个channelinitializer的实例被注册到serverbootstrap或者bootstrap,当它的initchannel方法被调用的时候,channelinitializer将在channelpipeline中安装一组自定义的channelhandler,最后channelinitializer将它自己从channelpipeline中移除。
3、bytebuf
网络数据的基本单位是字节,nio提供了bytebuffer作为网络数据的字节容器,但是bytebuffer本身设计并不优雅,使用繁琐,netty使用bytebuf来替代bytebuffer,在我们的demo中,不能直接看到bytebuf,但是在netty提供的内置编解码器stringdecoder/stringencoder中,操作的对象就是bytebuf。
4、serverbootstrap、bootstrap
serverbootstrap和bootstrap是一个非常抽象的概念。serverbootstrap是netty创建服务器的辅助类,负责封装服务启动的一系列操作。和serverbootstrap一样,bootstrap也是封装客户端向服务端发送请求的一系列操作。
5、codec
通过netty发送和接收一个消息的时候,就会发生一次数据转换,入站消息会被解码,也就是从字节转换为原本的形式,如果是出站消息,就会从一种形式变成字节,这个就是编码,编解码的根本原因就是因为网络数据就是一系列的字节。在我们的demo中,stringencoder和stringdecoder就是编解码器。
最后总结一下:
1、通过一个简单的demo,介绍了一下netty,站在应用的角度看了下netty是如何运行的;
2、从demo中提取出netty的重要的模块,简单介绍一下各个模块,在后续的文章中将详细介绍每个模块组件。
参考资料:
上一篇: 你了解GC吗?一文读懂分代回收机制