Mọi trang web giờ đều là HTTPS, và điều đó tốt cho bảo mật. Nhưng "S" trong HTTPS không miễn phí: trên mỗi kết nối, sau khi bắt tay TCP (bài 1) xong, còn một bắt tay TLS nữa — thỏa thuận thuật toán mã hóa, trao khóa, xác thực chứng chỉ. Bắt tay này tốn thêm thời gian (một RTT hoặc hơn) và tốn CPU đáng kể cho toán khóa công khai. Với hệ mở nhiều kết nối TLS, chi phí này cộng dồn nhanh. Bài này (phần 7 loạt Mạng) chạy thật để đo chính xác TLS đắt hơn TCP bao nhiêu, và session resumption cứu vãn thế nào.
Cơ chế: một bắt tay nữa trên TCP
Quy trình một kết nối HTTPS: bắt tay TCP → bắt tay TLS → mới gửi dữ liệu (đã mã hóa). Bắt tay TLS 1.2 tốn ~2 RTT (nhiều bước qua lại); TLS 1.3 gộp bước xuống còn ~1 RTT. Phần đắt nhất về CPU là toán khóa công khai (bất đối xứng): trao đổi khóa ECDHE và ký/kiểm chứng chỉ — nặng hơn nhiều so với mã hóa đối xứng dùng cho dữ liệu sau đó.
Session resumption cứu vãn cho các kết nối sau: lần đầu làm full handshake, server cấp một session ticket; lần sau client trình ticket, hai bên dùng lại khóa cũ (PSK) — bỏ được toán khóa công khai và bớt RTT (TLS 1.3 còn hỗ trợ 0-RTT: gửi dữ liệu ngay).
// server TLS với self-signed cert
ln, _ := tls.Listen("tcp", addr, &tls.Config{Certificates: cert})
// client đo bắt tay, bật cache để resumption
cfg := &tls.Config{ClientSessionCache: tls.NewLRUClientSessionCache(64)}
c, _ := tls.Dial("tcp", addr, cfg)
c.ConnectionState().Version // TLS 1.2 / 1.3
c.ConnectionState().DidResume // có phải resumed không

Hình 1: HTTPS = TCP + TLS; bắt tay TLS thỏa thuận cipher/khóa/cert (TLS 1.3 ~1 RTT), tốn CPU toán khóa công khai; session resumption dùng ticket để bỏ phần đắt cho kết nối sau.
Đo thật: TLS đắt hơn TCP bao nhiêu
Mình dựng HTTPS server self-signed và đo thời gian bắt tay ba trường hợp: TCP thường, TLS full, TLS resumption:

Hình 2: Chạy thật (TLS 1.3) — TCP thường 23µs, TLS full handshake 391µs, TLS resumption 353µs (resumed 500/500); TLS thêm ~368µs so với TCP (đắt gấp
17 lần, phần lớn là CPU); resumption chỉ nhanh 1,1 lần trên localhost vì RTT0.
Đọc kết quả đo được:
- TLS thêm ~368µs so với TCP thường: TCP thường chỉ 23µs/kết nối, còn TLS full handshake tốn 391µs — đắt gấp
17 lần. Chênh lệch này không phải do RTT (localhost RTT0) mà do CPU: toán khóa công khai ECDHE và kiểm chứng chỉ. Đây là chi phí thật của TLS, hiện rõ ngay cả trên loopback. - Resumption hoạt động: 500/500 resumed: với
ClientSessionCache, cả 500 kết nối sau đềuDidResume=true— chúng dùng lại session cũ thay vì full handshake. Cơ chế đúng như thiết kế. - Nhưng trên localhost resumption chỉ nhanh 1,1 lần: 353µs vs 391µs. Đây là chỗ cần trung thực: phần tiết kiệm chính của resumption là bỏ bớt RTT (full = 1-RTT, resumed/0-RTT = 0-RTT). Localhost gần như không có RTT nên lợi ích đó ẩn đi; con số 1,1x chỉ phản ánh phần CPU tiết kiệm được. Trên mạng thực với RTT 50ms, bỏ một RTT là tiết kiệm ~50ms mỗi kết nối — lớn hơn nhiều lần con số localhost.
Đánh đổi cần cân nhắc
Cách rẻ nhất để giảm chi phí TLS là bắt tay TLS ít lần hơn. Giống bài 1 (bắt tay TCP), lời giải không phải làm bắt tay TLS nhanh hơn mà là tránh lặp lại nó. Kết hợp keep-alive + connection pool (bài 5, 6): một kết nối TLS đã bắt tay được tái dùng cho nhiều request, nên chi phí 368µs chỉ trả một lần cho cả loạt. Đây là lý do một client HTTPS cấu hình pool tốt gần như không cảm nhận chi phí TLS trong thực tế.
TLS 1.3 và session ticket nên là mặc định. TLS 1.3 giảm full handshake xuống 1 RTT (so với 2 của 1.2) và bảo mật tốt hơn. Session ticket cho phép resumption. Cả hai đều bật sẵn trong thư viện hiện đại (Go crypto/tls mặc định hỗ trợ). Đừng tắt session ticket vì lý do "bảo mật" mà không hiểu rõ — nó có đánh đổi về forward secrecy nhưng thường được cân bằng tốt trong cài đặt mặc định.
CPU của TLS đáng kể ở quy mô lớn — đó là lý do có TLS offload. 368µs CPU mỗi full handshake nghe nhỏ, nhưng một server nhận hàng nghìn kết nối HTTPS mới mỗi giây sẽ tốn CPU đáng kể chỉ cho bắt tay. Đây là lý do các hệ lớn dùng TLS termination ở load balancer / reverse proxy (nginx, CDN) — gánh chi phí TLS ở một tầng chuyên dụng (đôi khi có phần cứng tăng tốc), để server ứng dụng phía sau chỉ nói HTTP thường.
Ba ý mang về
- HTTPS = TCP + TLS, và TLS tốn CPU thật: đo thật TLS full handshake 391µs so với TCP thường 23µs (
17 lần) — chênh lệch chủ yếu là CPU toán khóa công khai (ECDHE + chứng chỉ), hiện rõ cả trên localhost nơi RTT0. - Session resumption bỏ phần đắt cho kết nối sau: đo thật 500/500 kết nối resumed; nhưng trên localhost chỉ nhanh 1,1 lần vì lợi ích chính là bỏ RTT mà localhost không có — trên mạng thực RTT cao, resumption tiết kiệm cả một vòng khứ hồi.
- Giảm chi phí TLS = bắt tay TLS ít lần hơn + offload: tái dùng kết nối (keep-alive/pool) trả chi phí handshake một lần cho cả loạt; dùng TLS 1.3 + session ticket; và ở quy mô lớn, đưa TLS termination lên load balancer/CDN.
Nguồn
- RFC 8446 — TLS 1.3: https://www.rfc-editor.org/rfc/rfc8446.html
- Cloudflare — Keyless SSL và TLS handshake performance: https://blog.cloudflare.com/tls-1-3-overview-and-q-and-a/
- Go docs — crypto/tls (ClientSessionCache, ConnectionState): https://pkg.go.dev/crypto/tls
Phần sau ta lùi về bước xảy ra trước cả bắt tay TCP: DNS resolution — phân giải tên miền thành IP là một chuyến đi mạng ẩn trước mỗi kết nối tới host mới; đo thật thời gian phân giải và tác dụng của cache.