python - 为什么 TLS 很慢?

标签 python performance sockets ssl tls1.2

我预计 SSL/TLS 会增加多少开销?

我正在使用 Ubuntu 14.04 与 OpenSSL 1.0.1f 和 Python 3.4.3。

测试.py

from datetime import datetime
import socket
import ssl

with socket.socket() as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('', 8001))
    server.listen(1)
    while True:
        with server.accept()[0] as client:
            start = datetime.now()
            client = ssl.wrap_socket(client, 'key.pem', 'cert.pem', True)
            end = datetime.now()
            print('{:.0f}ms'.format((end - start).total_seconds() * 1000))
            client.send(b'hello')
            client.shutdown(socket.SHUT_RDWR)

启动服务器

$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
$ python3 test.py

并连接

$ for i in $(seq 20); do openssl s_client -cert cert.pem -key key.pem -connect localhost:8001; done 
$ # output shows TLSv1.2 is being used

即使在同一台主机上, TLS 协商也需要 15 毫秒。当我删除 TLS 时,我测得整个连接、传输和关闭时间为 < 1 毫秒。

我在这里没有太多经验,但这似乎比我预期的要慢得多。那是数百万个时钟周期。从洛杉矶到纽约比光还慢。

(1) 这种表现是否符合我的预期? (2) 导致这种表现的限制因素是什么? (3) 我可以更改此代码以使初始 TLS 协商更快吗?

最佳答案

在普通 TCP 的情况下,您有一个 TCP 握手 (1 RTT),它完全在操作系统内核内部处理,以最大限度地减少延迟和资源使用。

而对于 TLS 和您的代码,由于您的代码不进行 session 重用,因此您除此之外一直都有完整的 TLS 握手。这意味着 2 RTT 仅用于每次握手和用于 key 交换的大量加密(影响取决于密码)。在(内核中)TCP 关闭之上,还有一个额外的 RTT 用于有序的 TLS 关闭。此外,比 TCP 握手传输更多的数据,并且 TLS 握手完全在用户空间中完成,这会导致额外的延迟,这些延迟是由调度引起的,许多读取和写入数据的系统调用以及涉及的内核和用户空间之间的上下文切换和复制这些系统调用。此外,您每次都设置一个新的 SSL 上下文,包括加载证书等,这会增加更多不必要的开销。

所有这些开销在同一个系统上完成两次,因为您在同一个系统上有客户端和服务器,并且您还使用客户端证书。 此外,您不会只传输很少的数据(比单独 TLS 握手所需的少得多),因此您只测量 TLS 握手所需的时间,即 TLS 中最昂贵的部分。而且,由于您有一个单线程客户端和一个单线程服务器,其中每一个都可能是速度限制。

换句话说:您的基准测试并未反射(reflect)在常见的现实世界场景中 TLS 与 TCP 的实际开销。但它表明,如果以过于简单的方式使用 TLS 会产生显着影响,即没有通常由应用程序执行大量 TLS 来完成的任何优化。

关于python - 为什么 TLS 很慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40581896/

相关文章:

python - 如何使用 Python 将 HTML 转换为 MHTML?

python - 将 dict 键映射到 pandas 数据框的列(如果它们接近)

MySQL加入速度很慢

python - 有效地扩展 float 的 Pandas 字符串列

json - Grails 批量更新 postgres 性能

linux - 服务器 - 同一主机上的客户端通信

javascript - 为什么我的 socket.io 应用程序在 Raspberry pi 上运行时会立即断开连接?

c - 套接字接受 - "Too many open files"

javascript - Ghost.py 通过 javascript 链接

python - 读取 Django 模型的字段选项