15个稳定版本
1.10.0 | 2023年6月29日 |
---|---|
1.9.2 | 2023年2月6日 |
1.9.1 | 2023年1月20日 |
1.8.0 | 2022年11月3日 |
1.0.1 | 2020年7月24日 |
#35 in WebSocket
1.5MB
35K SLoC
Condure
Condure 是一种管理网络连接的服务,以便从多个进程中控制连接。它可以管理入站连接和出站连接。应用程序通过 ZeroMQ 与 Condure 通信。
Condure 只能管理它所知道的协议的连接。目前这是 HTTP/1 和 WebSocket。请参阅 支持的协议。
该项目受到 Mongrel2 的启发。
用例
- 将连接所有权从一个进程传递到另一个进程。
- 重新启动应用程序而不会断开其连接。
- 在多个进程之间平衡连接所有权。
基本用法
启动服务器
$ condure --listen 8000 --zclient-stream ipc://client
连接一个处理程序,例如这个简单的 Python 程序
# this handler responds to every request with "hello world"
import os
import time
import tnetstring
import zmq
instance_id = 'basichandler.{}'.format(os.getpid()).encode()
ctx = zmq.Context()
in_sock = ctx.socket(zmq.PULL)
in_sock.connect('ipc://client-out')
out_sock = ctx.socket(zmq.PUB)
out_sock.connect('ipc://client-in')
# await subscription
time.sleep(0.01)
while True:
m_raw = in_sock.recv()
req = tnetstring.loads(m_raw[1:])
print('IN {}'.format(req))
resp = {}
resp[b'from'] = instance_id
resp[b'id'] = req[b'id']
resp[b'code'] = 200
resp[b'reason'] = b'OK'
resp[b'headers'] = [[b'Content-Type', b'text/plain']]
resp[b'body'] = b'hello world\n'
print('OUT {}'.format(resp))
out_sock.send(req[b'from'] + b' T' + tnetstring.dumps(resp))
客户端请求
$ curl -i https://127.0.0.1:8000
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 12
hello world
接收请求的进程不需要与响应的进程相同!例如,这里有一个将请求 ID 输出到 stdout 的程序
# this handler just outputs the request ID
import tnetstring
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.PULL)
sock.connect('ipc://client-out')
while True:
m = sock.recv_multipart()
req = tnetstring.loads(m[0][1:])
print('{} {}'.format(req[b'from'].decode(), req[b'id'].decode()))
当客户端发起请求时,我们可以看到请求 ID 信息
$ python examples/printreq.py
condure 0-0-0
在另一个 shell 中,我们可以使用像这样的程序进行响应
# this program sends a response to a certain request ID
import sys
import time
import tnetstring
import zmq
body = sys.argv[1]
addr = sys.argv[2].encode()
rid = sys.argv[3].encode()
ctx = zmq.Context()
sock = ctx.socket(zmq.PUB)
sock.connect('ipc://client-in')
# await subscription
time.sleep(0.01)
resp = {}
resp[b'from'] = b'sendresp'
resp[b'id'] = rid
resp[b'code'] = 200
resp[b'reason'] = b'OK'
resp[b'headers'] = [[b'Content-Type', b'text/plain']]
resp[b'body'] = '{}\n'.format(body).encode()
m = [addr + b' T' + tnetstring.dumps(resp)]
sock.send_multipart(m)
例如
$ python examples/sendresp.py "responding from another process" condure 0-0-0
客户端看到
$ curl -i https://127.0.0.1:8000
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 32
responding from another process
为了便于测试,可以将程序连接起来
$ python -u examples/printreq.py | xargs -n 2 python examples/sendresp.py "responding from another process"
挂起和恢复连接
在将连接控制权从一个进程传递到另一个进程时,首先需要挂起连接。这通过发送 handoff-start
消息并等待 handoff-proceed
消息来完成。到那时,可以将连接信息提供给另一个进程,并通过发送任何消息(例如 keep-alive
)来恢复连接。请参阅 ZHTTP 规范。
REQ 模式
除了使用 PUSH/ROUTER/SUB 套接字的流模式之外,还有一个可用的 "REQ" 模式,它使用 DEALER 套接字。要启用它,将 req
设置为监听端口的模式。这种模式对于使用 ZeroMQ 实现简单的请求/响应服务器很有用。
支持的协议
Condure 支持 HTTP/1 和 WebSocket。
Condure在第七层管理连接,并且只支持它所了解的协议。这是为了简化其使用。处理任意协议需要应用程序构建能够在TCP流中任意字节位置暂停/恢复会话的协议栈,这使得Condure的使用变得不可行。相反,Condure是协议感知的,并为应用程序提供解析后的帧,这样应用程序只需要支持在帧边界处暂停/恢复会话。
性能
Condure是为高性能而构建的。它使用了多种优化技术,包括最小堆分配、环形缓冲区、向量I/O、分层时间轮和快速数据结构(例如slabs)。在一个实例上,仅使用2个工作者(总共4个线程)就测试了超过1M个并发连接。请参阅https://blog.fanout.io/2020/08/11/rewriting-pushpins-connection-manager-in-rust/
与Mongrel2的比较
- Condure支持作为服务器和客户端。
- Condure支持多核。
- Condure支持在不需要多个进程的情况下监听多个端口。
- Condure不支持多个路由,也不打算作为共享服务器。每个希望将连接保持在单独进程中的应用程序都应该启动自己的Condure实例。
- Condure没有配置文件。配置使用命令行参数提供。
- Condure使用基于ZeroMQ的协议ZHTTP,比Mongrel2的协议更容易使用且更可靠。
未来计划
- HTTP/2
- HTTP/3
依赖项
~8–18MB
~279K SLoC