Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

粘包的处理好像还是有问题 #7

Closed
lesismal opened this issue Mar 5, 2021 · 56 comments
Closed

粘包的处理好像还是有问题 #7

lesismal opened this issue Mar 5, 2021 · 56 comments

Comments

@lesismal
Copy link

lesismal commented Mar 5, 2021

试试一个字节一个字节、完整请求的数据随机拆分成不同长度的很多段丢给parser处理:

package test

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"github.com/antlabs/httparser"
)

func TestServerParserContentLength(t *testing.T) {
	data := []byte("POST /echo HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close \r\nAccept-Encoding : gzip \r\n\r\n")
	testParser(t, data)

	data = []byte("POST /echo HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close \r\nContent-Length :  0\r\nAccept-Encoding : gzip \r\n\r\n")
	testParser(t, data)

	data = []byte("POST /echo HTTP/1.1\r\nHost: localhost:8080\r\nConnection: close \r\nContent-Length :  5\r\nAccept-Encoding : gzip \r\n\r\nhello")
	testParser(t, data)
}

func TestServerParserChunks(t *testing.T) {
	data := []byte("POST / HTTP/1.1\r\nHost: localhost:1235\r\nUser-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n4\r\nbody\r\n0\r\n\r\n")
	testParser(t, data)
}

func TestServerParserTrailer(t *testing.T) {
	data := []byte("POST / HTTP/1.1\r\nHost: localhost:1235\r\nUser-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nTrailer: Md5,Size\r\nAccept-Encoding: gzip\r\n\r\n4\r\nbody\r\n0\r\nMd5: 841a2d689ad86bd1611447453c22c6fc\r\nSize: 4\r\n\r\n")
	testParser(t, data)
}

func testParser(t *testing.T, data []byte) error {
	setting := httparser.Setting{
		MessageBegin:    func(*httparser.Parser) {},
		URL:             func(*httparser.Parser, []byte) {},
		Status:          func(*httparser.Parser, []byte) {},
		HeaderField:     func(*httparser.Parser, []byte) {},
		HeaderValue:     func(*httparser.Parser, []byte) {},
		HeadersComplete: func(*httparser.Parser) {},
		Body:            func(*httparser.Parser, []byte) {},
		MessageComplete: func(*httparser.Parser) {},
	}
	p := httparser.New(httparser.REQUEST)

	var remain []byte
	for i := 0; i < len(data); i++ {
		b := append(remain, data[i:i+1]...)
		n, err := p.Execute(&setting, b)
		if err != nil {
			t.Fatal(fmt.Errorf("%v success, %v", n, err))
		}
		if n < len(b) {
			remain = append([]byte{}, b[n:]...)
		}
	}

	nRequest := 0
	data = append(data, data...)
	setting = httparser.Setting{
		MessageBegin:    func(*httparser.Parser) {},
		URL:             func(*httparser.Parser, []byte) {},
		Status:          func(*httparser.Parser, []byte) {},
		HeaderField:     func(*httparser.Parser, []byte) {},
		HeaderValue:     func(*httparser.Parser, []byte) {},
		HeadersComplete: func(*httparser.Parser) {},
		Body:            func(*httparser.Parser, []byte) {},
		MessageComplete: func(*httparser.Parser) {
			nRequest++
		},
	}

	tBegin := time.Now()
	loop := 100000
	for i := 0; i < loop; i++ {
		tmp := data
		var remain []byte
		for len(tmp) > 0 {
			nRead := int(rand.Intn(len(tmp)) + 1)
			readBuf := append(remain, tmp[:nRead]...)
			tmp = tmp[nRead:]
			n, err := p.Execute(&setting, readBuf)
			if err != nil {
				t.Fatal(fmt.Errorf("%v success, %v", n, err))
			}
			if n < len(readBuf) {
				remain = append([]byte{}, readBuf[n:]...)
			}

		}
		if nRequest != (i+1)*2 {
			return fmt.Errorf("nRequest: %v, %v", i, nRequest)
		}
	}
	tUsed := time.Since(tBegin)
	fmt.Printf("%v loops, %v s used, %v ns/op, %v req/s\n", loop, tUsed.Seconds(), tUsed.Nanoseconds()/int64(loop), float64(loop)/tUsed.Seconds())

	return nil
}
@lesismal
Copy link
Author

lesismal commented Mar 5, 2021

我也手撸了个 httpparser,没有追求极致的性能,该string的地方string,fasthttp那种 []byte 提升不算巨大,并且应用层多数也是需要 string([]byte) 一下,我通常也不建议别人 pointer hack 的方式去使用 fasthttp 的 []byte 字段,那点性能不是特别必要。
我这个test也还没覆盖更全面的用例,http的细节较多,等有空了我把 node http-parser test 里的用例一点点都加上,但是我这个粘包的处理应该是ok的:

https://github.com/lesismal/nbhttp/blob/master/parser_test.go

@lesismal
Copy link
Author

lesismal commented Mar 5, 2021

后面想要搞的还很多,nbio、nbhttp 基础上的 websocket、http2.0,还有之前魔改标准库的那个 tls,因为标准库那个 tls 太辣眼睛了,有当期以后也重写一份,太耗费体力了 ,要是等 http3.0 开始普及了,那只能卧槽了 😂

@guonaihong
Copy link
Contributor

guonaihong commented Mar 5, 2021 via email

@lesismal
Copy link
Author

lesismal commented Mar 5, 2021

是耗体力,最近都是靠磕曲强撑

https://www.bilibili.com/video/BV1Sx411T7QQ/?spm_id_from=333.788.videocard.0

@guonaihong
Copy link
Contributor

guonaihong commented Mar 5, 2021 via email

@guonaihong
Copy link
Contributor

下周回上海看下这个问题。

@lesismal
Copy link
Author

一起吧。一个人挑战这些点太花时间了。

---原始邮件--- 发件人: "lesismal"<notifications@github.com> 发送时间: 2021年3月5日(周五) 下午4:19 收件人: "antlabs/httparser"<httparser@noreply.github.com>; 抄送: "Subscribed"<subscribed@noreply.github.com>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 后面想要搞的还很多,nbio、nbhttp 基础上的 websocket、http2.0,还有之前魔改标准库的那个 tls,因为标准库那个 tls 太辣眼睛了,有当期以后也重写一份,太耗费体力了 ,要是等 http3.0 开始普及了,那只能卧槽了 😂 — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.

nbio对http1.x和websocket的支持已经基本完成,这里已经写了一些示例,基础的粘包测试等也都有了,常规内存使用的pool支持也都有了,http1.1的pipeline也是支持的:
https://github.com/lesismal/nbio/tree/master/examples

websocket的压缩我没有支持,因为搜了下,不是所有浏览器/client都支持,所以服务端如果开启websocket协议规定的那个压缩不是一件正确的事情,并且应用层自己wrap一下随便使用什么压缩都可以,所以框架层干脆就不支持了

parser的upgrader机制在websocket上基本理顺了,加其他的upgrade也同样套路就好,以后还需要完善更多细节

先缓缓节奏,下一步看看 http2.0 好不好支持吧,以后可能还得考虑对grpc、quic这些下手,支持它们的non-blocking

论坛上发了个http的帖子:
https://gocn.vip/topics/11779

websocket的我看看要不要整理下再发个

@lesismal
Copy link
Author

目前nbio性能比其他几个基本都强: https://github.com/lesismal/go_network_benchmark

其他那几个没有实现net.Conn之类的,很难扩展,nbio的功能完备程度和可扩展程度已经碾压它们了

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@guonaihong
Copy link
Contributor

websocket的解析器代码是用的gobwas/ws,还是从零实现的?

@guonaihong
Copy link
Contributor

对了,tps为什么nbio的会低于std?

@lesismal
Copy link
Author

s为什么nbio的会低于

从nbio的协程池的用法不同,可以分开说:

  1. nbio不跨协程传递数据处理:以echo为例,即epoll协程上读到就回写数据。设定适当的poller协程数量,最大程度发挥cpu利用率,这种情况调教好参数,nbio是可以获得比std更高的qps/tps/吞吐量的,但是这种对业务类型比较敏感,适合用于代理、网关这种不需要数据库等慢操作的基础设施,比如对标nginx,如果业务处理有慢操作,那这单个poller上某个连接某个消息处理得慢,这个poller的利用率就低了
  2. 跨协程传递数据去parse,parse后再跨协程去handle message,跨协程传递的成本也不低,所以在并发数不算特别大的时候,nbio或者其他异步库相比std也通常是劣势一些。但是如果并发数相对于硬件有点过大了,比如你至少开个10w、20w个连接(当然得看具体硬件配置,比如4c8t),内存和调度的成本平衡打破了,异步库的优势就又回来了,在整体qps/tps/吞吐量以及延迟标准差等稳定性上的指标,都会好于std

@lesismal
Copy link
Author

lesismal commented Mar 19, 2021

websocket的解析器代码是用的gobwas/ws,还是从零实现的?

http upgrade握手是直接gorillia那段拿过来改的。http upgrade握手之后的二进制,是直接按照websocket的协议格式自己手撸的,gorillia那些都写得过于复杂,没法拿来改。

gobwas/ws的代码我没看过,但是有写代码把他们相关的那个websocket百万连接的代码做过粘包测试,似乎他们那个不行啊,假的百万连接,我还去给他们提了个issue
这个issue里有我的arpc的一个issue里的评论,那里面有我的测试代码,你试下就知道了,他们这个百万连接的只是设置了event到epoll,但是没有设置fd为非阻塞,可读时调用读取读取整个websocket message,但是,如果client端没有及时把整个websocket message的数据发过来,因为它是阻塞的,它就一直在那等到超时,然后,这个epoll上的其他连接就都等它,随便一个慢连接攻击就把他们服务器打垮,这也敢号称百万连接?too simple too naive, sometimes stupid。。。 😂
“粘包”这个词虽然不准确,但是做网络服务tcp相关的,这是一项最基本的必须覆盖项

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

lesismal commented Mar 19, 2021

有分段,有兴趣的话可以来看一波源码

解析的部分是根据fin来判断的:
https://github.com/lesismal/nbio/blob/master/nbhttp/websocket/upgrader.go#L168

发送的地方是按照size,size太大就分多个frame发送,分帧的size暂时是写死的,以后再看要不要外放出来给应用层可配置的参数:
https://github.com/lesismal/nbio/blob/master/nbhttp/websocket/conn.go#L104

@lesismal
Copy link
Author

类似utf-8

utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些

@lesismal
Copy link
Author

有没有实现websocket里面分段传送的?我以前看rfc发现还有分段传的方式。。。有点类似utf-8的感觉。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上9:55 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) websocket的解析器代码是用的gobwas/ws,还是从零实现的? http upgrade握手是直接gorillia那段拿过来改的。http upgrade握手之后的二进制,是直接按照websocket的协议格式自己手撸的,gorillia那些都写得过于复杂,没法拿来改。 gobwas/ws的代码我没看过,但是有写代码把他们相关的那个websocket百万连接的代码做过粘包测试,似乎他们那个不行啊,假的百万连接,我还去给他们提了个issue: eranyanay/1m-go-websockets#20 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

http的chunk也有点绕,不只是chunk格式解析本身,还有trailer,你的这个parser之前好像是没有处理trailer的,前几天还有人问我nbio http的chunk问题,不过他的问题有点不够清楚,没能理解他具体想问什么,在这个帖子里:
https://gocn.vip/topics/11779

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

我只能说厉害。不过我最近刚刚换工作。等工作节奏cover住 写个websocket解析器玩玩的,完成2021计划中的一个玩具,没想到你先一步。到时候看看谁玩的快,哈哈。。。要注意休息啊,兄弟,细水长流。http2里面的流控算法还是有点好玩。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:14 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

http2的流控不能算流控的。你有兴趣的话看下我的 arpc 就懂了,单个连接除了tcp自己的线头阻塞,7层应用协议根本就不需要线头阻塞,是http 1.x太蠢非要处理完一个才能下一个才导致的这么浪费(1.1的pipeline只是缓解了发送方的发送,相当于只缓解了三分之一的应用层线头阻塞)

@lesismal
Copy link
Author

兄弟新工作加油~

@lesismal
Copy link
Author

我只能说厉害。不过我最近刚刚换工作。等工作节奏cover住 写个websocket解析器玩玩的,完成2021计划中的一个玩具,没想到你先一步。到时候看看谁玩的快,哈哈。。。要注意休息啊,兄弟,细水长流。http2里面的流控算法还是有点好玩。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:14 收件人: _@**._>; 抄送: _@.@.**_>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

http2的流控不能算流控的。你有兴趣的话看下我的 arpc 就懂了,单个连接除了tcp自己的线头阻塞,7层应用协议根本就不需要线头阻塞,是http 1.x太蠢非要处理完一个才能下一个才导致的这么浪费(1.1的pipeline只是缓解了发送方的发送,相当于只缓解了三分之一的应用层线头阻塞)

一个消息的发送和响应的完整流程:

  1. client发包
  2. server回包
  3. client读包
    http 1.1的pipeline只是缓解了步骤1中client的排队等待

我的 arpc,或者其他rpc应该也是类似的,每个请求自带个sequence或者其他的唯一标识,回包随便什么顺序,是按这个唯一标识来找到回包在哪里处理的,所以client和server端的收发都随时随地,只要保证包体不互相打乱就行,根本不需要像http 1.x那样傻x排队
并且,通讯模式又不只是请求-应答这一种,也不只是client -> server 这一种,方向上是两端都可以发起,还有的场景不需要响应,所以arpc支持
client call server
client notify server(server不需要应答回包)
server call client(需要注意广播场景单个连接阻塞导致其他连接跟着阻塞,业务层自己分析清楚怎么用,只在适合的场景用)
server notify client(client不需要应答回包)
并且,arpc的call还支持异步。。。姿势比其他rpc都太完整了,所以arpc不只可以用来做rpc,其实它是一个比较完整的网络库。。。:joy:

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

lesismal commented Mar 19, 2021

我理解http2里面的流是虚拟流。stream id串起一个个frame形成一个虚拟流。站在生产者消费者的角度,如果消费者太慢,生成者必然要阻塞虚拟流。这里就需要流控算法。如果不同frame有优先级的概念,会让这件事变得更复杂。就是因为多路复用多tcp,因为虚拟流跑在多个tcp上,有拆包的逻辑,所以更需要流控算法。(arpc有时间拜读下。)

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:50 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 我只能说厉害。不过我最近刚刚换工作。等工作节奏cover住 写个websocket解析器玩玩的,完成2021计划中的一个玩具,没想到你先一步。到时候看看谁玩的快,哈哈。。。要注意休息啊,兄弟,细水长流。http2里面的流控算法还是有点好玩。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:14 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. http2的流控不能算流控的。你有兴趣的话看下我的 arpc 就懂了,单个连接除了tcp自己的线头阻塞,7层应用协议根本就不需要线头阻塞,是http 1.x太蠢非要处理完一个才能下一个才导致的这么浪费(1.1的pipeline只是缓解了发送方的发送,相当于只缓解了三分之一的应用层线头阻塞) — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

其实你只要别让自己把tcp绑定在按顺序收发数据这个思维上就理解清楚了。tcp自己就像马路上,不同的车,都在这一条路上,但是每个车都有自己的标识,每个车是做不同的事情,血管和血液的各种成分也是类似的道理,不同的细胞不都是排队在做同一件事,他们可以各自做自己所属的事情,只是共用相同的管道

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

发送的顺序我倒是理的很清楚。无非加个seq,对端排个序就行。比如 我们用tcp加tlv组成私有协议。服务端不调用read。客户端填满内核skbuf。这时候应该是阻塞的。http2,如果一个tcp跑两个虚拟流,一个消费快,一个不消费。慢的对端还可以继续发数据?

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上11:02 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 我理解http2里面的流是虚拟流。stream id串起一个个frame形成一个虚拟流。站在生产者消费者的角度,如果消费者太慢,生成者必然要阻塞虚拟流。这里就需要流控算法。如果不同frame有优先级的概念,会让这件事变得更复杂。就是因为多路复用多tcp,因为虚拟流跑在多个tcp上,有拆包的逻辑,所以更需要流控算法。(arpc有时间拜读下。) … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:50 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 我只能说厉害。不过我最近刚刚换工作。等工作节奏cover住 写个websocket解析器玩玩的,完成2021计划中的一个玩具,没想到你先一步。到时候看看谁玩的快,哈哈。。。要注意休息啊,兄弟,细水长流。http2里面的流控算法还是有点好玩。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:14 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. http2的流控不能算流控的。你有兴趣的话看下我的 arpc 就懂了,单个连接除了tcp自己的线头阻塞,7层应用协议根本就不需要线头阻塞,是http 1.x太蠢非要处理完一个才能下一个才导致的这么浪费(1.1的pipeline只是缓解了发送方的发送,相当于只缓解了三分之一的应用层线头阻塞) — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 其实你只要别让自己把tcp绑定在按顺序收发数据这个思维上就理解清楚了。就像马路上,不同的车,都在这一条路上,但是每个车都有自己的标识,每个车是做不同的事情,血管里血液的各种成分也是类似的道理,不同的细胞不都是排队在做同一件事,他们可以各自做自己所属的事情,只是共用相同的管道 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

http2.0我还没有细看。但是从原理上来讲,他是4层之上的,所以他的虚拟流也是7层数据,只要如此,肯定就是需要把tcp缓冲区的数据读出来,所以即使它的部分虚拟流不消费也应该是应用层的不消费而不是不读取tcp的读缓冲吧?

@lesismal
Copy link
Author

首先要读出来才知道这个数据是哪个虚拟流的,而不是出来之前就知道tcp缓冲区里的数据是谁的,并且tcp也不提供seek到缓冲指定位置再选择性读取的功能

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@guonaihong
Copy link
Contributor

guonaihong commented Mar 19, 2021 via email

@lesismal
Copy link
Author

我也没看http2,只是用tcp的思路推测它。不消费至少要设计一个特殊的frame,让不消费的虚拟流停止发数据。所以应该有停等之类的确认机制。或者滑动窗口。这块可以放到我们都看过rfc再讨论。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上11:17 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 发送的顺序我倒是理的很清楚。无非加个seq,对端排个序就行。比如 我们用tcp加tlv组成私有协议。服务端不调用read。客户端填满内核skbuf。这时候应该是阻塞的。http2,如果一个tcp跑两个虚拟流,一个消费快,一个不消费。慢的对端还可以继续发数据? … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上11:02 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 我理解http2里面的流是虚拟流。stream id串起一个个frame形成一个虚拟流。站在生产者消费者的角度,如果消费者太慢,生成者必然要阻塞虚拟流。这里就需要流控算法。如果不同frame有优先级的概念,会让这件事变得更复杂。就是因为多路复用多tcp,因为虚拟流跑在多个tcp上,有拆包的逻辑,所以更需要流控算法。(arpc有时间拜读下。) … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:50 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 我只能说厉害。不过我最近刚刚换工作。等工作节奏cover住 写个websocket解析器玩玩的,完成2021计划中的一个玩具,没想到你先一步。到时候看看谁玩的快,哈哈。。。要注意休息啊,兄弟,细水长流。http2里面的流控算法还是有点好玩。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:14 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 是的,收敛问题,通常要写很多test code。写代码只要很短的时间,收敛问题要花太多时间精力。 … ---原始邮件--- 发件人: @.> 发送时间: 2021年3月19日(周五) 晚上10:06 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 类似utf-8 utf8通常不需要判断粘包的问题,tcp网络数据的解析通常要更麻烦一些 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 嗯,我得缓缓节奏,休息下,http2可能比http1.x+webwocket还难啃,搞太狠了最近音乐刺激已经没啥效果了 😂 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. http2的流控不能算流控的。你有兴趣的话看下我的 arpc 就懂了,单个连接除了tcp自己的线头阻塞,7层应用协议根本就不需要线头阻塞,是http 1.x太蠢非要处理完一个才能下一个才导致的这么浪费(1.1的pipeline只是缓解了发送方的发送,相当于只缓解了三分之一的应用层线头阻塞) — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. 其实你只要别让自己把tcp绑定在按顺序收发数据这个思维上就理解清楚了。就像马路上,不同的车,都在这一条路上,但是每个车都有自己的标识,每个车是做不同的事情,血管里血液的各种成分也是类似的道理,不同的细胞不都是排队在做同一件事,他们可以各自做自己所属的事情,只是共用相同的管道 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. http2.0我还没有细看。但是从原理上来讲,他是4层之上的,所以他的虚拟流也是7层数据,只要如此,肯定就是需要把tcp缓冲区的数据读出来,所以即使它的部分虚拟流不消费也应该是应用层的不消费而不是不读取tcp的读缓冲吧? — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

嗯,等细看了再讨论这块

@lesismal
Copy link
Author

看了下http2.0基础介绍,stream其实没必要分frame,websocket也是如此,分帧本身是一种浪费,因为通常应用层无法自主控制,并且协议层也没提供这个暴露给应用层控制的接口。并且不管怎么分帧,接收方都需要把这些数据解析拼接或者流式处理,而分帧本身就成了额外消耗,完全是多余的性能浪费。http2.0的优先级更进一步过度设计了,同样道理,不管是应用层还是协议层自己都很难控制这个优先级,并且,为了实现stream+分帧,再优先级,实现本身更复杂、性能更浪费。再并且,优先级意味着tcp管道的拥堵或者消息消费方处理资源的竞争,对于目前的所有编程语言,不管是异步的还是erlang、golang这种语言层轻量并发流的,优先级意味着更多更复杂的开销,其实是对性能的下降,但是实际场景中带来的收益却不明显。
arpc没有分帧和优先级,单个连接之上的多stream其实本质都是相同的

websocket分帧是画蛇添足,本来extended payloadlen最大就支持8bytes呢,还分帧,简直是弱智
http2.0虽然比1.x优秀,但也同样足够垃圾,尤其golang标准库的实现,刚稍微扫了下代码,跟标准库那个tls类似,也性能不友好,就像资源不要钱似的,一点都不节约

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

@lesismal
Copy link
Author

从你描述上看。我猜测http2的设计思路很多是copy tcp的。对了,你设计的rpc没有分frame,是如何提升单tcp的使用率的?比如tcp跑的数据500ms才有个数据包。还有500ms是空闲的。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月20日(周六) 下午3:03 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 看了下http2.0基础介绍,stream其实没必要分frame,websocket也是如此,分帧本身是一种浪费,因为通常应用层无法自主控制,并且协议层也没提供这个暴露给应用层控制的接口。并且不管怎么分帧,接收方都需要把这些数据解析拼接或者流式处理,而分帧本身就成了额外消耗,完全是多余的性能浪费。http2.0的优先级更进一步过度设计了,同样道理,不管是应用层还是协议层自己都很难控制这个优先级,并且,为了实现stream+分帧,再优先级,实现本身更复杂、性能更浪费。再并且,优先级意味着tcp管道的拥堵或者消息消费方处理资源的竞争,对于目前的所有编程语言,不管是异步的还是erlang、golang这种语言层轻量并发流的,优先级意味着更多更复杂的开销,其实是对性能的下降,但是实际场景中带来的收益却不明显。 arpc没有分帧和优先级,单个连接之上的多stream其实本质都是相同的 websocket分帧是画蛇添足,本来extended payloadlen最大就支持8bytes呢,还分帧,简直是弱智 http2.0虽然比1.x优秀,但也同样足够垃圾,尤其golang标准库的实现,刚稍微扫了下代码,跟标准库那个tls类似,也性能不友好,就像资源不要钱似的,一点都不节约 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

frame不是关键,stream是关键。frame反倒增加框架层的编解码成本

@lesismal
Copy link
Author

从你描述上看。我猜测http2的设计思路很多是copy tcp的。对了,你设计的rpc没有分frame,是如何提升单tcp的使用率的?比如tcp跑的数据500ms才有个数据包。还有500ms是空闲的。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月20日(周六) 下午3:03 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 看了下http2.0基础介绍,stream其实没必要分frame,websocket也是如此,分帧本身是一种浪费,因为通常应用层无法自主控制,并且协议层也没提供这个暴露给应用层控制的接口。并且不管怎么分帧,接收方都需要把这些数据解析拼接或者流式处理,而分帧本身就成了额外消耗,完全是多余的性能浪费。http2.0的优先级更进一步过度设计了,同样道理,不管是应用层还是协议层自己都很难控制这个优先级,并且,为了实现stream+分帧,再优先级,实现本身更复杂、性能更浪费。再并且,优先级意味着tcp管道的拥堵或者消息消费方处理资源的竞争,对于目前的所有编程语言,不管是异步的还是erlang、golang这种语言层轻量并发流的,优先级意味着更多更复杂的开销,其实是对性能的下降,但是实际场景中带来的收益却不明显。 arpc没有分帧和优先级,单个连接之上的多stream其实本质都是相同的 websocket分帧是画蛇添足,本来extended payloadlen最大就支持8bytes呢,还分帧,简直是弱智 http2.0虽然比1.x优秀,但也同样足够垃圾,尤其golang标准库的实现,刚稍微扫了下代码,跟标准库那个tls类似,也性能不友好,就像资源不要钱似的,一点都不节约 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

只要摆脱一应一答的策略,其他就都好办了。arpc同一个连接的client可以多个不同协程同时Call,比如同时发送了10个Call消息给server端,server端可以自己决定是同步还是异步处理,如果这10个Call请求都是异步处理,比如1-5个处理得慢,6-10处理得快,6-10的响应包就先回复给client了,client端先收到了6-10的应答也就结束了Call流程,1-5处理完之后再响应给client,不会因为1-5处理得慢而导致6-10必须等待,只要应用层自己控制好是同步还是异步处理就好了,这又涉及到业务层的资源调度以及其他的依赖资源比如数据库连接池等的分配,所以通常还要有限流、削峰、熔断等机制

@lesismal
Copy link
Author

你现在可能还是被tcp 1.x的思路限制了,我最初就不是做web相关的,而是做其他类业务,都是自家定制的协议,所以从来没有被请求-应答这种思维束缚过,反倒是从其他领域再来搞http,从一开始就觉得http太二了。。

@lesismal
Copy link
Author

lesismal commented Mar 20, 2021

tcp也是二,握手挥手慢启动和老版本的拥塞算法,也都是在秀智商下限,明明可以简单粗暴更高效,非得搞一堆画蛇添足、理解起来吃力、性能反倒下降,所以tcp BBR的拥塞策略不是更easy了些,其实早期的一些策略看上去牛逼闪闪高大上,但却然并卵、作茧自缚

@lesismal
Copy link
Author

从你描述上看。我猜测http2的设计思路很多是copy tcp的。对了,你设计的rpc没有分frame,是如何提升单tcp的使用率的?比如tcp跑的数据500ms才有个数据包。还有500ms是空闲的。

---原始邮件--- 发件人: @.> 发送时间: 2021年3月20日(周六) 下午3:03 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 看了下http2.0基础介绍,stream其实没必要分frame,websocket也是如此,分帧本身是一种浪费,因为通常应用层无法自主控制,并且协议层也没提供这个暴露给应用层控制的接口。并且不管怎么分帧,接收方都需要把这些数据解析拼接或者流式处理,而分帧本身就成了额外消耗,完全是多余的性能浪费。http2.0的优先级更进一步过度设计了,同样道理,不管是应用层还是协议层自己都很难控制这个优先级,并且,为了实现stream+分帧,再优先级,实现本身更复杂、性能更浪费。再并且,优先级意味着tcp管道的拥堵或者消息消费方处理资源的竞争,对于目前的所有编程语言,不管是异步的还是erlang、golang这种语言层轻量并发流的,优先级意味着更多更复杂的开销,其实是对性能的下降,但是实际场景中带来的收益却不明显。 arpc没有分帧和优先级,单个连接之上的多stream其实本质都是相同的 websocket分帧是画蛇添足,本来extended payloadlen最大就支持8bytes呢,还分帧,简直是弱智 http2.0虽然比1.x优秀,但也同样足够垃圾,尤其golang标准库的实现,刚稍微扫了下代码,跟标准库那个tls类似,也性能不友好,就像资源不要钱似的,一点都不节约 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

不是 copy tcp,另外,500ms忙500ms空闲的说法和思路也有误区——是否能让传输管道充分利用起来不是根据管道自己的忙、闲来判断,而是要根据上层数据是否能够被有效进行传输来判断。如果业务层没数据,管道没法不闲着。
1.1、2.0分别解决了一部分线头阻塞的问题:
1.1支持了pipeline,client发送可以不等待上个应答返回,但server处理请求和client处理响应还是按顺序的
2.0提出的虚拟stream,就是收发处理应该是都可以不等待上一个了,但这个stream只是应用层,都还是跑在tcp之上的,所以它没办法解决tcp自己的拥塞场景下的线头阻塞,尤其是浏览器这种client、需要请求的资源量太多了,每个来源一个连接遇到拥塞就会性能降低到不如1.x的尴尬地步。但如果只是作为普通接口交互,2.0足够了,类似arpc,只做非静态资源类的交互,足够了(不过arpc不限制底层Conn所使用的协议,想用kcp/quic或者其他也都可以)

http3.0,就是over quic的版本,才是王道,quic本身不依赖tcp了,单个连接线头阻塞的问题也得到了彻底解决。不过现在2.0还没普及,3.0只是草案并且很多语言框架厂商还没支持呢,路漫漫

@lesismal
Copy link
Author

你可以看下简单的rpc实现,比如golang标准库的 net/rpc,代码不多,花几分钟扫一会就差不多懂了,再多花点时间理解下数据收发和协议处理的流程,换个思路再思考,把基础的东西想明白了,就不会有这么多疑问了

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

@lesismal
Copy link
Author

你还是被固有概念限制了自己的思维,抛开这些概念,只考虑数据传输通道和数据交互方式,就简单了

你们太多人把思考方式搞反了,都是在用这些既有的概念去理解为什么合理,但如果你反过来,直接思考怎样的方式更合理、再去为了合理而设计交互模式、然后这些模式就成了那些概念,这样思考就容易理解了

@lesismal
Copy link
Author

lesismal commented Mar 20, 2021

比如你想去思考tcp是否合理,不应该用tcp自己的那些设计原理去理解它的合理性,因为它设计的时候就是觉得那样合理所以那样设计并且把这个设计原因整理成原理给你看,相当于自己说自己合理,你得跳脱出来,抛开它说它的合理性,从更底层或者更高的层次去分析它

我当年第一眼看tcp三路握手四次断开双工模式就喷tcp二逼,并且跟好多人掰扯过tcp二逼这事情了,就xtaci和另外一个也是老家伙大家观点比较一致、同意tcp二逼,其他太多人都在用tcp设计的原理来跟我们撕逼tcp的合理性,没法聊

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

@lesismal
Copy link
Author

这块可以用数学证明他的二逼性嘛?我觉得挺好玩的

---原始邮件--- 发件人: @.> 发送时间: 2021年3月20日(周六) 下午5:36 收件人: @.>; 抄送: @.@.>; 主题: Re: [antlabs/httparser] 粘包的处理好像还是有问题 (#7) 比如你想去思考tcp是否合理,应该用tcp自己的那些设计原理去理解它的合理性,因为它设计的时候就是觉得那样合理所以那样设计并且把这个设计原因整理成原理给你看,相当于自己说自己合理,你得跳脱出来,抛开它说它的合理性,从更底层或者更高的层次去分析它 我当年第一眼看tcp三路握手四次断开双工模式就喷tcp二逼,并且跟好多人掰扯过tcp二逼这事情了,就xtaci和另外一个也是老家伙大家观点比较一致、同意tcp二逼,其他太多人都在用tcp设计的原理来跟我们撕逼tcp的合理性,没法聊 — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

不需要数学证明

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

@lesismal
Copy link
Author

lesismal commented Mar 20, 2021

比如第三次是为了确认第二次已经被收到,那谁来确认第三次已经被收到?照这个握手逻辑,应该是无穷无尽确认下去

四次挥手也一样,即使是对应FIN的那个ACK发出去了,也不能确认这个ACK是否丢失在网络链路中了,因为没有下一个ACK来确认这个ACK被收到,同样,如果需要确认,则无穷无尽

双工分开关闭也是二逼,极少数场景才用到半关闭、并且这些场景也可以改变下机制来改成一次全关

因为任意一个数据包都可能丢失,无穷尽的确认或者半关闭之类的这些拖泥带水的机制,都是垃圾

BBR/KCP/QUIC这些简单的拥塞处理方式更高效。至于流量增加,如果http早年不设计的这么垃圾,本身节省出来的流量就足以抵消由于在途容量增加导致的重传带来的额外流量增加了、甚至节省更多,而且现在5G已经来了,即使偏高的波动时段30%的重传流量增加也不算什么大麻烦

@guonaihong
Copy link
Contributor

guonaihong commented Mar 20, 2021 via email

guonaihong added a commit that referenced this issue Mar 30, 2021
@guonaihong
Copy link
Contributor

每个字节的排列组合基本都测试过, 该问题已经解决, 用法可看
https://github.com/antlabs/httparser/blob/main/parser_porting_code_test.go#L1980

@lesismal
Copy link
Author

算了,流媒体服务,rust比go更适合,我还是不用go肝它了

@guonaihong
Copy link
Contributor

厉害厉害, 我打算今年github再写5w行go, 然后再调戏下rust.

解码器这块, go的性能也可以的, 你benchmark 图像go和rust的库, 性能差不多, �前几年看别人压过.

@guonaihong
Copy link
Contributor

对了, 刚忘了问了, 解码器也从零开撸?

@lesismal
Copy link
Author

厉害厉害, 我打算今年github再写5w行go, 然后再调戏下rust.

解码器这块, go的性能也可以的, 你benchmark 图像go和rust的库, 性能差不多, �前几年看别人压过.

会差很多,不只是解码器的cpu运算速度,还有对内存的控制能力、gc的消耗、调度的消耗。
这种基础设施,go肯定不如rust。

@lesismal
Copy link
Author

对了, 刚忘了问了, 解码器也从零开撸?

流媒体服务器不需要实现播放器逻辑,这种编解码只是协议头、字段的解析,就单个编解码协议而言,应该是比实现http更简单。你看下livego或者其他几个开源的代码里编解码部分大概就清楚了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants