- 爬虫失败告警的一个装饰器样例
import traceback
import os
from utils.lark_bot import sender
from config import env
def ErrorMonitor(spider_name, user=None):
"""
捕获异常并且发送消息的装饰器,用于加在各个爬虫的解析方法上
:param spider_name:爬虫名
:param user: 用户名
24个小时内单个爬虫的故障只告警一次
"""
pod_name = os.getenv("POD_NAME")
webhook = 'https://open.larksuite.com/open-apis/bot/v2/hook/dc183d37-05f8-436d-bbde-cfa7b163a79b'
title = f'{spider_name}\n POD_NAME: {pod_name}\n @{user}'
key_base = 'process:failed:filter:{}'
from utils.redisdb import redis_cli
redis_c = redis_cli()
# 捕获异常
def catch_exception(func):
def inner(*args, **kwargs):
try:
return func(*args, **kwargs)
# 捕获异常,发送告警
except Exception as e:
err_info = traceback.format_exc()
print(err_info)
key = key_base.format(spider_name)
filter_status = redis_c.get(key)
if filter_status:
print('过滤该告警')
return
# 只有线上环境才告警
if env == 'prod':
sender(err_info, url=webhook, title=title)
# 24个小时内单个爬虫的故障只告警一次
redis_c.setex(key, 24*60*60, 1)
raise e
return inner
return catch_exception
- 该装饰器如果被大量使用会导致2个严重的问题
- 1、redis连接数被打满
- 2、慢加载,爬虫启动极慢
- 为何会有这个问题呢?这得从python的装饰器特性说起
python装饰器一旦被使用,那么不管被装饰的方法有没有执行,有没有被调用;只要被定义后者别别的代码作为模块导入,那么其就会自动执行
- 除了inner这个嵌套方法内部代码不会被执行,其他的代码都会被自动执行;
- 如果这个装饰器大量被使用,那么redis连接这个代码就会被大量执行,但是这并不是我们想要的
- 我们想要的是,被装饰的方法真正执行的时候才创建redis连接
- 这就导致了
程序慢加载
和redis连接数过多的问题
而做出如下修改,则可以避免上述问题
- 把redis连接移到inner方法内,并且进一步移到只有程序异常的时候才连接
import traceback
import os
from utils.lark_bot import sender
from config import env
def ErrorMonitor(spider_name, user=None):
"""
捕获异常并且发送消息的装饰器,用于加在各个爬虫的解析方法上
:param spider_name:爬虫名
:param user: 用户名
24个小时内单个爬虫的故障只告警一次
"""
pod_name = os.getenv("POD_NAME")
webhook = 'https://open.larksuite.com/open-apis/bot/v2/hook/dc183d37-05f8-436d-bbde-cfa7b163a79b'
title = f'{spider_name}\n POD_NAME: {pod_name}\n @{user}'
key_base = 'process:failed:filter:{}'
# 捕获异常
def catch_exception(func):
def inner(*args, **kwargs):
try:
return func(*args, **kwargs)
# 捕获异常,发送告警
except Exception as e:
from utils.redisdb import redis_cli
redis_c = redis_cli()
err_info = traceback.format_exc()
print(err_info)
key = key_base.format(spider_name)
filter_status = redis_c.get(key)
if filter_status:
print('过滤该告警')
return
# 只有线上环境才告警
if env == 'prod':
sender(err_info, url=webhook, title=title)
# 24个小时内单个爬虫的故障只告警一次
redis_c.setex(key, 24*60*60, 1)
raise e
return inner
return catch_exception
Top comments (0)