asyncio 实现 TCP 隧道代理服务延迟过大

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 的调用.

我的疑惑有:

  1. 有什么可能的原因会导致耗时增加得这么夸张
  2. 增加一层代理服务, 耗时增加的合理范围是多少? 是否可以达到毫秒级?
  3. 求推荐更好的实现方案

求助, 完整项目代码不太方便发, 抱歉.

补充:
目前已经定位到是 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

回到顶部