An toàn khi sử dụng Thread.current [] trong đường ray


81

Tôi tiếp tục nhận được những ý kiến ​​trái chiều về cách lưu trữ thông tin trong Thread.currenthàm băm (ví dụ: current_user, tên miền phụ hiện tại, v.v.). Kỹ thuật này đã được đề xuất như một cách để đơn giản hóa quá trình xử lý sau này trong lớp mô hình (phạm vi truy vấn, kiểm tra, v.v.).

Nhiều người coi cách làm này là không thể chấp nhận được vì nó phá vỡ mô hình MVC. Những người khác bày tỏ lo ngại về độ tin cậy / an toàn của cách tiếp cận, và câu hỏi gồm 2 phần của tôi tập trung vào khía cạnh thứ hai.

  1. Thread.currentbăm đảm bảo được cung cấp và tư nhân để một và chỉ một phản ứng, trong suốt toàn bộ chu kỳ của nó?

  2. Tôi hiểu rằng một chuỗi, ở cuối phản hồi, cũng có thể được chuyển giao cho các yêu cầu khác, do đó làm rò rỉ bất kỳ thông tin nào được lưu trữ trong đó Thread.current. Việc xóa thông tin như vậy trước khi kết thúc phản hồi (ví dụ bằng cách thực thi Thread.current[:user] = niltừ người kiểm soát after_filter) có đủ để ngăn chặn vi phạm bảo mật như vậy không?

Cảm ơn! Giuseppe


9
Xem phần "Làm bẩn với Thread.current" tại đây. m.onkey.org/thread-safety-for-your-rails . Điều đó được viết bởi một trong những tác giả Jruby. # 1 Bản thân mã ROR sử dụng Thread.current cho I18N và time_zone. Điều đó có nói về sự đảm bảo của nó không? # 2. Nếu # 1 là đúng thì nó là đủ.
so_mv

Cảm ơn, tôi đã thêm tài liệu tham khảo.
Giuseppe

3
Trong bài đăng mà bạn liên kết đến, các ví dụ chỉ liên quan đến lớp bộ điều khiển và giải pháp được đề xuất rõ ràng là phù hợp. Tuy nhiên, tôi nghi ngờ rằng điều mà hầu hết mọi người sẽ quan tâm là một cách rõ ràng để cấp cho các mô hình quyền truy cập vào 1-2 phần thông tin thường bị loại trừ với họ, mà không cần thêm các tham số bổ sung cho mỗi lần gọi đến các mô hình. Về mặt này, tất cả những dấu hiệu cảnh báo đáng sợ lớn "tránh xa Thread.current" mà không có lý do cụ thể tại sao cho đến nay khiến tôi không chắc chắn. Cảm ơn
Giuseppe

Câu trả lời:


48

Không có lý do cụ thể nào để tránh xa các biến cục bộ của chuỗi, các vấn đề chính là:

  • khó kiểm tra chúng hơn, vì bạn sẽ phải nhớ đặt các biến cục bộ của chuỗi khi bạn đang kiểm tra mã sử dụng nó
  • các lớp sử dụng cục bộ luồng sẽ cần biết rằng các đối tượng này không có sẵn cho chúng nhưng bên trong một biến cục bộ của luồng và kiểu chuyển hướng này thường phá vỡ luật demeter
  • không dọn dẹp thread-local có thể là một vấn đề nếu khung công tác của bạn sử dụng lại các chuỗi (biến thread-local đã được khởi tạo và mã dựa vào lệnh gọi || = để khởi tạo biến có thể không thành công

Vì vậy, mặc dù không hoàn toàn không nên sử dụng, nhưng cách tốt nhất là không sử dụng chúng, nhưng thỉnh thoảng bạn gặp phải một bức tường mà cục bộ luồng sẽ là giải pháp đơn giản nhất có thể mà không cần thay đổi khá nhiều mã và bạn sẽ phải thỏa hiệp, có một mô hình hướng đối tượng kém hoàn hảo với luồng cục bộ hoặc thay đổi khá nhiều mã để làm điều tương tự.

Vì vậy, chủ yếu là vấn đề suy nghĩ đâu sẽ là giải pháp tốt nhất cho trường hợp của bạn và nếu bạn thực sự đang đi theo con đường cục bộ luồng, tôi chắc chắn khuyên bạn nên làm điều đó với các khối nhớ dọn dẹp sau chúng đã được thực hiện, như sau:

around_filter :do_with_current_user

def do_with_current_user
    Thread.current[:current_user] = self.current_user
    begin
        yield
    ensure
        Thread.current[:current_user] = nil
    end      
end

Điều này đảm bảo biến cục bộ của luồng được làm sạch trước khi được sử dụng nếu luồng này được tái chế.


1
Việc sử dụng bộ lọc xung quanh với khối ensure sẽ gây rối với việc xử lý / ghi nhật ký ngoại lệ nếu bạn không cẩn thận. Kiểm tra RequestStore trong câu trả lời của Dejan để biết giải pháp sạch hơn dựa trên phần mềm trung gian.
Irongaze.com

Có thể cách tiếp cận của tôi đối với vấn đề là tôi đã giải cứu ngoại lệ trước (thay vì chỉ đảm bảo như trên). Ngăn xếp cuộc gọi của ngoại lệ đang được đặt lại về vị trí mà tôi đã nâng cấp lại, làm rối tung nhật ký lỗi Rails và các nơi khác. Tôi đã tự kết xuất thủ công ngoại lệ đã chụp vào tệp nhật ký để bảo vệ nó nhưng nó rất xấu. Sử dụng phương pháp dựa trên phần mềm trung gian IMHO sạch hơn nhiều.
Irongaze.com

1
Đó là lý do tại sao đoạn mã ở trên chỉ có một ensurekhối và không cố gắng bắt ngoại lệ.
Maurício Linhares

Đơn giản khi bạn nhìn thấy nó :)
244an



4

Câu trả lời được chấp nhận là chính xác về mặt kỹ thuật, nhưng như đã chỉ ra trong câu trả lời một cách nhẹ nhàng và trong http://m.onkey.org/thread-safety-for-your-rails thì không nhẹ nhàng như vậy:

Không sử dụng bộ nhớ cục bộ của chuỗi, Thread.currentnếu bạn không nhất thiết phải

Gem for request_storelà một giải pháp khác (tốt hơn) nhưng chỉ cần đọc readme ở đó để biết thêm lý do để tránh xa lưu trữ cục bộ chuỗi.

Hầu như luôn luôn có một cách tốt hơn.


3
Tôi đang sử dụng Unicorn với ứng dụng Rails Multi-tenant. Bạn có thể đề xuất một ví dụ về "cách tốt hơn" không? Liên kết bạn đã đăng ném lỗi ứng dụng. Cảm ơn.
lacostenycoder
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.