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 并没有用,所以应该不是它的问题。


相关推荐
Jazz_z12 小时前
纯 Python 环境下 Excel 文件的读取与导入详解
python
Thomas.Sir12 小时前
第38课:TensorFlow|可视化工具TensorBoard全用法【日志写入、指标监控、网络可视化】
人工智能·python·tensorflow
SEO_juper12 小时前
外贸多语言站最隐蔽的流量杀手:hreflang 错了,谷歌把德语页推给美国人(附审计脚本)
开发语言·前端·python·seo·独立站·谷歌优化
Metaphor69213 小时前
快速合并 Word 文档【Python 指南】
python·word
JAVA老架构AI智能化13 小时前
如何高质量分块
人工智能·python
泡海椒13 小时前
告别反射低效:JQuick-Java ASM动态调用链性能优化实战
java·python·性能优化
2601_9563198813 小时前
用示例和练习,把量化规则先练清楚
人工智能·python
满怀冰雪13 小时前
25-迁移学习入门:加载预训练模型并微调
人工智能·python·机器学习·迁移学习·paddle
2601_9622845013 小时前
安卓APP UI自动化测试:Python + UiAutomator2 + pytest + pytest-html
python·pytest·uiautomator2·ui自动化测试·安卓app
2601_9620780314 小时前
构建RESTful APIs:使用Python和Flask
python·flask·web开发·api设计·构建restfulapis