33

【架构入门 - 高性能篇】单机高性能

 5 years ago
source link: http://www.wangtianyi.top/blog/2019/04/20/jia-gou-ru-men-gao-xing-neng-pian-dan-ji/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

协作方式

在高并发场景中,必须要让服务器同时维护大量请求连接,可能是一个服务进程创建另一个进程,也可能是一个服务线程去创建另一个线程,但连接结束后进程或线程就销毁了,这是一个巨大的浪费

一个自然的想法就是通过创建一个进程/线程池从而达到资源复用,一个进程/线程可以处理多个连接

那么如何处理多个连接?

同步阻塞

MBrqYvv.jpg!web

一个请求占用一个进程处理,先等待数据准备好,然后从内核向进程复制数据,最后处理完数据后返回

如果一个进程处理一个请求,再来请求再开进程,虽然会有CPU在等待IO时的浪费和进程数量限制,但还是可以做到一定的高性能。如果一个进程处理多个连接,那么其他连接会在第一个连接导致的IO操作时被阻塞,这样无法做到高性能,所以不会选择该模式实现高性能

同步非阻塞

6FFvumJ.jpg!web

进程先将一个套接字在内核中设置成非阻塞再等待数据准备好,在这个过程中反复轮询内核数据是否准备好,准备好之后最后处理数据返回

一个进程处理一个请求不太实际,一个进程处理多个请求的性能上限会更高,所以简单的处理 同步阻塞 中的阻塞问题的方式就是一个进程轮询多个连接,但轮询是有CPU开销的,且如果一个进程有成千上万的连接时效率很低,也不会选择该模式实现高性能

I/O多路复用

UfyY7nz.jpg!web

相当于对 同步非阻塞 的优化版本,区别在于 I/O多路复用 阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。换句话说,轮询机制被优化成通知机制,多个连接公用一个阻塞对象,进程只需要在一个阻塞对象上等待,无需再轮询所有连接

当某条连接有新的数据可以处理时,操作系统会通知进程,进程从阻塞状态返回,开始处理业务,这是高性能的基础,但仍不算高效,因为让一个进程/线程进行select是不够的,还需要某种机制来分配进程/线程去负责监听、处理数据这个两个过程才能实现高性能

Reactor

I/O多路复用结合 线程池 就是 Reactor

Reactor的核心包括Reactor(监听和分配事件)和处理资源池(负责处理事件),具体实现可以多变,体现在:

  • Reactor的数量可以变化
  • 处理资源池的数量可以变化,可以是单个进程/线程,也可以是多个进程/线程

单Reactor单进程/线程

zm6raiJ.jpg!web

  1. Reactor对象通过select监控连接事件,收到事件后通过dispatch分发
  2. 如果是建立连接,交给Acceptor处理,通过accept接收连接,创建一个Handler来处理连接后续的事件
  3. 如果是不是建立连接事件,交给之前建立连接阶段创建的对应的Handler处理

优点是简单,没有进程间通信、竞争,缺点是只有一个进程,无法发挥多核CPU性能,且Handler上处理某个连接的业务时,整个进程无法处理任何其他事件

所以适用场景不多,适合于业务处理非常快的场景,如Redis

单Reactor多线程

feE3Qrq.jpg!web

单Reactor单进程/线程 在于Handler只负责响应事件,业务处理交给Processor,且Processor会在独立的子线程中处理,然后将结果发给主进程的Handler处理

优点是充分发挥了多核CPU的能力,缺点是多线程数据共享复杂,且Reactor承担所有事件的监听和响应,高并发会成为瓶颈

多Reactor多进程/线程

MNNviuN.jpg!web

为了解决 单Reactor多线程 的问题,这个模式的区别:

  1. 父进程的select监听到连接建立事件后通过Acceptor将新的连接分配给子进程
  2. 子进程的Reactor将新的连接加入自己的连接队列进行监听,并创建一个Handler用于处理连接的事件
  3. 当有新的事件发生,子Reactor会调用连接的Handler
  4. Handler完成read->业务处理->send的业务流程

看起来比 单Reactor多线程 更复杂,但实现更简单,因为:

  1. 父进程只负责接收并建立新连接,子进程只负责业务处理
  2. 父子进程之间的交互只有父进程把连接交给子进程,子进程不需要把结果返回给父进程

Nginx、Memcache、Netty使用的就是该模式

Proactor

实践方式

高性能的代码

性能 日志

合适的服务器

规格 配置

参考

号外号外

最近在总结一些针对 Java 面试相关的知识点,感兴趣的朋友可以一起维护~

地址: https://github.com/xbox1994/2018-Java-Interview


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK