@@ -42,27 +42,6 @@ errors = { | |||||
} | } | ||||
def worker(): | |||||
# start_wxchat_thread() | |||||
# start_kafka_consumer_thread() | |||||
start_kafka_consumer_thread() | |||||
# 尝试获取分布式锁 | |||||
lock_name = "wx_worker_lock" | |||||
#identifier = str(time.time()) | |||||
identifier = lock_name | |||||
while True: | |||||
if redis_helper.redis_helper.acquire_lock(lock_name, timeout=60): | |||||
try: | |||||
logger.info(f"获取{lock_name}工作锁, 开始任务") | |||||
start_wxchat_thread() | |||||
except Exception as e: | |||||
logger.error(f"处理任务时发生错误: {e}") | |||||
# 释放分布式锁 | |||||
redis_helper.redis_helper.release_lock(lock_name, identifier) | |||||
logger.info(f"释放{lock_name}锁") | |||||
time.sleep(1) | |||||
def fetch_and_save_contacts(): | def fetch_and_save_contacts(): | ||||
""" | """ | ||||
获取联系人列表并保存到缓存 | 获取联系人列表并保存到缓存 | ||||
@@ -182,16 +161,41 @@ def process_add_contacts_from_chatrooms(wxchat:gewe_chat.GeWeChatCom,status, wxi | |||||
else: | else: | ||||
logger.info(f'微信ID {wxid} 未登录 {app_id} ,群成员不能定时定时') | logger.info(f'微信ID {wxid} 未登录 {app_id} ,群成员不能定时定时') | ||||
def start_wxchat_thread(): | |||||
scan_wx_login_info() | |||||
# 启动同步联系人线程 | |||||
threading.Thread(target=fetch_and_save_contacts).start() | |||||
threading.Thread(target=auto_add_contacts_from_chatrooms).start() | |||||
# def background_wxchat_thread(): | |||||
# scan_wx_login_info() | |||||
# # 启动同步联系人线程 | |||||
# threading.Thread(target=fetch_and_save_contacts).start() | |||||
# threading.Thread(target=auto_add_contacts_from_chatrooms).start() | |||||
def background_wxchat_thread(): | |||||
lock_name = "background_wxchat_thread_lock" | |||||
lock_identifier = str(time.time()) # 使用时间戳作为唯一标识 | |||||
# 尝试获取分布式锁 | |||||
if redis_helper.redis_helper.acquire_lock(lock_name, timeout=60): | |||||
try: | |||||
logger.info("分布式锁已成功获取") | |||||
# 启动任务 | |||||
scan_wx_login_info() | |||||
threading.Thread(target=fetch_and_save_contacts).start() | |||||
threading.Thread(target=auto_add_contacts_from_chatrooms).start() | |||||
# 保持锁的续期 | |||||
while True: | |||||
time.sleep(30) # 每30秒检查一次锁的状态 | |||||
if not redis_helper.redis_helper.renew_lock(lock_name, lock_identifier, timeout=60): | |||||
break # 如果无法续期锁,退出循环 | |||||
finally: | |||||
# 释放锁 | |||||
redis_helper.redis_helper.release_lock(lock_name, lock_identifier) | |||||
else: | |||||
# 如果获取锁失败,等待一段时间后重试 | |||||
time.sleep(10) | |||||
background_wxchat_thread() | |||||
def scan_wx_login_info(): | def scan_wx_login_info(): | ||||
wxchat = gewe_chat.wxchat | wxchat = gewe_chat.wxchat | ||||
cursor = 0 | cursor = 0 | ||||
while True: | while True: | ||||
@@ -267,17 +271,18 @@ flask_api.add_resource(SendSNSVideoResource, '/api/sns/sendvideo') | |||||
load_config() | load_config() | ||||
kafka_helper.start() | kafka_helper.start() | ||||
redis_helper.start() | redis_helper.start() | ||||
gewe_chat.start() | gewe_chat.start() | ||||
#worker() | |||||
threading.Thread(target=worker).start() | |||||
start_kafka_consumer_thread() | |||||
# background_wxchat_thread() | |||||
threading.Thread(target=background_wxchat_thread).start() | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
# 获取环境变量 | # 获取环境变量 | ||||
environment = os.environ.get('environment', 'default') | environment = os.environ.get('environment', 'default') | ||||
port = 80 if environment == 'default' else 5000 | port = 80 if environment == 'default' else 5000 | ||||
app.run(debug=False, host='0.0.0.0', port=port) | |||||
app.run(debug=False, host='0.0.0.0', port=port) | |||||
@@ -11,6 +11,7 @@ class RedisHelper: | |||||
def __init__(self, host='localhost', port=6379, password=None ,db=0): | def __init__(self, host='localhost', port=6379, password=None ,db=0): | ||||
# 初始化 Redis 连接 | # 初始化 Redis 连接 | ||||
self.client = redis.Redis(host=host, port=port,db=db,password=password) | self.client = redis.Redis(host=host, port=port,db=db,password=password) | ||||
self.lock_renewal_thread = None | |||||
def set_hash(self, hash_key, data, timeout=None): | def set_hash(self, hash_key, data, timeout=None): | ||||
"""添加或更新哈希,并设置有效期""" | """添加或更新哈希,并设置有效期""" | ||||
@@ -42,37 +43,22 @@ class RedisHelper: | |||||
"""更新哈希表中的某个字段""" | """更新哈希表中的某个字段""" | ||||
self.client.hset(hash_key, field, value) | self.client.hset(hash_key, field, value) | ||||
# def acquire_lock(self, lock_name, timeout=60): | |||||
# """ | |||||
# 尝试获取分布式锁,成功返回 True,失败返回 False | |||||
# :param lock_name: 锁的名称 | |||||
# :param timeout: 锁的超时时间(秒) | |||||
# :return: bool | |||||
# """ | |||||
# identifier = str(time.time()) # 使用时间戳作为唯一标识 | |||||
# end_time = time.time() + timeout | |||||
# while time.time() < end_time: | |||||
# if self.client.set(lock_name, identifier, nx=True, ex=timeout): | |||||
# self.lock_renewal_thread = threading.Thread(target=self.renew_lock, args=(lock_name, identifier, timeout)) | |||||
# self.lock_renewal_thread.start() | |||||
# return True | |||||
# time.sleep(0.1) | |||||
# return False | |||||
def acquire_lock(self, lock_name, timeout=60): | def acquire_lock(self, lock_name, timeout=60): | ||||
""" | |||||
尝试获取分布式锁,成功返回 True,失败返回 False | |||||
:param lock_name: 锁的名称 | |||||
:param timeout: 锁的超时时间(秒) | |||||
:return: bool | |||||
""" | |||||
identifier = str(time.time()) # 使用时间戳作为唯一标识 | |||||
if self.client.set(lock_name, identifier, nx=True, ex=timeout): | |||||
self.lock_renewal_thread = threading.Thread(target=self.renew_lock, args=(lock_name, identifier, timeout)) | |||||
self.lock_renewal_thread.start() | |||||
return True | |||||
return False | |||||
""" | |||||
尝试获取分布式锁,成功返回 True,失败返回 False | |||||
:param lock_name: 锁的名称 | |||||
:param timeout: 锁的超时时间(秒) | |||||
:return: bool | |||||
""" | |||||
#print('获取锁') | |||||
identifier = str(time.time()) # 使用时间戳作为唯一标识 | |||||
#if self.client.set(lock_name, identifier, nx=True, ex=timeout): | |||||
if self.client.setnx(lock_name, identifier): | |||||
self.client.expire(lock_name, timeout) | |||||
self.lock_renewal_thread = threading.Thread(target=self.renew_lock, args=(lock_name, identifier, timeout)) | |||||
self.lock_renewal_thread.start() | |||||
return True | |||||
return False | |||||
def renew_lock(self, lock_name, identifier, timeout): | def renew_lock(self, lock_name, identifier, timeout): | ||||
""" | """ | ||||
@@ -99,7 +85,6 @@ class RedisHelper: | |||||
if self.lock_renewal_thread: | if self.lock_renewal_thread: | ||||
self.lock_renewal_thread.join() | self.lock_renewal_thread.join() | ||||
def start(): | def start(): | ||||
global redis_helper | global redis_helper | ||||
@@ -43,6 +43,6 @@ if [ "$environment" == "default" ]; then | |||||
else | else | ||||
# 非默认环境,使用 Gunicorn 启动 | # 非默认环境,使用 Gunicorn 启动 | ||||
#gunicorn -w 1 -b 0.0.0.0:5000 --timeout 1200 app:app | #gunicorn -w 1 -b 0.0.0.0:5000 --timeout 1200 app:app | ||||
gunicorn -w 4 -b 0.0.0.0:5000 --timeout 1200 -k gevent app:app | |||||
#gunicorn -c gunicorn_config.py app:app | |||||
#gunicorn -w 4 -b 0.0.0.0:5000 --timeout 1200 -k gevent app:app | |||||
gunicorn -c gunicorn_config.py app:app | |||||
fi | fi |
@@ -1,41 +1,25 @@ | |||||
"""gunicorn + gevent 的配置文件""" | |||||
# 多进程 | |||||
import multiprocessing | import multiprocessing | ||||
# 绑定ip + 端口 | |||||
# 绑定地址与端口 | |||||
bind = '0.0.0.0:5000' | bind = '0.0.0.0:5000' | ||||
# 进程数 = cup数量 * 2 + 1 | |||||
workers = multiprocessing.cpu_count() * 2 + 1 | |||||
# 等待队列最大长度,超过这个长度的链接将被拒绝连接 | |||||
backlog = 2048 | |||||
# 工作模式--协程 | |||||
worker_class = 'gevent' | |||||
# 最大客户客户端并发数量,对使用协程的 worker 的工作有影响 | |||||
# 服务器配置设置的值 1000:中小型项目 上万并发: 中大型 | |||||
worker_connections = 1000 | |||||
# 进程名称 | |||||
proc_name = 'gunicorn.pid' | |||||
# 进程 pid 记录文件 | |||||
pidfile = 'gunicorn.pid' | |||||
# 日志等级 | |||||
loglevel = 'warning' | |||||
# 日志文件名 | |||||
logfile = 'tmp/gunicorn_log.log' | |||||
# 设置访问日志 | |||||
accesslog = 'tmp/gunicorn_acess.log' | |||||
# 设置错误信息日志 | |||||
errorlog = 'tmp/gunicorn_error.log' | |||||
# 代码发生变化是否自动重启 | |||||
reload = True | |||||
# 工作进程与协程配置 | |||||
#workers = multiprocessing.cpu_count() # 异步模式 Worker 数等于 CPU 核数 | |||||
workers=4 | |||||
# worker_class = 'gevent' # 启用协程 | |||||
worker_connections = 2000 # 适当增加单 Worker 并发量 | |||||
# 超时与自动重启 | |||||
timeout = 120 | |||||
max_requests = 1000 | |||||
max_requests_jitter = 50 | |||||
# 日志配置(确保 logs 目录存在) | |||||
loglevel = 'info' | |||||
accesslog = 'logs/gunicorn_access.log' | |||||
errorlog = 'logs/gunicorn_error.log' | |||||
# 生产环境安全配置 | |||||
reload = False | |||||
preload_app = True | |||||
forwarded_allow_ips = '*' # 根据实际代理 IP 调整 |
@@ -48,4 +48,5 @@ gunicorn | |||||
opencv-python | opencv-python | ||||
moviepy | moviepy | ||||
celery | |||||
celery | |||||
gevent |
@@ -34,44 +34,6 @@ def wx_messages_process_callback(agent_tel,message): | |||||
except Exception as e: | except Exception as e: | ||||
print(f"处理消息时发生错误: {e}, 消息内容: {message}") | print(f"处理消息时发生错误: {e}, 消息内容: {message}") | ||||
def process_group_sending_v0(wxchat:gewe_chat.GeWeChatCom, content_data, agent_tel:str): | |||||
# 获取登录信息 | |||||
hash_key = f"__AI_OPS_WX__:LOGININFO:{agent_tel}" | |||||
logininfo = redis_helper.redis_helper.get_hash(hash_key) | |||||
if not logininfo: | |||||
logger.warning(f"未找到 {agent_tel} 的登录信息") | |||||
return | |||||
token_id = logininfo.get('tokenId') | |||||
app_id = logininfo.get('appId') | |||||
agent_wxid = logininfo.get('wxid') | |||||
# 获取联系人列表并计算交集 | |||||
hash_key = f"__AI_OPS_WX__:CONTACTS_BRIEF:{agent_wxid}" | |||||
cache_friend_wxids_str=redis_helper.redis_helper.get_hash_field(hash_key,"data") | |||||
cache_friend_wxids_list=json.loads(cache_friend_wxids_str) if cache_friend_wxids_str else [] | |||||
cache_friend_wxids=[f["userName"] for f in cache_friend_wxids_list] | |||||
wxid_contact_list_content_data = [c['wxid'] for c in content_data.get("contact_list", [])] | |||||
intersection_wxids = list(set(cache_friend_wxids) & set(wxid_contact_list_content_data)) | |||||
# 发送消息 | |||||
wx_content_list = content_data.get("wx_content", []) | |||||
for wx_content in wx_content_list: | |||||
if wx_content["type"] == "text": | |||||
send_text_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content["text"]) | |||||
elif wx_content["type"] == "image_url": | |||||
send_image_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content.get("image_url", {}).get("url")) | |||||
elif wx_content["type"] == "tts": | |||||
send_tts_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content["text"]) | |||||
def process_group_sending(wxchat:gewe_chat.GeWeChatCom, content_data, agent_tel:str): | def process_group_sending(wxchat:gewe_chat.GeWeChatCom, content_data, agent_tel:str): | ||||
# 获取登录信息 | # 获取登录信息 | ||||
hash_key = f"__AI_OPS_WX__:LOGININFO:{agent_tel}" | hash_key = f"__AI_OPS_WX__:LOGININFO:{agent_tel}" | ||||
@@ -108,13 +70,7 @@ def process_group_sending(wxchat:gewe_chat.GeWeChatCom, content_data, agent_tel: | |||||
# 发送消息 | # 发送消息 | ||||
wx_content_list = content_data.get("wx_content", []) | wx_content_list = content_data.get("wx_content", []) | ||||
# for wx_content in wx_content_list: | |||||
# if wx_content["type"] == "text": | |||||
# send_text_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content["text"]) | |||||
# elif wx_content["type"] == "image_url": | |||||
# send_image_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content.get("image_url", {}).get("url")) | |||||
# elif wx_content["type"] == "tts": | |||||
# send_tts_message(wxchat, token_id, app_id, agent_wxid, intersection_wxids, wx_content["text"]) | |||||
wxchat.forward_video_aeskey = '' | wxchat.forward_video_aeskey = '' | ||||
wxchat.forward_video_cdnvideourl = '' | wxchat.forward_video_cdnvideourl = '' | ||||
wxchat.forward_video_length = 0 | wxchat.forward_video_length = 0 | ||||
@@ -130,8 +86,6 @@ def process_group_sending(wxchat:gewe_chat.GeWeChatCom, content_data, agent_tel: | |||||
elif wx_content["type"] == "file": | elif wx_content["type"] == "file": | ||||
send_file_message(wxchat, token_id, app_id, agent_wxid, [intersection_wxid], wx_content.get("file_url", {}).get("url")) | send_file_message(wxchat, token_id, app_id, agent_wxid, [intersection_wxid], wx_content.get("file_url", {}).get("url")) | ||||
def send_text_message(wxchat:gewe_chat.GeWeChatCom, token_id, app_id, agent_wxid, intersection_wxids, text): | def send_text_message(wxchat:gewe_chat.GeWeChatCom, token_id, app_id, agent_wxid, intersection_wxids, text): | ||||
for t in intersection_wxids: | for t in intersection_wxids: | ||||
# 发送文本消息 | # 发送文本消息 | ||||