Go net/http Internals: TCP 소켓 관리
Wenhao Wang
Dev Intern · Leapcell

서문
시작하기 전에, 나중에 이해를 돕기 위해 소켓과 파일 디스크립터라는 두 가지 개념을 간략하게 설명하겠습니다.
소켓이란 무엇인가요?
소켓은 네트워크 통신을 위한 기본적인 추상화입니다. 애플리케이션이 네트워크 프로토콜 스택에 접근할 수 있는 표준 인터페이스를 제공합니다. 간단히 말해서, 소켓은 네트워크 통신을 위한 엔드포인트이며, 서로 다른 컴퓨터의 프로그램이 네트워크를 통해 데이터를 교환할 수 있도록 합니다.
소켓의 주요 기능:
- 애플리케이션 계층과 전송 계층 간의 인터페이스 역할을 합니다.
- 읽기 및 쓰기 작업을 지원하는 특수한 종류의 파일로 간주할 수 있습니다.
- TCP 소켓(연결 지향), UDP 소켓(비연결 지향) 등 다양한 유형이 있습니다.
파일 디스크립터란 무엇인가요?
파일 디스크립터는 운영 체제가 열린 파일을 식별하고 관리하는 데 사용하는 정수 값입니다. Unix/Linux 시스템에서는 일반 파일, 디렉토리, 장치, 심지어 네트워크 연결까지 모든 것이 파일입니다.
파일 디스크립터에 대한 핵심 사항:
- 0부터 시작하는 음수가 아닌 정수입니다(0은 표준 입력, 1은 표준 출력, 2는 표준 오류).
- OS 커널에서 파일 디스크립터는 파일 테이블 항목을 가리키는 인덱스입니다.
- 각 프로세스에는 자체 파일 디스크립터 테이블이 있습니다.
소켓과 파일 디스크립터의 관계
Unix/Linux 시스템에서 소켓은 특수한 종류의 파일로 간주되므로 해당 파일 디스크립터도 있습니다. 소켓을 만들 때:
- 운영 체제는 파일 디스크립터를 할당합니다.
- 이 파일 디스크립터는 후속 네트워크 작업(읽기, 쓰기, 닫기 등)에 사용할 수 있습니다.
- 애플리케이션은 이 파일 디스크립터를 통해 소켓과 상호 작용합니다.
TCP 연결 설정 프로세스
소켓 생성
// net 패키지의 내부 구현 fd, err := socket(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
이 단계에서는 시스템 호출을 통해 소켓 파일 디스크립터를 만듭니다.
서버 바인딩 및 수신 대기
// 단순화된 서버 코드 흐름 bind(fd, addr) listen(fd, backlog)
서버는 소켓을 특정 주소 및 포트에 바인딩한 다음 연결 요청 수신 대기를 시작합니다.
연결 수락
// net/http/server.go의 단순화된 버전 func (srv *Server) Serve(l net.Listener) error { for { rw, err := l.Accept() // 새 연결 수락 if err != nil { // 오류 처리 continue } go srv.newConn(rw).serve(ctx) // 각 연결에 대해 새 고루틴 생성 } }
클라이언트 연결
// net/http/transport.go의 단순화된 버전 func (t *Transport) dialConn(ctx context.Context, addr string) (*conn, error) { // TCP 연결 생성 netConn, err := t.dial(ctx, "tcp", addr) if err != nil { return nil, err } // HTTP 연결로 래핑 return &conn{ conn: netConn, // ... 기타 필드 }, nil }
데이터 전송
// 데이터 읽기 n, err := syscall.Read(fd, buf) // 데이터 쓰기 n, err := syscall.Write(fd, data)
연결 닫기
err := syscall.Close(fd)
주요 구현 세부 정보
멀티플렉싱
- HTTP/1.1은 Keep-Alive 메커니즘을 사용하여 TCP 연결을 재사용합니다.
- HTTP/2는 스트림을 통해 멀티플렉싱을 구현하여 여러 HTTP 요청이 단일 TCP 연결을 공유할 수 있도록 합니다.
연결 풀 관리
// net/http/transport.go type Transport struct { // 유휴 연결 풀 idleConn map[connectMethodKey][]*persistConn // 최대 유휴 연결 수 maxIdleConns int // ... 기타 필드 }
시간 초과 제어
// 연결 시간 초과 설정 conn.SetDeadline(time.Now().Add(timeout))
오류 처리 및 재시도 메커니즘
// 단순화된 재시도 로직 for retry := 0; retry < maxRetries; retry++ { conn, err := dial() if err == nil { return conn } // 재시도하기 전에 대기 time.Sleep(backoff) }
워크플로
클라이언트가 HTTP 요청을 시작할 때:
- 먼저 연결 풀에 사용 가능한 연결이 있는지 확인합니다.
- 그렇지 않은 경우 새 TCP 연결을 만듭니다.
- HTTP 요청 데이터를 보냅니다.
- 응답을 기다리고 읽습니다.
서버가 요청을 처리할 때:
- Accept 루프는 새 연결을 수락합니다.
- 각 연결에 대해 고루틴이 생성됩니다.
- HTTP 요청이 구문 분석됩니다.
- 요청이 처리되고 응답이 반환됩니다.
이것이 net/http
패키지가 TCP 프로토콜을 기반으로 HTTP 연결을 구현하는 핵심 메커니즘입니다. 추상화 및 캡슐화를 통해 개발자는 하위 수준의 TCP 연결 세부 정보를 직접 처리할 필요가 없으며 효율적인 연결 관리 및 재사용 메커니즘의 이점을 누릴 수 있습니다.
저희는 Go 프로젝트 호스팅을 위한 최고의 선택인 Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포
- 사용량에 대해서만 지불하세요. 요청도 청구도 없습니다.
탁월한 비용 효율성
- 유휴 요금 없이 사용한 만큼만 지불하세요.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
간편한 확장성 및 고성능
- 높은 동시성을 쉽게 처리할 수 있도록 자동 확장됩니다.
- 운영 오버헤드가 전혀 없으므로 구축에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ