fix: nodata timeout check

This commit is contained in:
langhuihui
2025-09-26 11:12:44 +08:00
parent c0a13cbbf2
commit 5fb769bfa2
8 changed files with 374 additions and 19 deletions

8
go.mod
View File

@@ -4,6 +4,7 @@ go 1.23.0
require (
github.com/VictoriaMetrics/VictoriaMetrics v1.102.0
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go v1.55.7
github.com/beevik/etree v1.4.1
github.com/bluenviron/gohlslib v1.4.0
@@ -15,7 +16,7 @@ require (
github.com/emiago/sipgo v1.0.0-alpha
github.com/go-delve/delve v1.23.0
github.com/gobwas/ws v1.3.2
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.6.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
@@ -40,6 +41,7 @@ require (
github.com/samber/slog-common v0.17.1
github.com/shirou/gopsutil/v4 v4.24.8
github.com/stretchr/testify v1.10.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.69
github.com/valyala/fasthttp v1.61.0
github.com/vishvananda/netlink v1.1.0
github.com/yapingcat/gomedia v0.0.0-20240601043430-920523f8e5c7
@@ -67,6 +69,7 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 // indirect
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/elgs/gostrgen v0.0.0-20220325073726-0c3e00d082f6 // indirect
@@ -75,6 +78,7 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@@ -92,6 +96,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/pion/datachannel v1.5.10 // indirect
@@ -128,6 +133,7 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect
)

21
go.sum
View File

@@ -13,6 +13,8 @@ github.com/abema/go-mp4 v1.2.0 h1:gi4X8xg/m179N/J15Fn5ugywN9vtI6PLk6iLldHGLAk=
github.com/abema/go-mp4 v1.2.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
github.com/alchemy/rotoslog v0.2.2 h1:yzAOjaQBKgJvAdPi0sF5KSPMq5f2vNJZEnPr73CPDzQ=
github.com/alchemy/rotoslog v0.2.2/go.mod h1:pOHF0DKryPLaQzjcUlidLVRTksvk9yW75YIu1yYiiEQ=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -46,6 +48,8 @@ github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3I
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/cloudwego/goref v0.0.0-20240724113447-685d2a9523c8 h1:K7L7KFg5siEysLit42Bf7n4qNRkGxniPeBtmpsxsfdQ=
@@ -87,8 +91,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -98,8 +102,11 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -167,10 +174,13 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -246,6 +256,7 @@ github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
github.com/samber/lo v1.44.0 h1:5il56KxRE+GHsm1IR+sZ/6J42NODigFiqCWpSc2dybA=
github.com/samber/lo v1.44.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/samber/slog-common v0.17.1 h1:jTqqLBgoJshpoxlPSGiypyOanjH6tY+i9bwyYmIbjhI=
@@ -272,6 +283,11 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.69 h1:9O5/Nt1eXf/Y6HNP4yUC0OdbKbSv5MDZRNGZBA/XXug=
github.com/tencentyun/cos-go-sdk-v5 v0.7.69/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20250515025012-e0eec8a5d123/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -321,6 +337,7 @@ golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

319
pkg/storage/mmap.go Normal file
View File

@@ -0,0 +1,319 @@
package storage
import (
"fmt"
"os"
"golang.org/x/exp/mmap"
)
// MmapFile 使用内存映射的文件实现
type MmapFile struct {
file *os.File
mmapFile *mmap.ReaderAt
data []byte
size int64
}
// NewMmapFile 创建新的内存映射文件
func NewMmapFile(filename string) (*MmapFile, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
mmapFile, err := mmap.Open(filename)
if err != nil {
file.Close()
return nil, fmt.Errorf("failed to mmap file: %w", err)
}
// 获取文件大小
stat, err := file.Stat()
if err != nil {
mmapFile.Close()
file.Close()
return nil, fmt.Errorf("failed to stat file: %w", err)
}
// 获取内存映射的数据
data := make([]byte, stat.Size())
_, err = mmapFile.ReadAt(data, 0)
if err != nil {
mmapFile.Close()
file.Close()
return nil, fmt.Errorf("failed to read mmap data: %w", err)
}
return &MmapFile{
file: file,
mmapFile: mmapFile,
data: data,
size: stat.Size(),
}, nil
}
// Read 实现 io.Reader 接口
func (m *MmapFile) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
// 使用内存映射的数据进行零拷贝读取
n = copy(p, m.data)
if n == 0 {
return 0, fmt.Errorf("no data available")
}
// 更新数据指针,模拟读取进度
m.data = m.data[n:]
return n, nil
}
// ReadAt 实现 io.ReaderAt 接口
func (m *MmapFile) ReadAt(p []byte, off int64) (n int, err error) {
if off >= m.size {
return 0, fmt.Errorf("offset beyond file size")
}
// 使用内存映射的数据进行零拷贝读取
available := int(m.size - off)
if len(p) > available {
p = p[:available]
}
// 直接从内存映射区域复制数据,避免系统调用
start := int(off)
end := start + len(p)
if end > len(m.data) {
end = len(m.data)
}
n = copy(p, m.data[start:end])
return n, nil
}
// Write 实现 io.Writer 接口
func (m *MmapFile) Write(p []byte) (n int, err error) {
// 内存映射文件通常是只读的,这里返回错误
return 0, fmt.Errorf("mmap file is read-only")
}
// WriteAt 实现 io.WriterAt 接口
func (m *MmapFile) WriteAt(p []byte, off int64) (n int, err error) {
// 内存映射文件通常是只读的,这里返回错误
return 0, fmt.Errorf("mmap file is read-only")
}
// Seek 实现 io.Seeker 接口
func (m *MmapFile) Seek(offset int64, whence int) (int64, error) {
// 对于内存映射文件,我们通过调整数据指针来模拟 seek
switch whence {
case 0: // io.SeekStart
if offset < 0 || offset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return offset, nil
case 1: // io.SeekCurrent
current := m.size - int64(len(m.data))
newOffset := current + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return newOffset, nil
case 2: // io.SeekEnd
newOffset := m.size + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[newOffset:]
return newOffset, nil
default:
return 0, fmt.Errorf("invalid whence")
}
}
// Close 关闭文件
func (m *MmapFile) Close() error {
var err error
if m.mmapFile != nil {
err = m.mmapFile.Close()
}
if m.file != nil {
if closeErr := m.file.Close(); closeErr != nil && err == nil {
err = closeErr
}
}
return err
}
// Stat 返回文件信息
func (m *MmapFile) Stat() (os.FileInfo, error) {
return m.file.Stat()
}
// Name 返回文件名
func (m *MmapFile) Name() string {
return m.file.Name()
}
// Size 返回文件大小
func (m *MmapFile) Size() int64 {
return m.size
}
// Data 返回内存映射的数据切片(零拷贝访问)
func (m *MmapFile) Data() []byte {
return m.data
}
// MmapFileWriter 支持写入的内存映射文件
type MmapFileWriter struct {
file *os.File
filename string
data []byte
size int64
}
// NewMmapFileWriter 创建新的可写内存映射文件
func NewMmapFileWriter(filename string) (*MmapFileWriter, error) {
file, err := os.Create(filename)
if err != nil {
return nil, fmt.Errorf("failed to create file: %w", err)
}
return &MmapFileWriter{
file: file,
filename: filename,
data: make([]byte, 0),
size: 0,
}, nil
}
// Write 实现 io.Writer 接口
func (m *MmapFileWriter) Write(p []byte) (n int, err error) {
// 将数据追加到内存缓冲区
m.data = append(m.data, p...)
m.size += int64(len(p))
return len(p), nil
}
// WriteAt 实现 io.WriterAt 接口
func (m *MmapFileWriter) WriteAt(p []byte, off int64) (n int, err error) {
// 确保数据缓冲区足够大
if int64(len(m.data)) < off+int64(len(p)) {
newSize := off + int64(len(p))
newData := make([]byte, newSize)
copy(newData, m.data)
m.data = newData
m.size = newSize
}
// 写入数据到指定位置
copy(m.data[off:], p)
return len(p), nil
}
// Sync 同步数据到磁盘
func (m *MmapFileWriter) Sync() error {
// 将内存中的数据写入文件
_, err := m.file.WriteAt(m.data, 0)
if err != nil {
return fmt.Errorf("failed to write data: %w", err)
}
// 同步到磁盘
return m.file.Sync()
}
// Close 关闭文件
func (m *MmapFileWriter) Close() error {
// 先同步数据
if err := m.Sync(); err != nil {
m.file.Close()
return err
}
return m.file.Close()
}
// Read 实现 io.Reader 接口
func (m *MmapFileWriter) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
n = copy(p, m.data)
if n == 0 {
return 0, fmt.Errorf("no data available")
}
// 更新数据指针
m.data = m.data[n:]
return n, nil
}
// ReadAt 实现 io.ReaderAt 接口
func (m *MmapFileWriter) ReadAt(p []byte, off int64) (n int, err error) {
if off >= m.size {
return 0, fmt.Errorf("offset beyond file size")
}
available := int(m.size - off)
if len(p) > available {
p = p[:available]
}
n = copy(p, m.data[off:])
return n, nil
}
// Seek 实现 io.Seeker 接口
func (m *MmapFileWriter) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0: // io.SeekStart
if offset < 0 || offset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return offset, nil
case 1: // io.SeekCurrent
current := m.size - int64(len(m.data))
newOffset := current + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return newOffset, nil
case 2: // io.SeekEnd
newOffset := m.size + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[newOffset:]
return newOffset, nil
default:
return 0, fmt.Errorf("invalid whence")
}
}
// Stat 返回文件信息
func (m *MmapFileWriter) Stat() (os.FileInfo, error) {
return m.file.Stat()
}
// Name 返回文件名
func (m *MmapFileWriter) Name() string {
return m.filename
}
// Size 返回文件大小
func (m *MmapFileWriter) Size() int64 {
return m.size
}
// Data 返回内存中的数据切片(零拷贝访问)
func (m *MmapFileWriter) Data() []byte {
return m.data
}

View File

@@ -557,6 +557,12 @@ func (p *DebugPlugin) GetHeapGraph(ctx context.Context, empty *emptypb.Empty) (*
if err != nil {
return nil, err
}
// 清理不重要的函数,使图形更干净明了
if err := profile.RemoveUninteresting(); err != nil {
return nil, fmt.Errorf("could not remove uninteresting functions: %v", err)
}
// Generate dot graph.
dot, err := debug.GetDotGraph(profile)
if err != nil {

View File

@@ -9,7 +9,12 @@ import (
)
func GetDotGraph(profile *profile.Profile) (string, error) {
rpt := report.NewDefault(profile, report.Options{})
// 设置节点数量限制,使图形更简洁(类似官方 pprof
options := report.Options{
NodeCount: 80, // 限制最大节点数
NodeFraction: 0.005, // 过滤掉小于 0.5% 的节点
}
rpt := report.NewDefault(profile, options)
g, config := report.GetDOT(rpt)
dot := &bytes.Buffer{}
graph.ComposeDot(dot, g, &graph.DotAttributes{}, config)

View File

@@ -166,9 +166,10 @@ func (i *NodeInfo) PrintableName() string {
// NameComponents returns the components of the printable name to be used for a node.
func (i *NodeInfo) NameComponents() []string {
var name []string
if i.Address != 0 {
name = append(name, fmt.Sprintf("%016x", i.Address))
}
// 注释掉内存地址显示,使图形更简洁
// if i.Address != 0 {
// name = append(name, fmt.Sprintf("%016x", i.Address))
// }
if fun := i.Name; fun != "" {
name = append(name, fun)
}

View File

@@ -279,6 +279,9 @@ func (plugin *HLSPlugin) processTsFiles(w http.ResponseWriter, r *http.Request,
w.Header().Set("Content-Length", strconv.FormatUint(totalSize, 10))
w.WriteHeader(http.StatusOK)
// 创建可重用的缓冲区,避免重复分配
buffer := make([]byte, 64*1024) // 64KB 缓冲区
// 第二次遍历:写入数据
for i, info := range fileInfoList {
if r.Context().Err() != nil {
@@ -296,11 +299,11 @@ func (plugin *HLSPlugin) processTsFiles(w http.ResponseWriter, r *http.Request,
reader := bufio.NewReader(file)
if i == 0 {
// 第一个文件,直接拷贝
_, err = io.Copy(writer, reader)
// 第一个文件,使用 io.CopyBuffer 重用缓冲区
_, err = io.CopyBuffer(writer, reader, buffer)
} else {
// 后续文件跳过PAT/PMT包只拷贝媒体数据
err = plugin.copyTsFileSkipHeaders(writer, reader)
err = plugin.copyTsFileSkipHeadersWithBuffer(writer, reader, buffer[:mpegts.TS_PACKET_SIZE])
}
file.Close()
@@ -314,12 +317,10 @@ func (plugin *HLSPlugin) processTsFiles(w http.ResponseWriter, r *http.Request,
plugin.Info("TS download completed")
}
// copyTsFileSkipHeaders 拷贝TS文件跳过PAT/PMT包
func (plugin *HLSPlugin) copyTsFileSkipHeaders(writer io.Writer, reader *bufio.Reader) error {
buffer := make([]byte, mpegts.TS_PACKET_SIZE)
// copyTsFileSkipHeadersWithBuffer 拷贝TS文件跳过PAT/PMT包,使用提供的缓冲区
func (plugin *HLSPlugin) copyTsFileSkipHeadersWithBuffer(writer io.Writer, reader *bufio.Reader, tsBuffer []byte) error {
for {
n, err := io.ReadFull(reader, buffer)
n, err := io.ReadFull(reader, tsBuffer)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
break
@@ -332,12 +333,12 @@ func (plugin *HLSPlugin) copyTsFileSkipHeaders(writer io.Writer, reader *bufio.R
}
// 检查同步字节
if buffer[0] != 0x47 {
if tsBuffer[0] != 0x47 {
continue
}
// 提取PID
pid := uint16(buffer[1]&0x1f)<<8 | uint16(buffer[2])
pid := uint16(tsBuffer[1]&0x1f)<<8 | uint16(tsBuffer[2])
// 跳过PAT(PID=0)和PMT(PID=256)包
if pid == mpegts.PID_PAT || pid == mpegts.PID_PMT {
@@ -345,7 +346,7 @@ func (plugin *HLSPlugin) copyTsFileSkipHeaders(writer io.Writer, reader *bufio.R
}
// 写入媒体数据包
_, err = writer.Write(buffer)
_, err = writer.Write(tsBuffer)
if err != nil {
return err
}

View File

@@ -69,7 +69,7 @@ func (t *AVTracks) GetOrCreate(dataType reflect.Type) *AVTrack {
}
func (t *AVTracks) CheckTimeout(timeout time.Duration) bool {
if t.AVTrack == nil || t.AVTrack.LastValue.WriteTime.IsZero() {
if t.AVTrack == nil {
return false
}
return time.Since(t.AVTrack.LastValue.WriteTime) > timeout