fasthttp

测试

使用一个简单的例子测试fasthttp和原生net/http库,代码如下:

fasthttp

    1

    2

    3

    4

    5

    6

    7

    8

    9

   10

   11

func FastHttpBench() {

        router := fasthttprouter.New()

        router.GET("/", FastHandle)

        fasthttp.ListenAndServe(":8080", router.Handler)

}

func FastHandle(ctx *fasthttp.RequestCtx) {

        a := rand.Intn(100)

        a = a * a

        fmt.Fprintln(ctx, strconv.Itoa(a))

}

http

    1

    2

    3

    4

    5

    6

    7

    8

    9

   10

func HttpBench() {

        http.HandleFunc("/", HttpHanle)

        http.ListenAndServe(":8080", nil)

}

func HttpHanle(w http.ResponseWriter, r *http.Request) {

        a := rand.Intn(100)

        a = a * a

        fmt.Fprintln(w, strconv.Itoa(a))

}

使用wrk测试(4线程,1000连接,持续60秒)

    1

wrk -t4 -c1000 -d60s --latency http://localhost:8080 

fasthttp:

http:

可以看到请求数量以及响应时间上fasthttp的表现都更加的出色。

WorkPool实现

在并发量比较大的情况下,net/http对每一个请求都开启一个新的goroutine,并且在完成请求之后回收,这样会导致GC的压力很大。fasthttp使用workpool来完成对goroutine的复用。

看一下fasthttp中WorkPool实现的大致思路:

1、WorkPool结构体:

    1

    2

    3

    4

    5

    6

    7

type workerPool struct {

        WorkerFunc ServeHandler

        …

        ready []*workerChan

        workerChanPool sync.Pool

        …

}

2、在server的serve函数中调用到了WorkPool的serve函数中,其实现如下:

    1

    2

    3

    4

    5

    6

    7

    8

func (wp *workerPool) Serve(c net.Conn) bool {

        ch := wp.getCh()

        if ch == nil {

                return false

        }

        ch.ch <- c

        return true

}

3、getCh()的实现,首先是看ready中有没有空闲的grroutine,有的话直接取出来处理请求。如果空闲队列是空的,则新建一个goroutine来处理。

    1

    2

    3

    4

    5

    6

    7

    8

    9

   10

   11

   12

   13

   14

   15

   16

   17

   18

   19

   20

   21

   22

   23

   24

   25

   26

   27

   28

   29

   30

   31

   32

   33

   34

func (wp *workerPool) getCh() *workerChan {

        var ch *workerChan

        createWorker := false

        wp.lock.Lock()

        ready := wp.ready

        n := len(ready) - 1

        if n < 0 {

                if wp.workersCount < wp.MaxWorkersCount {

                        createWorker = true

                        wp.workersCount++

                }

        } else {

                ch = ready[n]

                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

}

4、处理请求时使用server传过来的workFunc处理请求

    1

    2

    3

    4

    5

    6

    7

    8

    9

   10

   11

func (wp *workerPool) workerFunc(ch *workerChan) {

        …

for{

        wp.WorkerFunc(c)

        …

        if !wp.release(ch) {

                break

        }

        …

}

}

每次处理完一个channel之后,把这个goroutine对应的workChan append到wp.ready中,等到下一次serve的调用从ready的切片中取出来继续在这个goroutine的for循环中继续处理,开始下一次循环。

sync.Pool

fasthttp中使用的sync.Pool也对其性能的提升起到了很大的作用,比如workPool中的workerChanPool也是用的sync.Pool实现的。可以在源码中看到,每次ready数组空的时候,通过Get去申请内存,调用完了workerFunc的时候Put来释放内存到pool中。

[]bytestring之间的转换

fasthttp中有实现byte数组和string之间的转换函数如下:

    1

    2

    3

    4

    5

    6

    7

    8

    9

   10

   11

   12

   13

   14

func b2s(b []byte) string {

        return *(*string)(unsafe.Pointer(&b))

}

func s2b(s string) (b []byte) {

        /* #nosec G103 */

        bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))

        /* #nosec G103 */

        sh := (*reflect.StringHeader)(unsafe.Pointer(&s))

        bh.Data = sh.Data

        bh.Cap = sh.Len

        bh.Len = sh.Len

        return b

}

可以看到都只是进行指针的转换和赋值操作,并没有进行重新的内存分配和赋值,所以会比直接使用类型转换操作来的快,但是源码中也有写道依赖于StringHeader和SliceHeader的实现,需要注意go后续的版本会不会变动。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值