You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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证书访问失败)。如果日志信息太少,可以在代码里调高日志级别:
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s: %(message)s')
    
    这样能看到XMPP交互的详细过程,判断是连接已断开还是有其他权限问题。
  • 设置服务工作目录:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:45:04