Connection: keep-alive 迷一样的东西

2020-09-09 23:51:03 +08:00
 supermoonie

用 netty 写了一个 http & https 代理,在将服务器响应的发送到客户端(浏览器)后,如果不手动关闭代理与服务器的连接以及客户端的连接,netty 就会重用之前的连接,这样的话,整个网站(比如 youtobe )的流量都只通过一个连接通道进行传输,看视频卡得一批。当响应发送到客户端后就关闭 Channel,哇,整个世界都舒服了,网速直接满载,连接的通道也多了,再加上二级代理,youtube 1080p 妥妥的

3351 次点击
所在节点    Java
21 条回复
ysc3839
2020-09-09 23:59:55 +08:00
这是你网络的问题吧?针对单连接进行了限速。如果是 HTTP/2 的话,你这种操作就没用了,一个服务器的所有请求都是使用单个连接的。
est
2020-09-10 00:18:58 +08:00
@ysc3839 这个是 chrome 的特性。h2 协议本身可以多路复用 m:n 来并发传输 chunk
supermoonie
2020-09-10 00:49:19 +08:00
@ysc3839 限速应该不会有,因为网络在测试期间没有变动,而且测试了很多次。HTTP/2 的话会进行降级处理。单个连接是没问题,如果 keep-alive  的话,新的请求就会使用现有的连接,那么这个连接的传输能力岂不是就会很慢吗?现在通过手动关闭,就有机会创建多个连接,是不是就会快了很多?以上只是猜想,我还需要更多的测试进行验证才能找到问题所在
supermoonie
2020-09-10 00:56:00 +08:00
@est 看了下协议的版本,是 HTTP/1.1,应该是 Chrome 对 keep-alive 做了处理,现在就测试的现象来看,主动关闭客户端( Chrome )的通道,网速就上去了,可能是我代理服务器的连接重用机制需要调整,我去查一下 netty 关于连接复用的处理细节再测试看看
Mohanson
2020-09-10 07:03:54 +08:00
估计你处理完一个 http 请求后没有接着从该 tcp 通道继续读取 http 请求,导致浏览器重用通道后发的请求会喂了狗然后等待几秒时间后重试,和 persistent connection 没关系。另外 http 代理的 persistent connection 在 firefox 上有 bug
supermoonie
2020-09-10 07:52:20 +08:00
@Mohanson 应该不是没有继续读取 http 请求的问题,因为 netty 每次的 channelRead 事件,我都有日志打印跟踪,最终都会与服务器的连接通道发出去,还是觉得 Chrome 在处理 keep-alive 的时候,有点迷。如果不主动关闭 Chrome 的 channel,Chrome 就会一直转圈圈,也不会失败,而是等很久才加载出来。现在代理的线程池配置是处理客户端 channel 的固定 16 个线程,处理服务器端 channel 也是固定 16 个线程,所以我在想是不是因为 channel 重用导致线程池被耗尽了
domosekai
2020-09-10 09:03:45 +08:00
youtube 的视频(googlevideo)本来就是单连接,打开 F12 看看卡在哪儿了
supermoonie
2020-09-10 09:21:41 +08:00
@domosekai 卡在了 pending ……
domosekai
2020-09-10 11:16:29 +08:00
pending 表示现有连接被占用,那我的猜测和#5 类似,你的代理因为某种原因让 chrome 无法复用连接,导致卡死超时后再重新连接,虽然你主动断开看上去也可以,但不一定适用于其他网站,我试了下 youtube 的视频在 chrome 使用代理和直连的情况下都是单连接复用
supermoonie
2020-09-10 18:40:48 +08:00
@domosekai 待我今天晚上再验证下
supermoonie
2020-09-10 22:09:18 +08:00
@ysc3839
@est
@Mohanson
@domosekai

感谢各位,确实是代码的问题,在处理 EmptyLastHttpContent 的时候有客户端连接状态的判断,如果没建立连接的话,EmptyLastHttpContent 就会被丢弃掉,导致发送到目标服务器的请求迟迟不能结束,所以造成了客户端请求的 pending 。
arloor
2020-09-10 22:24:45 +08:00
按道理来说,youtube 现在应该是 http2 的了
即使走了 http 代理,也会使用 connect 隧道,在这种情况下,仍然应该是 http2

另外,你的这个项目应该可以在一定程度上对比下这个 https://github.com/arloor/HttpProxy

既然你提到 EmptyLastHttpContent,应该和这个项目的实现差不多
supermoonie
2020-09-10 22:51:35 +08:00
@arloor youtube 现在确实是 h2 的协议了,不过代理会对其进行降级,目前还没深入研究,后面再细细研究下 h2 。确实和那个项目差不多,不过我现在写的提供了 HttpRequest 、HttpResponse 的拦截器,动态二级代理支持,黑白名单,远程及本地映射以及即将实现的网络信息统计、自定义 DNS 、限速、vmess 协议、ss 协议等。另外基于拦截器做了一个类似 Charles 的单页面应用,基本上和 Charles 的功能一样,只不过我把请求和响应保存到 sqllite 数据库中了,可以进行查询。另外的打算可能是做插件管理,比如某盘的嗅探多线程下载插件。感觉可以做的很多。。。
arloor
2020-09-10 23:01:24 +08:00
@supermoonie 其实我觉得还是 http 代理本身比较有意义,其他的其实并不是很有意义

友情提醒一下,代理这种场景中需要注意控制背压,不然会有 outofDerectMemory
supermoonie
2020-09-10 23:08:39 +08:00
@arloor 现在还没遇到过,等做完了做下各种网络测试。GlobalChannelTrafficShapingHandler 这个 handler 应该能解决很多问题,特别是请求堆积造成的内存泄漏
monkeyWie
2020-09-11 07:42:08 +08:00
@supermoonie 跟我这个很像啊,另外还实现了 https 的中间人攻击,https://github.com/monkeyWie/proxyee
supermoonie
2020-09-11 09:11:44 +08:00
@monkeyWie 哎呦,大佬大佬,确实是参考了你的那个项目,没想到碰到作者了,只不过我重新写了一遍,加了一些以及需要的功能,另外用 vue 写了一个抓包的界面🤣
monkeyWie
2020-09-11 09:28:43 +08:00
@supermoonie 哈哈,巧了,抓包最麻烦的地方就是要处理大响应,比如上传文件和下载文件,搞不好就 OOM 了😄
supermoonie
2020-09-11 10:07:48 +08:00
@monkeyWie 大的请求和响应,是不是可以通过 Content-Length 判断下,然后超过一定的阈值,先序列化到本地文件中,再通过 Buffer 分块读取,这只是我的猜想,不知道可不可行🤣
monkeyWie
2020-09-11 10:40:29 +08:00
@supermoonie 我也是这样考虑的,不过 chunked 编码的还要特殊处理下,因为拿不到 Content-Length🤣,还有那个 h2 的如果是 connect 隧道的话应该是不会降级的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/705647

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX