fasthttp 中如何使用 linux 系统调用 `sendfile`

发布时间 2023-10-26 16:35:37作者: ahfuzhang

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


接上一篇:fasthttp 中如何使用Transfer-Encoding: chunked 方式的流式内容输出

首先是找了一个库,封装了 linux 下的 sendfile 系统调用:github.com/hslam/sendfile
上代码:

  • go.mod 中:
replace (
	github.com/valyala/fasthttp => github.com/ahfuzhang/compress v1.49.2
)
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	syslog "log"
	"net"
	"os"
	"strconv"
	"time"

    "github.com/hslam/sendfile"
	"github.com/valyala/bytebufferpool"
	"github.com/valyala/fasthttp"
)

var fileToSend *os.File
var fileSize int64

func FasthttpHandler(ctx *fasthttp.RequestCtx) {
    ctx.HijackSetNoResponse(true)  // 不使用  fasthttp 框架的输出
	//ctx.Hijack(func(c net.Conn) {  // 这里会  go 出来一个协程执行  hijack 回调函数。 我改了一个版本,不配置就不会 go 出来协程
	//	log.Println("do nothing")
	//})
	//
	buf := bytebufferpool.Get()
	defer bytebufferpool.Put(buf)
	buf.Reset()
	buf.B = append(buf.B, "HTTP/1.1 200 OK\r\n"+
		"Server: fasthttp\r\n"+
		"Date: Thu, 26 Oct 2023 01:09:23 GMT\r\n"+
		"Content-Type: text/plain\r\n"+
		"Cache-Control: no-cache, no-store, must-revalidate\r\n"+
		"Transfer-Encoding: chunked\r\n"+
		"\r\n"...)
	conn := ctx.Conn()
	_, _ = conn.Write(buf.B)  // 写  http 头
    //
    for i := 0; i < 9; i++ {
           _, _ = conn.Write([]byte(fmt.Sprintf("%x\r\n%d \r\n", 1, i)))
	}
    //
    _, _ = conn.Write([]byte(fmt.Sprintf("%x\r\n", fileSize, i)))
    n, err := sendfile.SendFile(ctx.Conn(), int(fileToSend.Fd()), 0, fileSize)  // 使用系统调用
	if err != nil {
		syslog.Fatalln(err)
	}
    _, _ = conn.Write([]byte("\r\n"))
	syslog.Println("send count:", n)
    // 写入结束信息
    _, _ = conn.Write([]byte("0\r\n\r\n")
}

func main() {
     fileToSend, _ = os.Open("xxxx.file")
     fileSize = 1024*2
    //
	syslog.SetFlags(syslog.Lshortfile | syslog.LstdFlags)
	server := &fasthttp.Server{
		Handler: FasthttpHandler,
	}
	syslog.Fatalln(server.ListenAndServe(":8089"))
}

相比直接从内存中 send 数据,实际测试中并未发现明显的性能提升,也可能是我只使用了 2kb 的数据,差异不明显。

希望对大家有用, have fun ?