Selector作用
关于套接字编程,有一套经典的IO模型需要提前介绍一下:.
同步IO模型:
阻塞式IO模型
非阻塞式IO模型
IO复用模型 使用selector
信号驱动式IO模型
异步IO模型
使用aio_read
thrift里面用到IO模型就是IO复用模型,《Unix网络编程》一书中说它是同步IO模型,selector用法是阻塞的。
实际上selector模型中的套接字可以通过如下方式设定位非阻塞方式。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
Selector就是一个事件选择器。是个操作系统内核实现的套接字连接选择器。
早期的linux系统是用poll模式,新的模式是epoll。
目前用C开发套接字编程仍然可以使用这两种方式。
而用JDK开发套接字编程时,linux JDK默认使用epoll作为selector的事件触发机制。
Thrift是如何使用Selector
thirft里面使用了两个Selector,一个是监听socket连接是否进来
另外一个是监听socketChanell是否可读或者可写。
第一处TThreadedSelectorServer.AcceptThread
/** * Select and process IO events appropriately: If there are connections to * be accepted, accept them. */ private void select() { try { // wait for connect events. acceptSelector.select(); // process the io events we received Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); // skip if not valid if (!key.isValid()) { continue; } if (key.isAcceptable()) { handleAccept(); } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } }
这个selector主要用来接受客户端发过来的tcp连接请求,每当进来一个连接,就用handleaccept方法分配到一个线程里面去处理,
将对应socketchannel加入BlockingQueue进行缓冲。然后在消费线程run里面慢慢消费处理。
第二处:TThreadedSelectorServer.SelectorThread
/** * Select and process IO events appropriately: If there are existing * connections with data waiting to be read, read it, buffering until a * whole frame has been read. If there are any pending responses, buffer * them until their target client is available, and then send the data. */ private void select() { try { // wait for io events. selector.select(); // process the io events we received Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); // skip if not valid if (!key.isValid()) { cleanupSelectionKey(key); continue; } if (key.isReadable()) { // deal with reads handleRead(key); } else if (key.isWritable()) { // deal with writes handleWrite(key); } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } }
第二处selector是用来监听已经进来的连接是否可读可写的,并进行对应处理。
thirft高性能的原因
thrift之所以能在高并发时维持高性能,我认为主要是因为:
1、使用了同步非阻塞IO
2、使用了多线程和队列来处理套接字并发连接
3、支持数据压缩,节省带宽
4、代码封装得比较成熟,比如一些channel抽象还有processor的封装等。
5、易用性。支持跨语言,使用简单方便
目前thrift在hadoop大部分生态组件中都广泛使用到了。