Mẹo cải thiện hiệu suất cho Ruby on Rails
Hiệu suất của Ruby on Rails chịu ảnh hưởng bởi nhiều yếu tố, đặc biệt là cấu hình của máy chủ triển khai. Tuy nhiên, các đoạn mã có thể tạo ra một sự khác biệt lớn dù trang web của bạn chậm hoặc đáp ứng cao. Trong bài viết này,tôi xin giới thiệu với các bạn 1 vài cách để làm tăng hiệu suất khi ...
Hiệu suất của Ruby on Rails chịu ảnh hưởng bởi nhiều yếu tố, đặc biệt là cấu hình của máy chủ triển khai. Tuy nhiên, các đoạn mã có thể tạo ra một sự khác biệt lớn dù trang web của bạn chậm hoặc đáp ứng cao.
Trong bài viết này,tôi xin giới thiệu với các bạn 1 vài cách để làm tăng hiệu suất khi viết code của Ruby on Rails:
- Caching
- Tối ưu các câu truy vấn database
- Tránh sử dụng các phương thức tìm động
- Tối ưu code html trên view
h3. **1. Caching**
Đây là cách đơn giản nhất để tăng tốc độ một ứng dụng Rails. Dưới đây là một phương thức của Caching:
Page Caching:
Rails cung cấp một cách cài đặt caches_page :index để thông báo cho webserver tự động generate trang HTML tĩnh và sử dụng cho các lần request tiếp theo từ controller Product action là index. mặc định thì trang HTML này sẽ được tạo ra tại folder /public bạn cũng có thể thay đổi chúng bằng cách thay đổi config trong config.action_controller.page_cache_directory=new_cache_path
class ProductsController < ActionController caches_page :index def index @products = Products.all end def create expire_page :action => :index end end
p<Ở đoạn code trên bạn sử dụng expire_page :action => :index trong action create để thông báo cho webserver hủy bỏ trang tĩnh index đã lưu trên webserver khi có một record mới được tạo. điều này nghĩa là khi bạn gọi lại trang index lần sau đó thì server sẽ thực hiện nạp lại trang index vào trang HTML bà bạn sẽ nhìn thấy dữ liệu của một record đã được thêm mới
Bạn cũng có thể cấu hình cho webserver (Ngix) sẽ tự động zip lại page khi gửi trả lại phía client để tăng tốc độ truy cập bằng cách bật thuộc tính gzip_static:on trong setting server Nginx.
location / { gzip_static on; #to serve pre-gzipped version }
sau đó bạn có thể set thuộc tính gzip bằng cách caches_page :index, :gzip => :best_speed
Action Caching
Có những nhược điểm của việc sử dụng cache page là với những trang mà yêu cầu quyền truy cập thì bạn không thể sử dụng dc. do request sẽ được trả lại ngay từ webserver nên ứng dụng không thể fillter được request với một trang đã được cache này từ đó chúng ta có cache_action
class ProductsController :index caches_action :index def index @products=Product.all end def create expire_action :action => :index end end
Fragment Caching
Fragment caching là việc cache lại từng phần tử thông tin của trang để thực hiện việc caching từng phần tử của trang ta làm như sau:
<% cache(:action => ‘index’, :action_suffix => ‘all_products’) do %> All available products: <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %> <% end %>
Đoạn code trên chúng ta đang cache lại thông tin của tất cả các product lấy ra từ db trong block
<% cache() do end %>
Để dọn dẹp cache chúng ta sử dụng
<% expire_fragment(:controller => ‘products’, :action => ‘recent’, :action_suffix => ‘all_products’) %>
Khi sử dụng doạn code trên thì rails sẽ thông báo cho sever biết để hủy cache có suffix: ‘all_products’
SQL Caching
Nếu bạn sử dụng câu lệnh truy vấn lại nhiều lần Rails sẽ hỗ trợ bạn lưu lại kết quả truy vấn vào cache VD: nếu bạn chạy 2 lần một câu lệnh truy vấn như bên dưới thì rails sẽ chỉ chạy một lần duy nhất thao tác vào db.
các lần sau đó rails sẽ tự động lấy kết quả từ trong memory.
class ProductsController < ActionController def index # Run a find query @products = Product.all ... # Run the same query again @products = Product.all end end
Tuy nhiên việc cache lại câu lệnh sql trong Rails chỉ có tác dụng trong chính hàm đó nên nếu bạn muốn sử dụng rộng rãi việc cache lại kết quả của lệnh truy vấn thì bạn sẽ phải sử dụng cache ở chế độ thấp hơn.
h3. **2. Tối ưu các câu truy vấn database**
Eager loading là một cách để giải quyết các vấn đề về hiệu suất truy vấn gây ra bằng cách sử dụng không hiệu quả của các đối tượng trong quan hệ 1 – n trong database.Ví dụ :
Bên cạnh đó,ActiveRecord tự động gói việc thêm mới hoặc thay đổi 1 bản ghi thành 1 transaction,vì thế nếu thực hiện việc thêm mới nhiều bản ghi cùng một lúc thì sẽ phải sinh ra nhiều transaction tương ứng thì có thể sẽ làm giảm hiệu suất của hệ thống.VIệc nhóm nhiều câu lệnh thành 1 transaction sẽ giúp cải thiện lại với cấu trúc như sau: Thay vì cách viết :
my_collection.each do |q| Quote.create({:phrase => q}) end
có thể đổi thành :
Quote.transaction do my_collection.each do |q| Quote.create({:phrase => q}) end
h3. **3. Tránh sử dụng các phương thức tìm động**
Các phương thức tìm động như MyModel.find_by_* mặc dù rất tiện dụng,dễ sử dụng,dễ nhớ đối với những người lập trình Ruby nhưng các phương thức này cũng là 1 nguyên nhân làm giảm hiệu suất của hệ thống,lý do là do ActiveRecord tự động sinh ra những phương thức trong hàm method_missing. Lời khuyên với các bạn là sử dụng MyModel.find_by_sql trực tiếp đôi khi sẽ tiện dụng hơn,đồng thời có thể giúp tăng tốc 1 phần khi thao tác với cơ sở dữ liệu.
h3. **4. Tối ưu code html trên view**
Trên tầng view của Ruby on Rails,nên chia các view thành các patial view nhỏ hơn,để dễ quản lý,và dễ cache lại.Bên cạnh đó trách sử dụng các helper của Rails để sinh ra các đoạn mã html vì những helper này sẽ làm mất thêm 1 khoảng thời gian không nhỏ để có thể biên dịch các đoạn code.Ví dụ dưới đây sử dụng Acunote để tính số bộ nhớ dành ra để biên dịch các helper:
a) ActionView::Helpers::JavaScriptHelper#link_to_function tiêu tốn rất nhiều bộ nhớ khi chạy.Để sinh ra 120 links sử dụng link_to_function sẽ tiêu tốn 5M bộ nhớ và sẽ làm tăng thời gian để sử lý thêm là từ 100 đến 110 ms.
Lời khuyên : sử dụng trực tiếp các đoạn mã html . thay cho link_to_function.
b) ActionView::Helpers::FormTagHelper#text_field_tag: Để sinh ra 120 thẻ input sử dụng text_field_tag sẽ tiêu tốn 2M bộ nhớ và sẽ làm tăng thời gian để sử lý thêm là từ 40 đến 50 ms.
Lời khuyên : sử dụng trực tiếp các đoạn mã html thay cho text_field_tag.
c) ActionView::Helpers::AssetTagHelper#image_tag Để sinh ra 120 thẻ img sử dụng image_tag sẽ tiêu tốn 8M bộ nhớ và sẽ làm tăng thời gian để sử lý thêm là từ 100 đến 150 ms.
Lời khuyên : sử dụng trực tiếp các đoạn mã html thay cho image_tag