博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从线程模型的角度看 Netty 为什么是高性能的?
阅读量:6875 次
发布时间:2019-06-26

本文共 3004 字,大约阅读时间需要 10 分钟。

传统 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 的线程模型与之的类似。

我们回到之前 中的服务端代码:

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 的线程模型之后能否对我们平时做高性能应用带来点启发呢?

我认为是可以的:

  • 接口同步转异步处理。
  • 回调通知结果。
  • 多线程提高并发效率。

无非也就是这些,只是做了这些之后就会带来其他问题:

  • 异步之后事务如何保证?
  • 回调失败的情况?
  • 多线程所带来的上下文切换、共享资源的问题。

这就是一个博弈的过程,想要做到一个尽量高效的应用是需要不断磨合试错的。

相关的代码:

PS

传播一个喜大普奔的消息,现在是入手云服务器的绝佳时机,腾讯云有史以来最大优惠,注册即领取500减350卷!云服务器最低2折,1核1G内存50G硬盘1年最低325元!


转自https://www.cnblogs.com/crossoverJie/p/9411012.html

你可能感兴趣的文章
教你用一条SQL搞定跨数据库查询难题
查看>>
09 用MaterialRefreshLayout实现下拉刷新&上拉加载更多(与服务器交互)
查看>>
零基础带你吃掉JNI全家桶(三)
查看>>
前端项目框架搭建随笔---DatePicker组件编写
查看>>
细说equals()方法和hashCode()方法
查看>>
探索iOS内存分配
查看>>
计算机科学中抽象的好处与问题—伪共享实例分析
查看>>
TWEEN动画、JQUERY、ES6 — 2、轮播图-渐隐渐现版本
查看>>
php填坑小记
查看>>
API Token 驗證方式設計
查看>>
青芒 for Mac客户端开发笔记
查看>>
XTCP 一个便捷的TCP消息包拼装和解析框架
查看>>
阿里云态势感知服务(上篇)
查看>>
基于 Spring Boot 2.0 构建一个 RESTful WebService
查看>>
Qtum研究院:以太坊智能合约潜在风险
查看>>
iOS快速集成支付宝
查看>>
Java中的数组
查看>>
git stash 怎么用?
查看>>
如何处理错误消息Please install the gcc make perl packages
查看>>
图片加载之SDWebImage(上)
查看>>