THREADING IN RAILS
Bài viết này được thực hiện từ năm 2012 với Ruby 1.9 và AR 3.x. Trên Ruby 2.x trở đi, Threadsafe là mặc định nên sẽ có một vài thông tin đã trở nên không cần thiết. Multi-threaded Rails Thread (1) hiểu đơn giản là 1 tiến trình hoàn thiện rất nhỏ được CPU thực hiện trong 1 khoảng thời gian. ...
Bài viết này được thực hiện từ năm 2012 với Ruby 1.9 và AR 3.x. Trên Ruby 2.x trở đi, Threadsafe là mặc định nên sẽ có một vài thông tin đã trở nên không cần thiết.
Multi-threaded Rails
Thread (1) hiểu đơn giản là 1 tiến trình hoàn thiện rất nhỏ được CPU thực hiện trong 1 khoảng thời gian. Multi-threading là thuật ngữ chỉ việc xử lý đồng thời nhiều thread của CPU. Việc này làm tăng tốc độ xử lý, đồng thời tận dụng được hết khả năng vật lý thực tế của hệ thống.
Trong Ruby on Rails, thread mang 1 tính chất quan trọng đặc thù: chỉ có 1 thread duy nhất thực hiện trong 1 khoảng thời gian dù trên thực tế, request tới Rails hoàn toàn có thể hội tụ đồng thời nhiều thread. Điều này được xử lý bởi trình thông dịch GIL ( Global Interpreter Lock )(4)
「illustrated in this diagram from igvita.com」
Config và sử dụng thread cho Rails
Threadsafe
Xử lý thread quan trọng vẫn là đảm bảo cho các thread không bị kẹt, hay còn gọi là thread safe.
Bắt đầu từ ActiveRecord 2.x việc đảm bảo thread safe sẽ được AR điều khiển.
Để enable thread safe ta thêm 1 dòng lệnh vào
# config/environments/production.rb
config.threadsafe!
Ruby threadsafe(7)
「 illustrated in this diagram from Yehuda Katz」
Muốn chắc chắn threadsafe đã hoạt động, ta có thể tạo 1 action foo()(2):
def foo
n = params[:n].to_i
sleep n
render text: "I should have taken #{n} seconds!"
end
Mở trình duyệt W1 và truyền cho ?n=15, sau đó nhanh chóng mở thêm 1 trình duyệt W2 nữa và truyền ?n=2. W1 sẽ hiển thị text sau 15s, và sau đó 2s W2 mới hiển thị. Như vậy threadsafe đã hoạt động.
Multi Threading
Sau khi enable threadsafe, chúng ta cần setup thêm preload vì khi active thread trên AR 3.2 thì chế độ automatic loading(6) của ActiveSupport::Dependencies đã bị tắt đi. Để làm được điều đó chúng ta chỉ cần thêm dòng sau
# config/environments/production.rb
config.eager_load_paths << "#{RAILS_ROOT}/lib"
Như vậy, multi-threading đã sẵn sàng cho Rails.
Nhằm giải thích về sự khác nhau giữa Rails thông thường và multithreaded Rails, chúng ta sẽ khởi tạo 2 hàm f1() và f2() trong rails console:
def f1
i = 0
while i <= 2
puts "F1 loop #{i+1} start at #{Time.now}"
sleep 2
puts "F1 loop #{i+1} end at #{Time.now}"
i = i + 1
end
end
def f2
j = 0
while j <= 2
puts "F2 loop #{j+1} start at #{Time.now}"
sleep 2
puts "F2 loop #{j+1} end at #{Time.now}"
j = j + 1
end
end
Khi dùng Multi-thread, ta sử dụng Thread.new để khởi tạo 1 thread mới, và f1(),f2() sẽ được thực hiện trong những thread đó. Sau khi f1() và f2() kết thúc cần sử dụng join để đóng 2 thread trên trả tài nguyên lại cho thread chính
def multithreaded
time_start = Time.now
puts "Started at #{time_start}"
t1 = Thread.new{ f1() }
t2 = Thread.new{ f2() }
t1.join
t2.join
time_end = Time.now
puts "End at #{time_end}"
puts "Total time taken: #{time_end.sec - time_start.sec} second(s)"
end
Và kết quả:
Started at 2012-09-28 10:48:59 +0700
F1 loop 1 start at 2012-09-28 10:48:59 +0700
F2 loop 1 start at 2012-09-28 10:48:59 +0700
F1 loop 1 end at 2012-09-28 10:49:01 +0700
F2 loop 1 end at 2012-09-28 10:49:01 +0700
F2 loop 2 start at 2012-09-28 10:49:01 +0700
F1 loop 2 start at 2012-09-28 10:49:01 +0700
F1 loop 2 end at 2012-09-28 10:49:03 +0700
F1 loop 3 start at 2012-09-28 10:49:03 +0700
F2 loop 2 end at 2012-09-28 10:49:03 +0700
F2 loop 3 start at 2012-09-28 10:49:03 +0700
F1 loop 3 end at 2012-09-28 10:49:05 +0700
F2 loop 3 end at 2012-09-28 10:49:05 +0700
End at 2012-09-28 10:49:05 +0700
Total time taken: 6 second(s)
Còn khi không sử dụng Multi thread:
def nonthreaded
time_start = Time.now
puts "Started at #{time_start}"
t1 = f1()
t2 = f2()
time_end = Time.now
puts "End at #{time_end}"
puts "Total time taken: #{time_end.sec - time_start.sec} second(s)"
end
kết quả nhận được
Started at 2012-09-28 10:51:20 +0700
F1 loop 1 start at 2012-09-28 10:51:20 +0700
F1 loop 1 end at 2012-09-28 10:51:22 +0700
F1 loop 2 start at 2012-09-28 10:51:22 +0700
F1 loop 2 end at 2012-09-28 10:51:24 +0700
F1 loop 3 start at 2012-09-28 10:51:24 +0700
F1 loop 3 end at 2012-09-28 10:51:26 +0700
F2 loop 1 start at 2012-09-28 10:51:26 +0700
F2 loop 1 end at 2012-09-28 10:51:28 +0700
F2 loop 2 start at 2012-09-28 10:51:28 +0700
F2 loop 2 end at 2012-09-28 10:51:30 +0700
F2 loop 3 start at 2012-09-28 10:51:30 +0700
F2 loop 3 end at 2012-09-28 10:51:32 +0700
End at 2012-09-28 10:51:32 +0700
Total time taken: 12 second(s)
So sánh 2 kết quả ta có thể thấy rằng khi multithread thì f1() và f2() chạy đồng thời song song còn khi không dùng, f1() chạy cho đến khi kết thúc rồi f2() mới bắt đầu, và đương nhiên multithreaded tốn ít thời gian hơn để xử lý 2 hàm đó.
Bạn có thể xem tại ĐÂY
Một số điều cần biết về multi-threaded rails hiện tại(3)
Bắt đầu từ AR 2.x multi-thread mới bắt đầu được hỗ trợ, và cho tới hiện tại ( AR 3.2 ) vẫn chưa hoàn thiện. Vì thế nó vẫn chưa thể hiện được khả năng vượt trội của mình so với GIL. Multi-threading chỉ nên sử dụng khi
- data unshared : Chỉ sử dụng với các dữ liệu không share như khi đăng nhập, đăng ký, tính toán nội hàm … còn những hoạt động có khả năng trao đổi thông tin thời gian thực như chat, đặt hàng online thì fork hiện tại an toàn hơn
- Supported server : các server Ruby on Rails hỗ trợ multi-threaded Rails chưa có nhiều, chỉ có 1 vài server như Thin hoặc NGinx có khả năng.
- JRuby : multi-threaded Rails hiện tại hoạt động tốt nhất trên JRuby bởi JRuby không sử dụng GIL làm trình thông dịch mà sử dụng JVM – một nền tảng hỗ trợ multi-thread tuyệt đối
Kết luận
Khả năng tận dụng tài nguyên hệ thống và tốc độ xử lý nhiều nhiệm vụ cùng lúc của multi-thread đã được chứng minh giá trị ở những ngôn ngữ lập trình khác như C, Java …và tương lai trong Ruby on Rails chắc chắn cũng sẽ là 1 kỹ thuật rất được quan tâm.
Tuy nhiên, Ruby là một ngôn ngữ lập trình tuổi đời còn rất trẻ, cộng với việc GIL vẫn đảm bảo cho Ruby hoạt động tốt, multi-threaded Rails vẫn chỉ nên sử dụng hạn chế cho tới khi ActiveRecord hỗ trợ multithread tốt hơn.
Tham khảo
- http://en.wikipedia.org
- http://www.jordanhollinger.com
- http://www.unlimitednovelty.com
- http://www.igvita.com
- http://bibwild.wordpress.com
- http://m.onkey.org
- http://yehudakatz.com
- http://blog.smartlogicsolutions.com