Một request đi từ client tới Spring Boot như thế nào?

4 câu hỏi~20 phútTừ khóa: DNS, TCP, TLS, HTTP, reverse proxy, Nginx, Tomcat thread pool, connection pool, latency

Bắt đầu phần Networking nhé. Mình viết Spring Boot quen rồi nhưng không hình dung được request đi qua những gì trước khi tới controller.

Chương này dùng một project chạy xuyên suốt: order-service (Spring Boot, port 8080) deploy trên một Azure VM Ubuntu, sau này đặt sau Nginx. Database Postgres nằm trên một VM khác, port 5432. Một developer ngồi ở laptop (hoặc một VM khác) gọi vào order-service. Bài này vẽ bản đồ tổng thể; các bài sau đi sâu từng chặng.

1. Bài toán: curl một dòng, nhưng bên dưới là cả chục bước

curl https://api.example.com/orders/123

Developer chỉ thấy: gõ lệnh, nhận JSON. Nhưng khi nó chậm 5 giây, hoặc báo Connection timed out, bạn phải biết nó hỏng ở chặng nào.

2. Vấn đề: không có bản đồ thì debug bằng cách đoán

3. Giải pháp: chia request thành các chặng

#ChặngCâu hỏi nó trả lờiBài
1DNSapi.example.com là IP nào?9.4
2IP + TCPTới máy đó, mở kết nối vào port nào?9.2, 9.3
3TLSCó đúng server thật không? Mã hóa ra sao?9.5
4HTTPGửi method, path, header, body gì?9.6
5Load balancer / NATRequest vào VM nào? IP public đổi thành private ra sao?9.7
6NginxReverse proxy chuyển request sang 127.0.0.1:80809.8
7TomcatThread nào xử lý? Hết thread thì sao?9.9
8Controller → DBLấy connection từ HikariCP, gọi Postgres :54329.10
*Timeout ở mọi chặngChờ bao lâu thì bỏ cuộc?9.10
*DebugDùng công cụ nào cho chặng nào?9.11

4. Từng bước trên order-service

Bước 1: App đơn giản nhất:

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final OrderRepository repo;

    public OrderController(OrderRepository repo) {
        this.repo = repo;
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> get(@PathVariable Long id) {
        return repo.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }
}

Bước 2: Config (application.yml), password lấy từ biến môi trường:

server:
  port: 8080
spring:
  datasource:
    url: jdbc:postgresql://db-vm.internal:5432/orders
    username: ${DB_USER}
    password: ${DB_PASSWORD}

Bước 3: Nhìn thời gian từng chặng bằng curl -w:

curl -o /dev/null -s -w '
dns:      %{time_namelookup}s
tcp:      %{time_connect}s
tls:      %{time_appconnect}s
ttfb:     %{time_starttransfer}s
total:    %{time_total}s
' https://api.example.com/orders/123
dns:      0.012s
tcp:      0.045s
tls:      0.110s
ttfb:     0.380s   <-- server xử lý ~270ms (Tomcat + DB)
total:    0.381s

Các số là cộng dồn. ttfb - tls ≈ thời gian server xử lý. Nếu dns mất 2 giây thì đừng tối ưu SQL.

5. Cách hoạt động: bản đồ toàn cảnh

Laptop developer
  |
  | (1) DNS: api.example.com -> 203.0.113.10
  v
Internet
  |
  | (2) TCP SYN tới 203.0.113.10:443   (3-way handshake)
  | (3) TLS handshake (certificate, key)
  | (4) HTTP: GET /orders/123
  v
Azure: Public IP / Load Balancer (NAT -> private IP 10.0.1.4)
  |  NSG cho phép inbound 443?
  v
Azure VM (Ubuntu)  -- firewall ufw cho phép 443?
  |
  v
Nginx :443  (TLS kết thúc ở đây)
  |  proxy_pass http://127.0.0.1:8080
  v
Tomcat :8080 (embedded trong Spring Boot)
  |  acceptor nhận connection, worker thread "http-nio-8080-exec-7"
  v
DispatcherServlet -> OrderController.get(123)
  |  HikariCP lấy connection
  v
Postgres VM 10.0.2.5:5432   (chỉ cho subnet app vào)
  |
  v
Response đi ngược lại: JSON -> Tomcat -> Nginx -> TLS -> TCP -> Laptop

6. So sánh: lỗi thường gặp theo chặng

Triệu chứngChặng nghi ngờ
Could not resolve hostDNS
Connection refusedTCP: không có process listen port đó
Connection timed outTCP: firewall/NSG chặn
SSL certificate problemTLS
502 Bad GatewayNginx không gọi được Tomcat
504 Gateway TimeoutTomcat/DB trả lời quá chậm
Chậm dần khi tải caoTomcat thread pool / Hikari pool cạn

7. Khi nào cần bản đồ này

Chạy local thì gọi localhost:8080 là xong, sao lên VM lại thêm nhiều thứ vậy?

Đúng rồi, local đơn giản vì client và server cùng một máy:

Local:   curl -> loopback (127.0.0.1) -> Tomcat :8080
VM:      curl -> DNS -> Internet -> NSG -> ufw -> Nginx -> Tomcat :8080

Loopback không đi qua card mạng, không qua firewall cloud, thường không có TLS. Lên VM, mỗi lớp thêm vào là một chỗ có thể chặn. Bài 9.3 sẽ đi từng lớp đó.

Trong sơ đồ có cả Nginx lẫn Tomcat, mỗi cái lo phần nào?

Cả hai đều là HTTP server, nhưng vai trò khác:

Nhờ Nginx đứng trước, Tomcat chỉ cần listen 127.0.0.1:8080, không lộ ra Internet. Chi tiết ở bài 9.8 và 9.9.

Phỏng vấn hỏi "chuyện gì xảy ra khi gõ URL vào trình duyệt" thì trả lời sao?

Q1. Chuyện gì xảy ra khi client gọi https://api.example.com/orders/123?

🇻🇳 Tiếng Việt:

Đầu tiên client resolve DNS để lấy IP. Sau đó mở TCP connection tới port 443 bằng 3-way handshake, rồi làm TLS handshake để xác thực server và thống nhất key. Tiếp theo gửi HTTP request. Request đi qua load balancer, tới Nginx; Nginx kết thúc TLS và proxy sang Tomcat ở 8080. Tomcat giao cho một worker thread, DispatcherServlet gọi controller, controller lấy connection từ HikariCP để query Postgres. Response đi ngược lại cùng đường.

🇬🇧 English:

First the client resolves the domain via DNS. Then it opens a TCP connection to port 443 with a three-way handshake, followed by a TLS handshake to verify the server and agree on keys. Then it sends the HTTP request. The request goes through the load balancer to Nginx, which terminates TLS and proxies to Tomcat on port 8080. Tomcat assigns a worker thread, the DispatcherServlet calls the controller, and the controller borrows a connection from HikariCP to query Postgres. The response travels back the same way.

Q2. API chậm, bạn khoanh vùng thế nào?

🇻🇳 Tiếng Việt:

Em tách theo chặng: dùng curl -w để xem thời gian DNS, TCP connect, TLS và time-to-first-byte. Nếu TTFB lớn thì vấn đề ở server: xem access log Nginx, metric Tomcat thread, Hikari pending connection và slow query của DB. Nếu DNS hoặc connect lớn thì là vấn đề mạng.

🇬🇧 English:

I split it by stage: curl -w shows DNS, TCP connect, TLS and time to first byte. If TTFB is high, the problem is on the server side, so I check the Nginx access log, Tomcat thread metrics, Hikari pending connections and slow queries. If DNS or connect time is high, it is a network problem.

Cách nhớ ngắn gọn


Tự kiểm tra

Thứ tự các chặng từ client tới controller?

DNS, TCP handshake, TLS handshake, HTTP request, load balancer/NAT, Nginx, Tomcat worker thread, DispatcherServlet, controller, (DB qua connection pool).

502 và 504 từ Nginx nói lên điều gì?

502: Nginx không kết nối được/nhận phản hồi lỗi từ backend. 504: backend trả lời quá chậm, vượt timeout của Nginx.

Trong curl -w, làm sao ước lượng thời gian server xử lý?

Lấy time_starttransfer - time_appconnect (hoặc trừ time_connect nếu không có TLS).

Vì sao để Tomcat listen 127.0.0.1 khi có Nginx?

Chỉ Nginx trên cùng máy gọi được Tomcat; Tomcat không lộ trực tiếp ra Internet.