Heap dump trong Java

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ẽ chia sẻ một kỹ thuật hay nói chính xác hơn một cách để các bạn có thể theo dõi được là khi chương trình của chúng ta chạy, nó sẽ chiếm bộ nhớ như thế nào và những object nào được tạo ra nhiều nhất. Việc này để làm gì thì các bạn xem tiếp ở phần dưới nhé, cái này là mở đầu thôi, hehe.

Đối với các bạn làm backend như mình thì rất có thể sẽ gặp một tình huống chỉ xảy ra trên môi trường production đó là khi ứng dụng của chúng ta chạy một thời gian, nó sẽ ngốn rất nhiều bộ nhớ và thỉnh thoảng warning dù cho chúng ta có khai báo maxHeap cho ứng dụng là bao nhiêu đi nữa. (Các project của mình thường khai báo 4 – 8G cho maxHeap).

Đến lúc này thì sẽ xảy ra lỗi cực kỳ nghiêm trọng, ứng dụng sẽ không thể chạy được nữa do thiếu bộ nhớ. Cách giải quyết tạm thời cho vấn đề này là restart lại chương trình và tăng maxHeap cho ứng dụng. Tuy nhiên, cách làm này không giải quyết được vấn đề triệt để. Vì nếu vấn đề nằm ở việc code của ứng dụng không giải phóng tốt bộ nhớ thì lỗi này sẽ lại tiếp tục xảy ra trong một ngày không xa.

Có một cách nữa là chúng ta sẽ cho chương trình tự động restart sau một khoảng thời gian nhất định để giải phóng vùng nhớ, cách này có vẻ khả thi hơn. Tuy nhiên nếu ứng dụng sử dụng cache local,  restart sẽ ảnh hưởng đến performance do lúc ban đầu warm up cache.

Và nếu bạn nghi ngờ là ứng dụng chưa được tốt trong việc quản lý bộ nhớ thì bạn có thể kiểm tra bằng cách gọi dump heap. Bằng cách này bạn có thể xem được là tại lúc ứng dụng bị đầy bộ nhớ, object nào là được tạo nhiều nhất và chiếm bộ nhớ nhiều nhất, từ đó phân tích và tối ưu code.

Dump heap chỉ là thuật ngữ chỉ “hình ảnh” toàn bộ các object của chương trình được tạo ra khi chương trình chạy tại một thời điểm trên bộ nhớ. Và để “chụp” hình ảnh này thì java đã hỗ trợ tool mà mình rất hay dùng đó là jmap (có rất nhiều cách để dump heap)

Đầu tiên bạn cần xác định pid của ứng dụng bằng cách sử dụng cmd:

ps -ef|grep "tên ứng dụng"

Ứng dụng của mình tên là pkg999c, cái này config trong file build

Sau khi có được pid của ứng dụng thì bạn chỉ việc chạy lện dump của jmap

jmap -dump:live,file=path/name. pid

Ở lệnh trên mình đã lấy được pid của chương trình pkg999c3443

Đến lúc này, chúng ta chỉ việc sử dụng VisualVM để load file dump vừa được tạo và xem thống kê object được tạo nhiều nhất và chiếm nhiều vùng nhớ nhất, từ đó tối ưu chương trình (nếu cần thiết)

Hình ảnh về các instances của ứng dụng pkg999

Vừa rồi là phần trình bày của mình về cách để dump heap một ứng dụng trong Java, thật là đơn giản phải không nào? Các bạn có thể thử ngay với một chương trình đơn giản để hiểu hơn nhé.

Bài viết của mình đến đây là hết nhé, thỉnh thoảng mình sẽ share những gì mình học được hoặc những kinh nghiệm trong quá trình làm việc, nên bạn nào có hứng thú với các bài viết của mình thì hãy follow page để nhận thông báo khi có bài viết mới nhé. Chào thân ái và hẹn gặp lại.

Tham khảo thêm

https://www.javaworld.com/article/2072864/heap-dump-and-analysis-with-visualvm.html

 

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