Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ Performance, Stress và Load Testing trong Rails

Test là phần không thể thiếu của hầu hết các ứng dụng Rails hoạt động tốt, nơi việc bảo trì không phải là "ác mộng" và các tính năng mới được thêm vào liên tục hoặc các tính năng hiện có được cải tiến. Tuy nhiên, đối với nhiều ứng dụng, môi trường production là nơi chúng lần đầu tiên chịu tải trọng lớn hoặc lưu lượng truy cập đáng kể. Điều này dễ hiểu vì loại test này tốn kém.

May mắn thay, Rails hỗ trợ tốt không chỉ cho unit test, end-to-end test và integration test mà còn cho các test liên quan đến hiệu suất và tải. Tôi sẽ bao quát tất cả trong bài viết và đưa ra một số ví dụ thực tế giúp bạn hiểu cách sử dụng hiệu quả các công cụ test hiệu suất ứng dụng.

Bài viết được chia thành hai phần:

  • Lý thuyết — Tôi sẽ chỉ cho bạn thấy tại sao test là cần thiết, các loại test có thể thực hiện và các chỉ số thiết yếu khi test ứng dụng

  • Thực hành — Chúng ta sẽ "tay ngang" viết test cho một ứng dụng thực tế để lấy kết quả

Sau khi đọc hai phần, bạn sẽ hiểu sâu hơn về các loại test khác nhau và cách thực hiện chúng trên ứng dụng Rails của mình. Nghe thú vị? Hãy bắt đầu với chút lý thuyết về test.

Test trong Lý thuyết

Test phải luôn là một phần nội tại của quá trình phát triển bất kỳ loại ứng dụng nào. Nếu bạn vẫn chưa tin hoặc chưa từng viết test, đây là một số lập luận ủng hộ test sẽ giúp bạn:

  • Thay đổi code mà không lo lắng làm hỏng gì — Đây là lý do chính. Hãy tưởng tượng làm việc trên một app khổng lồ mà bạn phải click qua toàn bộ app để đảm bảo không gì bị hỏng mỗi khi thay đổi, dù chỉ là thay đổi nhỏ. Với test, bạn chỉ chạy một lệnh và quy trình xác minh diễn ra tự động và nhanh chóng.
  • Quy trình refactor dễ dàng — Như đã nói, test thiết yếu khi thêm tính năng hoặc thay đổi. Có test sẵn, bạn sẽ thoải mái hơn khi cải tiến code hiện có.

  • Test là một dạng tài liệu — Test viết tốt có thể đóng vai trò tài liệu cho các tập tính năng trong ứng dụng. Chúng không chỉ mô tả tính năng là gì mà còn mô tả nó nên hoạt động như thế nào.

  • Cơ hội suy nghĩ lại cách triển khai — Khi viết test, bạn có cơ hội suy nghĩ lại xem cách triển khai code có đúng và hợp lý không. Cũng đơn giản là kiểm tra code có chạy như kỳ vọng không.

Hy vọng các lập luận trên đã thuyết phục bạn sử dụng test trong quá trình phát triển app. Biết tại sao test là quan trọng, nhưng cũng quan trọng không kém là học về các loại test khác nhau.

Các Loại Test Khác Nhau

Có ba loại test chính bạn có thể viết để đảm bảo hiệu suất ứng dụng Rails đúng đắn và hạ tầng hoạt động tốt dưới tải trọng lớn:

  • Load testing (Test tải) — Loại test này trả lời câu hỏi: hệ thống có thể xử lý bao nhiêu user đồng thời trong một khoảng thời gian cho trước. Hãy tưởng tượng bạn tung ra sản phẩm hot trên website và hàng nghìn user muốn đặt hàng cùng lúc. Không có load test thích hợp, bạn rủi ro crash vào lúc quan trọng nhất.
  • Stress testing (Test căng thẳng) — Với loại test này, bạn không tập trung vào việc kiểm tra số user đồng thời hệ thống chịu đựng được, mà tập trung vào cách hệ thống hành xử khi giới hạn user bị chạm đến.
  • Performance testing (Test hiệu suất) — Tôi nói đây là loại test "cha đẻ" của stress và load testing. Mục đích chính là lấy một tập chỉ số cụ thể mà dựa trên đó chúng ta có thể hành động để cải thiện code ứng dụng. Sẽ nói về các chỉ số này ngay sau.

Vậy là chúng ta đã sẵn sàng cho bước cuối của phần lý thuyết: học những chỉ số thiết yếu khi làm performance testing trên Rails. Không có kiến thức này, chúng ta không thể diễn giải đúng kết quả test và quyết định có nên thay đổi code hay không.

Các Chỉ số Quan trọng

Loại chỉ số bạn nhận được có thể khác nhau tùy công cụ test, nhưng chung chung ta có thể nhóm thành bộ chỉ số khá phổ biến:

  • Response time (Thời gian phản hồi) — Khoảng thời gian giữa lúc request được gửi và lúc response được render trên trình duyệt. Chỉ số này cho thấy user phải chờ bao lâu trước khi nhận thông tin yêu cầu. Đôi khi gọi là process time.
  • Memory usage (Sử dụng bộ nhớ) — Lượng bộ nhớ tiêu thụ cho một request cho trước. Đây là thông tin thiết yếu vì nó chỉ ra nơi bạn có thể cải thiện code để hệ thống phản hồi nhanh hơn và dùng ít tài nguyên hơn.
  • Objects allocation (Cấp phát đối tượng) — Cấp phát bộ nhớ cao gây ra sử dụng bộ nhớ cao và thời gian phản hồi lâu. Chỉ số này có thể dẫn bạn đến đúng chỗ trong code nơi nhiều đối tượng được cấp phát, để bạn kiểm tra ngay lập tức.

Bạn có thể có thêm chỉ số khi test, nhưng ba chỉ số trên là quan trọng nhất và áp dụng cho bất kỳ ứng dụng nào bạn test. Bây giờ chúng ta có thể "tay ngang" viết test thực tế.

Thực hành

Chúng ta không thể viết test nếu không có gì để test. Đó là lý do bước đầu tiên trong phần thực hành là viết một ứng dụng Rails đơn giản để viết test cho nó.

Ứng dụng Rails Mẫu

Tôi dùng Ruby 3.0.1 và Rails 6.1.3.1 nhưng bạn thoải mái dùng phiên bản nào bạn quen. Nếu đã cài Ruby và Rails, bước tiếp theo là tạo khung ứng dụng:

rails new simpleapp -d=postgresql

Cho mục đích bài viết, tôi sẽ tạo app hiển thị danh sách user kèm tên thú cưng. Cấu trúc này cho phép dễ dàng tạo truy vấn N+1 — mang来更多乐趣 khi test hiệu suất và kiểm tra tác động của các thay đổi đến tốc độ và các chỉ số khác.

Trước khi generate model, hãy tạo database trước:

cd simpleapp/ bin/rails db:create

Bây giờ generate model:

rails g model User name:string rails g model Animal name:string user:references bin/rails db:migrate

Chỉ cần cập nhật nhỏ model User để phản ánh quan hệ với model Animal:

class User < ApplicationRecord has_many :animals end

Chúng ta có thể thêm seeds trong file db/seeds.rb:

people = { 'Tim' => ['Pinky', 'Rick'], 'Martha' => ['Rudolph'], 'Mark' => ['Niki', 'Miki', 'Bella'], 'Tina' => ['Tom', 'Luna'] } people.each_pair do |name, pets| user = User.create(name: name) pets.each do |pet_name| user.animals.create(name: pet_name) end end

và nạp dữ liệu vào database:

bin/rails db:seed

Tôi sẽ tạo một controller gán users, sau đó trong view liệt kê tất cả users với tên thú cưng. Tôi ý thức dùng code gây vấn đề hiệu suất để bạn đo lường sự cải thiện sau này.

touch app/controllers/home_controller.rb mkdir app/views/home touch app/views/home/index.html.erb

Controller đơn giản:

class HomeController < ApplicationController def index @users = User.all end end

View cũng đơn giản:

<h1>List</h1> <ul> <% @users.each do |user| %> <li><%= user.name %> (<%= user.animals.count %>) <ul> <% user.animals.each do |animal| %> <li><%= animal.name %></li> <% end %> </ul> </li> <% end %> </ul>

Bước cuối: cập nhật config/routes.rb để Rails biết hiển thị gì khi truy cập URL gốc:

Rails.application.routes.draw do root to: 'home#index' end

Load Test với JMeter

JMeter là phần mềm mã nguồn mở của Apache Software Foundation, thiết kế để load test hành vi chức năng. Vì là chương trình Java, bạn có thể cài trên bất kỳ hệ điều hành nào. Tải tại: https://jmeter.apache.org/download_jmeter.cgi

Nếu dùng macOS, cài JMeter dễ dàng với Homebrew:

brew install jmeter

Sau cài đặt, chạy chương trình bằng lệnh:

jmeter

Cấu hình test

Quá trình cấu hình gồm các bước:

  • Thêm thread group — chỉ định số user và thời gian mỗi user truy cập website
  • Cấu hình HTTP request — chỉ định endpoint JMeter cần hit
  • Thiết lập chỉ số chúng ta quan tâm

Hãy đi từng bước qua cấu hình test đơn giản để mô phỏng request của một user đơn lẻ đến trang chủ của simple app vừa tạo.

Thêm thread group

Chọn Add → Threads (Users) → Thread Group từ menu hiện ra sau khi right-click vào "Test Plan":

Chỉ định số user và các thuộc tính bổ sung:

Cấu hình HTTP request

Right-click vào thread vừa tạo ở bước trước và chọn Add → Sampler → HTTP Request:

Cấu hình protocol, server name, port và path của request:

Chỉ định view kết quả

Right-click vào HTTP request và chọn Add → Listener → View Results Tree:

Chạy Test

Test đã cấu hình xong, chúng ta có thể chạy. Chỉ cần click nút play màu xanh:

Như bạn thấy, app đã pass test, nhưng đó chỉ là một request đơn lẻ nên kết quả hiển nhiên. Bây giờ bạn có thể thử nghiệm với số user và các tùy chọn cấu hình khác để xem ứng dụng hành xử ra sao. Từ test của tôi, simple app bắt đầu crash khi khoảng 200 user truy cập đồng thời.

Các Bước Tiếp Theo

Sau khi thực hiện load test, bạn sẽ biết "điểm đau" của ứng dụng. Hiểu được giới hạn user, bạn có thể thực hiện stress test để xem ứng dụng hành xử ra sao.

Performance Test với Ruby-prof

Tính năng performance test được built-in trong Rails cho đến phiên bản 3, sau đó được tách thành gem riêng https://github.com/rails/rails-perftest. Vì tôi gặp một số vấn đề dùng nó với Rails phiên bản mới, tôi quyết định không đưa vào bài viết. Thay vào đó, tôi sẽ dùng thư viện ruby-prof hoạt động rất tốt.

Như thường lệ, bước đầu là thêm gem vào ứng dụng:

bundle add ruby-prof

Bước thứ hai và cũng là bước cuối cấu hình: cập nhật config/application.rb và dùng middleware cho gem để thư viện tự động kiểm tra request và tạo báo cáo dựa trên chúng:

module Simpleapp class Application < Rails::Application config.middleware.use Rack::RubyProf, :path => './tmp/profile' end end

Bây giờ bạn có thể truy cập app, và mỗi lần thực hiện request, gem sẽ tạo báo cáo mới. Nó trông như sau:

Bạn tìm thấy nó tại đường dẫn cấu hình, trong trường hợp chúng ta là tmp/profile. Báo cáo thứ hai cũng được tạo, hiển thị call stack — chỉ số khá hữu ích khi debug vấn đề hiệu suất trong Rails.

Quan trọng: thiết lập cache_classes và cache_template_loading thành true sẽ làm chậm ứng dụng và làm méo metrics vì Rails sẽ cố gắng load các file cần thiết.

Tóm tắt

Test là phần thiết yếu của mọi quy trình phát triển. Kiểm tra code có hành xử như mong muốn cũng quan trọng như xác minh giải pháp có hiệu suất tốt. Bỏ qua test dẫn đến vấn đề nghiêm trọng ảnh hưởng đến hiệu suất app và niềm tin của user. Hy vọng test không quá khó.

Trong bài viết, chúng ta đã bao quát các khía cạnh quan trọng sau của testing:

  • lý do bạn nên test code
  • các loại performance test khác nhau
  • cách test hiệu suất ứng dụng Rails của bạn

Tôi hy vọng bạn hiện đã tin phục hơn về tầm quan trọng của viết test vì bạn biết tại sao và làm thế nào.

Nếu bạn quan tâm đến giám sát hiệu suất app không chỉ local mà cả production hay staging, bạn cũng nên xem qua AppSignal.

P.S. Nếu bạn muốn đọc bài Ruby Magic ngay khi ra mắt, hãy subscribe newsletter Ruby Magic và không bao giờ bỏ lỡ bài nào!