2013-05-25 25 views
7

Gần đây tôi đã chuyển từ chương trình phụ trợ I18n đơn giản mặc định sang chương trình phụ trợ Redis cho I18n của tôi. Tôi đã làm như vậy để giúp chúng tôi xử lý các bản dịch dễ dàng hơn, nhưng tôi thấy rằng có một hiệu suất đáng kể trên mỗi trang.Có phải Redis quá chậm để sản xuất Rails I18n không?

Tôi đã chạy một số Điểm chuẩn với Rails 3.2 và Redis 2.6.4 được cài đặt trên MBP của tôi để minh họa. Tôi đang sử dụng hiredis-rb làm khách hàng của mình.

Đó là sự khác biệt khá rõ ràng khi thực hiện hai chương trình phụ trợ khác nhau. Với sự phụ trợ đơn giản có sự chậm trễ ngắn về cuộc gọi đầu tiên - tôi giả bản dịch đang được nạp vào bộ nhớ - và hiệu suất thì lớn sau đó:

pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.143246 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.00415 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.004153 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.004056 

Các Redis backend là luôn chậm:

pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.122448 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.263564 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.232637 
pry(main)> Benchmark.realtime { 500.times { I18n.t 'shared.slogan' } } 
=> 0.122304 

Điều này làm tôi cảm thấy tuyệt đối tại sao điều này lại chậm đối với I18n ... Tôi đang xếp hàng chục cuộc gọi I18n trong toàn bộ cơ sở mã của mình. Nếu tôi có thể trộn chúng lại với nhau, tôi sẽ có hình dạng tốt:

pry(main)> keys = $redis.keys[0..500] 
pry(main)> Benchmark.realtime { $redis.mget keys } 
=> 0.04264 

Nhưng tôi thực sự không thấy cách nào để làm điều này với bất kỳ chương trình phụ trợ I18n hiện có nào. Có ai ngoài đó giải quyết vấn đề này không?

CHỈNH SỬA

Tôi lấy đề xuất của Chris Heald và tạo phụ trợ ghi nhớ một bộ nhớ cache đơn giản. Các ý chính là ở đây:

https://gist.github.com/wheeyls/5650947

Tôi sẽ cố gắng này ra trong một vài ngày và sau đó biến nó thành một viên ngọc.

CẬP NHẬT

Giải pháp của tôi là có sẵn như là một viên ngọc bây giờ:

https://github.com/wheeyls/cached_key_value_store

Và tôi cũng viết blog về vấn đề này:

http://about.g2crowd.com/faster-i18nredis-on-rails/

+0

Máy chủ redis của bạn liên quan đến hộp chạy các điểm chuẩn này ở đâu? – deefour

+0

Chúng được chạy trên máy cục bộ của tôi. Tôi sẽ chỉnh sửa câu hỏi của mình để làm rõ. – Wheeyls

Trả lời

3

Mạng lưới giao thông sẽ luôn chậm hơn công việc địa phương. Bạn có thể xem xét một bộ nhớ đệm trong bộ nhớ, và sau đó chỉ cần kéo phiên bản địa phương hóa hiện tại trên mỗi yêu cầu (hoặc thậm chí chỉ trên một bộ đếm thời gian ngắn) để xác định xem có làm mất hiệu lực bộ nhớ cache hay không. Có vẻ như có mô-đun Ghi nhớ (mỗi the source here) mà bạn có thể kết hợp vào giao diện I18n. Sau đó, chúng tôi chỉ tinh chỉnh phương thức #lookup sao cho cứ sau 5 phút, nó sẽ kiểm tra Redis để biết phiên bản miền địa phương được cập nhật và đảm bảo rằng nó tăng phiên bản miền địa phương khi lưu bản dịch mới.

Điều này cung cấp cho bạn bộ nhớ cache trong tất cả các bản dịch của bạn để tra cứu rất nhanh, đồng thời cho phép bạn thực hiện các thay đổi dịch nhanh chóng - bản dịch của bạn có thể mất đến 5 phút để cập nhật, nhưng bạn không phải thực hiện bất kỳ thao tác xóa bộ nhớ cache rõ ràng nào.

Nếu bạn muốn, bạn có thể kiểm tra mọi yêu cầu bằng before_filter thay vì chỉ sử dụng hết hạn 5 phút lười biếng, có nghĩa là có nhiều yêu cầu quay lại, nhưng bạn sẽ không thấy bất kỳ bản dịch cũ nào.

module I18n 
    module Backend 
    class CachedKeyValueStore < KeyValue 
     include Memoize 

     def store_translations(locale, data, options = {}) 
     @store.incr "locale_version:#{locale}" 
     reset_memoizations!(locale) 
     super 
     end 

     def lookup(locale, key, scope = nil, options = {}) 
     ensure_freshness(locale) 
     flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale, 
      key, scope, options[:separator]).to_sym 
     flat_hash = memoized_lookup[locale.to_sym] 
     flat_hash.key?(flat_key) ? flat_hash[flat_key] : (flat_hash[flat_key] = super) 
     end 

     def ensure_freshness(locale) 
     @last_check ||= 0 

     if @last_check < 5.minutes.ago 
      @last_check = Time.now 
      current_version = @store.get "locale_version:#{locale}" 
      if @last_version != current_version 
      reset_memoizations! locale 
      @last_version = current_version 
      end 
     end 
     end 
    end 
    end 
end 

Tôi vừa mới bắt đầu đọc nguồn I18n và chưa thử nghiệm, vì vậy có thể cần một số công việc, nhưng tôi nghĩ nó truyền đạt ý tưởng đủ tốt.

+0

Tiện lợi. Đây là giải pháp cơ bản mà tôi nghĩ đến, chỉ tốt hơn. Tôi tự hỏi tại sao không ai khác dường như đã giải quyết vấn đề này? – Wheeyls

+0

Gist ở đây: https://gist.github.com/wheeyls/5650947 – Wheeyls

+0

Tốt. Tôi sẽ suy nghĩ về điều này và dán nó vào đống sử dụng sau này của tôi. :) –

Các vấn đề liên quan