요약
- SMTP PIPELINING은 서버가 기능을 광고한 뒤 클라이언트가 명령들을 묶어 보낼 수 있게 하여, 각 명령에 붙은 판단을 하나로 합치지 않으면서 반복되는 왕복 대기를 줄였다.
- 이 최적화는 클라이언트가 모든 응답을 셈하고, 상태를 바꾸는 명령을 그룹 경계로 다루며, 여러 줄 응답을 처리하고, TCP 흐름 제어를 존중하고, 서버가 읽지 않은 입력을 절대 버리지 않는다는 신뢰를 유지할 때만 작동한다.
그 비용은 침묵이었다
보통의 SMTP는 대화다. 클라이언트는 자신을 알리고 기다린다; 발신자를 지목하고 기다린다; 수신자를 제안하고 기다린다; 수신자를 더 제안한다면 매번 그 뒤에 기다린다; 메시지 시작을 요청한 뒤에도 다시 기다린다. 이 리듬은 추론하기 쉽다. 보통은 하나의 질문만 미결 상태로 남기 때문이다.
거리는 그 비용을 바꾼다.RFC 2920은 SMTP의 한 명령·한 응답 구조가 지연이 큰 링크에서 상당히 불리했으며, 때로는 회전 시간이 연결을 지배할 정도였다고 기술한다.MAIL FROM이나RCPT TO의 바이트는 작았다. 비싼 부분은 다음 명령이 이미 알 수 있음에도 아직 보낼 수 없는 빈 간격이었다.
명령 파이프라이닝은 그 간격의 일부를 없앴다. 클라이언트는 여러 명령을 하나의 그룹에 담아, 앞선 명령의 응답을 기다리지 않고 전송할 수 있게 되었다. 서버는 여전히 모든 명령을 개별적으로 판단했다. 절약은 여러 질문이 동시에 진행 중일 수 있도록 허용한 데서 왔다.
이 구분이 이 글의 핵심 경계다. PIPELINING은 많은 명령을 하나의 명령으로 바꾸지 않았다. 그것은 별개의 판단들로 이루어진 큐를 만들었으며, 명령들 사이의 안심되는 정지가 사라진 뒤에도 그 소유 관계가 복구 가능해야 했다.
동작 중인 코드가 이 확장을 필요하게 만들었다
순수하게 추상적인 프로토콜이라면 클라이언트가 그냥 묶어 보내기 시작했을 수도 있다. TCP는 바이트 순서를 보존하고, SMTP 서버는 이미 연속된 명령 줄을 파싱해야 했다. 배포된 현실은 그렇게 깔끔하지 않았다.
RFC 2920은 세 부류의 서버 결함을 기록한다. 서버가 일부 바이트를 이미 첫 번째 프로세스의 사적 버퍼로 읽어 둔 상태에서 연결을 다른 프로세스에 넘기면, 미리 읽은 명령들이 사라질 수 있었다. 한 SMTP 명령이 실패한 뒤 서버가 TCP 입력을 플러시할 수 있었는데, 그 실패가 뒤따르는 바이트를 무효로 만드는 것은 아니었다. 또는 서버가 수신자 결과와DATA의 관계를 잘못 처리하여, 마지막 수신자가 모든 수신자를 대표하는 것처럼 다루거나 모든 수신자가 실패한 뒤에도 메시지 데이터를 받아들일 수 있었다.
이것들은 묶어 보내기를 금지할 이유가 아니었다. 설계의 우아함만으로 기능 지원을 추정해서는 안 된다는 이유였다. 이 확장은 지원을 명시적으로 만들었다. 서버는PIPELINING을 선언해야 했고, 클라이언트는 그 선언을 기다려야 했다.
이 역사는 세 개의 표준 트랙 문서를 거쳤다.RFC 1854가 1995년 10월에 이 확장을 도입했다.RFC 2197은 1997년에 편집상 변경을 거쳐 이를 대체하며 프로토콜은 바뀌지 않았다고 밝혔다. RFC 2920은 2000년에 RFC 2197을 대체하며 STD 60이 되었다. 안정된 메커니즘은, 공개된 명세가 모든 서버가 이를 구현했다는 증명이라고 가장하지 않으면서, 두 문서보다 오래 살아남았다.
한 단어가 더 빠른 차선을 열었다
클라이언트는EHLO로 시작한다. 서버가 성공적인 250 응답을 반환하고PIPELINING키워드를 포함하는 경우에만, 클라이언트는 명령을 묶어 보내는 것을 선택할 수 있다.IANA SMTP 서비스 확장 레지스트리는 지금도 이 키워드를 기록하고, RFC 2920을 가리키며, EHLO 매개변수가 없음을 명시한다.
선언의 작음이 중요하다. PIPELINING은 새 SMTP 동사를 정의하지 않는다.MAIL FROM이나RCPT TO에 매개변수를 추가하지도 않는다. 서버는 배치 크기, 속도, 큐 깊이, 전송 약속을 공표하는 것이 아니다. 행동 불변식을 선언하는 것이다: 개별 답보다 먼저 명령을 받아도 순서를 잃지 않을 수 있다는 것.
클라이언트에는 지역적 선택권이 남는다. 선언 이후 파이프라이닝을 할 수도 있고 의무는 아니다. 더 작은 그룹을 고르거나 완전히 한 걸음씩 동작할 수도 있다. 공통 규칙은 좁고, 운영상 결정은 교환을 수행하는 참여자에게 남는다.
그룹은 상태가 바뀔 수 있는 지점에서 끝났다
모든 알려진 명령을 어디에나 둘 수 있는 것은 아니었다. RFC 2920은 재설정, 발신자, 수신자 명령은 그룹 안에 있을 수 있도록 허용한다. 반면EHLO,DATA,VRFY,EXPN,TURN,QUIT,NOOP같은 명령은 그룹의 끝에만 나타나야 한다. 그 성공이나 실패가 클라이언트가 수용해야 하는 상태 변화를 만들기 때문이다. 따라서NOOP는 응용 의미가 의도적으로 사소함에도, 동기화 지점 역할을 할 수 있다.
이 규칙은 의례적인 목록이 아니다. 추측과 의존을 분리한다. 클라이언트는 여러 수신자를 제안할 수 있다. 그들의 독립적인 답을 나중에 처리할 수 있기 때문이다. 그러나 상태를 바꾸는 답이 이미 도착했다고 행동하는 것은 안전하지 않다. 이후의 확장이 자체 그룹 경계를 정의할 수 있지만, 침묵은 허가가 아니다.
RFC 2920은 심지어 새로운RSET/MAIL FROM순서가 이전 메시지의 마지막 콘텐츠 전송과 하나의 그룹을 공유할 수 있도록 허용한다. 이는 트랜잭션 사이에서 세심하게 정의된 겹침이지, SMTP 상태가 사라졌다는 증거가 아니다. 이전 데이터의 끝은 여전히 자기 결과를 가지고, 재설정과 새 발신자도 각자의 결과를 가진다.
모든 답은 여전히 하나의 명령에 속했다
파이프라이닝은 회계 문제를 만든다. 클라이언트가 발신자 명령 하나, 수신자 세 개,DATA를 보냈다고 하자. 이제 다섯 개의 판단이 미결이다. 응답들은 모두 익숙한 세 자리 코드로 시작할 수 있고; 몇몇은 심지어 같은 코드를 가질 수 있다. 본문 텍스트는 구현과 언어에 따라 다르다. 코드도 산문도 어떤 미답 명령이 그 응답의 소유자인지 식별해 주지 않는다.
그래서 RFC 2920은 위치를 권위적인 기준으로 만든다. 클라이언트는 각각의 응답을 하나씩 셈하고, 그 개수를 자신이 발행했다고 아는 명령 수와 대조해야 한다. 오류 코드 값이나 설명 텍스트로 대조하는 것은 명시적으로 금지된다.
또한 모든 상태를 검사해야 한다. 모든 수신자가 실패하면DATA가 실패할 것으로 예상할 수 있지만, 그 결과를 가정하고 읽기를 멈출 수는 없다. 서버가DATA를 거부하면 클라이언트는 재설정할 수 있다. 서버가 유효한 수신자가 없는데도 잘못DATA를 받아들이면, 클라이언트는 종료 점 하나만 보내야 한다. 그러면 서버가 잘못된 상태를 메시지를 주입하지 않고 마무리할 수 있다. 이 예가 인상적인 이유는, 올바른 클라이언트 응답이 앞선 응답들이 시사하는 바가 아니라 실제 응답에 의해 결정되기 때문이다.
이것은 축소판의 동작 코드 증거다. 표준은 대응 규칙을 정의한다. 상대의 순서화된 응답이 동작 중인 서버가 실제로 들어간 상태를 드러낸다.
여러 줄 응답은 여러 개의 답이 아니었다
SMTP는 하나의 응답이 여러 줄을 차지할 수 있게 허용한다. 마지막 줄은 응답 코드와 중간 텍스트를 잇는 대시가 없다는 점으로 구별된다. 파이프라이닝 클라이언트는 이 프레이밍을 지원해야 하고, 각 줄을 새 응답으로 세어서는 안 된다.
줄 수를 응답 수와 혼동하면 그 뒤의 모든 답이 잘못된 명령으로 옮겨 간다. 수신자 거부가 발신자 판단으로 기록될 수 있고;DATA수락이 수신자로 귀속될 수 있다. 색인이 한 번 어긋나면, 대화는 문법적으로 계속 바쁘게 흐르는 동안 지역 상태는 거짓이 될 수 있다.
이 규율은 의도적으로 기계적이다: 명령 수, 응답 수, 여러 줄 종결자. 의미론은 소유 관계가 밝혀진 뒤에 결과를 설명한다. 소유 관계를 재구성하지는 않는다.
TCP 순서는 교착을 막아 주지 않았다
파이프라이닝은 TCP 위에 있지만, 순서 보존은 진행과 같지 않다. 논블로킹 클라이언트는 이전 전송의 바이트가 아직 대기 중인 동안에도 서버 응답을 처리할 수 있다. 그렇게 하지 못하는 블로킹 클라이언트는 명령 그룹 전체가 가용한 TCP 윈도 안에 들어가도록 보장해야 한다.
그렇지 않으면 클라이언트는 큰 그룹 전송을 마치려다 막히고, 서버는 클라이언트가 읽기 시작하지도 않은 응답을 전달하려다 막히는 교착이 생길 수 있다. RFC 2920은 당시 윈도가 보통 4K 옥텟이었지만 항상 그랬던 것은 아니라고 언급한다. 이 관찰은 역사적이며 현대의 보편 튜닝 값이 아니다. 영구적인 규칙은 윈도를 존중하거나 읽기와 쓰기를 동시에 수행하는 것이다.
이것은 대기를 제거하는 데 숨어 있던 비용을 드러낸다. 한 걸음씩의 순서화는 미결 의도를 한 명령으로 제한했다. 파이프라이닝은 그 양을 늘리고, 따라서 구현은 이를 명시적으로 제어해야 한다. 처리량은 동시성에서 나오고, 활성은 흐름을 인식하는 한계에서 나온다.
서버는 무손실 약속을 했다
PIPELINING을 광고하는 서버는 명령이 도착한 순서대로 응답해야 한다. 아직 받지 못한 명령을 가정해서는 안 된다. 자신의 지역 TCP 입력 버퍼가 비게 되면 대기 중인 응답을 해제해야 한다. 가장 강조할 것은, 그 버퍼의 내용을 절대 플러시하거나 잃어서는 안 된다는 것이다.
이 마지막 규칙이 역사적 구현 결함을 그 근원에서 수선한다. 실패한 수신자는 TCP가 이미 실어 온 뒤따르는 수신자나DATA명령을 지울 권한을 서버에 주지 않는다. 프로세스 인계는 미리 읽은 바이트를 버릴 수 있게 하지 않는다. 입력 스트림은 클라이언트 의도의 증거이며, 구현 경계가 그것을 다시 쓸 수 없다.
서버는 응답 일부를 버퍼링해 하나의 단위로 효율적으로 보낼 수 있지만, 상태 경계 명령이나 인식 불가 명령에 대한 응답은 버퍼링해서는 안 된다. 이 확장은 대기를 최적화하면서, 클라이언트가 방향을 바꿔야 하는 지점을 서버가 감추는 것은 허용하지 않는다.
이 기능이 말하지 않은 것
RFC 5321은 여전히 SMTP를 의도적으로 한 걸음씩, 한 번에 하나씩 동작하는 프로토콜로 기술하면서, RFC 2920과 같은 상호 합의된 확장이 그 대화를 바꿀 수 있다고 언급한다. “상호 합의된”이 바로 정확한 한계다.
광고된 키워드는 어느 쪽 피어도 인증하지 않는다. 발신자나 수신자 수락을 보장하지 않는다. 저장 공간을 예약하거나, 메시지를 전송하거나, 콘텐츠를 압축하거나, 다음 릴레이가 같은 확장을 지원함을 증명하지 않는다. 운영자에게 최적 배치 크기를 알려 주지도 않는다. 이 서버가 이 세션에서 특정한 동시성 규율을 받아들인다는 것만 말한다.
그래서 이 확장의 영구적 성취는 일반적인 속도 약속보다 더 작고 더 유용했다. PIPELINING은, 동작 중인 양쪽 구현이 올바르게 남기 위해 필요한 증거를 보존할 수 있는 곳에서, 지연을 선택 사항으로 만들었다.
출처와 한계
첫 명세는RFC 1854이고, 그 편집 후속은RFC 2197, 현재의 규범적 확장은RFC 2920이다. 더 넓은 SMTP 상태 모델은RFC 5321에서 오고, 현재 키워드 기록은IANA SMTP 레지스트리에서 온다. 이 출처들은 프로토콜 역사와 요구 사항을 확립한다. 그러나 현재의 채택률, 공급업체 기본값, 성능 이득이나 실패율은 측정하지 않는다.
회원 브리핑
프로필 심층 컨텍스트
올바른 멤버십 등급으로 로그인하면 전체 브리핑과 출처 노트를 볼 수 있습니다.
Strategic Circle 전용
Strategic Circle
모든 독자에게 공개됩니다. 가입 후 로그인하면 프로필 브리핑를 열람할 수 있습니다.
Strategic Circle 가입하기Leadership Alliance 전용
Leadership Alliance
자격을 갖춘 IP 자산 소유자 및 운영진을 위한 콘텐츠입니다. 로그인하면 Leadership Alliance 브리핑을 열람할 수 있습니다.
Leadership Alliance 가입하기





