- vừa được xem lúc

Có thể chạy backend chung port không

0 0 4

Người đăng: Nguyen Minh Tuan

Theo Viblo Asia

TL;DR: Chỉ cần bind vào 2 địa chỉ mạng khác nhau là được.

Giới thiệu

Nếu các bạn còn thắc mắc tại sao khi chạy backend thì mọi người hay nói "bind vào địa chỉ 0.0.0.0" thì frontend mới truy cập được, còn bind vào địa chỉ localhost hay 127.0.0.1 thì frontend sẽ không truy cập được thì đây là bài viết dành cho bạn. Các bạn cần có một chút kiến thức về hệ điều hành, địa chỉ ip là gì, port là gì, backend, frontend, ...

Bắt đầu

Như các bạn đã biết khi frontend gửi một request thì request đó sẽ đi từ máy client (desktop, mobile, laptop, ...) qua card mạng của máy đó sau đó được chuyển tiếp thông qua một vài thiết bị mạng như switch, wifi, router, public internet cuối cùng sẽ đến card mạng của máy đích và được hệ điều hành sử lý sau đó.

Nếu bạn gọi tới localhost thì vẫn phải qua một thiết bị chính là thiết bị bạn đang sử dụng.

Câu chuyện của việc bind 0.0.0.0, localhost, 127.0.0.1 khác nhau như nào - cùng tìm hiểu chi tiết phía dưới nhé.

Thử nghiệm

Mình sẽ viết một webserver đơn giản bằng nodejs, sau đó chạy chúng với các trường hợp bind khác nhau.

const http = require("http");
const express = require("express"); const port = process.env.PORT || 8080;
const bind = process.env.BIND; console.log(bind, port); const app = express();
app.get('/', (req, res) => res.send('Hello, World!\n')); const server = http.createServer(app);
server.listen(port, bind);

Chạy thử project, ở đây mình truyền bind thông qua biến môi trường BIND

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ BIND=localhost node main.js localhost 8080

Ok vậy là đã chạy thành công backend ở port 8080, các bạn có thể kiểm tra bằng lệnh netstat

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ netstat -lnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State ...
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN ...

Nếu các bạn ở trên Windows các bạn có thể vào Resource Monitor > Network > Listening Ports để xem thông tin liên quan tới port.

Trước khi thử nghiệm với request mình sẽ show các bạn thông tin network của máy đang chạy backend (mình đã lược bỏ các thông tin không cần thiết).

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:97ff:fe97:6cf7 prefixlen 64 scopeid 0x20<link> ether 02:42:97:97:6c:f7 txqueuelen 0 (Ethernet) lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500 inet 10.103.107.31 netmask 255.255.255.0 destination 10.103.107.31 inet6 fe80::50b8:173b:e4af:d62b prefixlen 64 scopeid 0x20<link> unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) wlp2s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.14 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 2001:ee0:8202:d8a3:3522:e37:5cb0:9edc prefixlen 64 scopeid 0x0<global> ether 38:ba:f8:95:07:3b txqueuelen 1000 (Ethernet) tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ 

Ở đây mình có 3 card mạng:

  • wlp2s0: là card mạng wifi, đang bắt wifi và có ip là 192.168.1.14
  • tun0: là card mạng ảo, do openvpn quản lý, ip của máy hiện tại trong vpn là 10.103.107.31
  • lo: là card mạng ảo, viết tắt của loopback, card mạng đặc biệt này tham chiếu tới chính máy đó, tí nữa các bạn sẽ hiểu ý của mình.

Các bạn chưa hiểu card mạng có thể xem thêm phần Giải thích thêm phía dưới.

Ok vậy mình cùng test thử các request tới các địa chỉ khác nhau và từ các máy khác nhau xem sao:

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ curl localhost:8080
Hello, World!
tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ 

Việc gửi request tới localhost trên chính máy tính đó: Thành công.

Giờ mình sẽ chuyển qua một máy khác.

vagrant@tuana9a-dev ~ $ ifconfig tun0
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500 inet 10.103.107.8 netmask 255.255.255.0 destination 10.103.107.8 ...
vagrant@tuana9a-dev ~ $ curl 10.103.107.31:8080
curl: (7) Failed to connect to 10.103.107.31 port 8080: Connection refused
vagrant@tuana9a-dev ~ $

Từ một máy tính trong cùng vpn có ip là 10.103.107.8: Không thành công.

Giờ mình sẽ thử chạy lại backend với BIND=0.0.0.0 xem sao nhé

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ BIND=0.0.0.0 node main.js 0.0.0.0 8080

Trên cùng máy

tuana9a@tuana9a-XPS-13-9370:~$ curl localhost:8080
Hello, World!
tuana9a@tuana9a-XPS-13-9370:~$ 

Từ một máy khác trong mạng vpn

6:17:29 vagrant@tuana9a-dev ~ curl 10.103.107.31:8080
Hello, World!

Lần này đã được: địa chỉ 0.0.0.0 là một địa chỉ đặc biệt, địa chỉ này mang ý nghĩa tôi sẽ lắng nghe ở mọi card mạng trong máy tính hiện tại, tức bất kể request đến từ card mạng nào miễn là tới được máy tính hiện tại và có port là 8080 thì hệ điều hành sẽ forward nó tới process backend của bạn sau đó backend xử lý và trả về cũng thông qua card mạng đó và đi ngược về client.

Việc bind vào địa chỉ 0.0.0.0 khá tiện lợi (kiểu nhạc nào cũng nhảy) khi các bạn không cần am hiểu quá về network. Vậy liệu chúng ta có thể giới hạn lại việc backend của chúng ta chỉ lắng nghe từ một dải ip được hay không, đây chính là ý nghĩa thực sự của bind.

Ví dụ: Bạn có 2 hệ thống website ở trên cùng một máy tính và cùng ở port 8080 bạn không muốn phải đổi port và đi nhớ port nào cho website nào. Nếu máy tính đó có nhiều card mạng, ví dụ như máy tính hiện tại của mình có 2 card mạng:

  • wifi với ip: 192.168.1.14
  • openvpn với ip: 10.103.107.31

Thì chúng ta có thể chạy 2 backend và bind vào 2 địa chỉ ip tương ứng. Mình sẽ sửa app của mình đi một chút và chạy theo kiểu trên

const http = require("http");
const express = require("express"); const port = process.env.PORT || 8080;
const bind = process.env.BIND;
const id = process.env.ID; // Thêm cái này để phân biệt backend console.log(bind, port); const app = express();
app.get('/', (req, res) => res.send(`Hello, World! ${id}\n`)); const server = http.createServer(app);
server.listen(port, bind);

Chạy lại lệnh trên ở shell (terminal) hiện tại

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ BIND=192.168.1.14 ID=1 node main.js
192.168.1.14 8080

Mở thêm một shell (terminal) khác

tuana9a@tuana9a-XPS-13-9370:~/Projects/test$ BIND=10.103.107.31 ID=2 node main.js
10.103.107.31 8080

Test trên cùng máy

curl localhost:8080
tuana9a@tuana9a-XPS-13-9370:~$ curl localhost:8080
curl: (7) Failed to connect to localhost port 8080 after 0 ms: Connection refused
tuana9a@tuana9a-XPS-13-9370:~$ tuana9a@tuana9a-XPS-13-9370:~$ curl 192.168.1.14:8080
Hello, World! 1
tuana9a@tuana9a-XPS-13-9370:~$ tuana9a@tuana9a-XPS-13-9370:~$ curl 10.103.107.31:8080
Hello, World! 2
tuana9a@tuana9a-XPS-13-9370:~$ 

Test trên máy khác

6:17:50 vagrant@tuana9a-dev ~ curl 10.103.107.31:8080
Hello, World! 2
6:50:40 vagrant@tuana9a-dev ~ 

Vậy là máy ở giải mạng 10.103.107.x sẽ được backend 2 "phục vụ". Ngon vậy là có thể chạy 2 backend cùng port trên cùng một máy rồi.

Giải thích thêm

Giải thích nhanh card mạng: đây là thứ giúp hệ điều hành (OS) có thể truy cập internet và gửi các gói tin trong mạng, card mạng ảo đơn giản là cơ chế ảo hóa của hệ điều hành, thường các card mạng ảo sẽ được quản lý bởi phần mềm hệ thống và các phần mềm này sẽ điều khiển logic của các gói tin nếu gói tín đó tới được card mạng ảo.

VD: Với card mạng vật lý: khi bạn vào facebook, bạn gửi gói tin từ máy tính của bạn qua router-wifi sau đó gói tin của bạn tới card mạng máy chủ facebook, máy chủ sau đó forward tới server backend của facebook, sau đó server facebook xử lý và trả về kết quả, gói tin đi qua internet, đi tới router-wifi của bạn, sau đó router-wifi sẽ gửi gói tin này tới máy tính của bạn, card mạng trên máy tính của bạn (card wifi) sẽ nhận gói tin này và sau đó đưa cho OS sử lý và OS chuyển tiếp gói tin tới trình duyệt (Chrome chẳng hạn) và hiển thị trên giao diện cho bạn.

Với card mạng ảo, mình sẽ lấy openvpn làm ví dụ: mô hình của openvpn hay các vpn truyền thống sẽ có một vpn-server tập chung, sau đó các máy client sẽ cài một phần mềm vpn tương ứng (openvpn) để kết nối vào server tập chung này. Khi các bạn kết nối openvpn thì các bạn sẽ được cấp 1 ip "ảo" ứng với đó là một card mạng ảo (10.103.107.31 và "tun0" ở phía trên là một ví dụ).

Nhiệm vụ của card mạng ảo này như sau: nếu máy tính của mình gửi request tới một địa chỉ nằm trong dải 10.103.107.0/24 thì theo routing table OS sẽ sử dụng card mạng "tun0" này để gửi gói tin, card mạng ảo này thực chất được quản lý bởi phần mềm vpn client tương ứng, phần mềm này sau đó sử dụng card mạng vật lý để chuyển tiếp gói tin tới vpn server bằng kết nối đã thiết lập trước đó sau đó vpn server sẽ gửi tiếp gói tin đó tới máy trong mạng.

Như vậy về mặt hành vi chúng ta đang truy cập một máy tính khác trong dải mạng private (10.x.x.x) mà không cần phải ngồi chung văn phòng - Từ đó sinh ra VPN: Virtual Private Network.

Hết.

Cảm ơn ae đã đọc 😃

Bình luận

Bài viết tương tự

- vừa được xem lúc

RESTful API Design: Best Practices

Hey hey hey hey, cuối năm cũng khá bận bịu công việc này kia nên cũng không có nhiều thời gian viết bài phục vụ anh em được. Nay mình xin chia sẻ một vài những tiêu chí mà mình hay sử dụng khi viết REST API.

0 0 36

- vừa được xem lúc

18. Responsive là gì?

Truy cập http://fullstack.edu.

0 0 40

- vừa được xem lúc

19. Media queries?

Truy cập http://fullstack.edu.

0 0 39

- vừa được xem lúc

20. Tablet responsive

Truy cập http://fullstack.edu.

0 0 29

- vừa được xem lúc

21. Mobile menu responsive

Truy cập http://fullstack.edu.

0 0 27

- vừa được xem lúc

22. Mobile menu fix bug

Truy cập http://fullstack.edu.

0 0 23