zoukankan      html  css  js  c++  java
  • 网络编程——C10K简述

    C10K问题
     
    网络服务在处理数以万计的客户端连接时,往往出现效率底下甚至完全瘫痪,这被成为C10K问题。
    (C10K = connection 10 kilo 问题)。k 表示 kilo,即 1000 比如:kilometer(千米), kilogram(千克)。
     
    非阻塞I/O,最关键的部分是readiness notification(when ready, then notify!)和找出哪一个socket上面发生了I/O事件。

    一般我们首先会想到用select来实现。
    int select(int n, fd_set *rd_fds; fd_set *wr_fds, fd_set *ex_fds, struct timeval * timeout);
     
    其中用到了fd_set结构,而fd_set不能大于FD_SETSIZE,默认是1024,很容易导致数组越界。
    针对fd_set的问题,*nix提供了poll函数作为select的一个替代品:

    int poll(struct pollfd *ufds, unsigned int nfds, int timeout)
    第一个参数ufds是用户提供的一个pollfd数组,大小由用户自行决定,因此避免了FD_SETSIZE带来的麻烦。

    然而select和poll在连接数增加时,性能急剧下降。这有两方面的原因:
     
      《1》首先操作系统面对每次的select/poll操作,都需要重新建立一个当前线程的关心事件链表,并把线程挂在这个复杂的等待队列上,这是相当耗时的。
      《2》其次,应用软件在select/poll返回后也需要对传入的句柄链表做一次循环扫描来dispatch,这也是很耗时的。这两件事都是和并发数相关,而I/O事件的密度也和并发数相关,导致CPU占用率和并发数近似成O(n2)的关系。
     

    基于以上原因,*nix的hacker们开发了epoll, kqueue, /dev/poll这3套利器。epoll是Linux的方案,kqueue是freebsd的方案,/dev/poll是solaris的方案。
    简单的说,这些api做了两件事:
     
      《1》避免了每次调用select/poll时kernel分析参素建立事件等结构的开销,kernel维护一个长期的时间关注列表,应用程序通过句柄修改这个链表和捕获I/P事件
      《2》避免了select/poll返回后,应用程序扫描整个句柄表的开销,kernel直接返回具体的链表给应用程序。
     
    在接触具体api之前,先了解一下边缘触发(edge trigger)和条件触发(level trigger)的概念。边缘触发是指每当状态变化时发生一个io事件,假定经过长时间的沉默后,现在来了100个字节,这是无论边缘触发和条件触发都会产生一个read ready notification通知应用程序可读。应用程序在读完来的50个字节,然后重新调用api等待io事件。这时条件触发的api会因为还有50个字节可读从而立即返回用户一个read ready notification。而边缘触发的api会因为可读这个状态没有发生变化而陷入长期等待。

    因此在使用边缘触发的api时,要注意每次都要读到socket返回EWOULDBLOCK为止, 否则这个socket就算废了。而使用条件触发的api时,如果应用程序不需要写就不要关注socket可写的事件,否则会无限次的立即返回一个 write ready nitification. 大家常用的select就是属于条件触发这一类,以前本人翻过长期关注socket写事件从而CPU 100%的毛病。
     
    epoll相关调用:
    int epoll_create(int size);
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
     
    epoll_create 创建kernel中的关注事件表,相当于创建fd_set.
    epoll_ctl 修改这个表,相当与FD_SET等操作。
    epoll_wait 完全是select/poll的升级版,支持的事件完全一致。并且epoll同时支持边缘触发和条件触发,一般来讲边缘触发的性能要好一些。
     
    简单的例子:
    [cpp] view plaincopy
    
            strut epoll_event ev, *events;  
            int kdpfd = epoll_create(100); //创建kernel中的关注事件表,返回一个kernel事件表的句柄  
              
            ev.events = EPOLLIN | EPOLLET; //边缘触发  
            ev.data.fd = listener;   
            epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev); //将事件ev加入到kernel关注的事件表中  
              
            for(;;){  
              
               nfds = epoll_wait(kdpfd, events, maxevents, -1); //等待被通知   
               for(n = 0; n < nfds; n++){  
                  if(events[n].data.fd == listener){  
                     client = accept(listener, (struct sockaddr*)&local, &addrlen);   
                     if(client < 0){  
                        peror("accept");  
                        continue;  
                     }  
              
                     setnonblocking(client);  
                     ev.events = EPOLLIN | EPOLLET;  
                     ev.data.fd = client;  
                     if(epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0){  
                        fprintf(stderr, "epoll set insertion error: fd = %d, client);  
                        return -1;  
                 }  
                 }else  
                     do_use_fd(events[n].data.fd);  
               }  
            } 
  • 相关阅读:
    Recommended Books for Algo Trading in 2020
    Market Making is simpler than you think!
    Top Crypto Market Makers of 2020
    Top Crypto Market Makers, Rated and Reviewed
    爬取伯乐在线文章(五)itemloader
    爬取伯乐在线文章(四)将爬取结果保存到MySQL
    爬取伯乐在线文章(三)爬取所有页面的文章
    爬取伯乐在线文章(二)通过xpath提取源文件中需要的内容
    爬取伯乐在线文章(一)
    爬虫去重策略
  • 原文地址:https://www.cnblogs.com/Simon-xm/p/4093567.html
Copyright © 2011-2022 走看看