Comments (39)
TLS的支持也是个体力活
from httparser.
如果要支持返回一个request对象.更倾向于fasthttp的方式。因为http.Request,不是lazy parser。
from httparser.
如果支持 Request,可能 Parser.Execute 直接返回 request 比较好,而不是通过回调的方式,比如:
request, nparse, err := p.Execute(...)
request.Conn = ...
router(request, ...)
因为现在的回调方式,业务层不好判断一个 packet 解析完成后,MessageComplete 没有、也不方便带上这个 packet 对应的 Request、Conn 信息作为参数,业务层实际使用时要做更多的扩展并且姿势会有些难看
from httparser.
Setting 似乎可以考虑跟 Parser 合并,并提供 Request 相关字段解析的 defaut implementation ,但允许业务层定制字段解析的 func。
如果 Setting 跟 Parser 合并,Parser 再自带上次解析剩余的不完整 buf 进行后续的数据合并、再次解析,那么每个 Conn 上挂载一个 Parser 也就都容易了,当然这样依赖于 Conn 是否有挂载外部工具的功能,或者额外的 map 映射一下也算简单。
但不管怎么说,解析后的 packet 与 Conn 肯定是需要友好一些的映射机制的
from httparser.
看了下node原版c的,parser是带有void* data可以用来指向 connection或socket object的:
https://github.com/nodejs/http-parser/blob/master/http_parser.h#L330
并且setting的回调也是有把parser作为参数传递给业务层的,这样看通常是一个链接一个parser,业务层更方便
https://github.com/nodejs/http-parser/blob/master/http_parser.c#L86
https://github.com/nodejs/http-parser/blob/master/http_parser.c#L1849
from httparser.
c里面不支持lamda表达式,所以才要传参。支持lamda表达式的语言,不需要啊,直接捕获外面的变量。
from httparser.
这块,两种API的形式都考虑过。最后设计成这样,主要是应为go的回调函数支持捕获外部变量。用起来比c的形式简单点。
from httparser.
比如,标准库里面的回调函数都是无参回调。都是基于这个特性
举例: sync.Once
接口
https://golang.google.cn/src/sync/once.go?s=1473:1500#L30
from httparser.
c里面不支持lamda表达式,所以才要传参。支持lamda表达式的语言,不需要啊,直接捕获外面的变量。
这不是能不能实现的问题,而是出于设计和工程性角度的考虑
局部闭包对于工程结构的规划略有限制,应该给业务层多一点自由发挥的空间,如果业务层的hook如果写得复杂点,局部代码可读性会差很多,如果想复用则更难受
from httparser.
比如,标准库里面的回调函数都是无参回调。都是基于这个特性
举例:sync.Once
接口
https://golang.google.cn/src/sync/once.go?s=1473:1500#L30
Once的场景不一样,Once是针对未知业务场景、并且一个函数执行一次就结束、不需要多次执行时每次去判断与其他obj的映射关系,既然很多无法判断,干脆就留给业务层自己,go的闭包搞方便,而且也没必要反射去支持变长参数
但是你的parser是大概已知业务层需要做哪些相关处理,这种让业务层无参数的闭包回调、且多次调用业务层需要知道此次回调与业务层关联对象的映射关系,这对业务层就不那么友好了,想让工程结构更美观点都不方便了
from httparser.
这样吧。我花2-3天,优化下httparser里面的状态流程。然后用httparser写个wrk类似的工具实战下。实战对比两种设计的优劣。最后取最优者。
要不这块合作下,我看你有个nbio的库。
我现在需要一个薄薄的异步io库,可否设计成类似于libev的API形式。
from httparser.
因为要方便控制buffer,所以现在封装的比较厚的gnet和gev先不考虑。
from httparser.
方便加微信吗?很难得遇兴趣爱好如此接近的童鞋。。。
我的微信base64编码如下
NzEwMzkwNTE1
from httparser.
这样吧。我花2-3天,优化下httparser里面的状态流程。然后用httparser写个wrk类似的工具实战下。实战对比两种设计的优劣。最后取最优者。
要不这块合作下,我看你有个nbio的库。
我现在需要一个薄薄的异步io库,可否设计成类似于libev的API形式。
nbio 只支持linux,可以玩玩,虽然比目前其他那些开源的异步网络库性能都好、也更易用,但是因为http涉及的细节太多,我不打算花太多时间做更多支持
如果想跨平台更方便可以试试 gnet 之类的
from httparser.
好吧。
from httparser.
BTW,如果有其他需要,可以试试我的 arpc: https://github.com/lesismal/arpc 🤣🤣
from httparser.
ok
from httparser.
http底层支持相关的,工程量有点大,两三年前我还想把 node 那个 http-parser 翻译成 go 来着,但是后面还要有 http 2 、3,异步的 ssl/tls 支持
然后问题又来了,只是底层库,还是没人用,然后就得考虑再封装一套 http 框架,精力有限,除非有公司出钱让全职搞。。。🤣🤣
from httparser.
之前看字节分享他们的RPC优化,也是异步网络库,他们提到过一个问题:golang的异步网络库的方式,由于协程跟CPU的亲和性不够,所以没办法保证调度对业务造成的影响,好象是说他们有遇到过cpu毛刺,然后字节的内核团队定制了个内核补丁解决了
我目前还没有在商业项目里使用异步网络库来做业务,但是并发量大的话,调度可能确实会对异步网络库以及业务层协程产生一些不好的影响
不过大部分场景还好了,如果不是海量并发场景下的海量服务器,通常不需要考虑极致压榨软硬件性能来节约更多成本,只要不是极致压榨cpu到80-90+%这种,异步网络库已经能很大程度上提高了同等配置能够承载的并发量了
from httparser.
是的。而且商业项目的瓶颈在数据库。API服务那点性能损失,完全可以通过加机器来水平扩展。
from httparser.
方便加微信吗?很难得遇兴趣爱好如此接近的童鞋。。。
我的微信base64编码如下
NzEwMzkwNTE1
才看到,github 就挺好了,社交软件太浪费时间了,我都习惯了github一把梭 😄 😄
from httparser.
这样吧。我花2-3天,优化下httparser里面的状态流程。然后用httparser写个wrk类似的工具实战下。实战对比两种设计的优劣。最后取最优者。
要不这块合作下,我看你有个nbio的库。
我现在需要一个薄薄的异步io库,可否设计成类似于libev的API形式。
如果只是为了轻便的网络层,libev的API形式没什么必要,或者如果你喜欢,可以简单在nbio基础上 wrap 一层
我给你写了个简单的 nbio + httpparser 的例子,httpparser 的 bug 我先忽略了,默认 httpparser.Execute 每次能解析成功一个请求,详见注释:
server.go
package main
import (
"fmt"
"log"
"time"
"github.com/antlabs/httparser"
"github.com/lesismal/nbio"
)
var responseFormat = "HTTP/1.1 200 OK\r\n" +
"Date: Tue, 02 Feb 2021 10:58:43 GMT\r\n" +
"Content-Length: %v\r\n" +
"Content-Type: text/plain; charset=utf-8\r\n" +
"Connection: close\r\n\n%v"
// Request .
type Request struct {
Headers map[string]string
Body []byte
}
// Session .
type Session struct {
Parser *httparser.Parser
Setting *httparser.Setting
Request *Request
completed bool
}
// Next .
func (session *Session) Next() (*Request, bool) {
if session.completed {
return session.Request, true
}
return nil, false
}
func main() {
g, err := nbio.NewGopher(nbio.Config{
Network: "tcp",
Addrs: []string{":8080"},
})
if err != nil {
log.Printf("nbio.New failed: %v\n", err)
return
}
g.OnOpen(func(c *nbio.Conn) {
c.SetReadDeadline(time.Now().Add(time.Second * 120))
parser := httparser.New(httparser.REQUEST)
setting := &httparser.Setting{
MessageBegin: func() {
session := c.Session().(*Session)
session.Request = &Request{}
session.completed = false
},
Body: func(buf []byte) {
session := c.Session().(*Session)
session.Request.Body = append(session.Request.Body, buf...)
},
MessageComplete: func() {
session := c.Session().(*Session)
session.completed = true
},
}
c.SetSession(&Session{Parser: parser, Setting: setting})
log.Println("+ connected:", c.RemoteAddr().String(), time.Now().Format("15:04:05.000"))
})
g.OnClose(func(c *nbio.Conn, err error) {
log.Println("- disconnected:", c.RemoteAddr().String(), time.Now().Format("15:04:05.000"), err)
})
g.OnData(func(c *nbio.Conn, data []byte) {
c.SetReadDeadline(time.Now().Add(time.Second * 120))
session := c.Session().(*Session)
// 这里忽略了 httpparser 解析的bug,先默认认为解析成功
_, err := session.Parser.Execute(session.Setting, data)
if err != nil {
log.Printf("parse failed: %v", err)
c.Close()
return
}
c.SetWriteDeadline(time.Now().Add(time.Second * 3))
response := append([]byte(fmt.Sprintf(responseFormat, len(session.Request.Body), string(session.Request.Body))))
c.Write(response)
// Parser.Execute 应该是这样子更合理
// for {
// request, ok, err := session.Parser.Execute(session.Setting, data)
// if err != nil {
// log.Printf("parse failed: %v", err)
// c.Close()
// return
// }
// if ok {
// c.SetWriteDeadline(time.Now().Add(time.Second * 3))
// c.Write(responseData)
// }
// }
})
err = g.Start()
if err != nil {
log.Printf("nbio.Start failed: %v\n", err)
return
}
// go func() {
// for {
// time.Sleep(time.Second * 5)
// log.Println(g.State().String())
// }
// }()
g.Wait()
}
client.go
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
url := "http://localhost:8080/echo"
method := "POST"
client := &http.Client{}
for i := 0; i < 10; i++ {
payload := strings.NewReader("hello")
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(111, err)
}
res, err := client.Do(req)
if err != nil {
fmt.Println(222, err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
fmt.Println("body:", string(body))
time.Sleep(time.Second / 10)
}
}
from httparser.
单开一个issue,咱们来把server慢慢打通吧
from httparser.
我上上楼用闭包实现 parser 到 connection 的映射,确实是有点丑 😂😂
from httparser.
那写个你想要的设计的伪代码。我看下。
from httparser.
的设计的伪代码。
fork 改了一点,看下我这个commit: 3rdrepo@32bc942
这个只是一点小改,我觉得需要改的细节还有点多,所以我就不PR了,你跑测试的例子代码可以把这个手动拷贝下
对应我这个commit的新的例子,server.go
package main
import (
"fmt"
"log"
"time"
"github.com/antlabs/httparser"
"github.com/lesismal/nbio"
)
var setting = &httparser.Setting{}
func onOpen(c *nbio.Conn) {
c.SetReadDeadline(time.Now().Add(time.Second * 120))
parser := httparser.New(httparser.REQUEST)
c.SetSession(parser)
log.Println("+ connected:", c.RemoteAddr().String(), time.Now().Format("15:04:05.000"))
}
func onClose(c *nbio.Conn, err error) {
log.Println("- disconnected:", c.RemoteAddr().String(), time.Now().Format("15:04:05.000"), err)
}
func onData(c *nbio.Conn, data []byte) {
c.SetReadDeadline(time.Now().Add(time.Second * 120))
parser := c.Session().(*httparser.Parser)
parser.Read(data)
for {
if request, ok, err := parser.Execute(setting); err == nil {
if !ok {
break
}
c.SetWriteDeadline(time.Now().Add(time.Second * 3))
response := []byte(fmt.Sprintf("HTTP/1.1 200 OK\r\n"+
"Date: Tue, 02 Feb 2021 10:58:43 GMT\r\n"+
"Content-Length: %v\r\n"+
"Content-Type: text/plain; charset=utf-8\r\n"+
"Connection: close\r\n\n%v", len(request.Body), string(request.Body)))
c.Write(response)
} else {
log.Printf("parse failed: %v", err)
c.Close()
return
}
}
}
func main() {
g, err := nbio.NewGopher(nbio.Config{
Network: "tcp",
Addrs: []string{":8080"},
})
if err != nil {
log.Printf("nbio.New failed: %v\n", err)
return
}
g.OnOpen(onOpen)
g.OnClose(onClose)
g.OnData(onData)
g.OnMemAlloc(func(c *Conn) []byte {
return make([]byte, 1024)
})
err = g.Start()
if err != nil {
log.Printf("nbio.Start failed: %v\n", err)
return
}
g.Wait()
}
from httparser.
好的工程还有好多细节要完善
比如上次收到的数据已经处理过的部分,加个状态、下次不需要再从头处理(比如header的部分200字节已经处理完了,后续的部分数据来不需要重新处理header部分,但是body有1k字节,上次只读到900并且处理了900字节,然后剩余数据的部分才逐渐收到,但是这时候每次收到数据,上次的900字节好像是也都要重新处理一遍),还有昨天说的比如允许[][]byte做参数传入、减少buffer不必要的拷贝,但是这样比如body 1k字节,分多段来的,还是要for循环去拼接,Execute的部分更难看些,实际场景未必真能有性能提升所以这种改法有待商榷
另外工程本身,命名、注释,都还有很大的优化空间,IDE上装一些插件,很多需要规范化的提示,想做成好的工程,还有很多细节需要打磨
还有就是功能完备度的,不知道跟node c版本的相比是否功能完善,最好把它的那个test也都加进来、尽量覆盖更全
from httparser.
这样小改一下,实际上对业务层已经友好很多了,跟nbio或者其他异步网络库接入也都很简单
from httparser.
from httparser.
body是流式处理的。header那块这么做是比较快的,可以把汇编里面的avx和sse指令利用起来。分段那块我现在有一个想法,可以借鉴伙伴分配器的双缓冲思路。等会儿详聊
恩,header数据少,影响不大,主要是长body。其实我上面commit的那版本,本来是 [][]byte 的,但是 header/body/message Complete的时候,都要把之前分散在多个[]byte的做一次拼接,太ugly了,还不如 append一次清爽。
伙伴是管理池的,对这里说的多段数据处理的优化没什么用
如果是想优化内存,得是sync.Pool,比如Parser可以用Pool管理,异步网络库的内存分配可以用Pool管理
from httparser.
我commit的版本和新的例子里,Setting已经没什么用了,完全可以去掉或者合并到Parser里,由Parser给业务层留扩展的口子
go没必要完全参考c版的,很多c库虽然牛逼但是设计细节并不友好
from httparser.
你fork的代码我看下。特性我先了解。
httparser现在比c原版速度快。go的实现是630MB/s, c的实现是505MB/s
所以,特性我会加的比较谨慎。。。哈哈。。。
from httparser.
你fork的代码我看下。特性我先了解。
httparser现在比c原版速度快。go的实现是630MB/s, c的实现是505MB/s
所以,特性我会加的比较谨慎。。。哈哈。。。
我稍微扫了几眼,c版的state比你现在的多,解析的功能也更详细、功能更全
用阉割版的跟人家比性能,不具备可比性,说性能更强容易混淆视听、没有意义
from httparser.
这个看取舍吧。现在这么设计主要是想把汇编指令的优势利用起来。
node js的写法是有弊端的。性能上不去。对cpu缓存都不是太好。更不谈avx了。
from httparser.
这个不讨论非设计上的事情。我先把test case搬过来。
以前看过fasthttp代码,处理header也是index。一开始没有想明白,后面悟出来了,主要是把机器指令优势发挥出来。
from httparser.
如何传递数据。我看你在#5里面回复过,那就在#5里面讨论。
from httparser.
目前版本跟c的对比主要还是在功能支持率上,应该还没到cache影响性能的程度,等我闲了瞄瞄
from httparser.
可以可以,人多力量大。
from httparser.
新开#6 讨论下Setting回调函数形参。看有没有更好的设计
from httparser.
Related Issues (8)
- 一次解析1.5包的问题 HOT 4
- 一个完整包,从http每行数据中间位置拆成子包异常 HOT 5
- httpparser + nbio 实现http server基础功能 HOT 23
- Execute形参讨论 HOT 40
- Setting里面回调函数形参 HOT 4
- 粘包的处理好像还是有问题 HOT 56
- 测试下http method方法是否正确 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from httparser.