语言Web Server实现
Go的第三方Web库
在Go语言当中有很多知名的Web服务,例如gin和beego,在它们的描述中都提到了high-performance,我们的文章会从最原始的socket来实现一个简单的web服务器。实现的服务与gin、beego做一次性能对比,对比工具使用ab(我们只做了一个不严肃的性能测试,我们只能去做个人能做的事情)。
Go语言TCP服务的原理
一般语言实现
c 语言去实现一个简单的http server并不难,但如果要保持高性能,例如nginx自己做了所有的一切,并不是很容易。你需要去管理连接,并采用合理的io模型和策略。下面是一个最基本的流程。
服务器端的工作主要是listen()和accept()连接,然后进入到交互过程,最终close()连接。
go语言实现
服务器端和c语言差不多,客户端省掉了一个bind()过程,合并成Dial(),翻译成中文为拨号。
实现代码
gin 和 beego 是怎样实现的
我们先看看gin 和 beego都做了什么 它们都调用了net库下的net/http/server下的Serve()方法实现HTTP服务器,因此如果我们的代码与该方法一致或者做了精简,那么理论上性能是一致的。并没有做更多的额外的优化和处理。
// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
baseCtx := context.Background()
if srv.BaseContext != nil {
baseCtx = srv.BaseContext(origListener)
if baseCtx == nil {
panic("BaseContext returned a nil context")
}
}
var tempDelay time.Duration // how long to sleep on accept failure
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
if err != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}实现一个 http 服务器
client
server
server端与net/http/server下的Serve()方法几乎相同,只是做了更大的精简,核心是for + Accept() + goroutine。
go语言的goroutine本身就有极高的性能,因此替代了c语言自己去实现connections pool的麻烦,简单调用就可以实现很高的性能。
性能测试
使用ab做一个简单的性能测试,不是很严谨,但足够说明问题。我们看一个最终结果的对比图,没有本质上的差别大概都在每秒3万次左右。(并发20,总请求是10000次)
Requests per second
30682.19
33626.22
32875.27
测试在实现的 web server
测试 gin web server
测试 beego web server
配套代码和视频
视频地址
最后更新于