Code Monkey home page Code Monkey logo

Comments (39)

lesismal avatar lesismal commented on June 6, 2024

TLS的支持也是个体力活

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

如果要支持返回一个request对象.更倾向于fasthttp的方式。因为http.Request,不是lazy parser。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

如果支持 Request,可能 Parser.Execute 直接返回 request 比较好,而不是通过回调的方式,比如:

request, nparse, err := p.Execute(...)
request.Conn = ...
router(request, ...)

因为现在的回调方式,业务层不好判断一个 packet 解析完成后,MessageComplete 没有、也不方便带上这个 packet 对应的 Request、Conn 信息作为参数,业务层实际使用时要做更多的扩展并且姿势会有些难看

from httparser.

lesismal avatar lesismal commented on June 6, 2024

Setting 似乎可以考虑跟 Parser 合并,并提供 Request 相关字段解析的 defaut implementation ,但允许业务层定制字段解析的 func。
如果 Setting 跟 Parser 合并,Parser 再自带上次解析剩余的不完整 buf 进行后续的数据合并、再次解析,那么每个 Conn 上挂载一个 Parser 也就都容易了,当然这样依赖于 Conn 是否有挂载外部工具的功能,或者额外的 map 映射一下也算简单。
但不管怎么说,解析后的 packet 与 Conn 肯定是需要友好一些的映射机制的

from httparser.

lesismal avatar lesismal commented on June 6, 2024

看了下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.

guonaihong avatar guonaihong commented on June 6, 2024

c里面不支持lamda表达式,所以才要传参。支持lamda表达式的语言,不需要啊,直接捕获外面的变量。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

这块,两种API的形式都考虑过。最后设计成这样,主要是应为go的回调函数支持捕获外部变量。用起来比c的形式简单点。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

比如,标准库里面的回调函数都是无参回调。都是基于这个特性
举例: sync.Once接口
https://golang.google.cn/src/sync/once.go?s=1473:1500#L30

from httparser.

lesismal avatar lesismal commented on June 6, 2024

c里面不支持lamda表达式,所以才要传参。支持lamda表达式的语言,不需要啊,直接捕获外面的变量。

这不是能不能实现的问题,而是出于设计和工程性角度的考虑
局部闭包对于工程结构的规划略有限制,应该给业务层多一点自由发挥的空间,如果业务层的hook如果写得复杂点,局部代码可读性会差很多,如果想复用则更难受

from httparser.

lesismal avatar lesismal commented on June 6, 2024

比如,标准库里面的回调函数都是无参回调。都是基于这个特性
举例: sync.Once接口
https://golang.google.cn/src/sync/once.go?s=1473:1500#L30

Once的场景不一样,Once是针对未知业务场景、并且一个函数执行一次就结束、不需要多次执行时每次去判断与其他obj的映射关系,既然很多无法判断,干脆就留给业务层自己,go的闭包搞方便,而且也没必要反射去支持变长参数

但是你的parser是大概已知业务层需要做哪些相关处理,这种让业务层无参数的闭包回调、且多次调用业务层需要知道此次回调与业务层关联对象的映射关系,这对业务层就不那么友好了,想让工程结构更美观点都不方便了

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

这样吧。我花2-3天,优化下httparser里面的状态流程。然后用httparser写个wrk类似的工具实战下。实战对比两种设计的优劣。最后取最优者。

要不这块合作下,我看你有个nbio的库。
我现在需要一个薄薄的异步io库,可否设计成类似于libev的API形式。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

因为要方便控制buffer,所以现在封装的比较厚的gnet和gev先不考虑。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

方便加微信吗?很难得遇兴趣爱好如此接近的童鞋。。。
我的微信base64编码如下
NzEwMzkwNTE1

from httparser.

lesismal avatar lesismal commented on June 6, 2024

这样吧。我花2-3天,优化下httparser里面的状态流程。然后用httparser写个wrk类似的工具实战下。实战对比两种设计的优劣。最后取最优者。

要不这块合作下,我看你有个nbio的库。
我现在需要一个薄薄的异步io库,可否设计成类似于libev的API形式。

nbio 只支持linux,可以玩玩,虽然比目前其他那些开源的异步网络库性能都好、也更易用,但是因为http涉及的细节太多,我不打算花太多时间做更多支持
如果想跨平台更方便可以试试 gnet 之类的

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

好吧。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

BTW,如果有其他需要,可以试试我的 arpc: https://github.com/lesismal/arpc 🤣🤣

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

ok

from httparser.

lesismal avatar lesismal commented on June 6, 2024

http底层支持相关的,工程量有点大,两三年前我还想把 node 那个 http-parser 翻译成 go 来着,但是后面还要有 http 2 、3,异步的 ssl/tls 支持
然后问题又来了,只是底层库,还是没人用,然后就得考虑再封装一套 http 框架,精力有限,除非有公司出钱让全职搞。。。🤣🤣

from httparser.

lesismal avatar lesismal commented on June 6, 2024

之前看字节分享他们的RPC优化,也是异步网络库,他们提到过一个问题:golang的异步网络库的方式,由于协程跟CPU的亲和性不够,所以没办法保证调度对业务造成的影响,好象是说他们有遇到过cpu毛刺,然后字节的内核团队定制了个内核补丁解决了

我目前还没有在商业项目里使用异步网络库来做业务,但是并发量大的话,调度可能确实会对异步网络库以及业务层协程产生一些不好的影响

不过大部分场景还好了,如果不是海量并发场景下的海量服务器,通常不需要考虑极致压榨软硬件性能来节约更多成本,只要不是极致压榨cpu到80-90+%这种,异步网络库已经能很大程度上提高了同等配置能够承载的并发量了

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

是的。而且商业项目的瓶颈在数据库。API服务那点性能损失,完全可以通过加机器来水平扩展。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

方便加微信吗?很难得遇兴趣爱好如此接近的童鞋。。。
我的微信base64编码如下
NzEwMzkwNTE1

才看到,github 就挺好了,社交软件太浪费时间了,我都习惯了github一把梭 😄 😄

from httparser.

lesismal avatar lesismal commented on June 6, 2024

这样吧。我花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.

lesismal avatar lesismal commented on June 6, 2024

单开一个issue,咱们来把server慢慢打通吧

from httparser.

lesismal avatar lesismal commented on June 6, 2024

我上上楼用闭包实现 parser 到 connection 的映射,确实是有点丑 😂😂

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

那写个你想要的设计的伪代码。我看下。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

的设计的伪代码。

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.

lesismal avatar lesismal commented on June 6, 2024

好的工程还有好多细节要完善
比如上次收到的数据已经处理过的部分,加个状态、下次不需要再从头处理(比如header的部分200字节已经处理完了,后续的部分数据来不需要重新处理header部分,但是body有1k字节,上次只读到900并且处理了900字节,然后剩余数据的部分才逐渐收到,但是这时候每次收到数据,上次的900字节好像是也都要重新处理一遍),还有昨天说的比如允许[][]byte做参数传入、减少buffer不必要的拷贝,但是这样比如body 1k字节,分多段来的,还是要for循环去拼接,Execute的部分更难看些,实际场景未必真能有性能提升所以这种改法有待商榷
另外工程本身,命名、注释,都还有很大的优化空间,IDE上装一些插件,很多需要规范化的提示,想做成好的工程,还有很多细节需要打磨
还有就是功能完备度的,不知道跟node c版本的相比是否功能完善,最好把它的那个test也都加进来、尽量覆盖更全

from httparser.

lesismal avatar lesismal commented on June 6, 2024

这样小改一下,实际上对业务层已经友好很多了,跟nbio或者其他异步网络库接入也都很简单

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

from httparser.

lesismal avatar lesismal commented on June 6, 2024

body是流式处理的。header那块这么做是比较快的,可以把汇编里面的avx和sse指令利用起来。分段那块我现在有一个想法,可以借鉴伙伴分配器的双缓冲思路。等会儿详聊

恩,header数据少,影响不大,主要是长body。其实我上面commit的那版本,本来是 [][]byte 的,但是 header/body/message Complete的时候,都要把之前分散在多个[]byte的做一次拼接,太ugly了,还不如 append一次清爽。

伙伴是管理池的,对这里说的多段数据处理的优化没什么用
如果是想优化内存,得是sync.Pool,比如Parser可以用Pool管理,异步网络库的内存分配可以用Pool管理

from httparser.

lesismal avatar lesismal commented on June 6, 2024

我commit的版本和新的例子里,Setting已经没什么用了,完全可以去掉或者合并到Parser里,由Parser给业务层留扩展的口子
go没必要完全参考c版的,很多c库虽然牛逼但是设计细节并不友好

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

你fork的代码我看下。特性我先了解。
httparser现在比c原版速度快。go的实现是630MB/s, c的实现是505MB/s
所以,特性我会加的比较谨慎。。。哈哈。。。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

你fork的代码我看下。特性我先了解。
httparser现在比c原版速度快。go的实现是630MB/s, c的实现是505MB/s
所以,特性我会加的比较谨慎。。。哈哈。。。

我稍微扫了几眼,c版的state比你现在的多,解析的功能也更详细、功能更全
用阉割版的跟人家比性能,不具备可比性,说性能更强容易混淆视听、没有意义

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

这个看取舍吧。现在这么设计主要是想把汇编指令的优势利用起来。
node js的写法是有弊端的。性能上不去。对cpu缓存都不是太好。更不谈avx了。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

这个不讨论非设计上的事情。我先把test case搬过来。
以前看过fasthttp代码,处理header也是index。一开始没有想明白,后面悟出来了,主要是把机器指令优势发挥出来。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

如何传递数据。我看你在#5里面回复过,那就在#5里面讨论。

from httparser.

lesismal avatar lesismal commented on June 6, 2024

目前版本跟c的对比主要还是在功能支持率上,应该还没到cache影响性能的程度,等我闲了瞄瞄

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

可以可以,人多力量大。

from httparser.

guonaihong avatar guonaihong commented on June 6, 2024

新开#6 讨论下Setting回调函数形参。看有没有更好的设计

from httparser.

Related Issues (8)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.