asyncio 实现 TCP 隧道代理服务延迟过大
使用Python asyncio.Server 实现了一个TCP隧道代理服务.
项目代码比较多, 从网上找到一个相同思路的实现 这个.
Demo在这里
async def pipe(reader, writer):
try:
while not reader.at_eof():
writer.write(await reader.read(2048))
finally:
writer.close()
async def handle_client(local_reader, local_writer):
try:
remote_reader, remote_writer = await asyncio.open_connection(
'127.0.0.1', 8889)
pipe1 = pipe(local_reader, remote_writer)
pipe2 = pipe(remote_reader, local_writer)
await asyncio.gather(pipe1, pipe2)
finally:
local_writer.close()
# Create the server
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_client, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
区别在于, 我这边还维护了一个内存中的代理池, 是第三方的代理. 我写的这个服务用来连接第三方代理, 接受客户端的请求, 转发给第三方代理. 为了提高速度(希望有效果), 我还加了代理连接池. 然后, 爬虫客户端使用我的代理服务时, 相比直接使用第三方代理, 耗时几乎增加了10s. 哭瞎了. 我能想到耗时会增加,但不知道为什么慢了这么多.
大致的流程是:
读取客户端请求报文 -> 连接第三方代理 -> 写入HTTP首行和请求头(代理认证) -> 启动两个协程交叉读写 -> 回收代理连接(StreamReader和StreamWriter)
用了 strace 和 cProfile 分析, 所有操作的耗时都很短. 没有产生阻塞的地方, 也没有累计耗时达到10s 的调用.
我的疑惑有:
- 有什么可能的原因会导致耗时增加得这么夸张
- 增加一层代理服务, 耗时增加的合理范围是多少? 是否可以达到毫秒级?
- 求推荐更好的实现方案
求助, 完整项目代码不太方便发, 抱歉.
补充:
目前已经定位到是 pipe()
协程那里花费的时间比较多,但是具体为什么没有像期望的那样结束还在分析中...
再补充:
似乎是因为.. 代理这里没办法准确的判断传输结束.. 在连接正常的情况下会等待很久... pipe()
也不会退出.. 考虑做一个强制超时?
继续补充:
用Go写了一个版本的服务,也会多10秒延迟... 我现在怀疑是不是net.ipv4.tcp_syn_retries
这个东西或者 tcp_retries2
之类的TCP重传导致的...
再继续补充:
调整了一下Go版本的代码,之前延迟是因为goroutine 有个地方没有正确关闭,导致goroutine泄漏了,延迟会越来越大。改好之后基本很稳定。比直接用代理慢几秒,但没有Python那版那么离谱了。而且好像 io.Copy 传输数据用的是 sendfile
吗?再继续了解优化看看...
补充:
低并发的情况下速度和直连代理几乎一样... 但是并发稍微高一点,可能都不到 50 req/s,就又变成了延迟10秒....
最后一次补充:
根据测试结果,只有出现代理失败重试的情况,才会发生延迟增加的情况。大致可以认为是流程增加导致爬虫超时条件改变,实际的超时时间比原本的方式要长。而增加的这部分时间,可能来自于TCP层的重传策略... 没有找到其他更合理的答案... 希望有大佬来帮忙解答...
回答:
- 做成纯粹的 TCP 中继服务,不要解析 HTTP 或 SOCKS 代理协议。
- 调整系统设置,例如:增加 TCP 同时连接数量、加大缓存空间等。
- 改用 golang 语言实现。
这类需求可以很复杂。。。最好采用现成稳定的负载均衡器。
以上是 asyncio 实现 TCP 隧道代理服务延迟过大 的全部内容, 来源链接: utcz.com/p/937966.html