127.0.0.1 và localhost khác nhau như thế nào??
Chắc hẳn trong đời làm dev của chúng ta, ai cũng ít nhất một lần gặp phải cái kịch bản dở khóc dở cười này: Lấy source code về, cài cắm database đầy đủ, setup file .env với cấu hình DB_HOST=localhost. Chạy app lên và... BÙM! "Connection refused" hoặc "Can't connect to local MySQL server through socket".
Cay cú, bạn lướt StackOverflow, thấy một pháp sư nào đó bảo đổi localhost thành 127.0.0.1 đi. Bạn làm theo và... ủa, ứng dụng chạy phà phà!
Tại sao lại như vậy? Thầy cô ở trường luôn dạy localhost chính là 127.0.0.1 cơ mà? Hai cái này đổi qua đổi lại có gì khác nhau đâu? Hôm nay, chúng ta cùng bóc trần sự thật về cú lừa thế kỷ này nhé.
1. Bản chất sự khác biệt: Tên gọi vs Địa chỉ
Hãy tưởng tượng thế này cho dễ hiểu:
127.0.0.1giống như tọa độ GPS chính xác ngôi nhà của bạn (Ví dụ: Vĩ độ X, Kinh độ Y). Nó là một địa chỉ IP (cụ thể là IPv4 loopback address). Hệ điều hành nhìn vào cái này là biết ngay cần phải giao tiếp với chính máy tính hiện tại mà không cần suy nghĩ.localhostgiống như việc bạn gọi ngôi nhà đó là "Nhà mình". Nó là một cái Tên miền (Hostname/Alias). Khi bạn bảo máy tính gửi dữ liệu tới "Nhà mình", máy tính không thể gửi thẳng được. Nó phải tra từ điển (quá trình phân giải DNS) để xem "Nhà mình" nằm ở tọa độ nào.
Và cuốn từ điển đó trên máy tính của bạn chính là file hosts (nằm ở /etc/hosts trên Linux/Mac hoặc C:\Windows\System32\drivers\etc\hosts trên Windows). Mặc định, nó sẽ map chữ localhost với 127.0.0.1.
Nghe có vẻ cũng chỉ chậm hơn một bước tra từ điển (vài mili-giây, chả bõ bèn gì), tại sao lại sinh ra bug? Vấn đề bắt đầu từ đây.
2. Cú lừa thứ nhất: Câu chuyện của IPv4 và IPv6
Thời đại bây giờ, IPv6 bắt đầu lên ngôi. Khi bạn gõ localhost, cuốn từ điển hosts không chỉ trả về 127.0.0.1 (IPv4) mà nó còn có thể trả về ::1 (địa chỉ loopback của IPv6).
Gần đây nhất, anh em hệ Node.js (từ bản v17 trở lên) đã bị ăn hành ngập mặt vì điều này. Node.js ưu tiên phân giải localhost ra IPv6 (::1). Nhưng khốn nỗi, cái server database của bạn (như MySQL hay Redis) lại chỉ đang cấu hình lắng nghe (listen) trên IPv4 (127.0.0.1).
Thế là thằng Node.js mang data qua nhà IPv6 gõ cửa, nhưng server database lại đang ngồi ở nhà IPv4. Kết quả: Connection refused! Đổi thành 127.0.0.1 là ép ứng dụng bỏ qua bước tra từ điển và đi thẳng đến đúng nhà IPv4, thế là chạy.
3. Cú lừa thứ hai: MySQL và cú "bẻ lái" Unix Socket
Đây là nguyên nhân kinh điển nhất khiến anh em gãi đầu gãi tai.
Trong thế giới của hệ quản trị cơ sở dữ liệu (đặc biệt là MySQL và các thư viện kết nối trong PHP), họ có một tính năng tối ưu hóa rất "thông minh" nhưng cũng rất hay "bóp dái" người dùng:
- Khi bạn khai báo Host là
127.0.0.1: Thư viện kết nối sẽ tuân thủ nguyên tắc mạng, sử dụng giao thức TCP/IP thông qua port (ví dụ 3306) để kết nối. - Nhưng khi bạn khai báo Host là
localhost: MySQL lại tự động ngầm hiểu là "À, hai thằng mình đang ở chung một máy tính cơ mà, xài mạng mẽo làm gì cho chậm chạp rườm rà. Bọn mình kết nối trực tiếp qua Unix Domain Socket đi!".
(Unix Socket là cơ chế giao tiếp trực tiếp qua file hệ thống, bỏ qua toàn bộ tầng Network, nên tốc độ nhanh hơn TCP/IP kha khá).
Vấn đề là: Để kết nối qua Unix Socket, ứng dụng của bạn phải biết chính xác cái file socket đó nằm ở đâu (thường là /tmp/mysql.sock hoặc /var/run/mysqld/mysqld.sock). Nếu ứng dụng tìm sai đường dẫn file socket, hoặc quyền truy cập file có vấn đề, bạn sẽ nhận ngay lỗi: Can't connect to local MySQL server through socket.
Khi bạn đổi về 127.0.0.1, bạn đang nói với MySQL rằng: "Dẹp cái trò tối ưu socket đi, cứ dùng TCP/IP cổng 3306 cho tao". Và bùm, mọi thứ lại bình yên.
Chốt lại: Thế tóm lại nên xài cái nào?
Thực ra không có đúng sai tuyệt đối, nhưng đây là lời khuyên thực chiến cho anh em để tránh mất thời gian với ba cái lỗi môi trường ngớ ngẩn:
- Muốn an toàn, ăn chắc mặc bền: Cứ táng
127.0.0.1vào file.env. Nó rõ ràng, tường minh, luôn luôn dùng TCP/IP và luôn là IPv4. Đảm bảo chạy phát ăn ngay trên mọi môi trường. - Chỉ dùng
localhostkhi: Bạn thực sự hiểu stack công nghệ của mình đang làm gì (nó ưu tiên IPv4 hay IPv6, nó có xài Unix Socket ngầm không). Hoặc khi bạn cần tối ưu tối đa hiệu năng (ví dụ setup web server và database chung một con VPS, muốn dùng Unix Socket để bóp nốt chút performance).
Code chạy được là một chuyện, hiểu tại sao nó chạy lại là một câu chuyện thú vị khác. Hy vọng bài viết này đã giải đáp được những thắc mắc "tâm linh" của anh em xung quanh cái biến môi trường quen thuộc này.
Anh em đã từng tốn bao nhiêu tiếng cuộc đời cho cái lỗi localhost này rồi? Kể nghe chơi dưới phần comment nhé. Hẹn gặp lại anh em ở bài viết sau!
All rights reserved