python xmlrpc踩坑记录

最近用 Python 写了一个 RPC 服务,之前在 Ubuntu 上跑,换到 Windows 上之后突然发现一个神奇的问题。我的目的是在一个循环里定时获取设备状态,然后广播给订阅的客户端。获取设备状态是一个 RPC 接口。大致结构如下:

python 复制代码
proxy = xmlrpc.client.ServerProxy(f"http://localhost:8080/", allow_none=True)
subscribers = [] # 客户端连接
running = True
async def periodic_pusher():
    while running:
        await asyncio.sleep(0.05)
        if subscribers:
            data = proxy.get_data()
            payload = json.dumps(data)
            # 将 payload 广播给客户端
async def main(port):
    asyncio.create_task(periodic_pusher())
    async with websockets.serve(handler, "0.0.0.0", port):
        await asyncio.Future()

任务很简单,客户端通过 websocket 连接上来,然后订阅数据,这里为了照护客户端的实现者,所以转了一手。

因为配环境还挺麻烦的,而且我的数据并不复杂,所以选了 python 标准库自带的 xmlrpc 作为 rpc 库。换到 Windows 上以后,客户端突然变得异常卡顿,打印日志后发现差不多两秒多才能从服务的收到一个条数据,不管循环里 sleep 的时间调的多小都没用。

开始怀疑是异步任务调度的问题,试了纯 sleepprint,还是飕飕的。然后是排查服务端响应,还真有问题。

python 复制代码
def rpc_get_xxx():
     data = None
     while data is None:
         data = robot.get_xxx()
         time.sleep(0.01)
     return data

因为获取数据 API 可能会返回 None,所以用了循环来保证一定能获取到数据,data=None 导致一定会进入循环,也就导致 sleep 一定至少会执行一次,带来不必要的延时。知错就改,只要把 data=None 改成 data=robot.get_xxx() 就可以了。

再次测试,但是问题依然存在,rpc 服务端的延时顶多几十毫秒,websocket 客户端那边是一两秒的问题。没别的办法加日志吧,在 rpc 调用前后加上日志,发现一次 rpc 调用就要两秒多,为进一步定位问题,在 rpc 服务端也加上日志。试了几次,结果如下:

复制代码
before rpc: 1763365566.3031723
before get: 1763365568.3533354
after  get: 1763365568.3543353
after  rpc: 1763365568.3543353

before rpc: 1763367026.1716492
before get: 1763367028.228031
after  get: 1763367028.228031
after  rpc: 1763367028.228031

before rpc: 1763367290.863952
before get: 1763367292.9115446
after  get: 1763367292.9115446
after  rpc: 1763367292.9135447

rpc 客户端发起请求到服务端收到请求居然花了两秒多?这也太离谱了。。。

正当我怀疑 xmlrpc 库到底靠不靠谱的时候,找到看一篇帖子:

真是救命稻草,当我把 rpc 客户端的 ip 地址换成 127.0.0.1 之后,还真就解决了。一次调用几十毫秒就完成了:

复制代码
before rpc: 1763367422.9848952
before get: 1763367422.9868922
after  get: 1763367422.9868922
after  rpc: 1763367422.9880338

这才正常嘛,不过这个坑是真坑爹啊。


其实这个问题也问过 AI,它坚持说是 getfqdn 反解 DNS 的问题,还给了一段测试代码:

python 复制代码
import socket, time
t0 = time.perf_counter()
socket.getfqdn('localhost')   # 模拟 xmlrpc 内部调用
print("getfqdn 耗时:", time.perf_counter() - t0)

但是实际上这段代码运行起来非常快:

复制代码
getfqdn 耗时: 0.012044099974445999

帖子里也说了跳过 getfqdn 并没有用,所以应该不是它的问题。


相关推荐
Ysn07191 分钟前
中文乱码:在 Docker 容器中设置中文语言环境
运维·python·docker·容器
qingfeng154156 分钟前
企业微信消息监听实战:如何实时接收客户消息回调?
人工智能·python·自动化·企业微信
晚霞的不甘41 分钟前
CANN Catlass 矩阵乘模板库深度解析:高性能矩阵运算的进阶之路
人工智能·python·线性代数·矩阵
小白学大数据1 小时前
深度探索:Python 爬虫实现豆瓣音乐全站采集
开发语言·爬虫·python·数据分析
用户6757049885021 小时前
Celery 太重了?这可能是你一直在找的 asyncio 任务队列
后端·python·消息队列
Cloud_Shy6181 小时前
Python 数据分析基础入门:《Excel Python:飞速搞定数据分析与处理》学习笔记系列(第十一章 Python 包跟踪器 下篇)
前端·后端·python·数据分析·excel
程序员榴莲1 小时前
网络编程入门 Python Socket 实现一个简单的用户认证系统
服务器·网络·python
知识分享小能手1 小时前
Flask入门学习教程,从入门到精通, 认识Flask路由 — 知识点详解 (2)
python·学习·flask
AI棒棒牛1 小时前
YOLO26改进创新 | 全网首发!VECA弹性核心注意力重塑全局建模,线性复杂度增强检测骨干,嘎嘎创新!
python·yolo·目标检测·yolo26·主干改进
DFT计算杂谈2 小时前
VASP新手入门: IVDW 色散修正参数
linux·运维·服务器·python·算法