Netty原理源码总结01-异步,BIO,NIO

Netty原理源码总结01-异步,BIO,NIO

Netty是Java网络一个大杀器,在很多行业都有广泛的应用,连Spring也在5版本提供了默认基于Netty的webflux,总之深入学习一下Netty一定会受益良多

Netty,异步

说到Netty往往不能避开Java的io模型,一般的文章会先说到BIO,再说到NIO,但是这里我想要先说一下异步这个概念.

对于开发者来说,异步是一种程序设计的思想,使用异步模式设计的程序可以显著减少线程等待,从而在高吞吐量的场景中,极大提升系统的整体性能,显著降低时延。

为什么异步能极大提升系统整体性能呢?我用一个钱包系统来举例.

同步

假定一个转账系统,同步的核心代码如下:

public void transfer(accountFrom, accountTo, amount){
    add(accountFrom,-amount);//黑盒,耗时50ms
    add(accountTo,amount);
}

上面的伪代码从 accountFrom 的钱包扣除 amount,再把 amount 转到 accountTo 的钱包里面去,这是同步的实现方式,但是性能如何呢?

假设微服务 add 的平均响应时间是50ms,那么整个 transfer 的耗时就是100ms,也就是说一个线程一秒可以处理十笔转账请求,假设服务器线程最多是100,也就是说,整个服务器,每秒能处理1000个请求,超出的请求就只能进入阻塞,或者等待延时了.

但是1000是不是系统极限呢,如果我们监测一下服务器的各项指标,会发现服务器没有一项是到了性能瓶颈的.这是因为, transfer 方法虽然耗时100ms,但是真正用在发送,接收和处理数据的时间都很短,绝大多数时间都用在等待add服务返回和网络传输上了.

也就是说,采用同步实现的方式,整个服务器的所有线程大部分时间都没有在工作,而是在等待

异步

如果是异步要怎么处理呢?核心代码如下:

public void transfer(accountFrom, accountTo, amount){
    CompletableFuture.runAsync(()->{
        add(accountFrom,-amount);
        return; 
    ).whenComplete(()->add(accountTo,amount));
}

这里借助了Java提供的CompletableFuture来实现异步的调用,如果没有用过没有关系,关键是思想.这段代码的流程是:执行给accountFrom扣除金额的方法,在成功的时候,再执行给accountTo添加金额的方法;

如果这个时候对transfer方法进行耗时统计,会发现每次执行时间只需要几ms,系统每秒的处理数量也会远远的超过同步的方式,直到达到服务器的物理性能上限

个人理解,异步的本质是提高cpu的利用率,同步时线程等待同样会占用大量的cpu时间片,这样的占用绝大多数是无意义的,而异步会减少线程等待占用的CPU时间片,从而提高了CPU时间片的利用率.

当然这个例子里面没有对add失败进行处理,真实开发的时候,异步会加大程序的复杂性,所以异步应该应用在性能敏感并且有io等耗时操作的地方.

BIO,NIO对比

明明是Netty,明明是讲BIO,NIO,为什么我要先说异步呢,其实这个世界上新技术有很多,一味地追逐上层的技术,人的精力有限,是做不到的.但是有很多东西其实是基石,讲NIO之前我先讲异步,等到我真的讲NIO的时候,你会发现NIO超越BIO,理所应当啊,也不需要去背什么概念了,再到以后,你或许学到了消息队列,Kafka,RocketMq,一个个产品花里胡哨,但是你会知道,他们最大的作用之一就是提供进程间的异步,为什么要异步,跟你今天理解的异步,其实是没有区别的.这个时候你会意识到,知识是成体系的,一个简单的异步,就可以串联起很多东西,这才是学习更重要的东西.

回到BIO和NIO,有一点需要认识到,网络并非时刻可读可写的,BIO就是不管不顾一直往Channel流读写数据,即使无数据可读,无数据缓冲可用的时候,也把持着线程资源. 我们用NIO就是在解决这个问题,NIO其实在读写操作的时候还是阻塞的,但是当没有数据可读,没有缓冲区可写的时候就会让渡出线程资源,等到有数据可读可写的时候在操作io.

举个例子:

有一个养鸡的农场,里面养着来自各个农户(Thread)的鸡(Socket),每家农户都在农场中建立了自己的鸡舍(SocketChannel)

  1. BIO:Block IO,每个农户盯着自己的鸡舍,一旦有鸡下蛋,就去做捡蛋处理;
  2. NIO:No-Block IO-单Selector,农户们花钱请了一个饲养员(Selector),并告诉饲养员(register)如果哪家的鸡有任何情况(下蛋)均要向这家农户报告(select keys);
  3. NIO:No-Block IO-多Selector,当农场中的鸡舍逐渐增多时,一个饲养员巡视(轮询)一次所需时间就会不断地加长,这样农户知道自己家的鸡有下蛋的情况就会发生较大的延迟。怎么解决呢?没错,多请几个饲养员(多Selector),每个饲养员分配管理鸡舍,这样就可以减轻一个饲养员的工作量,同时农户们可以更快的知晓自己家的鸡是否下蛋了;
  4. Epoll模式:如果采用Epoll方式,农场问题应该如何改进呢?其实就是饲养员不需要再巡视鸡舍,而是听到哪间鸡舍的鸡打鸣了(活跃连接),就知道哪家农户的鸡下蛋了;

在连接数不多的时候,其实BIO的性能并不差,因为BIO实现非常简单,这是它的优点,所以我们也没有必要去使用NIO.但是连接数多的时候,BIO就会存在很大的性能问题,也就是NIO发挥作用的时候了.

NIO和Reactor

上面的例子很生动的说明了BIO和NIO的特性,NIO思路就是,创建一个Selector,Channel告诉Selector自己关心的事件(SelectKey),Selector自己循环判断哪个Channel关心的事件可以触发了,当有事件的时候就通知Channel,Channel进行读写创建连接等操作.这就是Reactor模式,同时一个Selector可以注册很多Channel,这就是多路复用机制.

在上面的情况下,只有一个Selector,也就是在农场例子中的单Selector的情况,也称为Reactor单线程模式,但是在并发量很大的时候,单线程未必够用,所以我们还可以创建多个Selectot,每个Selector负责一部分channel,这就是Reactor多线程模式

走到这一步,Reactor模式还可不可以再优化呢?答案是可以的,对于服务器来说,接收连接是非常重要的事情,如果超长的读写操作影响了连接创建,这是不太能接收的,所以最主流的处理模式是Reactor主从模式,boss线程负责连接的创建,然后将创建好的连接交给子线程组,子线程组选取一个线程处理这个连接读写事件通知.

在Netty中,三种Reactor模式都是支持,但是除非选了一个垃圾的单核cpu的服务器,选主从模式就可以了,三种实现方式在下面:

//Reactor 单线程模式
EventLoopGroup eventGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);

//非主从 Reactor 多线程模式
EventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);

//主从 Reactor 多线程模式
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup); 

最后再补充一下,Netty的boosGroup大多时候并不能用到一个线程组,只会用到线程组中的一个,在Netty服务器启动的时候,会绑定地址和端口,一般来说我们服务器只会绑定一个地址和端口,所以实际上也只用到了bossGroup中的一个线程.如果我在文章中描述有什么不对的地方,欢迎指正.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容