OpenResty 에서 프로 세 스 간 통신 실현

6219 단어 openresty
Nginx 에 서 는 모든 워 커 프로 세 스 가 평등 합 니 다.그러나 어떤 때 는 서로 다른 역할 을 할당 해 야 할 때 가 있다. 이 럴 때 는 프로 세 스 간 통신 기능 을 실현 해 야 한다.
폴 링
간단 하고 거 칠 지만 보편적으로 사용 되 는 방안 은 모든 프로 세 스 가 자신의 list 형식 에 속 하 는 shdict key 를 나 누 어 일정 시간 마다 새로운 메시지 가 있 는 지 확인 하 는 것 이다.이런 방식 의 장점 은 실현 이 간단 하 다 는 데 있 고, 단점 은 실시 간성 을 보장 하기 어렵 다 는 데 있다.물론 프로 세 스 간 통신 이 필요 한 대부분의 장면 에 대해 0.1 번 에 한 번 씩 타이머 로 새로운 정 보 를 처리 하 는 것 만으로 도 충분 하 다.0. 1 초의 지연 은 길 지 않 기 때문에 1 초 에 10 개의 timer 비용 도 많 지 않 고 일반적인 통 신 량 에 대처 하기에 충분 하 다.
용병
폴 링 이 밀 린 다 고 생각 하거나 환경 에서 폴 링 이 밀 린 다 면 외부 의존 을 도입 해 실시 간 으로 개선 하 는 것 도 고려 할 수 있다.예 를 들 어 로 컬 에서 redis 를 시작 하여 유 닉 스 socket 을 감청 한 다음 에 모든 프로 세 스 는 Pub / Sub 또는 stream 형식 을 통 해 최신 정 보 를 발표 하거나 가 져 옵 니 다.이런 방안 은 실현 하기 도 간단 하고 실시 성과 성능 도 충분 하 며 단지 redis 서 비 스 를 도입 해 야 한다.
ngx_lua_ipc
만약 당신 이 미니 멀리 즘 자라 면 외부 의존 도입 에 대해 극도로 증오 하고 모든 것 이 Nginx 에서 이 루어 지 기 를 원한 다 면, ngxlua_ipc 는 광범 위 하 게 사용 되 는 선택 이다.ngx_lua_ipc 는 제3자 Nginx C 모듈 로 OpenResty 코드 에서 프로 세 스 간 통신 (IPC) 을 완성 할 수 있 는 Lua API 를 제공 합 니 다.
Nginx init 단계 에서 worker process + helper process 대 pipe fd 를 만 듭 니 다.각각 fd 는 read fd 로 데 이 터 를 수신 하고 다른 하 나 는 write fd 로 데 이 터 를 보 내 는 데 사 용 됩 니 다.Nginx 가 worker 프로 세 스 를 만 들 때 모든 worker 프로 세 스 는 이 pipe fd 를 계승 하여 프로 세 스 간 통신 을 실현 할 수 있 습 니 다.관심 있 는 독 자 는 man 7 pipe pipe 를 바탕 으로 하 는 프로 세 스 간 통신 이 어떻게 실현 되 는 지 알 수 있다.
물론 ngx_lua_ipc pipe 의 read fd 를 ngx_connection_t 을 통 해 Nginx 의 이벤트 순환 체제 에 접속 하여 구체 적 으로 실현 해 야 한다 ipc_channel_setup_conn.
  c = ngx_get_connection(chan->pipe[conn_type == IPC_CONN_READ ? 0 : 1], cycle->log);
  c->data = data;

  if(conn_type == IPC_CONN_READ) {
    c->read->handler = event_handler;
    c->read->log = cycle->log;
    c->write->handler = NULL;
    ngx_add_event(c->read, NGX_READ_EVENT, 0);
    chan->read_conn=c;
  }
  else if(conn_type == IPC_CONN_WRITE) {
    c->read->handler = NULL;
    c->write->log = cycle->log;
    c->write->handler = ipc_write_handler;
    chan->write_conn=c;
  }
  else {
    return NGX_ERROR;
  }
  return NGX_OK;

write fd 는 Lua 코드 로 작 동 되 기 때문에 Nginx 의 이벤트 순환 메커니즘 에 가입 할 필요 가 없습니다.
재 미 있 는 디 테 일 이 있 습 니 다. pipe fd 는 쓰기 데이터 가 PIPE_BUF 보다 적 을 때 만 쓰기 작업 의 원자 성 을 확보 할 수 있 습 니 다.만약 에 하나의 메시지 가 PIPE_BUF (Linux 에서 4K 이상) 을 초과 하면 그 메 시 지 는 원자 가 아 닙 니 다. 앞 PIPE_BUF 에 기록 한 후에 다른 worker 도 같은 프로 세 스에 메 시 지 를 기록 할 수 있 습 니 다.
서로 다른 워 커 프로 세 스 의 메시지 가 연결 되 지 않도록 ngx_lua_ipc 패 킷 개념 을 정의 합 니 다.모든 패 킷 은 PIPE_BUF 보다 크 지 않 고 하나의 메시지 가 여러 패 킷 으로 나 뉘 어 다시 포장 할 수 있 도록 헤더 가 있 습 니 다.
수신 단 에 서 는 메 시 지 를 받 은 후에 대응 하 는 Lua handler 를 실행 할 수 있 도록 ngx_lua_ipc 함 수 를 사용 하 였 으 며, 이 함 수 는 메시지 유형 에 따라 대응 하 는 handler 에 배 포 됩 니 다.이런 문 제 는 배달 이 완 료 될 수 있 느 냐 없 느 냐 하 는 것 이다. ngx.timer.at 집행 여부 에 달 려 있다.그리고 ngx.timer.at 집행 여 부 는 두 가지 요소 에 제한 을 받는다.
  • ngx.timer.at 크 지 않 으 면 lua_max_pending_timer timer
  • 를 만 들 수 없습니다.
  • ngx.timer.at 크 지 않 거나 timer 를 실행 할 충분 한 자원 이 없 으 면 lua_max_running_timer 만 든 timer 가 실행 되 지 않 을 수 있 습 니 다.

  • 사실 timer 가 실행 되 지 않 으 면 현재 단계 의 OpenResty 에 오류 로 그 를 기록 하지 않 을 수 있 습 니 다.잘못된 로 그 를 기록 하 는 PR 을 전 제 했 습 니 다.https://github.com/openresty/...그러나 합병 은 계속 되 지 않 았 다.
    그래서 엄격 한 의미 에서 ngx.timer.at 소식 이 배 달 될 수 있다 는 보장 도 없고 소식 배달 실패 타 임 스 가 틀 릴 수도 없다.근 데 이 냄비 는 ngx_lua_ipc 가 짊 어 져 야 돼 요.ngx.timer.at 그런 거 안 써 도 돼 요?이것 은 lua - nginx - module 에서 큰 코드 를 복사 하고 가끔 동기 화 해 야 합 니 다.복사 붙 여 넣 기 는 Nginx C 모듈 이 개발 한 오의 입 니 다.
    동적 감청 유 닉 스 소켓
    위의 방법 은 Redis 용병 법 을 제외 하고 응용 코드 에 로 그 를 추가 하지 않 으 면 외부 에서 메 시 지 를 배달 하 는 과정 을 보 려 면 gdb / systemtap / bcc 에 의존 해 야 합 니 다.인터넷 으로 연결 하면 tcpdump 와 같은 서민 기술 을 사용 하여 메시지 의 흐름 을 추적 할 수 있다.물론 유 닉 스 socket 이 라면 TCP proxy 를 임시로 만들어 야 하지만 조작 난이 도 는 앞의 큰 방법 보다 훨씬 낮 아 졌 다.
    그렇다면 IPC 를 네트워크 로 연결 시 킬 방법 은 없 지만 외부 의존 을 빌 릴 필 요 는 없 을 까?
    Redis 용병 법 을 떠 올 리 면 우리 가 Nginx 의 네트워크 요청 을 직접 할 수 없 는 이 유 는 Nginx 의 모든 worker 프로 세 스 가 평등 하기 때 문 입 니 다. 당신 의 요청 이 어느 프로 세 스에 떨 어 질 지 모 르 고 Redis 를 요청 하 는 것 은 문제 가 없습니다.그러면 우 리 는 서로 다른 워 커 프로 세 스 가 서로 다른 유 닉 스 socket 을 동적 으로 감청 하도록 할 수 있 습 니까?
    답 은 긍정 적 이다.우 리 는 이와 유사 한 인 터 페 이 스 를 실현 할 수 있다.
    ln = ngx.socket.listen(...)
    sock = ln.accept()
    sock:read(...)

    누군가가 비슷 한 PR 을 언급 한 적 이 있다.https://github.com/openresty/...나 자신 도 회사 프로젝트 에서 많 지 않 은 것 을 이 룬 적 이 있다.이 방법 으로 IPC 를 만 들 지 말 라 고 성명 하 세 요.위의 실현 에 치 명 적 인 문제 가 있 습 니 다. 바로 ln 과 뒤에서 만 든 모든 sock 은 같은 Nginx 요청 에 있 습 니 다.
    나 는 일찍이 Nginx 요청 에서 너무 많은 일 을 하면 자원 배분 에 문제 가 생 길 것 이 라 고 쓴 적 이 있다.https://segmentfault.com/a/11...이후 IPC 의 횟수 가 늘 어 나 면서 이런 문 제 는 더욱 뚜렷 해 질 것 이다.
    이 문 제 를 해결 하려 면 모든 sock 을 독립 된 fake request 에 넣 고 달 릴 수 있 습 니 다. 이렇게:
    ln = ngx.socket.listen(...)
    --     ngx.timer.at      
    ln.register_handler(function(sock)
        sock:read(...)
    end)

    근 데 또 문제 가 있어 요.워 커 id 를 감청 된 유 닉 스 socket 의 ID 로 사용 하면 이 유 닉 스 socket 은 워 커 프로 세 스 에서 동적 으로 감청 되 기 때문에 Nginx reload 나 binary upgrade 의 경우 여러 워 커 프로 세 스 가 같은 워 커 id 를 가지 고 있 기 때문에 같은 유 닉 스 socket 을 감청 하려 고 시도 하여 주소 가 점용 되 는 오류 가 발생 합 니 다.해결 방법 은 PID 를 감청 된 유 닉 스 socket 의 ID 로 바 꾸 고 처음 보 낼 때 PID 를 worker id 로 초기 화 하 는 것 입 니 다.reload 에서 정상적으로 메 시 지 를 보 내 는 것 을 지원 하 는 수요 가 있 으 면 신 구 두 그룹의 worker 를 기록 해 야 합 니 다. 예 를 들 어:
    1111 => old worker ID 1
    1123 => new worker ID 2

    워 커 마다 다른 유 닉 스 소켓 을 할당 합 니 다.
    워 커 마다 다른 유 닉 스 socket 을 통 해 프로 세 스 간 통신 을 실현 하 는 방법 도 있다.이런 방법 은 이렇게 교묘 한데, 나 는 단지 내 가 생각 한 것 이 아니 라 는 것 을 원망 할 뿐이다.이 방법 은 위의 동적 감청 유 닉 스 socket 방안 을 도태 시 킬 수 있다.
    우 리 는 Nginx 프로필 에서 설명 할 수 있 습 니 다. ngx_lua_ipc그리고 Nginx 를 수정 하여 ngx.timer.at 차이 가 많 지 않 은 표 시 를 볼 때 특정한 프로 세 스 가 특정한 유 닉 스 sock 을 감청 하도록 합 니 다. 예 를 들 어 listen unix:xxx.sock use_as_ipc_blah_blah, use_as_ipc_blah_blah 등 입 니 다.
    그것 은 동적 감청 유 닉 스 socket 방법 에 비해 실현 이 더욱 간단 하기 때문에 더욱 믿 을 만하 다.물론 reload 나 binary upgrade 에서 정확 한 워 커 에 게 메 시 지 를 보 낼 수 있 도록 하려 면 워 커 id 가 아 닌 PID 로 접미사 로 구분 하고 둘 사이 의 매 핑 을 유지 하 세 요.
    이 방법 은 datavisor 의 동업자 가 제시 한 것 으로 최근 에 개 원 될 것 으로 예상 된다.

    좋은 웹페이지 즐겨찾기