Screenshot 20191203 101244 Samsung Internet
24Th4

Tối ưu hóa App Performance bằng Amazon CodeGuru Profiler

 

Screenshot 20191203 101244 Samsung Internet

 

Amazon CodeGuru (Bản preview) ra mắt tại AWS re:Invent 2019 là dịch vụ phân tích các chỉ số hiệu suất của ứng dụng (App) và tự động đề xuất các thay đổi cần thiết đã. CodeGuru thực hiện điều này bằng cách phân tích runtime của ứng dụng (với CodeGuru Profiler) và tự động review các thay đổi trong source code (bằng CodeGuru Reviewer). Để hiểu rõ hơn, hãy cùng tìm hiểu Amazon CodeGuru Profiler là gì nhé!

 

Bài viết này sẽ đưa ra các thông tin tổng quát về cách thức hoạt động của CodeGuru Profiler, hướng dẫn sử dụng công cụ này cũng như nâng cao hiểu biết của bạn về hiệu năng ứng dụng. Giả định người đọc bài viết đã có một lượng kiến thức cơ bản về JVM (Java Virtual Machine) và các khái niệm liên quan như threads và call stacks.

 

Tại sao nên sử dụng CodeGuru Profiler?

CodeGuru Profiler cung cấp các thông tin và đánh giá chi tiết về quá trình chạy của App dựa trên runtime với một trình profiler chạy liên tục. Bằng cách thu thập dữ liệu runtime từ ứng dụng của bạn dựa trên các threads đang chạy và các đoạn code mà những threads này đang thực thi, CodeGuru Profiler giúp bạn biết ứng dụng của mình đang hoạt động như thế nào một cách trực quan, đồng thời chủ động đưa ra các đề xuất tối ưu hóa. Những đề xuất này tập trung vào việc giảm mức độ sử dụng CPU và độ trễ ứng dụng (latency), từ đó giảm chi phí infrastructure và mang đến cho khách hàng của bạn trải nghiệm nhanh hơn.

 

Những đề xuất tối ưu hóa này có thể giúp tiết kiệm đáng kể chi phí, đặc biệt đối với hệ thống lớn. Ở Amazon, CodeGuru Profiler đang chạy trên hơn 80.000 ứng dụng và tiết kiệm hàng chục triệu đô la nhờ các đề xuất tối ưu hóa. Công cụ này cùng lúc cũng được tận dụng để phân tích các vấn đề liên quan đến vận hành và tác động của chúng đến hiệu năng.

 

Tổng quan về CodeGuru Profiler

CodeGuru Profiler gồm ba component chính:

  • Agent – Chạy bên trong ứng dụng của bạn thăm dò trạng thái runtime hiện tại và gửi dữ liệu này đến CodeGuru Profiler
  • Bảng điều khiển (Console) – Cung cấp dữ liệu một cách trực quan về data đã được cung cấp từ agent và đưa ra đề xuất cải thiện hiệu năng
  • API – Cho phép quản lý các nhóm profile, truy xuất profile và các đề xuất cải tiến

 

CodeGuru Profiler hiện cung cấp một agent được thiết kế để sử dụng với các ứng dụng JVM (bạn có thể profile Java, Kotlin và Scala, nhưng CodeGury chủ yếu đưa ra các đề xuất cho Java). Khi bạn khởi động agent trong ứng dụng, agent sẽ sinh ra một thread mới trong JVM trên tất cả các thực thể (instance) trong ứng dụng của bạn. Mỗi giây (theo mặc định), agent thread sẽ check JVM về stack-trace của từng thread (hoặc một tập hợp con của thread) trong JVM hiện đang làm gì và trạng thái hiện tại của mỗi thread.

 

Cứ mỗi 5 phút (theo mặc định), agent sẽ gửi một bản tóm tắt thông tin này (gọi là 1 profile) cho back-end của CodeGuru Profiler. Các profile từ tất cả các máy chủ sẽ được tổng hợp lại vào một mốc thời gian, mỗi mốc cách nhau 5 phút.. Bạn có thể xem thông tin riêng lẻ được tổng hợp từ mỗi khoảng thời gian 5 phút, nhưng các khoảng thời gian 1 giờ hoặc 1 ngày thường sẽ cung cấp dữ liệu mang tính thống kê và chính xác hơn. Sơ đồ sau minh họa quá trình này:

Codeguru 1

 

Bạn có thể biến các thông tin từ profile trở nên trực quan bằng nhiều cách với CodeGuru Profiler, một vài cách trong số đó sẽ được chia sẻ trong bài viết này. Sau khi trình phân tích CodeGuru Profiler chạy, bảng điều khiển cung cấp các đề xuất cải thiện hiệu năng. Các đề xuất này cũng chỉ ra mức độ ảnh hưởng khác nhau của từng hướng tối ưu hóa nhằm giúp bạn xác định các vấn đề cần được ưu tiên và đưa ra các cách để giảm hoặc loại bỏ những ảnh hưởng đến hiệu năng trong quá trình giải quyết vấn đề.

 

Deploy CodeGuru Profiler

Bạn nên run CodeGuru Profiler trên môi trường production với lượng traffic từ khách hàng thực để có được những đánh giá thực tế về ứng dụng. Ngoài ra, CodeGuru Profiler cũng sẽ hỗ trợ test các thay đổi về hiệu năng trên môi trường pre-production trước khi được release lên productions. Profile môi trường production là cách tốt nhất để đảm bảo thời gian bạn dành cho việc tối ưu hóa thực sự mang đến những cải tiến đáng kể về hiệu năng trên product thật. 

 

Liên quan tới ứng dụng của bạn, CodeGuru Profiler thường thêm ít hơn 1% chi phí sử dụng CPU và sử dụng tối đa 100MB bộ nhớ. Bạn nên confirm với QA hoặc với release process hiện tại trước khi quyết định thêm CodeGuru Profiler agent để kiểm tra tính chính xác của các thông tin được gửi tới và đảm bảo chúng không có tác động tới ứng dụng của bạn.

 

Sắp xếp các ứng dụng của bạn thành các nhóm profiling

Nhóm profiling là cách CodeGuru Profiler nhóm nhiều thực thể (instance) trong ứng dụng của bạn vào một profile tổng hợp mà bạn sẽ thường thấy khi hợp nhất các profile từ một số host trong cùng một cluster mà tất cả đều chạy một ứng dụng như nhau.

 

Một nhóm profile thường đại diện cho một ứng dụng, mặc dù cách định nghĩa một ứng dụng nhiều khi lại nằm ở cá nhân bạn. Một cách khá phổ biến là nhóm các API liên quan thành 1 ứng dụng. Hoặc nếu các API hoàn toàn riêng rẽ thì tách ra thành nhiều nhóm profiling có thể sẽ tốt hơn.

 

Một số gợi ý để tạo nhóm profiling:

  • Sử dụng các nhóm profiling riêng biệt cho từng ứng dụng. Nếu bạn có các ứng dụng khác nhau với những mục đích khác nhau, bạn không nên sử dụng chung một nhóm profiling vì điều này có thể làm giảm độ chính xác khi phân tích. 
  • Sử dụng các nhóm profiling riêng cho từng vùng của ứng dụng. Điều này cho phép bạn so sánh hành vi của mỗi vùng, từ đó có thể phỏng đoán các vấn đề liên quan đến vận hành có ảnh hưởng đến những vùng cụ thể trong ứng dụng của bạn.
  • Sử dụng các nhóm profile riêng biệt cho từng giai đoạn của một ứng dụng. Môi trường testing và staging không nên submit profile chung với môi trường production. Hầu hết các môi trường testing không mô phỏng chuẩn xác 100% với môi trường production (cả về dữ liệu và code) do đó đặc tính thể hiện hiệu năng của 2 môi trường này cũng khác nhau. 

Những đề xuất này dẫn đến những group profiles có dạng tên như <my-application> -eu-west-1-beta và <my-application> -us-west-2-prod.

 

Bắt đầu với CodeGuru Profiler 

Ví dụ sau đây là kết quả bạn có thể đạt được nếu chạy CodeGuru Profiler trên ứng dụng của mình, bao gồm cả các hình ảnh trực quan hóa dữ liệu và các đề xuất tối ưu. Để biết thêm thông tin, hãy tham khảo Setting Up Amazon CodeGuru Profiler. Quy trình bao gồm các bước sau:

 

1.Tạo một nhóm profiling qua bảng điều khiển (console) CodeGuru Profiler.

2.Cung cấp cho ứng dụng của bạn quyền IAM để submit dữ liệu tới CodeGuru. Ứng dụng của bạn cần có quyền IAM để gửi profile và config agent. Bạn chỉ cần thêm Vùng, ID tài khoản và tên nhóm profiling:  

 

11

 

3. Thêm agent vào ứng dụng của bạn. Nếu ứng dụng của bạn sử dụng Maven hoặc Gradle, hãy làm theo các hướng dẫn được cung cấp trong tài liệu. Nếu không, bạn cần import agent JAR vào hệ thống một cách thủ công

4. Khởi động profiler khi ứng dụng của bạn bắt đầu chạy. Thêm code vào ứng dụng của bạn để xác định các nhóm profiling và vùng để submit dữ liệu tương ứng cũng như bất kỳ thông tin xác thực nào được yêu cầu. Tham khảo ví dụ dưới đây (Nếu CodeGuru đang ở bản preview, các API này có thể thay đổi):

 

11

 

Sau khi deploy CodeGuru Profiler lên ứng dụng, một profier sẽ được gửi đến sau 5 phút profiling. Có thể mất tới 15 phút để tổng hợp các profile từ tất cả các máy chủ của bạn, tiếp theo đó bạn có thể thấy bản mô tả bằng hình ảnh đầu tiên của mình trong bảng điều khiển CodeGuru Profiler. Mức độ chi tiết của bản mô tả đầu tiên phụ thuộc nhiều vào mức độ hoạt động của ứng dụng trong 5 phút đầu tiên profiling do một ứng dụng nhàn rỗi hầu hết thời gian sẽ không có nhiều điểm dữ liệu để vẽ. Tuy nhiên, bạn có thể khắc phục điều này bằng cách xem lại bản mô tả trong khoảng thời gian rộng hơn như một ngày hoặc thậm chí lên đến một tuần nếu ứng dụng của bạn có mức sử dụng CPU rất thấp.

 

Phải mất đến 24 giờ để xây dựng được báo cáo đề xuất đầu tiên cho bạn. Độ chính xác của các đề xuất này tỉ lệ thuận với lượng dữ liệu profile bạn phân tích. Bạn nên profile ứng dụng của mình liên tục trong giai đoạn này (và cả sau đó nếu bạn vẫn muốn tiếp tục được nhận các đề xuất mới).

 

Một bản báo cáo sẽ gợi ý các thay đổi cần thiết với ứng dụng mà CodeGuru Profiler xác định được và hướng dẫn các bước để thực hiện những cải tiến này. Nếu báo cáo của bạn không có bất kỳ đề xuất nào nghĩa là ứng dụng của bạn hiện không có bất kỳ vấn đề nào về hiệu suất mà CodeGuru Profiler có thể tìm ra. Tuy nhiên, bạn cũng cần check lại những bản báo cáo này trong tương lai bởi CodeGuru Profiler sẽ tìm cách nhận ra các vấn đề mới song song với sự những sự thay đổi trên ứng dụng của bạn theo thời gian.

 

Cách đọc bản visualization của CodeGuru Profiler

Bây giờ tôi sẽ đi qua một ví dụ về cách tôi sử dụng CodeGuru Profiler để tối ưu hóa ứng dụng của mình. Sau khi chạy CodeGuru Profiler lên ứng dụng, tôi có bản khái quát hóa đầu tiên về hiệu năng thời gian chạy của ứng dụng. Kiểu hình ảnh/sơ đồ hóa này thường được gọi là biểu đồ ngọn lửa (flame graph) và đây có thể là một công cụ rất hữu ích để bổ sung vào logs và metrics

 

Codeguru 2

 

Cách dễ nhất để đọc biểu đồ dạng ngọn lửa là bắt đầu từ một số frame mà bạn đã quen thuộc như phần entry point. Trong biểu đồ, Main (điểm số 1 trong ảnh chụp phía trên) là điểm bắt đầu của ứng dụng của tôi và ImageProcessor (ngay phía trên Main) là phần chứa hầu hết các business logic.

 

Xem kỹ trong biểu đồ, Amazon SQS xuất hiện ở giữa biểu đồ (điểm số 2). Ở bên phải, bạn có thể thấy một số thời gian dành cho việc serialize các đối tượng (điểm số 3), một số ghi log (điểm số 4) và một số khung xử lý hình ảnh phía góc phải (điểm số 5). Ở phía dưới bên trái, có một số frame garbage collection (6). Nhìn chung, điều này hợp lý bởi vì ứng dụng của tôi sử dụng Amazon S3 và Amazon SQS và một số thư viện xử lý hình ảnh.

 

Để tận dụng biểu đồ ngọn lửa này hiệu quả, bạn cần học cách đọc chúng. Mỗi hình chữ nhật là một frame mô tả 1 method call tại một vị trí cụ thể trong call stack. Các frame ở ngay phía trên tương ứng với các methods được gọi bởi frame này (đôi khi được gọi là callees) và các frame bên dưới là caller. Ví dụ: đối với frame AwsSyncClientBuilder.build (2), bạn có thể thấy nó được gọi bởi Main.sqsClient (khung phía dưới điểm số 2) và nó chỉ gọi AmazonSyncClientBuilder.build (khung phía trên điểm số 2). Điều này cho phép bạn ghép các phân cấp của các cuộc gọi trong suốt ứng dụng.

 

Độ rộng của mỗi frame tương ứng với phần trăm thời gian sử dụng CPU: khung càng rộng, thời gian execute hàm đó và các phương thức mà nó gọi càng dài. Khi đọc biểu đồ dạng này, bạn nên tập trung vào phần khung rộng nhất trước tiên và cố gắng xác định xem tại sao những frame này lại chiếm phần lớn thời gian CPU của bạn.

 

Khi agent kiểm tra 1 JVM thread, thread có thể ở trong một trong những trạng thái sau: 

  • Runnable – Thread đã được execute khi nó được lấy mẫu. Điều này có nghĩa là nó đã chạy hoặc được lên lịch để chạy.
  • Bị chặn – Thread đang đợi để nhập khối đồng bộ hóa Java hoặc chờ monitor nhưng có một thread khác hiện đang execute giữ và ngăn không cho thread này execute.
  • Đợi – Thread đang chờ tín hiệu từ một thread khác. Đây là trạng thái thread rất phổ biến khi làm việc với network request, disk I/O, và notify () và notify All() calls 
  • Thời gian chờ – trạng thái đợi bị timeout
  • Nhàn rỗi – Thread không có bất kỳ tác động nào đến CPU hay độ trễ. Ví dụ như một nhóm thread đang đợi request mới.
  • Native – Thread đã được lấy mẫu trong khi nó đang thực thi mã gốc thông qua Java Native Interface (JNI). CodeGuru Profiler map một số native frame với các trạng thread state  khác. Tuy nhiên nếu các frame left Native state, điều đó nghĩa là bạn không biết được các thread đang chạy hay đang chờ.

Để biết thêm thông tin về trạng thái của thread, vui lòng xem lại Get Thread State trong trang JVM Tool Interface.

 

Một khái niệm hữu ích khác là wall clock timethời gian CPU. Wall clock time cho biết lượng thời gian tiêu tốn trong thực tế khi chạy code. Ngược lại, thời gian CPU phản ánh số lượng CPU cycle cần thiết để hoàn thành công việc vận hành. Wall clock time tính cả thời gian chờ networks, disk I/O, và thời gian chờ các thread khác kết thúc. Sự khác biệt giữa hai khái niệm này đặc biệt quan trọng nếu bạn muốn giảm thiểu độ trễ.

 

Bản visualization này được gọi là CPU mode trong đó chỉ hiển thị thông tin từ các frame ở trạng thái Runnable, Blocked và Native. Bản mô tả này đưa ra các gợi ý để biết mức độ bận rộn của máy chủ CPU – điều này thực sự hữu ích nếu bạn muốn giảm sử dụng CPU để downsize. 

 

Bạn có thể xem thêm một số trạng thái thread khác nếu chuyển sang Latency mode. Chế độ này bao gồm tất cả các trạng thái thread trừ Idle và bạn có thể tận dụng những thông tin này để xác định các yếu tố ảnh hưởng đến wall clock time. Ảnh chụp dưới đây là biểu đồ ngọn lửa ở Latency mode, gồm disk I/O và network. Trong trường hợp này, ứng dụng vẫn dành phần lớn thời gian của mình trong ImageProcessor.extractTask (hàng dưới cùng thứ hai) và gần như toàn bộ thời gian là Runnable, điều đó có nghĩa là nó đang không phải đợi bất cứ điều gì.

 

Codeguru 3

 

Báo cáo về các đề xuất tối ưu hóa

Nút bên phải màn hình ở vị trí số 1 trong biểu đồ ngọn lửa phía trên cho biết CodeGuru Profiler đưa ra 3 đề xuất. Hãy click vào đó để xem chi tiết! Ảnh chụp màn hình dưới đây là một trong những đề xuất mà CodeGuru Profiler đưa ra đối với ứng dụng trong trường hợp phía trên. Một trong những điều đáng lưu ý là ứng dụng này đã dành 18,57% thời gian CPU để tạo các client AWS SDK mới. CodeGuru Profiler gợi ý bạn giảm xuống ít hơn 1% thời gian CPU và bạn nên tái sử dụng clients càng nhiều càng tốt.

Codeguru 4 (1)

Đối với trường hợp sử dụng này, chúng ta nên tái sử dụng SDK client. Do đó, tôi đã cache Amazon SQS client. Ảnh chụp màn hình sau đây cho thấy profile ở chế độ Latency sau khi được cập nhật với ít profile trong trạng thái Runnable hơn và các trạng thái Waiting xuất hiện nhiều hơn. Điều đó nghĩa là có nhiều profile đang được được sử dụng cho network operation, và ít tiêu tốn CPU. Trong ảnh chụp màn hình bên dưới, frame ImageProcessor.extracTask trong trạng thái Runnable giảm xuống chỉ còn 0.62% tổng thời gian. Đây thực sự là một sự cải thiện đáng kể. 

 

Codeguru 5

 

Nhờ những cải tiến này, độ trễ của ứng dụng trên mỗi yêu cầu đã giảm, điều này mang lại cho khách hàng những trải nghiệm tốt hơn. Tôi cũng đã giảm số lượng core CPU trên máy chủ chạy ứng dụng này để tiết kiệm chi phí của team. Nếu được tiếp tục tối ưu hóa, tôi có thể sẽ tìm cách để cải thiện network bằng cách chạy các request song song hay thậm chí cache chúng nếu có thể.

 

Kết luận

Bài viết này đã giải thích các nội dung chính liên quan đến CodeGuru Profiler như vai trò của agent, tại sao cần profile trên môi trường production và cách setup các nhóm profiling. Bài viết cũng đi qua một ví dụ ngắn gọn về cách đọc biểu đồ ngọn lửa và sử dụng các đề xuất tối ưu hóa từ CodeGuru.

 

Bạn có thể bắt đầu sử dụng CodeGuru Profiler bằng cách truy cập vào CodeGuru console.

 

Tác giả: Isaac Jordan

Bài viết gốc: Link

Người dịch: Vân Phạm, Nhật Anh

Like và follow fanpage của Pixta Việt Nam để cập nhật các thông tin công nghệ hữu ích nhé!