1. 程式人生 > >伺服器端預先建立子程序(work)同時監聽服務埠和驚群現象

伺服器端預先建立子程序(work)同時監聽服務埠和驚群現象

背景

原文連結: http://blog.csdn.net/ordeder/article/details/21721141

1.程序A在n埠上監聽,即呼叫listen(listenfd,backlog);

2.之後A呼叫fork產生子程序B,此時B拷貝了A的listenfd,該描述符使用的是相同的“檔案表項”(具體參考 http://blog.csdn.net/ordeder/article/details/21716639)

3.那麼A程序和B程序將共享一個socket,具體圖解如下:


驚群現象        

        在該模型下(多個子程序同時共享監聽套接字)即可實現伺服器併發處理客戶端的連線。這裡要注意的是,計算機三次握手建立連線是不需要服務程序引數的,而服務程序僅僅要做的事呼叫accept將已建立的連線構建對應的連線套接字connfd(可參考 http://blog.csdn.net/ordeder/article/details/21551567)。多個服務程序同時阻塞在accept等待監聽套接字已建立連線的資訊,那麼當核心在該監聽套接字上建立一個連線,那麼將同時喚起這些處於accept阻塞的服務程序,從而導致“驚群現象”的產生,喚起多餘的程序間影響伺服器的效能(僅有一個服務程序accept成功,其他程序被喚起後沒搶到“連線”而再次進入休眠)。

例項分析

服務程序:三個服務程序同時監聽一個埠,程式碼如下:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>       /* basic system data types */
#include<sys/socket.h>      /* basic socket definitions */
#include<netinet/in.h>      /* sockaddr_in{} and other Internet defns */
#include<arpa/inet.h>       /* inet(3) functions */
#include<sys/epoll.h>           /* epoll function */
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<sys/select.h>

#define WORKERSIZE 3

void waitall()
{
        pid_t cpid;
        while(1)
        {
           cpid = wait(NULL);
           if(cpid==-1){
	       perror("end of wait");
               break;
           }
           printf("worker pid#%d exit...\n",cpid);
        }
}

void worker_hander(int listenfd)
{
        fd_set rset;
        int cnt = 100,connfd,rc;
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec=0;

        printf("worker pid#%d is waiting for connection...\n",getpid());

        while(1)
        {
                FD_ZERO(&rset);
                FD_SET(listenfd,&rset);

                //rc = select(listenfd+1,&rset,NULL,NULL,&tv);//設定為非阻塞狀態
		rc = select(listenfd+1,&rset,NULL,NULL,NULL);//設定為阻塞

                if(rc == -1) perror("select");
 		else if(rc>0 && FD_ISSET(listenfd,&rset))
                {
			//sleep(1);//第四種,讓三個程序都有足夠的時間資源喚起(防止可能出現某個程序已近開始進行accept結束了,另一個程序還未被喚起(排程的問題))
                        printf("worker pid#%d 's listenfd is readable\n",getpid(),rc);
                        connfd = accept(listenfd,NULL,0);
                        if(connfd == -1)
                        {
                                perror("accept error");
                                continue;
                        }
                        printf("worker pid#%d create a new connection...\n",getpid());
                        sleep(1);
                        close(connfd);
                }
        }
}

int main(int argc,char*argv[])
{
        int listenfd,connfd;
        struct sockaddr_in cliaddr,servaddr;
        int queuelen=5,i,flag;
        pid_t cpid[WORKERSIZE];

        listenfd = socket(AF_INET,SOCK_STREAM,0);

	//此處設定listenfd的為阻塞
        /*flag = fcntl(listenfd,F_GETFL,0);
        fcntl(listenfd,F_SETFL,flag|O_NONBLOCK);*/

        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET,"172.20.52.140",&servaddr.sin_addr);
        //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(2989);

        bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));

        listen(listenfd,queuelen);

        for(i=0;i<WORKERSIZE;i++)
        {
                cpid[i]=fork();
                if(cpid[i] == -1){
                        perror("fork error");
                        waitall();
                        exit(0);
                }
                if(cpid[i]==0){
                        worker_hander(listenfd);
                        exit(0);
                }
        }
        waitall();
	return 0;
}

不同設定的結果輸出:

一個客戶端發起連結,而服務端有三個服務程序在監聽同一埠,從而核心會喚起所有處於監聽該埠的服務程序,從而導致驚群現象的發生。服務端設定不通的阻塞情況,可得如下不同的結果:

一個客戶端發起連結,而服務端有三個服務程序在監聽同一埠。服務端設定不通的阻塞情況,可得如下不同的結果。
1.select設定為阻塞,listenfd設定為阻塞或非阻塞,結果如下:
worker pid#25583 is waiting for connection...
worker pid#25584 is waiting for connection...
worker pid#25585 is waiting for connection...
worker pid#25585 's listenfd is readable
worker pid#25585 create a new connection...
分析:三個服務程序被喚起,25585第一個排程所以執行accept,所以此時連線被取走,其他兩個程序之後開始被排程,而此時連線數目為0,又進入隨眠...

2.select設定為非阻塞,listenfd設定為阻塞,結果如下:
worker pid#25743 is waiting for connection...
worker pid#25744 is waiting for connection...
worker pid#25745 is waiting for connection...
worker pid#25745 's listenfd is readable
worker pid#25744 's listenfd is readable
worker pid#25743 's listenfd is readable
worker pid#25745 create a new connection...
分析:三個服務程序都select都成功返回可讀的套接字,從而各個程序都喚起處於阻塞的accept,但是隻有一個程序建立連結,其餘兩個程序沒有獲取到連結而又進入睡眠...

3.select設定為非阻塞,listenfd設定為為非阻塞,結果如下:
worker pid#25240 is waiting for connection...
worker pid#25241 is waiting for connection...
worker pid#25242 is waiting for connection...
worker pid#25242 's listenfd is readable
worker pid#25240 's listenfd is readable
worker pid#25241 's listenfd is readable
worker pid#25242 create a new connection...
accept error: Resource temporarily unavailable
accept error: Resource temporarily unavailable
分析:三個服務程序都進入accept,但是隻有一個程序獲取到連結,其他兩個程序沒有獲取到連結而出錯

4.在select設定為阻塞,select和accept之間新增sleep(1),accept的套接字為非阻塞,結果如下:
worker pid#30689 is waiting for connection...
worker pid#30690 is waiting for connection...
worker pid#30691 is waiting for connection...
worker pid#30691 's listenfd is readable
worker pid#30691 create a new connection...
worker pid#30690 's listenfd is readable
worker pid#30689 's listenfd is readable
accept error: Resource temporarily unavailable
accept error: Resource temporarily unavailable
分析:三個程序被喚起後,由於有sleep(1),三個程序都被排程了,所以select都能查詢到連線,故而從select返回,但是在accept處,只有一個程序得到了連線


相關推薦

伺服器預先建立程序(work)同時服務現象

背景 原文連結: http://blog.csdn.net/ordeder/article/details/21721141 1.程序A在n埠上監聽,即呼叫listen(listenfd,backlog); 2.之後A呼叫fork產生子程序B,此時B拷貝了A的listenf

Nginx相同IP通過域名分發到不同的應用伺服器

1、Nginx配置檔案如下: #user nobody; worker_processes 3; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log inf

程序伺服器中,epoll的建立應該在建立程序之後

#include <iostream>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcn

2.fork建立程序:vfork

getpid/getppid getpid 獲取當前程序id getppid 獲取當前程序的父程序的id fork pid_t fork() 1.為什麼fork有兩個返回值? 因為這兩個返回值是由不同的程序return出來的,而不是由一個fork函式返回兩個數。 (fork

exec族函式詳解及迴圈建立程序

  前言:之前也知道exec族函式,但沒有完全掌握,昨天又重新學習了一遍,基本完全掌握了,還有一些父子程序和迴圈建立子程序的問題,還要介紹一下環境變數,今天分享一下。   一、環境變數   先介紹下環境的概念和特性,再舉例子吧。   環境變數,是指在作業系統中用來指定作業系統執行環境的一些引數。通常具備

linux c建立程序

*前言 瞭解fork()函式  c程式碼樣例: #include <stdio.h> #include <unistd.h> int main(int arg,cha

WebWork(在主執行緒建立程序

WebWork淺談 前言: 都知道JS是單執行緒語言,最讓人頭疼的莫過於在網路正常的情況下經常出現頁面的假死, 以及在進行大量的for迴圈計算時會導致執行緒阻塞,由於要進行大量的計算JS後面的執行會被阻隔在此處,使得效能較差,程式碼維護性差等一系列的問題發生。 本人也看了很多關於web

python建立程序的幾種常用方式(fork, Process,程序池)

linux下使用fork()建立子程序 Linux 作業系統提供了一個 fork() 函式用來建立子程序,這個函式很特殊,呼叫一次,返回兩次,因為作業系統是將當前的程序(父程序)複製了一份(子程序),然後分別在父程序和子程序內返回。子程序永遠返回0,而父程

【作業系統】如何建立程序

實驗環境:Linux Fedora13 需要用到的命令: top        動態檢視程序執行情況,包括殭屍程序的數量 ps         靜態檢視程序執行清情況 pstree  檢視程序樹

zz: Python subprocess建立程序(Windows下開啟新的CMD視窗)

Python提供多個建立子程序的模組,我比較習慣使用subprocess模組,因為在Python手冊中有這樣一段話:   This module intends to replace several other, older modules and functions, such as: os.syst

python 建立程序subprocess以及注意的問題(死鎖)

原文 : http://blog.csdn.net/jgood/article/details/4498166 最近,我們老大要我寫一個守護者程式,對伺服器程序進行守護。如果伺服器不幸掛掉了,守護者能即時的重啟應用程式。上網Google了一下,發現Python有很幾個模

android應用建立程序的方法探究

android應用建立子程序的方法探究 1:前言   android應用開發,當前大多數軟體還是停留在java層進行開發,然而android真正可玩的地方,偏偏是本地語言c與c++,藉助JNI這個橋樑,可以使得java呼叫到本地函式,本文則從建立子程序,來進行探究andro

node.js 建立程序 I

建立father.js檔案 /**************** * child_process模組提供產生子程序的方法 * 當有CPU密集型的任務,為了不阻塞事件迴圈, * 或者是執行一個外部命令可以,產生一個子程序, * 讓子程序執行這個任務

關於fork建立程序的一些疑惑

Python中執行 os.fork() 會建立 一個子程序,程序號為0 import os import time ret = os.fork() if ret == 0: while T

建立程序的4種方法及注意事項

/***********************************fork & vfork*********************************/1 1. fork:子程序拷貝父程序的資料段     vfork: 子程序與父程序共享資料段 2. 

程序利用fork()函式建立程序並且利用shared_memory進行通訊的例項

#include<stdio.h> #include<unistd.h> #include<sys/shm.h> #include<sys/stat.h>

pycharm 執行django伺服器 以及建立app方法

我們先來了解一下一些必要的概念。Django 裡什麼東西叫 APP 呢? 我們已經建立了 project , 那麼 project 和 APP 之間到底有什麼不同呢?它們的區別就是一個是配置另一個是程式碼: 一個project包含很多個Django app以及對它們的配置。 技術上,project的

C#實現Socket通訊(同時多客戶)

//建立socket物件 //第一個引數:設定網路定址的協議、第二引數設定資料傳輸的方式、第三個引數設定通訊協議 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketT

如果有2個ApacheMQ的客戶同時一個Queue會怎麼樣

我們知道Topic,是傳送一個訊息,多個監聽的客戶端都可以消費到訊息內容。而傳送一個訊息到Queue,只能一個監聽的客戶端可以消費。那麼,如果我有2個客戶端都去監聽同一個Queue,可以嗎?結果會怎麼樣?實際測試結果是逐一交叉監聽消費了訊息。這樣的情況,可以利用微服務➕MQ簡

客戶連線伺服器,配置出錯“連線超時”或者“無程式”解決方法

這兩天在進行Oracle的客戶端配置,伺服器OS為Windows XP 64,客戶端OS為Win7 64,oracle版本為11.2。 先說下伺服器端自己的疑惑,由於自己是新手,很多都不明白是怎麼個回事。 1)關於服務端監聽程式配置時候的SID設定 我在建立資料庫的時候,設定