【我和天涯的故事】寄十七岁的天涯

小说:【我和天涯的故事】寄十七岁的天涯作者:侯戏安更新时间:2019-03-27字数:39643

“好,我马上派兵来增援!”韩非只得这样先答应他,只有这样,黄军长他们的桂军阵地才不会出现崩溃的,宋家山坳那里的位置相当重要,千万不能崩溃,否则整个马当要塞将在劫难逃!

面对问题疫苗,我们怎么办?

裴宽的寿礼在裴府大堂里举行,这是一座足以容纳数千人的大堂,气势宏大,此时天还没有完全亮,大堂的灯笼已经熄灭了一半,光线显得有些昏暗,一百名裴 家子弟正忙碌地布置会场,几千张矮桌已经摆好,每张矮桌可坐两人,桌上已经摆上了鲜花和果蔬,上面还沾着清晨的露珠,这次裴家的菜肴将有长安最著名的十家大酒肆提供,菜肴将在中午时分陆续送到,而此时裴家要先进行会场的布置。
“那日我与比比东一战的情况大家都应该看到了,凭借着海神三叉戟,嘉陵关厚实的城墙并不算什么,只要给我充足的时间,我甚至可以将他们的城墙完全破坏。我的第二个办法就是,由在座的各位保护我,在近距离攻击嘉陵关城墙,我相信,我破坏的速度一定会比他们建设要快。这样做,不但有可能吸引对方魂师军团主动出击,同时也会为我们未来的总攻铺路。”

唐僧直往后退,悟空心中纳闷,老君已说过唐僧乃是真阳之身,女王是纯阴之体,怎的唐僧见了女王,丝毫没有感觉。

    关于I/O模型的文章比较多,参考多篇后理解上仍然不太满意,终需自己整理一次,也是编写高吞吐量高性能网络接口模块的基础。这里所说的主要针对网络I/O,近几年面对越来越大的用户请求量,如何优化这些步骤直接影响接口用户体验。

一、前言

 I/O模型有几个名词的解释 (比较容易混淆):

阻塞与非阻塞:区别在于调用函数时,是否立即返回还是让线程等待。阻塞模型需要等待操作完成,而非阻塞模型则是立即返回(未准备好则返回一个错误码)。

同步与非同步:区别在于网络数据从内核拷贝到用户空间时是否需要用户线程参与等待。

 

UNIX 网络I/O模型分类有几种 (参考 UNIX Network Programming Volume.1.3rd.Ed):

1.  Blocking I/O
2.  Nonblocking I/O
3.  I/O multiplexing (select and poll)
4.  Signal driven I/O (SIGIO)
5.  Asynchronous I/O (the POSIX aio_functions)

 

它们有两个独特的阶段差别 :

1. 等待数据准备好.  (阻塞/非阻塞的差异)

2. 数据从内核复制到用户空间.  (同步/非同步的差异)

理解上面2个阶段,对后面的解释就很容易明白。

 

二、I/O 模型详解

2.1 Blocking I/O

    下图以UDP服务端调用 recvfrom为例描述线程等待过程 (TCP稍微复杂):

主处理线程一直阻塞到有用户数据,并且数据从内核拷贝到用户空间,也就是说主处理线程同一时间只能处理一个用户请求:

伪代码类似如下:

while (1)
{
   Socket clientSock = serverSock.accept();
   processRequest(clientSock);
}

void processRequest(Socket clientSock)
{
read(...);
write(...);
}

    在复杂的网络环境中,经常出现一个“慢速”客户端。也就是说这个客户端数据到达很慢,在TCP网络编程中,如果以此方式等待足够的数据(根据协议定义),则会严重影响到其他客户端的处理等待时间。

    由此进行改善的模型就是使用线程池,伪代码如下:

while (1)
{
   Socket clientSock = serverSock.accept();
   threadPool.execute(new Task(processRequest(clientSock));
}

void processRequest(Socket clientSock)
{
    read(...);
    write(...);
}

线程池模式使得主线程能处理更多的客户端,N个客户端使用M个线程(N:M),主线程不会被一个慢客户端阻塞,但是处理能力仍然是比较有限的。

 

2.2 Nonblocking I/O

    将socket设置为非阻塞模式,告诉内核如果操作不能立即完成则返回一个错误码而不是等待,描述图如下:

如下图,主线程一直尝试调用网络函数,直到数据准备好,该模型的缺陷就是"忙等待",CPU空转浪费系统资源。此模式极少使用,在此仅用于介绍

 

 

 

2.3 I/O multiplexing (多路复用)

     Linux 中提供select/poll系统调用实现,线程阻塞在此方法上,监测多个socket fd(file descriptor)是否就绪,一旦有事件发生则顺序扫描具体是哪个就绪,但这样做会比较费时,模型如下:

 

 

在Linux kernel 2.6+提供了epoll实现,使用驱动方式替代顺序扫描,哪个fd有事件发生就返回哪一个(避免了select/poll的只要有一个发生就扫描全部找出是哪个,新的内核也可能对这模式实现做了一些优化)。

在ORACLE JDK有如下源码对内核做判断:

package sun.nio.ch;
import ...
public class DefaultSelectorProvider {
  public static SelectorProvider create() {
    ......

    if ("Linux".equals(str1)) {
      String str2 = (String)AccessController.doPrivileged(new GetPropertyAction("os.version"));

      String[] arrayOfString = str2.split("\.", 0);
      if (arrayOfString.length >= 2) {
        try {
          int i = Integer.parseInt(arrayOfString[0]);
          int j = Integer.parseInt(arrayOfString[1]);
          if ((i > 2) || ((i == 2) && (j >= 6))) {
            return new EPollSelectorProvider();
          }
        } catch (NumberFormatException localNumberFormatException) {}
      }
    }
    return new PollSelectorProvider();
  }
}

关于epoll的实现有专门文章详解,在此不做细说,另外一个epoll使用mmap内存映射避免内存复制损耗。 

 

2.4 Asynchronous I/O (异步)

异步I/O告诉内核开始某个操作,在内核完成后(包括数据从内核拷贝到用户空间)通知我们,如下图:

 

在ORACLE JDK 1.7+提供异步I/O的实现AsynchronousChannel,相对于原Selector实现I/O复用模式简单很多。

在这几种模型中,只有异步I/O是用户线程不参与数据从内核拷贝到用户空间这个过程。

 

2.5 几种I/O模型的对比

如下图,它们在 [等待数据] 和 [从内核复制数据到用户空间] 的差异:

从上图看出,只有Asynchronous I/O不参与内核数据复制。

 

 

三、实例举例说明

3.1 针对BIO拒绝服务攻击

    BIO中Boss线程处理请求后交给连接池Worker处理,但线程池有限,能轻易导致服务异常。比如针对Tomcat默认配置进行HTTP慢速攻击:

声明一个Content-Length为300的POST包,开启300个线程请求,每个请求中每秒发送1 byte。默认情况下Tomcat的200个线程将爆满,在持续攻击的这300秒内,都无法正常处理其他正常用户请求,造成服务异常。

改善方法:将连接器使用NIO处理,修改默认配置protocol为Http11NioProtocol,并且增大合适的线程数,对此类攻击有一定的缓解作用。

 

3.2  对外接口服务应用

    如果使用Java开发对外接口服务,在对响应时延和吞吐量有一定要求的话,通常可以考虑集成Netty (NIO),如果遵循Servlet标准可考虑集成Jetty使用NIO连接器处理。在协议方面,内部服务可选择高效的私有协议和高压缩比的组件(如protobuf / kryo等),对外服务可选择HTTP+JSON。

    WEB服务可考虑动静分离,html/css/js/image等文件使用Nginx (sendfile提供更高效的数据传输方式)处理,业务数据才透到后端服务中,后端服务加入缓存等方式减少响应时延。

 

3.3 关于JDK中一些名词解析

在JDK提供的NIO API与这里介绍的NIO(Nonblocking I/O)模型是不同的概念,JDK在1.7之前提供的Selector基于select/poll、epoll(Linux kernel > 2.6)实现I/O复用技术的非阻塞IO,JDK1.7以后提供的NIO2.0 API (如AsynchronousServerSocketChannel) 才是真正的异步I/O。

 

 

参考资料:

1.  http://www.madwizard.org/programming/tutorials/netcpp/5

2.  http://www.importnew.com/22019.html

3. 《UNIX Network Programming Volume.1.3rd.Edition》

4.《Netty权威指南》

 

编辑:海宗

发布:2019-03-27 00:26:31

当前文章:http://www.cnsdbtzg.com/content/2019-01/18/content_37343.html

我和老公情感上密不可分,肉体上却麻木不仁 从个案看幼小衔接需要做什么 怀念谷歌 - 缘于深恶痛觉百度的唯利是图 护肤必备手册之(一)日护理 论多引力空间——理论假说 南昌购房夫妻双方落户详细程序和材料 传奇影星夏梦:一生光影梦,绝代有佳人 “婚姻是爱情坟墓”是肤浅的人的专利

19478 68611 86936 14917 72358 30464 99904 12575 19400 87673 94754 71428 69883 83981 76254 58734 16685 84283 67661 86089

我要说两句: (0人参与)

发布