CentOS 7以服务运行uWSGI+Flask+SleekXMPP时发送消息失败求助
问题分析与解决方案
看起来你遇到的问题是手动启动uWSGI时XMPP收发正常,但通过CentOS 7的systemd服务运行时只能接收消息、无法发送。这种情况通常和服务运行的上下文环境、uWSGI的线程/进程配置,或者XMPP客户端的初始化时机有关,下面是具体的排查和解决思路:
1. 修复uWSGI配置的块冲突问题
你给出的配置里有两个[uwsgi]块,这种结构可能导致后面的配置项被前面的覆盖,尤其是enable-threads = True这个关键配置——SleekXMPP的发送逻辑依赖线程运行,必须确保这个配置生效。建议把所有配置合并到一个[uwsgi]块中:
[uwsgi] uid = uwsgi gid = uwsgi pidfile = /run/uwsgi/uwsgi.pid emperor = /etc/uwsgi.d stats = /run/uwsgi/stats.sock chmod-socket = 660 emperor-tyrant = true cap = setgid,setuid plugin = python http-socket = :8080 wsgi-file = /var/www/uwsgi/flask_uwsgi.py callable = app module = app enable-threads = True logto = /var/www/uwsgi/flask_uwsgi.log master = true # 新增:启用主进程模式,确保线程管理正常
2. 排查systemd服务的环境与权限问题
CentOS 7的systemd服务默认运行在受限环境中,很可能因为环境变量缺失、权限不足导致XMPP发送失败:
- 优先查看日志:打开
/var/www/uwsgi/flask_uwsgi.log,看看发送消息时有没有报错(比如连接断开、SSL证书访问失败)。如果日志信息太少,可以在代码里调高日志级别:
这样能看到XMPP交互的详细过程,判断是连接已断开还是有其他权限问题。logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s: %(message)s') - 设置服务工作目录:在systemd的uWSGI服务文件(通常是
/etc/systemd/system/uwsgi.service)中添加WorkingDirectory=/var/www/uwsgi,确保服务运行在你的代码目录下,避免依赖加载路径错误。 - 临时权限测试:如果怀疑是权限问题,可以临时把服务的
User改成root测试(测试完务必改回uwsgi用户,不要用root长期运行)。如果改完能正常发送,说明是uwsgi用户缺少某些资源的访问权限,比如SSL证书目录、临时文件目录等,再针对性调整权限。
3. 确保XMPP客户端只在主进程初始化
当uWSGI以服务方式启动时,默认会创建多个worker进程,你的代码在模块加载时就启动了XMPP线程,会导致每个worker都创建一个XMPP连接,部分连接可能无法正常工作。修改代码,让XMPP线程只在主进程启动:
import ssl, json, logging, threading, time from flask import Flask from sleekxmpp import ClientXMPP from sleekxmpp.exceptions import IqError, IqTimeout import uwsgi # 新增:引入uwsgi模块判断进程身份 smsg = """{ \"version\":1, \"type\":\"request\", \"messageId\":\"xxyyzz\", \"payload\": { \"deviceType\":\"ctlr\", \"command\":\"getDeviceInfo\" } }""" class XMPP(ClientXMPP): rosterList=[] def __init__(self, jid, password): ClientXMPP.__init__(self, jid, password) self.add_event_handler('session_start', self.session_start, threaded = True) self.add_event_handler('message', self.message, threaded=True) self.ssl_version = ssl.PROTOCOL_SSLv23 def session_start(self, event): self.send_presence(pshow='online') try: self.rosterList.append(self.get_roster()) except IqError as err: print('Error: %' % err.iq['error']['condition']) except IqTimeout: print('Error: Request time out') def message(self, msg): data = msg['body'][12:] dictData = json.loads(data) print(data) if 'payload' in dictData.keys(): for lists in dictData['payload']['indexes']: print(lists) elif 'message' in dictData.keys(): print('Request accepted') app = Flask(__name__) logging.basicConfig(level = logging.DEBUG) xmpp = XMPP('jid', 'password') class XmppThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): if xmpp.connect(('server', '5222')): xmpp.process(block=True) # 只在uWSGI主进程中启动XMPP线程 if uwsgi.worker_id() == 0: xt = XmppThread() xt.start() @app.route('/') def send(): # 发送前检查连接状态,断开则尝试重连 if not xmpp.is_connected(): if xmpp.connect(('server', '5222')): xmpp.process(block=False) xmpp.send_message(mto='receiver', mbody=smsg, mtype='chat') return '<h1>Send</h1>'
4. 调整systemd服务的Type设置
如果你的uWSGI服务文件里Type是simple,可能会导致后台线程还没启动完成,systemd就认为服务已经就绪。可以修改为notify类型,配合uWSGI的master = true配置:
在systemd服务文件中:
[Service] Type=notify ExecStart=/usr/sbin/uwsgi --ini /etc/uwsgi.d/uwsgi.ini
这样systemd会等待uWSGI主进程完成初始化后再标记服务为启动成功,确保XMPP线程有足够时间建立连接。
按照上面的步骤逐一排查,应该能定位并解决问题。如果还是不行,可以把uWSGI日志和XMPP调试日志贴出来,能更精准地分析问题。
内容的提问来源于stack exchange,提问作者Junho Kim
相关产品推荐
相关产品推荐

