HTML5技术

Linux网络编程“惊群”问题总结 - Ankers Blog

字号+ 作者:H5之家 来源:H5之家 2017-06-24 18:03 我要评论( )

1、前言 我从事Linux系统下网络开发将近4年了,经常还是遇到一些问题,只是知其然而不知其所以然,有时候和其他人交流,搞得非常尴尬。如今计算机都是多核了,网络编程框架也逐步丰富多了,我所知道的有多进程、多线程、异步事件驱动常用的三种模型。最经典

1、前言

  我从事Linux系统下网络开发将近4年了,经常还是遇到一些问题,只是知其然而不知其所以然,有时候和其他人交流,搞得非常尴尬。如今计算机都是多核了,网络编程框架也逐步丰富多了,我所知道的有多进程、多线程、异步事件驱动常用的三种模型。最经典的模型就是Nginx中所用的Master-Worker多进程异步驱动模型。今天和大家一起讨论一下网络开发中遇到的“惊群”现象。之前只是听说过这个现象,网上查资料也了解了基本概念,在实际的工作中还真没有遇到过。今天周末,结合自己的理解和网上的资料,彻底将“惊群”弄明白。需要弄清楚如下几个问题:

(1)什么是“惊群”,会产生什么问题?

(2)“惊群”的现象怎么用代码模拟出来?

(3)如何处理“惊群”问题,处理“惊群”后的现象又是怎么样呢?

2、何为惊群

  如今网络编程中经常用到多进程或多线程模型,大概的思路是父进程创建socket,bind、listen后,通过fork创建多个子进程,每个子进程继承了父进程的socket,调用accpet开始监听等待网络连接。这个时候有多个进程同时等待网络的连接事件,当这个事件发生时,这些进程被同时唤醒,就是“惊群”。这样会导致什么问题呢?我们知道进程被唤醒,需要进行内核重新调度,这样每个进程同时去响应这一个事件,而最终只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠或其他。网络模型如下图所示:

简而言之,惊群现象(thundering herd)就是当多个进程和线程在同时阻塞等待同一个事件时,如果这个事件发生,会唤醒所有的进程,但最终只可能有一个进程/线程对该事件进行处理,其他进程/线程会在失败后重新休眠,这种性能浪费就是惊群。

3、编码模拟“惊群”现象

  我们已经知道了“惊群”是怎么回事,那么就按照上面的图编码实现看一下效果。我尝试使用多进程模型,创建一个父进程绑定一个端口监听socket,然后fork出多个子进程,子进程们开始循环处理(比如accept)这个socket。测试代码如下所示:

1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <assert.h> 8 #include <sys/wait.h> 9 #include <string.h> 10 #include <errno.h> IP "127.0.0.1" 13 #define PORT 8888 14 #define WORKER 4 worker(int listenfd, int i) 17 { 18 while (1) { , i); 20 struct sockaddr_in client_addr; 21 socklen_t client_addrlen = sizeof( client_addr ); 22 int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen ); 23 if (connfd != -1) { , i); ,inet_ntoa(client_addr.sin_addr)); ,client_addr.sin_port); 27 } else { , i, strerror(errno));
         close(connfd);
29 } 30 } ; 32 } main() 35 { 36 int i = 0; 37 struct sockaddr_in address; 38 bzero(&address, sizeof(address)); 39 address.sin_family = AF_INET; 40 inet_pton( AF_INET, IP, &address.sin_addr); 41 address.sin_port = htons(PORT); 42 int listenfd = socket(PF_INET, SOCK_STREAM, 0); 43 assert(listenfd >= 0); ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address)); 46 assert(ret != -1); 47 48 ret = listen(listenfd, 5); 49 assert(ret != -1); (i = 0; i < WORKER; i++) { , i+1); 53 pid_t pid = fork(); (pid == 0) { 56 worker(listenfd, i); 57 } (pid < 0) { ); 61 } 62 } status; 66 wait(&status); ; 68 }

编译执行,在本机上使用telnet 127.0.0.1 8888测试,结果如下所示:

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 如何实现在Windows上运行Linux程序,附示例代码 - q303248153

    如何实现在Windows上运行Linux程序,附示例代码 - q303248153

    2017-05-16 14:00

  • Linux使用Jexus托管Asp.Net Core应用程序 - 玩双截棍的熊猫

    Linux使用Jexus托管Asp.Net Core应用程序 - 玩双截棍的熊猫

    2017-05-15 08:06

  • 使用Visual Studio 2017作为Linux C++开发工具 - 星夜落尘

    使用Visual Studio 2017作为Linux C++开发工具 - 星夜落尘

    2017-03-12 14:01

  • Linux系统(一)文件系统、压缩、打包操作总结 - 张龙豪

    Linux系统(一)文件系统、压缩、打包操作总结 - 张龙豪

    2017-02-23 16:00

网友点评