20211128Java高并发002
Linux 默认支持多少 fd
在 Linux 下,通过调用 ulimit 可以看到一个进程可以打开的最大文件句柄数量。因为在 Linux 下面一切皆文件,所以无论是数据库,还是文件,还是线程,还是 socket 都是对应的 fd 在,因此我们频繁的开启和删除线程也是对 fd 的一种浪费,可能会达到最大 fd 数量限制而抛出异常。
可以通过编辑 Linux 的极限配置文件 /etc/security/limits.conf 来配置支持更多的 fd, 但是这个一般应用也做不到这个权限,即使能做到这种黑招也不是什么好招。
NIO 和 OIO 的对比
OIO 阻塞,是面向流,而 NIO 是非阻塞的。NIO 也可以称为 no blocking io。
在使用 NIO 的 ByteBuffer 的时候是有读写两种模式的,在写入之后不能直接读取,只有调用 flip 等方法翻转,才能进入读模式去读取。
选择器与注册,选择器就是 selector 底层依赖的是 select 或者 epoll ,在 nio 中有一个通道的概念,通过这个通道就可以桥接几个不同的读写端去配合,然后进行 io 的读取和写入等等,在 NIO 的设计中,通道身上带有一个 register 方法,通过这个方法可以将对应的 channel 注册到这个 selector 上面,后面就是对应的操作 ops ,例如写入,建立连接,读取等等。(刚刚看了源码,我的是 JDK 1.8 的,对应的是 AbstractSelector 提供了 注册 channel 到 selector 的方法,本来想看看 JDK17 的代码,但是 code 不给力,先继续写文章)
SPI 服务发现
Java 有一种机制称为 SPI, 也就是 Service provider interface ,服务提供者接口,是一种可以扩展的服务提供和发现机制,在选择器模型中,就可以自定义选择器提供者的 SPI,底层可以是 select 和 epoll 也可以是更高级的实现。
Reactor 反应器模式
Reactor 反应器模式的核心是 reactor 和各种 Handler 之间的交互,他们之间可以使用一个分发器去分发,这样就可以在不同的场景使用不同的 handler 去处理事情,这里会在对应的 实例上进行 attach 和 get 的动作,这个是为了为下面驱动流程继续进行的一种设计。
这种设计的优化版本就是多线程处理,这里可以把不同种类的 handler 分发到不同的线程去处理,然后再进行提速,这里同时也提高几个 selector ,比如有的专门进行负责连接的建立和断开,一个专门负责 IO 相关的处理,同时数据传输和业务处理也都放到不同的线程池中,
总结
通过这个 Reactor 的构成发现他也是一个逐步完善的过程,知道问题的核心是什么,述求是什么,那么就围绕着这个目的去设计对应的处理方式,例如提高并发,那么就根据不同的角色从不同的维度去提高并发,例如这里的 selector 和 handler 都是可以支持并发的,那这样就可以达到我们的目的。