(Py)ZeroMQ为什么打开这么多Unix套接字文件?

Peq*_*que 12 python zeromq pyzmq

lsof -U | wc -l在执行以下代码时尝试监视打开的Unix套接字文件的数量:

>>> import zmq
# 1375 Unix socket files
>>> c = zmq.Context()
# 1377 Unix socket files
>>> s = c.socket(zmq.PUSH)
# 1383 Unix socket files
>>> s.close()
# 1381 Unix socket files
>>> c.destroy()
# 1375 Unix socket files
Run Code Online (Sandbox Code Playgroud)

这是为什么?我希望在连接套接字时打开TCP / IPC套接字文件,但是在连接之前这些文件又如何呢?

看来它们都是“ STREAM”类型:

在此处输入图片说明

更新资料

有关重现此问题的有用脚本,请参见@gdlmx的答案。

看来,如果您使用Conda来安装,pyzmq一切都会按预期进行。我,但是,我仍然想知道为什么它不会工作,如果你安装pyzmqpip,我会考虑安装的软件包的标准方式。

重现步骤:

使用Conda:

conda create -n foo python=3.6
conda activate foo
pip install pyzmq
python test_script.py
Run Code Online (Sandbox Code Playgroud)

使用Python的venv

python3.6 -m venv venv
source ./venv/bin/activate
pip install pyzmq
python test_script.py
Run Code Online (Sandbox Code Playgroud)

gdl*_*lmx 4

我建议使用普通 python 或 ipython (不带控制台)重新运行测试。还请将计数限制为单个进程,以lsof -p <pid>排除计算机中其他进程(测试中的 1375 个 Unix 套接字文件)的不必要干扰。

这是一个简单的测试脚本:

import os
pid = os.getpid()
count=0

def lsof():
    global count
    count += 1
    print(count,':')
    os.system("lsof -p {0:d} 2>/dev/null | grep -E 'unix|IPv4|IPv6'".format(pid)) # -U doesn't work togeter with -p option
    # Alternatively, you can use "lsof -U 2>/dev/null | grep -E {0:d}"
    # but only unix socket file will be listed.

import zmq
c = zmq.Context();lsof()
tcp = c.socket(zmq.PUSH);lsof()
unix = c.socket(zmq.PUSH);lsof()

print('--- To bind  ---')
tcp.bind('tcp://127.0.0.1:19413');lsof()
unix.bind('ipc://filename');lsof()

print('--- To close ---')
tcp.close();lsof()
unix.close();lsof()
Run Code Online (Sandbox Code Playgroud)

下面是我的环境中的测试结果(python 3.6.6,pyzmq 17.1.2,在 CentOS 7 中使用 Anaconda)。

1 :
2 :
3 :
--- To bind  ---
4 :
ZMQbg/1 284018 gdlmx   13u     IPv4           49443178      0t0      TCP localhost:19413 (LISTEN)
5 :
ZMQbg/1 284018 gdlmx   13u     IPv4           49443178      0t0      TCP localhost:19413 (LISTEN)
ZMQbg/1 284018 gdlmx   14u     unix 0xffff9cd6c5bf4800      0t0 49443204 filename
--- To close ---
6 :
ZMQbg/1 284018 gdlmx   14u     unix 0xffff9cd6c5bf4800      0t0 49443204 filename
7 :
Run Code Online (Sandbox Code Playgroud)

我使用 python 和 ipython 来运行脚本并得到相同的结果。

总而言之,套接字文件或网络端口仅在socket.bind调用时才打开。在我的测试期间,python/ipython 进程没有打开其他套接字。

更新

针对 PO 的更新:

异常(意外)行为可能是由 PyPI 上 pyzmq 包中捆绑的预构建二进制文件引起的。pip install pyzmq将从 PyPI 下载该发行版 tar 球,其中包含以下预编译的二进制文件:

zmq/backend/cython:
    _device.so  _proxy_steerable.so  constants.so  error.so    socket.so
    _poll.so    _version.so          context.so    message.so  utils.so

zmq/.libs:
    libzmq-39117701.so.5.2.1         libsodium-72341b7d.so.23.2.0
Run Code Online (Sandbox Code Playgroud)

为了与尽可能多的 Linux 操作系统兼容,这些二进制文件是在名为Manylinux的 docker 环境中的非常旧的操作系统 (CentOS 5) 中构建的。

Anaconda 使用不同的方法来预构建二进制文件,并在conda/envs文件夹中包含所有依赖项。因此他们的二进制文件是在相对最新的环境中构建的。

我使用上述脚本在我的 CentOS 7 机器上测试了 PyPI 的二进制文件。我可以确认 ZeroMQ 打开一些“后台”套接字(创建上下文后打开 2 个套接字,创建第一个套接字后打开 8 个套接字)。虽然我下面的测试表明它们用于ZeroMQ内部机制的线程间通信,但最好直接询问PyPI包的维护者。

您还可以尝试强制pip/setuptools为您的操作系统构建 ZeroMQ:

sudo yum install libzmq3-devel #  RHEL-based
pip install --no-use-wheel pyzmq 
# Use `--no-binary :all:` instead of `--no-use-wheel` in pip >= 10.0.0
Run Code Online (Sandbox Code Playgroud)

如果您想要的话,这可能会消除后台套接字。

后台套接字的用途是什么?

ZeroMQ内部使用多线程进行IO操作。线程数可以通过IO_THREADS配置。我发现这个数字会影响正在使用的套接字数量。测试一下

num_io_threads = int(sys.argv[1])
c = zmq.Context()
c.set(zmq.IO_THREADS,num_io_threads)
s = c.socket(zmq.PUSH)
lsof()
Run Code Online (Sandbox Code Playgroud)

你会发现number_of_sockets= 6 + 2 * num_io_threads。因此,我假设 PyPI 中的 ZeroMQ 二进制文件在内部使用套接字进行主线程和工作/IO 线程之间的线程间通信。