Load balancing với nginx

Chào mừng các bạn đã quay trở lại với thachleblog. Bài viết hôm nay mình sẽ cùng các bạn tìm hiểu về cơ chế cân bằng tải (mình sẽ gọi đúng tên là load balancing) được sử dụng dưới backend.

Một ứng dụng có nhiều user chắc chắn sẽ phải sử dụng đến phương pháp này. Thông thường, việc tự xây dựng một server backend thì quá tốn kém và phức tạp nên phần lớn sẽ sử dụng các service có sẵn như AWS, Google Cloud, Microsoft Azure…

Tuy nhiên, việc hiểu về cơ chế load balancing sẽ cho chúng ta cái nhìn tổng quan hơn về hệ thống backend cũng như có thể hiểu được nguyên nhân của một vài lỗi “hư cấu”. Vậy các lỗi hư cấu đó là gì? Cơ chế balancing như thế nào? Chúng ta cùng bắt đầu nhé.

Giới thiệu

Load balancing là cơ chế phân chia request cho server. Một ứng dụng lớn, có nhiều user, thường sẽ được deploy trên các server khác nhau. Lúc này, ứng dụng sẽ gồm nhiều bản, gọi là instance chạy song song, xử lý độc lập nhưng dùng chung database.

Hình minh họa cơ chế load balancing

Hình minh họa cơ chế load balancing

Mục đích

Sử dụng load balancing với mục đích cân bằng tải và tăng khả năng chịu tải của server trong hệ thống phân tán. Ví dụ nếu một server có khả năng chịu tải 1000 user cùng lúc nhưng nếu có 2 instance thì sẽ tăng khả năng chịu tải lên 2000 user. Bình thường, tùy cơ chế mà ta cấu hình, số request sẽ được “chia” cho 2 instance để giảm tải cho từng server. Hệ thống sẽ xử lý nhanh hơn.

Ngoài ra, load balancing sẽ giảm thiểu khả năng xảy ra lỗi hay nói cách khác là tăng mức độ tin cậy của ứng dụng. Ví dụ, vì lí do gì đó (rất ít xảy ra) mà một server bị lỗi, hệ thống sẽ có cơ chế kiểm tra và chuyển request sang server đang hoạt động để xử lý.

Cơ chế

Tùy vào hệ thống balacing mà sẽ có cơ chế để balancer (nơi tiếp nhận request), xác định server nào sẽ nhận và xử lý request. Hôm nay, mình chỉ giới thiệu sơ lược về các cơ chế phân chia request của nginx balancer, hệ thống server được sử dụng cũng khá rộng rãi, các bạn có thể đọc thêm và nginx tại đây.

Ví dụ về cấu hình đơn giản của nginx cho việc load balancing:

http {
   upstream myapp1 {
       server srv1.thachleblog.com;
       server srv2.thachleblog.com;
       server srv3.thachleblog.com;
   }
   server {
       listen 80;
          }
}

Dựa vào cấu hình ta có thể thấy, có 3 instance của thachleblog.com được chạy trên 3 con server srv1, srv2, serv3. Để xác định được server nào sẽ tiếp nhận request thì có các cơ chế sau:

Round-robin: cơ chế phân chia request cho server dựa vào thuật toán round – robin. Các bạn muốn biết chi tiết về round robin có thể đọc thêm tại đây. Ở ví dụ trên, khi không cấu hình gì thêm, balancer sẽ mặc định sử dụng cơ chế round – robin

Least-connected: request sẽ được chuyển đến cho server đang có ít request đang active nhất

upstream myapp1 {
       least_conn;
       server srv1.thachleblog.com;
       server srv2.thachleblog.com;
       server srv3.thachleblog.com;
   }

ip-hash: cơ chế phân chia request dựa vào ip của client, đảm bảo rằng với cùng 1 ip, sẽ được xử lý trên cùng 1 server. Trừ trường hợp có lỗi xảy ra.

upstream myapp1 {
   ip_hash;
   server srv1.thachleblog.com;
   server srv2.thachleblog.com;
   server srv3.thachleblog.com;
}

weighted-load: ta có thể tự phân chia request dựa vào sức tải của từng server.

  upstream myapp1 {
       server srv1.thachleblog.com weight=3;
       server srv2.thachleblog.com weight=2;
       server srv3.thachleblog.com;
   }

Ví dụ với cấu hình trên, 6 request đến sẽ được chia lần lượt srv1 3 request, srv2 2 request và srv3 1 request.

Kết

Vừa rồi là phần giới thiệu của mình về load balancing cũng như cơ chế phân chia request của nginx balancer. Việc hiểu cơ chế này sẽ giúp các bạn có cái nhìn tổng quan hơn về hệ thống. Ví dụ trường hợp lỗi chỉ xuất hiện “chập chờn” trên môi trường live thì các bạn có thể nghĩ đến khả năng do cấu hình hoặc cache trên các server khác nhau dẫn đến kết quả không đồng nhất …

Mọi thắc mắc hay góp ý vui lòng comment bên dưới, mình sẽ follow nhé. Chào các bạn và hẹn gặp lại các bạn ở các bài viết sau.

Tham khảo thêm:

http://tutorials.jenkov.com/software-architecture/load-balancing.html

https://nginx.org/en/docs/http/load_balancing.html  

Clean Code – để những dòng code trở nên chuyên nghiệp hơn

Bất kỳ kẻ ngu ngốc nào cũng có thể viết code mà máy tính có thể hiểu

Lập trình viên giỏi là người viết code để con người có thể hiểu.

Martin Fowler, 2008

Chào mừng các bạn đã quay trở lại với thachleblog. Bài viết hôm nay mình sẽ nói về chủ đề Clean Code trong Java. Clean Code là chủ đề tuy không mới nhưng sẽ không bao giờ là cũ. Đối với mỗi người, chắc hẳn ai cũng có lúc “cảm thấy tự hào” với những dòng code của mình. Tuy nhiên, sau một thời gian vài tháng đến một năm, phần lớn khi đọc lại, chúng ta sẽ cảm thấy cái thứ mà mình tự hào trước kia giờ như đống shi* :))).

Clean Code - để những dòng code trở nên chuyên nghiệp hơn

Do đó, bài viết hôm này mình mong muốn chia sẻ với các bạn 1 vài kinh nghiệm đã học được và áp dụng để code rõ ràng và dễ đọc hơn. Tất nhiên, sẽ rất thiếu sót, do đó, cũng rất mong các bạn cùng chia sẻ để mình cập nhật thêm. Nào, chúng ta cùng bắt đầu nhé.

Nói về lợi ích của Clean Code, rất đơn giản. Code sẽ dễ đọc, ít khả năng gây ra bug và dễ bảo trì, nâng cấp hơn. Vậy làm thế nào để code trở nên “clean”? Dưới đây là vài điều mình đã học được trong cuốn “Clean Code” và trong quá trình làm việc:

– Đặt tên

+ Class, Object nên là danh từ hoặc cụm danh từ

+ Method nên sử dụng động từ hoặc cụm động từ

Class, object, variable, method tốt nhất sẽ cần trả lời được 3 câu hỏi: nó là cái gì (what), nó để làm gì (why) và dùng như thế nào (how).

Tên rất quan trọng, tên rõ ràng sẽ giúp chúng ta hiểu được business của method, class, module một cách dễ dàng. Cùng xem ví dụ sau:

public List<int[]> getThem() {
	List<int[]> list1 = new ArrayList<int[]>();
	for (int[] x : theList)
		if (x[0] == 4)
		list1.add(x);
		return list1;
}

Giả sử bạn debug đến đoạn code này, bạn sẽ nghĩ gì? Các cái tên getThem(), x, list1 củ chuối rồi, còn thêm cái số 4 “huyền bí”, sao lại là “4”? Sẽ mất thời gian để hiểu logic của method trên.

Chúng ta đến với đoạn code khác:

public List<int[]> getFlaggedCells() {
	List<int[]> flaggedCells = new ArrayList<int[]>();
	for (int[] cell : gameBoard)
		if (cell[STATUS_VALUE] == FLAGGED)
		flaggedCells.add(cell);
		return flaggedCells;
}

Đoạn code này có gì khác biệt? Những cái tên rõ ràng hơn, method sẽ có chức năng lấy danh sách những “Cell” được đánh dấu (flagged). Đoạn for thì ra là duyệt tất cả các cell trên gameBoard nếu status là “flagged” thì sẽ “get”. Có vẻ rõ ràng và dễ hiểu hơn. Like!

Tuy nhiên vẫn còn chỗ có thể làm tốt hơn, ta cùng xem đoạn code sau

public List<Cell> getFlaggedCells() {
	List<Cell> flaggedCells = new ArrayList<Cell>();
	for (Cell cell : gameBoard)
		if (cell.isFlagged())
		flaggedCells.add(cell);
		return flaggedCells;
}

Đoạn code sẽ rõ ràng hơn khi ta tạo class Cell thay cho int[].

Chúng ta qua phần tiếp theo

– Không return null

List<Employee> employees = getEmployees();
	if (employees != null) {
		for(Employee e : employees) {
			totalPay += e.getPay();
		}
}

Đối với đoạn code trên, để code “clean” hơn và giảm khả năng xảy ra NullPointerException, chúng ta cần trị ở “gốc”, tức là sẽ không bao giờ return về 1 giá trị null.

Đoạn code getEmployees() sẽ có đoạn như sau:

public List<Employee> getEmployees() {
	if( .. there are no employees .. )
	return Collections.emptyList();
}

– Sử dụng các thư viện

Sử dụng các thư viện (libs) có sẵn NumberUtils, StringUtils, IOUtils, CollectionUtils… sẽ giúp chúng ta giảm thiểu thời gian code và debug vì đối với libs tốt thì khả năng xảy ra lỗi là rất hiếm. Cùng xem ví dụ đơn giản sau:

 NumberUtils.toInt(studentId, 0)

Đoạn code trên sẽ trả về giá trị mặc định là 0 nếu studentId là null, empty hoặc không hợp lệ. Nếu tự viết, chúng ta sẽ mất thời gian để xử lý những điều trên và đôi khi phát sinh lỗi không đáng có.

– Một vài lưu ý khác

– Sử dụng properties hoặc final variable cho các instant

– Clean đúng nghĩa đen: xóa các packages, classes, methods, fields, parameters, variables không sử dụng. (tránh comment để đó, nhìn rất “gai” ^.^)

– Chia nhỏ method nhất có thể (mỗi method chỉ thể hiện 1 chức năng)

– Viết các method thường được sử dụng thành class Utils để sử dụng lại code, tránh trùng lặp code

Sẽ cập nhật thêm ….

Hẹn gặp lại các bạn ở các bài viết sau.

Tham khảo thêm: Clean Code – Robert C.Martin