关于select:一文说透IO多路复用selectpollepoll

概述如果咱们要开发一个高并发的TCP程序。惯例的做法是:多过程或者多线程。即:应用其中一个线程或者过程去监听有没有客户端连贯上来,一旦有新客户端连贯,就新开一个线程,将其扔到线程(或过程)中去解决具体的读写操作等业务逻辑,主线程(过程)持续期待,监听其余的客户端。 这样操作往往存在很大的弊病。首先是浪费资源,要晓得,单个过程的最大虚拟内存是4G,单个线程的虚拟内存也有将近8M,那么,如果上万个客户端连贯上来,服务器将会承受不住。 其次是浪费时间,因为你必须始终等在accept那个中央,非常被动。 上述的网络模型,其实说白了,就是一个线程一路IO,在单个线程里只能解决一个IO。因而,也可称之为单路IO。而一路IO,就是一个并发。有多少个并发,就必须要开启多少个线程,因而,对资源的节约是显而易见的。 那么,有没有一种形式,能够在一个线程里,解决多路IO呢? 咱们回顾一下多线程模型 ,它最大的技术难点是accept和recv函数都是阻塞的。只有没有新连贯上来,accept就阻塞住了,无奈解决后续的业务逻辑;没有数据过去,recv又阻塞住了 ,无奈解决新的accept申请。因而,只有可能搞定在同一个线程里同时accept和recv的问题,仿佛所有问题就迎刃而解了。 有人说,这怎么可能嘛?必定要两个线程的 。 还真有可能,而这所谓的可能 ,就是IO多路复用技术。 IO多路复用所谓的IO多路复用,它的核心思想就是,把监听新客户端连贯的操作转包进来,让零碎内核来做这件事件。即由内核来负责监听有没有连贯建设、有没有读写申请 ,作为服务端,只须要注册相应的事件,当事件触发时,由内核告诉服务端程序去解决就行了。 这样做的益处不言而喻:只须要在一个主线程里,就能够实现所有的工作,既不会阻塞,也不会节约太多资源。 说得通俗易懂一些,就是原来须要由主线程干的活,当初都交给内核去干了。咱们不必阻塞在accept和recv那里,而是由内核通知程序,有新客户端连贯上来了 ,或者有数据发送过去了,咱们再去调用accept和recv就行了,其余工夫,咱们能够解决其余的业务逻辑。 那么有人问了,你不还是要调用accept和recv吗?为什么当初就不会阻塞了呢 ? 这就要深刻说一下listen和accept的关系了。 如果服务器是海底捞火锅店的话,listen就是门口迎宾的小姐,当来了一个客人(客户端),就将其迎进店内。而accept则是店内的大堂经理 ,当没人来的时候,就始终闲在那里没事做,listen将客人 迎进来之后,accept就会调配一个服务员(fd)专门 服务于这个客人 。 所以说,只有listen失常工作,就能源源不断地将客人迎进饭店(客户端能 失常连贯上服务器),即便此时并没有accept。那么,有人必定有疑难,总不能始终 往里迎吧,酒店也是有大小的,全副挤在大堂也装不下 那么多人啊。还记得 listen函数的第二个参数backlog吗?它就示意在没有accept之前,最多能够迎多少个客人进来。 因而,对于多线程模型来说,accept作为大堂经理,在 没客人来的时候 ,就眼巴巴地盯着门口 ,啥也不干,当listen把人迎进来了,才开始干活。只能说,摸鱼,还是accpet会啊。 而IO多路复用,则相当于请了一个秘书。accept作为大堂经理,必定有很多其余事件能够忙,他就不必 始终盯着门口,当listen把人迎进来之后,秘书就会把客人(们)带到经理身边,让经理安顿服务员(fd)。 只是这个秘书是内核提供的,因而不仅收费,而且勤快。收费的劳动力 ,何乐而不为呢? 它的流程图大略是上面这样子的: 咱们通常所说的IO多路复用技术,在Linux环境下,次要有三种实现,别离为select、poll 和 epoll,以及io_uring。在darwin平台 ,则有kqueue,Windows 下则是 iocp。从性能上来说,iocp要优于epoll,与io_uring并驾齐驱。但select、poll、epoll的演变是一个继续迭代的过程,虽说从效率以及应用普及率上来说,epoll堪称经典,但并不是另外两种实现就毫无用处,也是有其存在的意义的,尤其是select。 本文不会花太多笔墨来介绍kqueue,笔者始终认为,拿MacOS作为服务器开发,要么脑子瓦特了,要么就是钱烧的。基本上除了本人写写 demo外,极少能在生产环境真正用起来。而iocp自成一派,将来有暇,将专门开拓专题细说。io_uring作为较新的内核才引入的个性,本文也不宜大肆开展。 唯有select、poll 以及epoll,久经工夫考验,已被宽泛使用于各大出名网络应用,并由此诞生出许多经典的网络模型,切实是值得好好细说。 select原型select函数原型: /* According to POSIX.1-2001, POSIX.1-2008 */ #include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);参数阐明: ...

April 14, 2023 · 6 min · jiezi

关于select:彻底搞懂IO多路复用

上一篇文章以近乎啰嗦的形式详细描述了BIO与非阻塞IO的各种细节。如果各位还没有读过这篇文章,强烈建议先浏览一下,而后再来看本篇,因为逻辑关系是层层递进的。 1. 多路复用的诞生非阻塞IO应用一个线程就能够解决所有socket,然而付出的代价是必须频繁调用零碎调用来轮询每一个socket的数据,这种轮询太消耗性能,而且大部分轮询都是空轮询。 咱们心愿有个组件能同时监控多个socket,并在socket把数据筹备好的时候通知过程哪些socket已“就绪”,而后过程只对就绪的socket进行数据读写。 Java在JDK1.4的时候引入了NIO,并提供了Selector这个组件来实现这个性能。 2. NIO在引入NIO代码之前,有点事件须要解释一下。 “就绪”这个词用得有点暧昧,因为不同的socket对就绪有不同的表白。比方对于监听socket而言,如果有客户端对其进行了连贯,就阐明处于就绪状态,它并不像连贯socket一样,须要对数据的收发进行解决;相同,连贯socket的就绪状态就至多蕴含了数据筹备好读(is ready for reading)与数据筹备好写(is ready for writing)这两种。 因而,能够设想,咱们让Selector对多个socket进行监听时,必然须要通知Selector,咱们对哪些socket的哪些事件感兴趣。这个动作叫注册。 接下来看代码。 public class NIOServer { static Selector selector; public static void main(String[] args) { try { // 取得selector多路复用器 selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 监听socket的accept将不会阻塞 serverSocketChannel.configureBlocking(false); serverSocketChannel.socket().bind(new InetSocketAddress(8099)); // 须要把监听socket注册到多路复用器上,并通知selector,须要关注监听socket的OP_ACCEPT事件 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // 该办法会阻塞 selector.select(); // 失去所有就绪的事件,事件被封装成了SelectionKey Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if (key.isAcceptable()) { handleAccept(key); } else if (key.isReadable()) { handleRead(key); } else if (key.isWritable()) { //发送数据 } } } } catch (IOException e) { e.printStackTrace(); } } // 解决「读」事件的业务逻辑 private static void handleRead(SelectionKey key) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer allocate = ByteBuffer.allocate(1024); try { socketChannel.read(allocate); System.out.println("From Client:" + new String(allocate.array())); } catch (IOException e) { e.printStackTrace(); } } // 解决「连贯」事件的业务逻辑 private static void handleAccept(SelectionKey key) { ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); try { // socketChannel肯定是非空,并且这里不会阻塞 SocketChannel socketChannel = serverSocketChannel.accept(); // 将连贯socket的读写设置为非阻塞 socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap("Hello Client, I am Server!".getBytes())); // 注册连贯socket的「读事件」 socketChannel.register(selector, SelectionKey.OP_READ); } catch (IOException e) { e.printStackTrace(); } }}咱们首先应用Selector.open();失去了selector这个多路复用对象;而后在服务端创立了监听socket,并将其设置为非阻塞,最初将监听socket注册到selector多路复用器上,并通知selector,如果监听socket有OP_ACCEPT事件产生的话就要通知咱们。 ...

January 31, 2023 · 8 min · jiezi

关于select:React中select设置defaultValue不生效

问题这样一种写法下,defaultValue是不会失效的。 function Test() { const [dv, setDv] = useState(0) // 在某个元素的点击回调中setDv() // foo.onClick = () => {setDv(bar)} return( <select defaultValue={dv} > <option value='1' >1</option> <option value='2' >2</option> <option value='3' >3</option> </select> )}解决给<select/>加个动静key强制执行渲染。 function Test() { const [dv, setDv] = useState(0) // 在某个元素的点击回调中setDv() // foo.onClick = () => {setDv(bar)} return( <select key={Date.now()} defaultValue={dv} > <option value='1' >1</option> <option value='2' >2</option> <option value='3' >3</option> </select> )}

January 21, 2023 · 1 min · jiezi

关于select:GrowingIO-Design-组件库搭建之Select组件

前言Select 是最频繁应用的UI组件之一,它能够使用在很多场景。大多数状况下,原生HTML的<Select>标签无奈满足业务的性能过需要,以及原生HTML的<Select>标签在各个浏览器版本里款式体现不太一样。在这样的状况下,少数人都会抉择实现一个合乎UI要求以及产品性能需要的Select组件,或者抉择应用一些开源组件库提供的 Select 组件。本文次要梳理了 gio-design 中的Select组件在实现过程中遇到的一些妨碍及须要留神的中央,心愿能对大家在设计和实现select组件时提供一些帮忙。 数据源(dataSource)Select 组件的两种应用办法: // 第一种写法const options = [{label:'a',value:'a'},{label:'b',value:''b}];<Select options={options} />// 第二种<Select> <Select.Option value={'a'} >a</Select.Option> <Select.Option value={'b'} >b</Select.Option></Select>应用 Select 组件的时候,个别状况下,有两种形式来设定 dataSource。 通过 options 参数,传入纯数据格式。JSX,通过拦挡子组件 <Select.Option/> 的参数,转化为 nodeOptions。相比拟 JSX 而言,options 参数模式领有更好的性能( JSX 形式最终也会转为相似 options 参数的模式)。该转换形式借鉴了rc-select 中的写法。export function convertChildrenToData(nodes: React.ReactNode, group = {}): Option[] { let nodeOptions: Option[] = []; React.Children.forEach(nodes, (node: React.ReactElement & { type: { isSelectOptGroup?: boolean }; props: OptionProps }) => { if (!React.isValidElement(node)) return; const { type: { isSelectOptGroup }, props: { children, label, value }, } = node; if (!isSelectOptGroup) { // option nodeOptions.push(convertNodeToOption(node, group)); } else { // Group nodeOptions = concat(nodeOptions, convertChildrenToData(children, { groupLabel: label, groupValue: value })); } }); return nodeOptions;}// ReactNode To Optionsexport function convertNodeToOption(node: React.ReactElement, group: group): Option { const { props: { value, children, ...restProps }, } = node as React.ReactElement & { props: OptionProps }; const { groupValue, groupLabel } = group; if (groupLabel && groupLabel) { return { value, label: children !== undefined ? children : value, groupValue, groupLabel, ...restProps }; } return { value, label: children !== undefined ? children : value, ...restProps };}Group 和 Option 的定义: ...

July 5, 2021 · 5 min · jiezi