"놀 라 움 군", nginx 가 어떻게 해결 하 는 지 보 세 요.

8647 단어 nginx
nginx 를 말 하기 전에 먼저 '놀 라 움' 이 무엇 인지 볼 까요?쉽게 말 하면 다 중 스 레 드 / 다 중 프로 세 스 (Liux 하 스 레 드 프로 세 스 도 크게 다 르 지 않 습 니 다) 가 같은 socket 사건 을 기다 리 고 있 습 니 다. 이 사건 이 발생 했 을 때 이 스 레 드 / 프로 세 스 가 동시에 깨 어 나 는 것 은 놀 라 운 일 입 니 다.효율 이 매우 낮 고 많은 프로 세 스 가 커 널 에 의 해 재 조정 되 어 깨 어 나 는 동시에 이 사건 에 응답 하 는 것 을 볼 수 있 습 니 다. 물론 하나의 프로 세 스 만 이 사건 을 처리 하 는 데 성공 할 수 있 습 니 다. 다른 프로 세 스 는 이 사건 을 처리 하 는 데 실패 한 후에 다시 휴면 합 니 다 (다른 선택 도 있 습 니 다).이런 성능 낭비 현상 은 바로 사람들 을 놀 라 게 하 는 것 이다.
놀 라 운 그룹 은 보통 server 에서 발생 합 니 다. 부모 프로 세 스 가 포트 를 연결 하여 socket 을 감청 한 다음 에 fork 에서 여러 개의 키 프로 세 스 를 내 고 하위 프로 세 스 들 은 순환 처리 (예 를 들 어 accept) 라 는 socket 을 시작 합 니 다.사용자 가 TCP 연결 을 시작 할 때마다 여러 개의 키 프로 세 스 가 동시에 깨 어 난 다음 에 한 개의 키 프로 세 스 accept 가 새 연결 에 성공 하면 나머지 는 실패 하고 다시 휴면 합 니 다.
그럼, 우 리 는 하나의 프로 세 스 로 만 새 연결 을 수락 할 수 없 습 니까?그리고 메시지 대기 열 등 동기 화 방식 을 통 해 다른 하위 프로 세 스 로 하여 금 이 새로운 연결 을 처리 하 게 하면 놀 라 움 을 피 할 수 있 지 않 습 니까?맞 아, 놀 라 움 은 피 했 지만 효율 이 떨 어 졌 다. 이 프로 세 스 는 accept 연결 에 만 사용 할 수 있 기 때문이다.다 핵 기계 에 있어 서 하나의 프로 세 스 만 accept 를 하 는 것 도 프로그래머 가 스스로 accept 병목 을 만 드 는 것 이다.그래서 저 는 accept 사건 을 다 중 프로 세 스 로 처리 해 야 한다 고 주장 합 니 다.
사실 linux 2.6 커 널 에서 accept 시스템 호출 은 놀 라 운 그룹 이 존재 하지 않 습 니 다 (적어도 저 는 2.6.18 커 널 버 전에 존재 하지 않 습 니 다).부모 프로 세 스에 서 bind, listen, 그리고 fork 에서 하위 프로 세 스 를 내 보 냅 니 다. 모든 하위 프로 세 스 가 이 감청 핸들 을 수락 합 니 다.이렇게 하면 새 연결 이 왔 을 때, 하위 프로 세 스 만 새 연결 로 돌아 가 고, 다른 하위 프로 세 스 는 accept 호출 에서 계속 휴면 하 며, 깨 어 나 지 않 는 것 을 발견 할 수 있 습 니 다.
그러나 불행 하 게 도, 일반적으로 우리 의 프로그램 은 그렇게 간단 하지 않 습 니 다. accept 호출 을 막 으 려 고 하지 않 습 니 다. 우 리 는 다른 네트워크 읽 기와 쓰기 사건 을 처리 해 야 합 니 다. Liux 에서 우 리 는 epoll 로 비 차단 socket 을 해결 하 는 것 을 좋아 합 니 다.그래서 accept 호출 이 놀 라 지 않 더 라 도 놀 라 운 일 을 처리 해 야 합 니 다. epoll 에 문제 가 있 기 때 문 입 니 다.위 에서 말 한 테스트 프로그램 입 니 다. 만약 우리 가 하위 프로 세 스 내 에서 accept 호출 을 막 는 것 이 아니 라 epoll 을 사용 합 니 다.wait, 새로 연 결 될 때 여러 개의 하위 프로 세 스 가 epoll 에 있 음 을 발견 할 수 있 습 니 다.wait 후 깨 우기!
nginx 는 이 렇 습 니 다. master 프로 세 스 감청 포트 번호 (예 를 들 어 80), 모든 nginx worker 프로 세 스 는 epoll 을 사용 하기 시 작 했 습 니 다.wait 에서 새 이벤트 (Liux 아래) 를 처리 합 니 다. 보호 하지 않 으 면 새로운 연결 이 올 때 여러 worker 프로 세 스 가 epoll 에 있 습 니 다.wait 후 깨 어 나 서 accept 가 실 패 했 습 니 다.이제 우 리 는 nginx 가 이 놀 라 운 문 제 를 어떻게 처리 하 는 지 볼 수 있다.
nginx 의 모든 worker 프로 세 스 는 함수 ngxprocess_events_and_timers 에서 이벤트 처리, (void) ngxprocess_events(cycle, timer, flags);서로 다른 이벤트 처리 체 제 를 봉 인 했 습 니 다. Liux 에 서 는 기본적으로 epoll 을 봉 인 했 습 니 다.wait 호출.우리 ngxprocess_events_and_timers 는 놀 라 움 을 해결 하기 위해 무엇 을 했 습 니까?
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
。。。 。。。
    //ngx_use_accept_mutex         accept         。 nginx worker   >1         accept_mutex ,      1
    if (ngx_use_accept_mutex) {
    		//ngx_accept_disabled       ,          ,   nginx.conf        nginx worker            ,       7/8 ,ngx_accept_disabled  ,   nginx worker      ,         ,           
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } else {
        		//  accept ,  worker           。         ,      ,      ngx_accept_mutex_held   1。   ,              epoll  ,       ,        epoll   。
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }

						//     , flag NGX_POST_EVENTS,    ngx_process_events   ,          ,  accept     ngx_posted_accept_events   ,epollin|epollout     ngx_posted_events   
            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;

            } else {
            		//    ,           ,  timer     epoll_wait     ,     ngx_accept_mutex_delay   epoll_wait       ,              
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }
。。。 。。。
		//linux ,  ngx_epoll_process_events      
    (void) 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_posted_events) {
        if (ngx_threaded) {
            ngx_wakeup_worker_thread(cycle);

        } else {
            ngx_event_process_posted(cycle, &ngx_posted_events);
        }
    }
}

위의 설명 을 통 해 알 수 있 듯 이 몇 개의 nginx worker 프로 세 스 가 있 든 같은 시간 에 하나의 worker 프로 세 스 만 자신의 epoll 에 감청 핸들 을 추가 할 수 있 습 니 다.accept 를 처리 하 는 nginx worker 프로 세 스 는 flag 를 NGX 로 설정 합 니 다.POST_EVENTS, 이렇게 하면 다음 ngxprocess_이벤트 함수 (linux 에서 ngx epoll process events 함수) 에 서 는 이 벤트 를 즉시 처리 하지 않 습 니 다. 뒤로 미 루 고 모든 accept 이 벤트 를 처리 한 후 자 물 쇠 를 풀 고 정상 적 인 읽 기와 쓰기 socket 이 벤트 를 처리 합 니 다.우리 ngxepoll_process_이벤트 어떻게 하 는 지:
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
。。。 。。。
    events = epoll_wait(ep, event_list, (int) nevents, timer);
。。。 。。。
    ngx_mutex_lock(ngx_posted_events_mutex);

    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;

。。。 。。。

        rev = c->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;
}

봐 봐 ngxuse_accept_mutex 는 어떤 상황 에서 열 립 니까?
    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
        ngx_use_accept_mutex = 1;
        ngx_accept_mutex_held = 0;
        ngx_accept_mutex_delay = ecf->accept_mutex_delay;

    } else {
        ngx_use_accept_mutex = 0;
    }

nginx worker 수량 이 1 보다 많 을 때 여러 프로 세 스 가 같은 감청 핸들 을 수락 할 수 있 습 니 다. 이 때 설정 파일 에 acceptmutex 스위치 가 열 리 면 ngxuse_accept_mutex 를 1 로 설정 합 니 다.
부하 균형 작용 이 있 는 ngxaccept_disabled 는 어떻게 유지 합 니까?event_accept 함수 중:
        ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;

사용 한 연결 수가 nginx. conf 에 설 정 된 worker 를 차지 할 때connections 총수 의 7 / 8 이상 시, ngxaccept_disabled 가 바 르 면 이 worker 는 ngxaccept_disabled 에서 1 을 줄 이 고 이번 에는 새 연결 을 처리 하지 않 습 니 다.
마지막 으로, 우리 ngxtrylock_accept_mutex 함 수 는 어떻게 합 니까?
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
//ngx_shmtx_trylock       ,  1    ,0      
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

//ngx_enable_accept_events            worker   epoll 
        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }
//ngx_accept_mutex_held  1,      ,  
        ngx_accept_events = 0;
        ngx_accept_mutex_held = 1;

        return NGX_OK;
    }

//          ,ngx_disable_accept_events       epoll   
    if (ngx_accept_mutex_held) {
        if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}                              

OK, 자물쇠 의 디 테 일이 어떻게 이 루어 졌 는 지 에 대해 서 는 편폭 에 한 하여 말 하지 않 겠 습 니 다. 다음 글 은 다시 이야기 하 겠 습 니 다.이제 nginx 가 놀 라 움 을 어떻게 처리 하 는 지 아 시 죠?쉽게 말 하면 같은 시간 에 하나의 nginx worker 만 자신의 epoll 에서 감청 핸들 을 처리 할 수 있 습 니 다.부하 균형 도 간단 합 니 다. 최대 connection 의 7 / 8 에 이 르 렀 을 때 이 worker 는 accept 자 물 쇠 를 가 져 오 려 고 하지 않 고 새로운 연결 을 처리 하지 않 습 니 다. 그러면 다른 nginx worker 프로 세 스 는 감청 핸들 을 처리 하고 새로운 연결 을 만 들 수 있 습 니 다.그리고 timeout 의 설정 으로 인해 자 물 쇠 를 가 져 오지 못 한 worker 프로 세 스 는 자 물 쇠 를 가 져 오 는 빈도 가 더욱 높 습 니 다.

좋은 웹페이지 즐겨찾기