Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?

前言
在之前的 SpringBoot 整合长连接心跳机制 一文中认识了 Netty。
但其实只是能用,为什么要用 Netty?它有哪些优势?这些其实都不清楚。
本文就来从历史源头说道说道。
传统 IO
在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包。
比如下面的伪代码:
ServeSocket serverSocket = new ServeSocket(8080);
Socket socket = serverSocket.accept() ;
BufferReader in = .... ;
String request ;
while((request = in.readLine()) != null){
new Thread(new Task()).start()
}
大概是这样,其实主要想表达的是:这样一个线程只能处理一个连接。
如果是 100 个客户端连接那就得开 100 个线程,1000 那就得 1000 个线程。
要知道线程资源非常宝贵,每次的创建都会带来消耗,而且每个线程还得为它分配对应的栈内存。
即便是我们给 JVM 足够的内存,大量线程所带来的上下文切换也是受不了的。
并且传统 IO 是阻塞模式,每一次的响应必须的是发起 IO 请求,处理请求完成再同时返回,直接的结果就是性能差,吞吐量低。
Reactor 模型
因此业界常用的高性能 IO 模型是 Reactor。
它是一种异步、非阻塞的事件驱动模型。
通常也表现为以下三种方式:
单线程

从图中可以看出:
它是由一个线程来接收客户端的连接,并将该请求分发到对应的事件处理 handler 中,整个过程完全是异步非阻塞的;并且完全不存在共享资源的问题。所以理论上来说吞吐量也还不错。
但由于是一个线程,对多核 CPU 利用率不高,一旦有大量的客户端连接上来性能必然下降,甚至会有大量请求无法响应。
最坏的情况是一旦这个线程哪里没有处理好进入了死循环那整个服务都将不可用!
多线程

因此产生了多线程模型。
其实最大的改进就是将原有的事件处理改为了多线程。
可以基于 Java 自身的线程池实现,这样在大量请求的处理上性能提示是巨大的。
虽然如此,但理论上来说依然有一个地方是单点的;那就是处理客户端连接的线程。
因为大多数服务端应用或多或少在连接时都会处理一些业务,如鉴权之类的,当连接的客户端越来越多时这一个线程依然会存在性能问题。
于是又有了下面的线程模型。
主从多线程

该模型将客户端连接那一块的线程也改为多线程,称为主线程。
同时也是多个子线程来处理事件响应,这样无论是连接还是事件都是高性能的。
Netty 实现
以上谈了这么多其实 Netty 的线程模型与之的类似。
我们回到之前 SpringBoot 整合长连接心跳机制 中的服务端代码:
private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
/**
* 启动 Netty
*
* @return
* @throws InterruptedException
*/
@PostConstruct
public void start() throws InterruptedException {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss, work)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(nettyPort))
//保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new HeartbeatInitializer());
ChannelFuture future = bootstrap.bind().sync();
if (future.isSuccess()) {
LOGGER.info("启动 Netty 成功");
}
}
其实这里的 boss 就相当于 Reactor 模型中处理客户端连接的线程池。
work 自然就是处理事件的线程池了。
那么如何来实现上文的三种模式呢?其实也很简单:
单线程模型:
private EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(group)
.childHandler(new HeartbeatInitializer());
多线程模型:
private EventLoopGroup boss = new NioEventLoopGroup(1);
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss,work)
.childHandler(new HeartbeatInitializer());
主从多线程:
private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss,work)
.childHandler(new HeartbeatInitializer());
相信大家一看也明白。
总结
其实看过了 Netty 的线程模型之后能否对我们平时做高性能应用带来点启发呢?
我认为是可以的:
- 接口同步转异步处理。
- 回调通知结果。
- 多线程提高并发效率。
无非也就是这些,只是做了这些之后就会带来其他问题:
- 异步之后事务如何保证?
- 回调失败的情况?
- 多线程所带来的上下文切换、共享资源的问题。
这就是一个博弈的过程,想要做到一个尽量高效的应用是需要不断磨合试错的。
上文相关的代码:
https://github.com/crossoverJie/netty-action
欢迎关注公众号一起交流:
Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?的更多相关文章
- 从线程模型的角度看Netty的高性能
转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...
- 聊聊Netty那些事儿之从内核角度看IO模型
从今天开始我们来聊聊Netty的那些事儿,我们都知道Netty是一个高性能异步事件驱动的网络框架. 它的设计异常优雅简洁,扩展性高,稳定性强.拥有非常详细完整的用户文档. 同时内置了很多非常有用的模块 ...
- 乱七八糟Nodejs系列二:线程模型
上一篇中说了这样一句话:Nodejs和浏览器javascript一样,都是单线程,所以和传统的不一样,这个后面有机会再说.挖了坑就得填,哎. 1.一个例子 来看一个例子,这个例子来自async jav ...
- 关于redis的几件小事(二)redis线程模型
1.memcached和redis有什么区别? (1)Redis支持服务器端的数据操作 redis和memcached相比,redis拥有更多的 数据结构并且支持更丰富的数据操作 ,通常在memcac ...
- 【Netty源码分析】Reactor线程模型
1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比 ...
- Netty系列之Netty线程模型
Reference: http://www.infoq.com/cn/articles/netty-threading-model 1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 ...
- Netty — 线程模型
一.前言 众所周知,netty是高性能的原因源于其使用的是NIO,但是这只是其中一方面原因,其IO模型上决定的.另一方面源于其线程模型的设计,良好的线程模型设计,能够减少线程上下文切换,减少甚至避免锁 ...
- Mina、Netty、Twisted一起学(十):线程模型
要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...
- Reactor三种线程模型与Netty线程模型
文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...
随机推荐
- net core体系-Standard-1概述
前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时会趋于成熟,所以一个新的.Net开发时代已经来临!未来属于.NET Core. . ...
- 输出第n个丑数
方法一:暴力法 代码如下: 判断是否是丑数 public static boolean isUgly(int n){ while(n!=1){ if(n%2 == 0){ n /= 2; }else ...
- html网页调用本地exe程序
1.使用记事本(或其他文本编辑器)创建一个protocal.reg文件,并写入以下内容 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\ ...
- TensorFlow之多核GPU的并行运算
tensorflow多GPU并行计算 TensorFlow可以利用GPU加速深度学习模型的训练过程,在这里介绍一下利用多个GPU或者机器时,TensorFlow是如何进行多GPU并行计算的. 首先,T ...
- SOAPA来临,SIEM时代终结?
安全信息和事件管理(SIEM)产品及服务负责从大量企业安全控件.主机操作系统.企业应用和企业使用的其他软件中收集安全日志数据,并进行分析和报告.有些SIEM还可以试图阻止它们检测到正在进行的攻击,这可 ...
- PCB差分线学习
问:何为差分信号? 答:通俗地说,就是驱动端发送两个等值.反相的信号,接收端通过比较这两个电压的差值来判断逻辑状态“0”还是“1”. 问:差分线的优势在哪? 答:差分信号和普通的单端信号走线相比,最明 ...
- ARM的编程模式
ARM的编程模式 Linux ARM的工作状态 (主要指指令模式) 基本概述 32位架构 指令相关 ARM 32bit Thumb指令 16bit Thumb-2 16||32 bit 兼容 复位后开 ...
- PHP通过经纬坐标计算两个地址的距离
<?php /** *求两个已知经纬度之间的距离,单位为米 * *@param lng1,lng2 经度 * *@param lat1,lat2 纬度 * *@return float 距离,单 ...
- Linux进阶命令用法
1.tr命令 可以对来自标准输入的字符进行替换.压缩和删除.它可以将一组字符变成另一组字符 选项 -c或——complerment:取代所有不属于第一字符集的字符: -d或——delete:删除所有属 ...
- 对于Python turtle的学习笔记
进一步地,我尝试学习了Python 的其中一个非常重要的函数库——turtle库 这是一个用于python绘图的函数库,方便又好用! 对于它的安装,现在我们所用的python 3的系统运用到的指令是: ...