让你的异步 io 库插上 http1.1 解析的翅膀。 httparser 来也。

2021-02-01 11:34:50 +08:00
 guonaihong

httparser

高性能 http 1.1 解析器,为你的异步 io 库插上解析的翅膀,目前每秒可以处理 300MB/s 流量[从零实现]

仓库位置

https://github.com/antlabs/httparser

出发点

本来想基于异步 io 库写些好玩的代码,发现没有适用于这些库的 http 解析库,索性就自己写个,弥补 golang 生态一小片空白领域。

特性

parser request

	var data = []byte(
		"POST /joyent/http-parser HTTP/1.1\r\n" +
			"Host: github.com\r\n" +
			"DNT: 1\r\n" +
			"Accept-Encoding: gzip, deflate, sdch\r\n" +
			"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" +
			"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " +
			"AppleWebKit/537.36 (KHTML, like Gecko) " +
			"Chrome/39.0.2171.65 Safari/537.36\r\n" +
			"Accept: text/html,application/xhtml+xml,application/xml;q=0.9," +
			"image/webp,*/*;q=0.8\r\n" +
			"Referer: https://github.com/joyent/http-parser\r\n" +
			"Connection: keep-alive\r\n" +
			"Transfer-Encoding: chunked\r\n" +
			"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n")

	var setting = httparser.Setting{
		MessageBegin: func() {
			//解析器开始工作
			fmt.Printf("begin\n")
		},
		URL: func(buf []byte) {
			//url 数据
			fmt.Printf("url->%s\n", buf)
		},
		Status: func([]byte) {
			// 响应包才需要用到
		},
		HeaderField: func(buf []byte) {
			// http header field
			fmt.Printf("header field:%s\n", buf)
		},
		HeaderValue: func(buf []byte) {
			// http header value
			fmt.Printf("header value:%s\n", buf)
		},
		HeadersComplete: func() {
			// http header 解析结束
			fmt.Printf("header complete\n")
		},
		Body: func(buf []byte) {
			fmt.Printf("%s", buf)
			// Content-Length 或者 chunked 数据包
		},
		MessageComplete: func() {
			// 消息解析结束
			fmt.Printf("\n")
		},
	}

	p := httparser.New( httparser.REQUEST)
	success, err := p.Execute(&setting, data)

	fmt.Printf("success:%d, err:%v\n", success, err)

response

response

request or response

如果你不确定数据包是请求还是响应,可看下面的例子
request or response

编译

生成 unhex 表和 tokens 表

如果需要修改这两个表,可以到_cmd 目录下面修改生成代码的代码

make gen

编译 example

make example

运行示例

make example.run

return value

吞吐量

2193 次点击
所在节点    Go 编程语言
25 条回复
guonaihong
2021-02-01 16:25:16 +08:00
@lesismal end 打印的是空行,修改下 fmt.Printf 就可以看到。是否复制我的 example 代码,

MessageComplete: func() {
// 消息解析结束
fmt.Printf("\n")
},
lesismal
2021-02-01 16:34:16 +08:00
@guonaihong 好,我 new 个 issue
guonaihong
2021-02-01 16:36:33 +08:00
@lesismal good 。这样有一些好的讨论别人也可以看到。
eudore
2021-02-02 09:32:57 +08:00
1 、不完全认可你这个 300m/s vs 124m/s 的结果,因为你没创建*http.Request 对象,创建是额外需要一定资源的,没创建易用性很差。

2 、Parse 函数长。。。
guonaihong
2021-02-02 11:03:13 +08:00
@eudore 2.Parse 长,没办法,如果 go 里面有宏替换,或者手动内联优化,也不需要写这么长了。这么写只是为了减少进 stack 出 stack 的成本。

1.哪怕使用内存分配比官方库快也是很容易的。分配可以保存 http header 内存+浅引用指向 field 和 value+惰性解析。

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

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

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

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

© 2021 V2EX