C/C++教程

service & io

本文主要是介绍service & io,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

web服务器

web服务器从概念上来讲, 可以区分成两类, 一类是http服务器, 一类是应用服务器。

http服务器

严格来讲, 叫http服务器不太正确, 因为他也可以是udp, 而http依赖于tcp。此类服务器就是nginx、apache、 node等, 在这里可以认为他们关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能。 访问静态资源, 转发动态请求等。

应用服务器

应用服务器, 即是tomcat, node等。 即支持开发语言的运行时,如tomcat => java, node => js, 简单来说就是能够动态的通过运行我们的业务代码对我们的请求进行处理。

补充

node即可以作为http服务器,也可以作为应用服务器。
tomcat实际上也是用java编写的一个运行程序。

IO模型

这里的讨论限制在linux的环境下。
io分为两个操作, 即

  1. 等待数据准备 (Waiting for the data to be ready)
  2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

阻塞&非阻塞

阻塞: 即io操作时, 在等待数据准备时, IO进程被阻塞了,必须等到数据准备完成后, 这个进程才被唤醒从而进行第二个操作即把数据从内核拷贝到进程中。
非阻塞: 即io操作时, 在等待数据准备时, IO进程通过不断的轮询数据是否准备后, 如果是则进行操作2。

同步&异步

同步: 即当发起io后, 必须等待数据从内核拷贝到进程中, 才继续后面的操作。(进程可以干别的事, 只是对于这个io动作的后系列, 必须等待数据拷贝完成后才可以继续)
异步: 即发起io后, 可以马上进行后面的操作, 当数据拷贝完成后, 会采用回调等方式来通知io进程。(同理于js中的异步操作, 发起http请求后, 可以注册一个函数来等待完成后调用,然后可以马上进行后面的代码块执行, 不必等待http)。

信号驱动

当数据准备完成时, 发起一个信号通知IO线程。
该模型在tcp中几乎无用, 问题在于该信号产生的过于频繁,并且它的出现并没有告诉我们发生了什么事情。下列条件均导致对于一个TCP套接字产生SIGIO信号

  1. 监听套接字上某个连接请求已经完成;
  2. 某个断连请求已经发起;
  3. 某个断连请求已经完成;
  4. 某个连接之半已经关闭;
  5. 数据到达套接字;
  6. 数据已经从套接字发送走
  7. 发生某个异步错误。

多路复用

多路复用即 通过一个进程来监听n个socket, 当某个socket准备完成时, 进程会通知对应的用户进程。 用户进程进行read操作把数据从内核考到自己的进程中。I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

select poll epoll
select,poll每次调用的时候都需要把fd从进程复制到内核, 并不断轮询所有fd集合直到设备就绪, 并且select的fd的个数有限制。
epoll通过拆分步骤完成(三个方法), 只在注册事件时把fd拷贝到内核(即一个fd只复制一次),同时遍历的时候会判断就绪列表是否为空, 只遍历就绪列表(一般来说,未就绪的比就绪的多得多), 并且epoll对于fd没有个数限制。

IO实现

一些服务器or协议的IO实现。

Node

Node的IO是异步非阻塞的, 对于磁盘io,他是通过线程池来实现的, 对于网络io,则是多路复用epoll。 具体参考之前写过的文章Node - 异步IO和事件循环.

nginx

Ng也是通过多路复用的epoll模型来实现的。
主进程:负责执行特权操作,如阅读配置文件、绑定套接字、创建 / 通知协调(Signalling)子进程。 工作进程:负责接收和处理连接请求,读取和写入磁盘,并与上游服务器通信。当 NGINX 处于活跃状态时,只有工作进程是忙碌的,工作进程采用单线程,并以非阻塞的方式处理多个并发连接。即一个工作进程对应多个套接字。

apache

apache也是基于多路复用的模型, 只是在2.4.1之前使用的是select模式, 之后使用的是epoll。

tomcat

tomcat是apache的扩展, 其IO拥有三种模式。 分别是bio,nio,apr。
bio, 即阻塞io, 每请求每线程, 效率最低。
nio, 非阻塞io, 底层的模型也是多路复用。
tomcat的nio有五类组件,分别是(参考文章tomcat nio,链接在最后)

  • LimitLatch是连接控制器,它负责维护连接数的计算,nio模式下默认是10000,达到这个阈值后,就会等待连接请求。
  • Acceptor负责接收连接,默认是1个线程来执行,将请求的事件注册到事件列表
  • Poller来负责轮询上述产生的事件。Poller线程数量是cpu的核数Math.min(2,Runtime.getRuntime().availableProcessors())。由Poller将就绪的事件生成SocketProcessor,然后交给Excutor去执行。
  • SocketProcessor继承了SocketProcessorBase,实现了Runnable接口,可以提交给线程池Excutor来执行。它里面的doRun()方法,封装了读写Socket、完成Container调用的逻辑
  • Excutor线程池是一个Tomcat线程池。用来执行Poller创建的SocketProcessor。Excutor线程池的大小就是我们在Connector节点配置的maxThreads的值。
    apr,apr是从操作系统级别解决异步IO问题,大幅度提高服务器的并发处理性能,也是Tomcat生产环境运行的首选方式,用c重写了模块。

补充

大量的服务器开始使用多路复用的模式,主要在于bio在阻塞情况下虽然不会占用cpu,但是频繁的切换上下文浪费了大量资源,另外bio在并发量不高的情况下, 性能会优于nio。

参考

知乎问题-tomcat 与 nginx,apache的区别是什么?
Linux IO模式及 select、poll、epoll详解
select、poll、epoll之间的区别总结[整理]
NGINX 如何实现高性能和可扩展性
Tomcat nio
深度解读Tomcat中的NIO模型

这篇关于service & io的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!