有 eventlet.hubs 相关的错误都会被捕获并打印而不会向外传播导致服务器崩溃

This commit is contained in:
CakeCN
2026-01-07 23:52:01 +08:00
parent 73275cffad
commit 607c6dfbab

View File

@@ -8,63 +8,67 @@ logging.getLogger('eventlet.greenio').setLevel(logging.CRITICAL)
# 进行猴子补丁操作 # 进行猴子补丁操作
eventlet.monkey_patch() eventlet.monkey_patch()
# 配置 eventlet hub 以更优雅地处理 IOClosed 错误 # 配置 eventlet hub 以更优雅地处理所有错误
try: try:
from eventlet.hubs import hub from eventlet.hubs import hub
from eventlet import hubs from eventlet import hubs
import traceback
# 保存原始的方法 # 保存原始的方法
original_handle_error = hub.Hub.handle_error original_handle_error = hub.Hub.handle_error
original_switch = hubs.hub.Hub.switch original_switch = hubs.hub.Hub.switch
original_fire_timers = hubs.hub.Hub.fire_timers original_fire_timers = hubs.hub.Hub.fire_timers
original_trampoline = hubs.trampoline
def custom_handle_error(self, context, type, value, tb): def custom_handle_error(self, context, type, value, tb):
# 忽略 IOClosed 错误和相关的 EOFError # 捕获并打印所有 eventlet.hubs 相关错误不再往外raise
error_str = str(value) print(f"[Eventlet Hub Error] Type: {type.__name__}, Value: {value}")
if (type.__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \ print(f"[Context] {context}")
(type.__name__ == 'EOFError') or \ print("[Traceback]")
(type.__name__ == 'OSError' and 'Bad file descriptor' in error_str) or \ traceback.print_tb(tb)
(type.__name__ == 'OSError' and 'Socket operation on non-socket' in error_str): # 不再调用原始方法,直接返回,不往外传播错误
return
# 其他错误调用原始方法
original_handle_error(self, context, type, value, tb)
def custom_switch(self): def custom_switch(self):
try: try:
return original_switch(self) return original_switch(self)
except Exception as e: except Exception as e:
# 捕获 switch 方法中可能出现的 IOClosed 错误 # 捕获 switch 方法中所有异常
error_str = str(e) print(f"[Eventlet Switch Error] {type(e).__name__}: {e}")
if (type(e).__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \ print("[Traceback]")
(type(e).__name__ == 'EOFError') or \ traceback.print_exc()
(type(e).__name__ == 'OSError' and 'Bad file descriptor' in error_str) or \ # 返回 None 继续运行,不往外传播错误
(type(e).__name__ == 'OSError' and 'Socket operation on non-socket' in error_str): return None
# 优雅处理,返回继续运行
return None
# 其他异常重新抛出
raise
def custom_fire_timers(self): def custom_fire_timers(self):
try: try:
return original_fire_timers(self) return original_fire_timers(self)
except Exception as e: except Exception as e:
# 捕获 fire_timers 方法中可能出现的 IOClosed 错误 # 捕获 fire_timers 方法中所有异常
error_str = str(e) print(f"[Eventlet Fire Timers Error] {type(e).__name__}: {e}")
if (type(e).__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \ print("[Traceback]")
(type(e).__name__ == 'EOFError') or \ traceback.print_exc()
(type(e).__name__ == 'OSError' and 'Bad file descriptor' in error_str) or \ # 返回继续运行,不往外传播错误
(type(e).__name__ == 'OSError' and 'Socket operation on non-socket' in error_str): return
# 优雅处理,记录日志后继续运行
return def custom_trampoline(fd, read=False, write=False, timeout=None, timeout_exc=None):
# 其他异常重新抛出 try:
raise return original_trampoline(fd, read, write, timeout, timeout_exc)
except Exception as e:
# 捕获 trampoline 方法中所有异常
print(f"[Eventlet Trampoline Error] {type(e).__name__}: {e}")
print(f"[Params] fd: {fd}, read: {read}, write: {write}, timeout: {timeout}")
print("[Traceback]")
traceback.print_exc()
# 返回 None 继续运行,不往外传播错误
return None
# 替换原始方法 # 替换原始方法
hub.Hub.handle_error = custom_handle_error hub.Hub.handle_error = custom_handle_error
hubs.hub.Hub.switch = custom_switch hubs.hub.Hub.switch = custom_switch
hubs.hub.Hub.fire_timers = custom_fire_timers hubs.hub.Hub.fire_timers = custom_fire_timers
hubs.trampoline = custom_trampoline
# 同样处理 greenio 模块的所有相关方法 # 同样处理 greenio 模块的所有相关方法,捕获所有异常
from eventlet import greenio from eventlet import greenio
# 保存原始的 greenio 方法 # 保存原始的 greenio 方法
@@ -74,34 +78,20 @@ try:
original_send = greenio.base.GreenSocket.send original_send = greenio.base.GreenSocket.send
def safe_io_operation(io_func, *args, **kwargs): def safe_io_operation(io_func, *args, **kwargs):
"""安全执行 IO 操作,处理常见的 IO 错误""" """安全执行 IO 操作,捕获所有异常"""
try: try:
return io_func(*args, **kwargs) return io_func(*args, **kwargs)
except Exception as e: except Exception as e:
# 检查是否为需要忽略的 IO 错误 # 捕获所有 IO 操作异常
error_str = str(e) print(f"[Eventlet IO Error] {io_func.__name__}: {type(e).__name__}: {e}")
is_safe_error = False print("[Traceback]")
traceback.print_exc()
# 检查错误类型 # 根据操作类型返回适当的默认值
if type(e).__name__ in ('IOClosed', 'EOFError'): if 'recv' in io_func.__name__ or 'read' in io_func.__name__:
is_safe_error = True return b'' if 'recv' in io_func.__name__ else ''
elif type(e).__name__ == 'OSError': elif 'send' in io_func.__name__ or 'write' in io_func.__name__:
# 检查具体的错误码 return 0
if hasattr(e, 'errno') and e.errno in (9, 88, 107): # Bad file descriptor, Socket operation on non-socket, Operation on closed file return None
is_safe_error = True
# 检查错误信息
elif any(msg in error_str for msg in ['Bad file descriptor', 'Socket operation on non-socket', 'Operation on closed file']):
is_safe_error = True
if is_safe_error:
# 根据操作类型返回适当的默认值
if 'recv' in io_func.__name__ or 'read' in io_func.__name__:
return b'' if 'recv' in io_func.__name__ else ''
elif 'send' in io_func.__name__ or 'write' in io_func.__name__:
return 0
return None
# 其他异常重新抛出
raise
def custom_recv_loop(self, recv_func, recv_args, *args, **kwargs): def custom_recv_loop(self, recv_func, recv_args, *args, **kwargs):
return safe_io_operation(original_recv_loop, self, recv_func, recv_args, *args, **kwargs) return safe_io_operation(original_recv_loop, self, recv_func, recv_args, *args, **kwargs)
@@ -121,7 +111,7 @@ try:
greenio.base.GreenSocket._send_loop = custom_send_loop greenio.base.GreenSocket._send_loop = custom_send_loop
greenio.base.GreenSocket.send = custom_send greenio.base.GreenSocket.send = custom_send
# 处理 WSGI 中的错误 # 处理 WSGI 中的所有错误
try: try:
import eventlet.wsgi import eventlet.wsgi
original_wsgi_handle_one_request = eventlet.wsgi.HttpProtocol.handle_one_request original_wsgi_handle_one_request = eventlet.wsgi.HttpProtocol.handle_one_request
@@ -131,61 +121,68 @@ try:
try: try:
return original_wsgi_handle_one_request(self) return original_wsgi_handle_one_request(self)
except Exception as e: except Exception as e:
error_str = str(e) # 捕获 WSGI 请求处理中的所有异常
if (type(e).__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \ print(f"[Eventlet WSGI Request Error] {type(e).__name__}: {e}")
(type(e).__name__ == 'EOFError') or \ print("[Traceback]")
(hasattr(e, 'errno') and e.errno in (9, 88, 107)): traceback.print_exc()
# 清理连接状态 # 清理连接状态并返回,不往外传播错误
try: try:
self.finish() self.finish()
except Exception: except Exception:
pass pass
return return
raise
def custom_wsgi_handle_one_response(self): def custom_wsgi_handle_one_response(self):
try: try:
return original_wsgi_handle_one_response(self) return original_wsgi_handle_one_response(self)
except Exception as e: except Exception as e:
error_str = str(e) # 捕获 WSGI 响应处理中的所有异常
if (type(e).__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \ print(f"[Eventlet WSGI Response Error] {type(e).__name__}: {e}")
(type(e).__name__ == 'EOFError') or \ print("[Traceback]")
(hasattr(e, 'errno') and e.errno in (9, 88, 107)): traceback.print_exc()
# 清理连接状态 # 清理连接状态并返回,不往外传播错误
try: try:
self.finish() self.finish()
except Exception: except Exception:
pass pass
return return
raise
eventlet.wsgi.HttpProtocol.handle_one_request = custom_wsgi_handle_one_request eventlet.wsgi.HttpProtocol.handle_one_request = custom_wsgi_handle_one_request
eventlet.wsgi.HttpProtocol.handle_one_response = custom_wsgi_handle_one_response eventlet.wsgi.HttpProtocol.handle_one_response = custom_wsgi_handle_one_response
except Exception as e: except Exception as e:
# 如果修改失败,忽略错误 # 如果修改失败,忽略错误
pass print(f"[WSGI Patch Error] {e}")
# 确保我们捕获所有可能的 IOClosed 错误 # 修改 eventlet 所有可能抛出异常的核心方法
# 修改 eventlet 的全局异常处理 # 遍历 hub.Hub 类的所有方法,包装可能抛出异常的方法
original_trampoline = hubs.trampoline for method_name in dir(hub.Hub):
if not method_name.startswith('_'):
method = getattr(hub.Hub, method_name)
if callable(method):
def wrap_method(original_method):
def wrapped_method(self, *args, **kwargs):
try:
return original_method(self, *args, **kwargs)
except Exception as e:
print(f"[Eventlet Hub Method Error] {method_name}: {type(e).__name__}: {e}")
print("[Traceback]")
traceback.print_exc()
# 返回适当的默认值
if method_name == 'add':
return None
elif method_name == 'remove':
return None
return None
return wrapped_method
def custom_trampoline(fd, read=False, write=False, timeout=None, timeout_exc=None): # 替换原始方法
try: setattr(hub.Hub, method_name, wrap_method(method))
return original_trampoline(fd, read, write, timeout, timeout_exc)
except Exception as e:
error_str = str(e)
if (type(e).__name__ == 'IOClosed' and 'Operation on closed file' in error_str) or \
(type(e).__name__ == 'EOFError') or \
(hasattr(e, 'errno') and e.errno in (9, 88, 107)):
# 优雅处理,返回 None
return None
# 其他异常重新抛出
raise
hubs.trampoline = custom_trampoline print("[Eventlet Error Handling] All eventlet.hubs errors will be caught and printed, not raised")
except Exception as e: except Exception as e:
# 如果修改失败,忽略错误 # 如果修改失败,打印错误但继续运行
pass print(f"[Eventlet Patch Initialization Error] {e}")
traceback.print_exc()
from flask import Flask from flask import Flask
from .views.routes import main_bp from .views.routes import main_bp