[golang]fasthttp源码

本文详细解析了Go语言中服务器的核心逻辑,包括监听端口、接收连接和处理连接的过程,特别是使用协程池进行高效并发处理。通过workerPool管理协程,避免了协程的过度创建和资源浪费。此外,文章还探讨了如何利用sync.pool减少对象分配以优化性能,如bufio.Reader和bufio.Writer的复用。整个流程展示了Go在高并发场景下的优秀设计和性能优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.核心逻辑

 

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呢?

image

因此 \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.

 

 

 

 

 

 

 

https://www.jianshu.com/p/a0e766f8dcb0

### 校园论坛的源码下载与开源项目 #### GoYouBBS 开源论坛项目 GoYouBBS 是一个基于 GolangFastHTTP 和 LevelDB 构建的高效开源 Web 论坛解决方案。该项目提供了快速启动指南以及详细的环境准备说明,适合用于构建高性能和低资源消耗的应用场景[^1]。其源代码托管在 GitHub 平台上,并采用 MIT 许可证发布,允许开发者自由修改和分发。 对于希望创建校园论坛的开发者而言,可以考虑使用此框架作为基础架构。通过调整界面设计和功能模块,使其更贴合校园社区的需求。例如,可以在原有基础上扩展公告板、活动通知等功能来增强用户体验。 #### 功能强大的校园综合服务小程序 另一款值得关注的是 2023 版本的功能强大校园综合服务小程序开源源码。这款程序不仅支持传统的讨论区功能,还集成了多种实用工具和服务入口,如快递代取、打印预约等[^2]。虽然它的主要定位并非传统意义上的在线交流平台,但其中包含了丰富的社交互动机制,比如评论回复系统、点赞分享按钮等特性,这些都可以被借鉴到定制化版本的校园论坛建设当中去。 需要注意的是,在实际部署之前需准备好必要的硬件设施(建议选用至少具备两核四千兆规格以上的云主机实例),并完成相应的域名注册手续;如果预算有限,则可以选择性价比更高的入门级方案——即单核心加两千五百六内存起步的小型VPS节点即可满足基本运行需求。 #### React 前端技术栈的选择 如果您倾向于现代化前端框架的话,“React 源码分析”系列文章或许能为您提供一些灵感[^3]。作者讲述了如何利用 React 结合 Koa 实现完整的前后端分离式应用程序开发流程。这种方法特别适用于那些追求灵活交互效果的新一代网络应用场合下。当然,这需要一定的 JavaScript 编程背景知识才能顺利上手操作。 综上所述,无论是选择成熟的第三方库还是自行搭建专属体系结构,都需要充分评估目标群体的具体偏好特征后再做决定。同时也要考虑到后期维护成本和技术团队实力等因素的影响程度。 ```bash git clone https://github.com/your-repo-url.git cd your-project-directory npm install || yarn install ``` 上述命令可以帮助您克隆指定仓库地址下的最新版代码副本至本地机器,并安装依赖项以便进一步测试验证。 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值