2012-02-09 30 views
13

Tôi đang xây dựng một ứng dụng bằng gevent. Ứng dụng của tôi hiện đang khá lớn vì có rất nhiều công việc được sinh ra và bị phá hủy. Bây giờ tôi đã nhận thấy rằng khi một trong những công việc này làm hỏng toàn bộ ứng dụng của tôi, hãy tiếp tục chạy (nếu ngoại lệ xuất phát từ một greenlet không chính). Nhưng vấn đề là tôi phải xem bảng điều khiển của mình để xem lỗi. Vì vậy, một số phần của ứng dụng của tôi có thể "chết" và tôi không ngay lập tức nhận thức được điều đó và ứng dụng tiếp tục chạy.Giám sát ngoại lệ gevent trong công việc

Đốt ứng dụng của tôi bằng công cụ thử tìm kiếm dường như không phải là giải pháp sạch. Có thể một hàm đẻ trứng tùy chỉnh thực hiện một số báo cáo lỗi?

Cách thích hợp để theo dõi công việc/greenlet gevent là gì? bắt ngoại lệ?

Trong trường hợp của tôi, tôi lắng nghe sự kiện của một vài nguồn khác nhau và tôi nên giải quyết từng vấn đề khác nhau. Có 5 công việc vô cùng quan trọng. Greenserver webs, greens websocket, greenlet cơ sở dữ liệu, greenlet báo động và green green zmq. Nếu bất kỳ của những người 'chết' ứng dụng của tôi hoàn toàn sẽ chết. Các công việc khác mà chết không quan trọng. Ví dụ, có thể là greens websocket chết do một số ngoại lệ được nâng lên và phần còn lại của các ứng dụng tiếp tục chạy tốt như không có gì xảy ra. Nó là hoàn toàn vô dụng và nguy hiểm bây giờ và chỉ nên sụp đổ cứng.

+0

Tôi đã thực hiện nhiệm vụ quan trọng của cây xanh rất nhỏ (8 dòng mã) Và chúng sẽ biến thành greenlets mà nó 'ok' sụp đổ. – Stephan

Trả lời

12

Tôi nghĩ cách sạch nhất là bắt ngoại lệ bạn xem là gây tử vong và làm sys.exit() (bạn sẽ cần gevent 1.0 kể từ trước đó SystemExit không thoát khỏi quá trình này).

Một cách khác là sử dụng link_exception, sẽ được gọi nếu greenlet chết với ngoại lệ.

spawn(important_greenlet).link_exception(lambda *args: sys.exit("important_greenlet died")) 

Lưu ý rằng bạn cũng cần gevent 1.0 để làm việc này.

Nếu trên 0.13.6, làm một cái gì đó như thế này để giết chết quá trình:

gevent.get_hub().parent.throw(SystemExit()) 
+0

Btw văn phòng tương lai mới của bạn trông thật đẹp;) – Stephan

+0

Trên 0.13.6 Tôi không có 'gevent.get_hub()'. Tôi có 'gevent.getcurrent()', nhưng thuộc tính cha là 'None'. – scottm

+0

@scottm thử gevent.hub.get_hub() sau đó. –

3

Bạn muốn greenlet.link_exception() tất cả các greenlets của bạn đến một chức năng quét dọn.

Chức năng gác cổng sẽ được chuyển qua bất kỳ greenlet nào chết, từ đó nó có thể kiểm tra số greenlet.exception để xem điều gì đã xảy ra và nếu cần làm điều gì đó về nó.

+0

Cách hợp lý hơn là giết toàn bộ quá trình. –

0

Vấn đề chính với greenlet.link_exception() là vấn đề không cung cấp bất kỳ thông tin nào về truy xuất lại có thể thực sự quan trọng để đăng nhập.

Đối đăng nhập với traceback, tôi sử dụng một trang trí để spwan công việc mà gián tiếp việc gọi vào một chức năng đăng nhập đơn giản:

from functools import wraps  

import gevent 

def async(wrapped): 

    def log_exc(func): 

     @wraps(wrapped) 
     def wrapper(*args, **kwargs): 
      try: 
       func(*args, **kwargs) 
      except Exception: 
       log.exception('%s', func) 
     return wrapper 

    @wraps(wrapped) 
    def wrapper(*args, **kwargs): 
     greenlet = gevent.spawn(log_exc(wrapped), *args, **kwargs) 

    return wrapper 

Tất nhiên, bạn có thể thêm link_exception gọi để quản lý công việc (mà tôi không cần)

2

Như @Denis và @lvo đã nói, link_exception là OK, nhưng tôi nghĩ sẽ có cách tốt hơn cho điều đó, mà không thay đổi mã hiện tại của bạn để tạo ra greenlet.

Nói chung, bất cứ khi nào ngoại lệ được ném vào greenlet, phương thức _report_error (trong gevent.greenlet.Greenlet) sẽ được gọi cho greenlet đó. Nó sẽ làm một số công cụ như gọi tất cả các chức năng liên kết và cuối cùng, gọi self.parent.handle_error với exc_info từ ngăn xếp hiện tại.self.parent ở đây là đối tượng toàn cầu Hub, điều này có nghĩa là tất cả các ngoại lệ xảy ra trong mỗi greenlet sẽ luôn tập trung vào một phương thức để xử lý. Theo mặc định, Hub.handle_error phân biệt loại ngoại lệ, bỏ qua một số loại và in các loại khác (đó là những gì chúng tôi luôn thấy trong bảng điều khiển).

Bằng cách vá phương thức Hub.handle_error, chúng tôi có thể dễ dàng đăng ký trình xử lý lỗi của riêng mình và không bao giờ bị mất nữa. Tôi đã viết một hàm helper để làm cho nó xảy ra:

from gevent.hub import Hub 


IGNORE_ERROR = Hub.SYSTEM_ERROR + Hub.NOT_ERROR 


def register_error_handler(error_handler): 

    Hub._origin_handle_error = Hub.handle_error 

    def custom_handle_error(self, context, type, value, tb): 
     if not issubclass(type, IGNORE_ERROR): 
      # print 'Got error from greenlet:', context, type, value, tb 
      error_handler(context, (type, value, tb)) 

     self._origin_handle_error(context, type, value, tb) 

    Hub.handle_error = custom_handle_error 

Để sử dụng nó, chỉ cần gọi nó trước khi vòng lặp sự kiện được khởi tạo:

def gevent_error_handler(context, exc_info): 
    """Here goes your custom error handling logics""" 
    e = exc_info[1] 
    if isinstance(e, SomeError): 
     # do some notify things 
     pass 
    sentry_client.captureException(exc_info=exc_info) 

register_error_handler(gevent_error_handler) 

Giải pháp này đã được thử nghiệm dưới gevent 1.0.2 và 1.1 b3, chúng tôi sử dụng nó để gửi thông tin lỗi greenlet đến sentry (một hệ thống theo dõi ngoại lệ), nó hoạt động khá tốt cho đến nay.

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