一.核心逻辑
func (s *Server) ListenAndServe(addr string) error {
//1.监听端口
ln, err := net.Listen("tcp4", addr)
if tcpln, ok := ln.(*net.TCPListener); ok {
return s.Serve(tcpKeepaliveListener{
TCPListener: tcpln,
keepalive: s.TCPKeepalive,
keepalivePeriod: s.TCPKeepalivePeriod,
})
}
return s.Serve(ln)
}
如注释所说,处理进来的连接.
// Serve serves incoming connections from the given listener.
//
// Serve blocks until the given listener returns permanent error.
func (s *Server) Serve(ln net.Listener) error {
//这里是协程池
wp := &workerPool{
WorkerFunc: s.serveConn,
MaxWorkersCount: maxWorkersCount,
LogAllErrors: s.LogAllErrors,
Logger: s.logger(),
connState: s.setState,
}
//开启协程,处理协程池的清理工作
wp.Start()
// Count our waiting to accept a connection as an open connection.
// This way we can't get into any weird state where just after accepting
// a connection Shutdown is called which reads open as 0 because it isn't
// incremented yet.
atomic.AddInt32(&s.open, 1)
defer atomic.AddInt32(&s.open, -1)
for {
//2.阻塞接受连接
if c, err = acceptConn(s, ln, &lastPerIPErrorTime); err != nil {
wp.Stop()
if err == io.EOF {
return nil
}
return err
}
//3.处理连接
if !wp.Serve(c) {
atomic.AddInt32(&s.open, -1)
c.Close()
}
c = nil
}
}
看代码貌似逻辑不多
1.监听
2.accept连接
3.把连接交给协程池去处理
那么看下处理过程
二.处理过程
//协程池的处理
1.先拿出一个协程
func (wp *workerPool) Serve(c net.Conn) bool {
ch := wp.getCh()
if ch == nil {
return false
}
2.将任务交给这个协程
ch.ch <- c
3.返回.就可以继续accept,继续处理了
return true
}
1.从协程池取出一个协程
2.任务交给协程
3.返回,继续accept,继续处理
那么怎么拿出协程的呢?
//取协程逻辑
func (wp *workerPool) getCh() *workerChan {
var ch *workerChan
createWorker := false
wp.lock.Lock()
//1.从ready队列中拿
ready := wp.ready
n := len(ready) - 1
//1.1如果ready队列为空,那么就需要创建一个
if n < 0 {
//这里用户是可以配置最大的work数量的
if wp.workersCount < wp.MaxWorkersCount {
createWorker = true
wp.workersCount++
}
//1.2 如果ready队列不为空,就拿出队尾的.可以看到这里拿协程需要加锁
} else {
ch = ready[n]
//这里给刚刚这个队尾置为nil,并把之前的再赋值回去.
ready[n] = nil
wp.ready = ready[:n]
}
wp.lock.Unlock()
//这里就可以创建新的协程或者直接返回队尾的协程了
if ch == nil {
if !createWorker {
return nil
}
//很细,这里还用了临时对象池
vch := wp.workerChanPool.Get()
ch = vch.(*workerChan)
go func() {
wp.workerFunc(ch)
wp.workerChanPool.Put(vch)
}()
}
return ch
}
workPool中的ready是一个FILO的栈,每次从队尾取出workChan(也就是对应的协程),工作结束后,放回栈尾.
同时,为了避免在流量高峰创建了大量协程,之后不再使用,造成协程浪费,会有一个协程,去清理这个workChan. 而clean()方法逻辑也很简单,wp.clean从头遍历ready队列,把空闲时间超过maxIdleWorkerDuration(默认10分钟)的都清理掉,就是去往这个workChan中发送一个nil.
workerFunc是协程的工作逻辑,从通道中读取任务.当通道收到了nil,就会退出执行
func (wp *workerPool) workerFunc(ch *workerChan) {
var c net.Conn
var err error
for c = range ch.ch {
//如果传进来nil,就退出
if c == nil {
break
}
//这里是核心处理逻辑,WorkFunc是serveConn方法
if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
}
//将协程归还,放到栈尾部
if !wp.release(ch) {
break
}
}
}
核心处理是serveConn
func (s *Server) serveConn(c net.Conn) error {
ctx := s.acquireCtx(c)
var connRequestNum int
for {
connRequestNum++
var br *bufio.Reader = acquireReader(ctx)
err = ctx.Request.readLimitBody(br, maxRequestSize)
err = s.Handler(ctx)
var wr *bufio.Writer = acquireWriter(ctx)
err = wr.Flush()
if err != nil {
return
}
if s.MaxRequestNumPerConn {
break;
}
}
}
首先,这里是一个for循环,同一个连接发送的请求,会一直在这个循环中读取.debug的时候,可以看到,当一个请求发送完,收到响应,马上会阻塞在
注意,在刚开始的时候,不知道这个br在啥时候读取了数据.我感觉应该是包装了conn,然后conn去读取了数据.额.就是没有找到.尴尬.
这里了,还是从reader池子中拿到reader对象.注意.如果没有的话,那么新建一个(传入conn)
如果能拿到,还是要传入conn,因为原来的对象肯定用不了了
到处都是用的临时对象池
func acquireReader(ctx *RequestCtx) *bufio.Reader {
v := ctx.s.readerPool.Get()
if v == nil {
n := ctx.s.ReadBufferSize
if n <= 0 {
n = defaultReadBufferSize
}
return bufio.NewReaderSize(ctx.c, n)
}
r := v.(*bufio.Reader)
r.Reset(ctx.c)
return r
}
思考一个问题.一个请求,我怎么划分header,怎么读取body呢?
因此 \r\n\r\n是header和body的分隔符,那么body呢?可以通过content-length.可以看下fasthttp中怎么读取的body
那如果没有content-length咋整呢?
没传按0处理.
处理完request,那么自然要传给业务逻辑的函数啦'
在之后响应给客户端
之后就ending了
四.一些优化
用了各种资源池
sync.pool可以减少对象的分配,减少gc.在fasthttp中有很多资源池的使用.例如request和response
request对象使用对象池
// AcquireRequest returns an empty Request instance from request pool.
//
// The returned Request instance may be passed to ReleaseRequest when it is
// no longer needed. This allows Request recycling, reduces GC pressure
// and usually improves performance.
func AcquireRequest() *Request {
v := requestPool.Get()
if v == nil {
return &Request{}
}
return v.(*Request)
}
归还request对象,并且reset对象内容
// ReleaseRequest returns req acquired via AcquireRequest to request pool.
//
// It is forbidden accessing req and/or its' members after returning
// it to request pool.
func ReleaseRequest(req *Request) {
req.Reset()
requestPool.Put(req)
}
// Reset clears request contents.
func (req *Request) Reset() {
req.Header.Reset()
req.resetSkipHeader()
req.timeout = 0
}
包括header,reader啊,各种.这些都减少了gc.