nginx 놀 라 움 현상

사실 저 자신 도 잘 모 릅 니 다. 여러분 들 이 서로 공부 하고 공부 하 는 것 도 제 가 많은 것 을 본 후에 쓴 것 입 니 다.
놀 라 움:
다 중 스 레 드 나 다 중 프로 세 스 의 경우 여러 워 커 가 같은 socket 이 벤트 를 기다 리 고 있 습 니 다. 이벤트 가 발생 했 을 때 이 스 레 드 는 커 널 스케줄 에 의 해 동시에 깨 어 났 습 니 다. 그러나 단지...
이 벤트 를 처리 하 는 워 커 가 있 습 니 다. 다른 워 커 들 은 accept () 가 실패 한 후에 다시 휴면 합 니 다. 이러한 성능 낭비 현상 은 바로 놀 라 움 입 니 다. 상상 할 수 있 습 니 다.
현상의 효율 이 매우 낮다.
저 희 는 서버 와 클 라 이언 트 의 통신 을 예 로 들 었 습 니 다.
놀 라 운 현상 은 서버 에서 발생 합 니 다. 주 스 레 드 (부모 프로 세 스) 는 포트 번호 감청 소켓 을 연결 한 다음 스 레 드 (하위 프로 세 스 생 성) 를 만 듭 니 다.
스 레 드 는 accept 연결 의 소켓 을 동시에 순환 처리 합 니 다. 클 라 이언 트 가 TCP 링크 를 시작 할 때마다 이 부분 스 레 드 는 동시에 깨 어 나 서 선택 합 니 다.
중 하위 스 레 드 에서 accept 연결 을 하면 다른 스 레 드 는 연결 에 실패 하고 휴면 기 에 다시 들 어 갑 니 다.
그러면 어떤 사람 이 말 했다. 왜 메 인 스 레 드 만 accept 로 연결 시 키 지 않 고 동기 화 된 실현 방법 으로 하위 프로 세 스 에 게 새로운 링크 를 처리 하 게 하 는 것 이 아니 냐 는 것 이다.
놀 라 움 을 피 할 수 있 습 니 다. 하지만 그렇다면 accept 연결 에 만 사용 할 수 있 습 니 다. 하지만 이 는 cpu 에 일종 의 자원 입 니 다.
낭비, 그래서 놀 라 운 현상 은 반드시 해결 해 야 한다.
Liux 에서 우 리 는 보통 epoll 로 비 차단 socket 을 해결 하기 때문에 accept 호출 은 놀 라 운 현상 이 없 지만, 우 리 는 epoll 을 호출 하면wait, 새로운 것 이 있 을 때
연결, 여러 스 레 드 가 epoll 에 있 습 니 다.wait 후 깨 어 납 니 다.(epoll wait 가 준 비 된 fd 의 개 수 를 되 돌려 줍 니 다)
nginx     
eg: 주 프로 세 스 (또는 스 레 드) 연결 포트 번호 와 ip, 모든 nginx worker 프로 세 스 호출 epollwait 에서 이 벤트 를 처리 합 니 다. 보호 하지 않 으 면 새로운 연결 이 됩 니 다.
다가 오 면 많은 프로 세 스 가 epollwait 후에 깨 어 나 서 accept 가 실 패 했 습 니 다..................................................
그럼 nginx 가 놀 라 움 을 어떻게 해결 하 는 지 살 펴 보 겠 습 니 다.
모든 worker 프로 세 스 는 ngxprocess_events_and_timers 에서 이벤트 처리,
ngx_process_이벤트 (cycle, timer, flags) 는 서로 다른 이벤트 의 처리 체 제 를 패키지 합 니 다.
void ngx_process_events_and_timers(ngx_ctcle_t *cycle)
{
	/*
	         accept        
	nginx worker   >1          
	accept_mutex    
	*/
	if(ngx_use_accept_mutex)
	{
		/*
		  ngx_accept_disabled    0
		ngx_accept_disabled ==           1/8-       
		nginx.conf      nginx worker            ,
		       7/8 ,nginx_accept_disabled    ,    
		       ,(        )
		  disabled       accept_mutex;   ,      
		*/
		if(ngx_accept_disabled > 0)
		{
			ngx_accept_disabled--;
		}
		else
		{
			/*
			   accept,    (accept  )    worker epoll  
			*/
			if(NGX_ERRNO == ngx_trylock_accept_mutex(cycle))
			{
				return ;
			}

			if(ngx_accept_mutex_held)
			{
				/*
				     , flag  NGX_POXT_EVENTS, accept    
				ngx_posted_accept_events     。EPOLLIN  EPOLLOUT
				   ngx_posted_events   ,           ,       
				    。
				*/
				flags |= NGX_POSTED_EVENTS;
			}
			else
			{
				/*
				       ,          , timer   
				ngx_accept_mutex_delay,           ,  
				      worker         。
				*/
				if(timer == NGX_TIMER_INFINITE ||timer >ngx_accept_mutex_delay)
				{
					timer = ngx_accept_mutex_delay
				}
			}
		}
	}
	//////////////////////////////////////////////////////////////////////////////
	/*
	    ngx_epoll_process_events      
	*/
	ngx_process_events(cycle,timer,flags);

	
	/*
	  ngx_posted_accept_events        
	     accept    。
	*/
	if(ngx_posted_accept_events)
	{
		ngx_event_process_posted(cycle,&ngx_posted_accept_events);
	}
	
	
	/*
	          EPOLLIN/EPOLLOUT  
	*/
    if(ngx_accept_mutex_held)
	{
		ngx_shmtx_unlock(&ngx_accept_mutex);

	}

	
    if (delta) {  
        ngx_event_expire_timers();  
    }  
  
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log,0,"posted events %p", ngx_posted_events);  
   
	 /*
	              。         ,   
	ngx_process_events NGX_POST_EVENTS        
	ngx_posted_events   ,          。  
	*/

	if(ngx_postet_events)
	{
		if(ngx_threaded)
			ngx_wakeup_worker_thread(cycle);
		else
			ngx_event_process_posted(cycle,&ngx_posted_events);
	}
}

여러분 들 의 마음 이 좀 혼 란 스 러 우 실 수도 있 습 니 다. 제 가 위의 코드 를 먼저 정리 하 겠 습 니 다.
1 > 우선 flags 즉 ngx 판단use_accept_mutex 가 1 보다 클 지 여부: ngix worker 만 프로 세 스 수가 1 보다 많 고
프로필 에서 accept 열기mutex 자 물 쇠 를 잠 글 때 만 자 물 쇠 를 넣 을 지 말 지 를 고려 합 니 다.
2 > 그리고 판단 ngxaccept_disabled 가 0 보다 클 지 여부: nginx. conf 에서 모든 프로 세 스 가 이벤트 에 연결 할 수 있 는 최대 수 를 설정 합 니 다.
최대 수의 7 / 8 에 도달 하면, ngxaccept_disabled 는 정 (만부 하 표시) 입 니 다. 이 숫자 가 클 수록 양보 합 니 다.
기회 가 클 수록 disabled 를 통 해 경쟁 여 부 를 제어 acceptmutex, 이 숫자 가 0 보다 적 을 때 accpet 를 경쟁 합 니 다.mutex
부하 균형
3 > 획득 자물쇠: ngxtrylock_accept_mutex();그리고 현재 워 커 의 epoll 에 감청 핸들 을 넣 고,
4 > 자 물 쇠 를 가 져 온 후, 우 리 는 flags 를 NGX 로 설치한다.POSITED_EVENTS, accept 이 벤트 를 ngx 에 넣 습 니 다.posted_accept_events
링크 에서 EPOLLIN | EPOLLOUT 이 벤트 를 ngx 에 넣 습 니 다.posted_이벤트 중 제거 (읽 기 및 쓰기 이벤트 시간 이 오래 걸 리 며 잠 금 해제 후 지연 처리)
5 > 자 물 쇠 를 받 지 못 한 worker, timer 는 ngx 로 수정accept_mutex_delay, 더 짧 은 시간 초과 로 돌아 와 자 물 쇠 를 얻 지 못 하 게 합 니 다.
워 커 가 자 물 쇠 를 가 져 오 는 빈도 가 더 높 아 요.
6 > 그리고 처 리 를 시작 합 니 다. ngxposted_accept_이벤트 에 데이터 가 있 을 때 새로운 연결 을 하고 자 물 쇠 를 풀 고 처리 합 니 다.
읽 기와 쓰기 동작.(여기 지연 과정 이 있 습 니 다)
위의 코드 주석 에서 알 수 있 듯 이:
nginx worker 프로 세 스 가 아무리 많 더 라 도 같은 시간 에 하나의 worker 프로 세 스 만 자신의 epoll 에 감청 핸들 을 추가 할 수 있 습 니 다. 이 처리 accept
nginx worker 프로 세 스 가 flags 를 NGX 로 설정 합 니 다.POST_EVENTS, 그렇다면 그 는 다음 ngxprocess_이벤트 함수
ngx epoll process events 함수) 에서 이 벤트 를 즉시 처리 하지 않 습 니 다. 지연 되 며, 모든 accept 를 처리 한 후 자 물 쇠 를 풀 고 나 서 다시 시작 합 니 다.
정상 적 인 읽 기와 쓰기 이벤트 처리 하기;
7 > 우 리 는 ngx 를 보기 시작 했다.epoll_process_events 는 accept 를 어떻게 처리 합 니까?::::::::::::::
static  ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)  
{  
    /*
    epoll_wait    fd   。
    */ 
    events = epoll_wait(ep, event_list, (int) nevents, timer);  

    /*
         (accept  )
    */  
    ngx_mutex_lock(ngx_posted_events_mutex);  
  
    for (i = 0; i read;  //(   )
  
        if ((revents & EPOLLIN) && rev->active) 
	{  
            /*
	     NGX_POST_EVENTS    ,  accept    ngx_posted_accept_events   ,
	            ngx_posted_events         
	   */
            if (flags & NGX_POST_EVENTS) 
	    {  
                queue = (ngx_event_t **) (rev->accept ?  &ngx_posted_accept_events : &ngx_posted_events);  
				
		ngx_locked_post_event(rev, queue);  
  
            } 
	    else 
	    {  
                rev->handler(rev);  
            }  
        }  
  
        wev = c->write;  
  
        if ((revents & EPOLLOUT) && wev->active) 
	{  
	    /*
	      , NGX_POST_EVENTS    ,       ,
	      ngx_posted_events     
	    */
            if (flags & NGX_POST_EVENTS) 
	    {  
                ngx_locked_post_event(wev, &ngx_posted_events);  
  
            } 
	    else 
	    {  
                wev->handler(wev);  
            }  
        }  
    }  
  
    ngx_mutex_unlock(ngx_posted_events_mutex);  
  
    return NGX_OK;  
}  

ngx_use_accept_mutex 는 어떤 상황 에서 열 립 니까?
nginx worker 의 수량 이 1 보다 많 을 때 여러 프로 세 스 가 동시에 accept 연결 을 할 수 있 습 니 다. 이 설정 파일 의 acceptmutex 스위치 가 열 렸 습 니 다.
바로 ngxuse_accept_mutex 를 1 로 설정 합 니 다.
그리고 제 가 아까 위 에 쓴 것 은 부하 균형 과 비슷 합 니 다: ngxaccept_disabled 는 어떻게 제어 하나 요??
ngx_accept_disabled = ngx_cycle->connection_n /8-ngx_cycle->free_connect_n;
표시: 사용 한 연결 수가 전체 7 / 8 이상 을 차지 할 때, ngxaccept_disabled 가 바 뀌 었 습 니 다.
worker 장 ngxaccept_disabled 에서 1 을 줄 이 고 이번 에는 이 새 연결 을 처리 하지 않 습 니 다.
다음은 제 가 쓴 일반적인 epoll 구현 코드 입 니 다.
생각 을 정리 하 다
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX 10

void main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);

	assert(sockfd != -1);

	struct sockaddr_in ser,cli;

	memset(&ser,0,sizeof(ser));

	ser.sin_family = AF_INET;

	ser.sin_port = (6500);

	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));

	assert(res != -1);

	listen(sockfd,5);

	int epfd = epoll_create(10);

	struct epoll_event events[MAX];

	events.event = EPOLLIN|EPOLLET;

	event.data.fd = sockfd;

	epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&events);

	while(1)
	{
		int n = epoll_wait(epfd,events,MAX,-1);

		if(n<=0)
		{
			continue;
		}

		int  i = 0;

		for(;i

하하, 여러분 이 어느 정도 알 게 되 었 을 거 라 고 믿 습 니 다. 다만 한 가 지 는 그들 이 어떤 방식 으로 자 물 쇠 를 빼 앗 았 는 지 잘 모 르 겠 습 니 다.
뭔 가 특별한 알고리즘 이 있 을 거 야.
여기 제 가 본 몇 가지 문제 가 있 습 니 다. 여러분 과 공유 하 겠 습 니 다.
1. 새 사용자 의 연결 이 벤트 를 처리 하고 새 연결 을 처리 하 는 자 물 쇠 를 풀 어 줍 니 다. 왜 이렇게 디자인 합 니까?자 물 쇠 를 풀 자마자 새로운 연결 이 있 습 니 다. 자 물 쇠 를 얻 은 프로 세 스 가 대기 열 에 sockfd 를 추가 하려 면 원래 자 물 쇠 를 얻 은 프로 세 스 도 대기 열 에서 sockfd 를 삭제 해 야 합 니 다.
TCP 의 세 번 의 악수 연결 은 비 스 레 드 가 안전 합 니 다.오류 가 발생 하지 않도록 sockfd 를 대기 열 에서 삭제 한 후 새로운 프로 세 스 가 자 물 쇠 를 선점 하여 새로운 연결 을 처리 합 니 다.
2. 자 물 쇠 를 가 져 와 임 무 를 퀘 스 트 대기 열 에 두 고 바로 처리 하 는 것 이 아니 라 왜 이렇게 설계 합 니까?
모든 프로 세 스 가 새 연결 이 벤트 를 처리 하려 면 자 물 쇠 를 가 져 와 야 합 니 다. 현재 프로 세 스 는 새 연결 이벤트 의 sokect 를 작업 대기 열 에 추가 하고 자 물 쇠 를 즉시 풀 어 다른 프로 세 스 가 빨리 자 물 쇠 를 가 져 올 수 있 도록 합 니 다.
사용자 의 연결 을 처리 합 니 다.
잠 금 을 추가 하지 않 으 면 새 이벤트 연결 이 있 을 때 모든 프로 세 스 가 깨 어 나 accept 를 실행 합 니 다. 있 고 하나의 프로 세 스 만 accept 를 되 돌려 줍 니 다.
다른 프로 세 스 는 모두 다시 수면 상태 로 들어간다.현재 자물쇠 가 있 습 니 다. accept 가 발생 하기 전에 프로 세 스 들 이 자 물 쇠 를 선점 해 야 합 니 다. 또한 하나의 프로 세 스 만 자 물 쇠 를 빼 앗 습 니 다.
다른 프로 세 스 도 다시 수면 상태 로 들어간다.
즉, accept 자물쇠 가 있 든 없 든 많은 과정 이 깨 어 나 다시 수면 상태 로 들 어 갈 것 이다. 그 놀 라 운 현상 은 해결 할 수 없 는 것 일 까??????
사실 자 물 쇠 는 놀 라 움 현상 을 해결 하지 못 하고 놀 라 움 현상 은 해결 할 수 없 으 며 많은 과정 이 동시에 깨 어 나 는 것 은 필연 적 인 과정 이다.
Nginx 에 서 는 현재 프로 세 스 의 연결 수 > 최대 연결 수 * 7 / 8 을 검사 하여 현재 프로 세 스 가 새 연결 을 처리 할 수 있 는 지, 깨 어 난 프로 세 스 의 수 를 줄 일 수 있 는 지 판단 합 니 다.
간단 한 부하 균형 도 이 루 었 다.잠 금 은 모든 프로 세 스 가 accept 함 수 를 호출 하지 않도록 보장 할 수 있 습 니 다. 많은 프로 세 스 가 accept 를 호출 하여 오 류 를 되 돌려 줍 니 다.
자물쇠 가 해결 한 것 은 놀 라 움 현상의 잘못 이지 놀 라 움 현상 을 해결 한 것 이 아니다!


좋은 웹페이지 즐겨찾기