作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题
代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等
在讲netty之前我们现总结一下JAVA NIO / JAVA AIO的不足之处。
一、JAVA NIO 、AIO的不足之处
- 虽然JAVA NIO 和 JAVA AIO框架提供了多路复用IO/异步IO的支持,但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对 ProtocolBuffer、JSON这些信息格式的封装,但是Netty框架提供了这些数据格式封装(基于责任链模式的编码和解码功能)
- 要编写一个可靠的、易维护的、高性能的(注意它们的排序)NIO/AIO服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的权限、还有上面提到的信息格式封装、简单的数据读取。这些Netty框架都提供了响应的支持。
- JAVA NIO框架存在一个poll/epoll bug:Selector doesn’t block on
Selector.select(timeout),不能block意味着CPU的使用率会变成100%(这是底层JNI的问题,上层要处理这个异常实际上也好办)。当然这个bug只有在Linux内核上才能重现。这个问题在JDK 1.7版本中还没有被完全解决:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719。虽然Netty 4.0中也是基于JAVA NIO框架进行封装的(上文中已经给出了Netty中NioServerSocketChannel类的介绍),但是Netty已经将这个bug进行了处理。
二、Netty介绍
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。但实际上呢,Netty框架并不只是封装了多路复用的IO模型,也包括提供了传统的阻塞式/非阻塞式 同步IO的模型封装。Netty提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。
三、代码示例
客户端在第二篇中已经给出。
服务端:
package demo.com.test.io.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.net.InetSocketAddress;
import java.nio.channels.spi.SelectorProvider;
import java.util.concurrent.ThreadFactory;
public class NettyServer {
public static void main(String[] args) throws Exception {
//这就是主要的服务启动器
ServerBootstrap serverBootstrap = new ServerBootstrap();
//=======================下面我们设置线程池
//BOSS线程池
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
//WORK线程池:这样的申明方式,主要是为了向读者说明Netty的线程组是怎样工作的
ThreadFactory threadFactory = new DefaultThreadFactory("work thread pool");
//CPU个数
int processorsNumber = Runtime.getRuntime().availableProcessors();
EventLoopGroup workLoogGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory, SelectorProvider.provider());
//指定Netty的Boss线程和work线程
serverBootstrap.group(bossLoopGroup , workLoogGroup);
//如果是以下的申明方式,说明BOSS线程和WORK线程共享一个线程池
//(实际上一般的情况环境下,这种共享线程池的方式已经够了)
//serverBootstrap.group(workLoogGroup);
//========================下面我们设置我们服务的通道类型
//只能是实现了ServerChannel接口的“服务器”通道类
serverBootstrap.channel(NioServerSocketChannel.class);
//当然也可以这样创建(那个SelectorProvider是不是感觉很熟悉?)
//serverBootstrap.channelFactory(new ChannelFactory<NioServerSocketChannel>() {
// @Override
// public NioServerSocketChannel newChannel() {
// return new NioServerSocketChannel(SelectorProvider.provider());
// }
//});
//========================设置处理器
//为了演示,这里我们设置了一组简单的ByteArrayDecoder和ByteArrayEncoder
//Netty的特色就在这一连串“通道水管”中的“处理器”
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {