|
|
// Copyright 2021, Chef. All rights reserved.
|
|
|
// https://github.com/q191201771/lal
|
|
|
//
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
// that can be found in the License file.
|
|
|
//
|
|
|
// Author: Chef (191201771@qq.com)
|
|
|
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
"flag"
|
|
|
"fmt"
|
|
|
"os"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
"github.com/q191201771/lal/pkg/base"
|
|
|
"github.com/q191201771/lal/pkg/rtmp"
|
|
|
"github.com/q191201771/naza/pkg/nazalog"
|
|
|
)
|
|
|
|
|
|
func main() {
|
|
|
_ = nazalog.Init(func(option *nazalog.Option) {
|
|
|
option.AssertBehavior = nazalog.AssertFatal
|
|
|
option.Level = nazalog.LevelLogNothing
|
|
|
})
|
|
|
defer nazalog.Sync()
|
|
|
|
|
|
urlTmpl, num := parseFlag()
|
|
|
urls := collect(urlTmpl, num)
|
|
|
|
|
|
var mu sync.Mutex
|
|
|
var succCosts []int64
|
|
|
var failCosts []int64
|
|
|
var wg sync.WaitGroup
|
|
|
wg.Add(len(urls))
|
|
|
|
|
|
go func() {
|
|
|
for {
|
|
|
mu.Lock()
|
|
|
succ := len(succCosts)
|
|
|
fail := len(failCosts)
|
|
|
mu.Unlock()
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "task(num): total=%d, succ=%d, fail=%d\n", len(urls), succ, fail)
|
|
|
time.Sleep(1 * time.Second)
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
totalB := time.Now()
|
|
|
for _, url := range urls {
|
|
|
go func(u string) {
|
|
|
pullSession := rtmp.NewPullSession(func(option *rtmp.PullSessionOption) {
|
|
|
option.PullTimeoutMs = 30000
|
|
|
option.ReadAvTimeoutMs = 30000
|
|
|
option.HandshakeComplexFlag = false
|
|
|
})
|
|
|
b := time.Now()
|
|
|
err := pullSession.Pull(u, func(msg base.RtmpMsg) {
|
|
|
})
|
|
|
e := time.Now()
|
|
|
cost := e.Sub(b).Milliseconds()
|
|
|
// 耗时不够1毫秒,我们将值取整到1毫秒,并打印更精确的实际耗时
|
|
|
if cost == 0 {
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "round to 1 ms but actual is %s\n", e.Sub(b).String())
|
|
|
cost = 1
|
|
|
}
|
|
|
|
|
|
mu.Lock()
|
|
|
if err == nil {
|
|
|
succCosts = append(succCosts, cost)
|
|
|
} else {
|
|
|
failCosts = append(failCosts, cost)
|
|
|
}
|
|
|
mu.Unlock()
|
|
|
wg.Done()
|
|
|
}(url)
|
|
|
}
|
|
|
wg.Wait()
|
|
|
totalE := time.Now()
|
|
|
totalCost := totalE.Sub(totalB).Milliseconds()
|
|
|
if totalCost == 0 {
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "round to 1 ms but actual is %s\n", totalE.Sub(totalB).String())
|
|
|
totalCost = 1
|
|
|
}
|
|
|
min, max, avg := analyse(succCosts)
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "task(num): total=%d, succ=%d, fail=%d\n", len(urls), len(succCosts), len(failCosts))
|
|
|
_, _ = fmt.Fprintf(os.Stderr, " cost(ms): total=%d, avg=%d, min=%d, max=%d\n", totalCost, avg, min, max)
|
|
|
}
|
|
|
|
|
|
func analyse(costs []int64) (min, max, avg int64) {
|
|
|
min = 2147483647
|
|
|
max = 0
|
|
|
sum := int64(0)
|
|
|
for _, cost := range costs {
|
|
|
if cost < min {
|
|
|
min = cost
|
|
|
}
|
|
|
if cost > max {
|
|
|
max = cost
|
|
|
}
|
|
|
sum += cost
|
|
|
}
|
|
|
if len(costs) > 0 {
|
|
|
avg = sum / int64(len(costs))
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func collect(urlTmpl string, num int) (urls []string) {
|
|
|
for i := 0; i < num; i++ {
|
|
|
url := strings.Replace(urlTmpl, "{i}", strconv.Itoa(i), -1)
|
|
|
urls = append(urls, url)
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func parseFlag() (urlTmpl string, num int) {
|
|
|
i := flag.String("i", "", "specify pull rtmp pull")
|
|
|
n := flag.Int("n", 0, "specify num of pull connection")
|
|
|
flag.Parse()
|
|
|
if *i == "" || *n == 0 {
|
|
|
flag.Usage()
|
|
|
_, _ = fmt.Fprintf(os.Stderr, `Example:
|
|
|
%s -i rtmp://127.0.0.1:1935/live/test -n 1000
|
|
|
%s -i rtmp://127.0.0.1:1935/live/test_{i} -n 1000
|
|
|
`, os.Args[0], os.Args[0])
|
|
|
base.OsExitAndWaitPressIfWindows(1)
|
|
|
}
|
|
|
return *i, *n
|
|
|
}
|