diff --git a/DEVELOPER.md b/DEVELOPER.md index 2209f76a..c39a407f 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -1159,6 +1159,22 @@ Deprecated and unused variables: Please restart service when `.env` changed. +## Go PPROF + +To analyze the performance of Oryx, you can enable the Go pprof tool: + +```bash +GO_PPROF=on go run . +``` + +Run CPU profile: + +```bash +go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 +``` + +Then use `top` to show the hot functions. + ## Coding Guide For the json field with more than 2 words: @@ -1197,6 +1213,7 @@ The following are the update records for the Oryx server. * Support setup global OpenAI settings. [v5.15.11](https://github.com/ossrs/oryx/releases/tag/v5.15.11) * Add youtube-dl binary. v5.15.12 * VLive: Fix bug when source codec is not supported. v5.15.13 + * Forward: Fix high CPU bug. v5.15.14 * v5.14: * Merge features and bugfix from releases. v5.14.1 * Dubbing: Support VoD dubbing for multiple languages. [v5.14.2](https://github.com/ossrs/oryx/releases/tag/v5.14.2) @@ -1225,6 +1242,7 @@ The following are the update records for the Oryx server. * Update model to gpt-3.5-turbo, gpt-4-turbo, gpt-4o. v5.14.20 * Transcript: Upgrade the hls.js to 1.4 for WebVTT. v5.14.21 * Disable version query and check. [v5.14.22](https://github.com/ossrs/oryx/releases/tag/v5.14.22) + * Forward: Fix high CPU bug. [v5.14.23](https://github.com/ossrs/oryx/releases/tag/v5.14.23) * v5.13: * Fix bug for vlive and transcript. v5.13.1 * Support AWS Lightsail install script. v5.13.2 diff --git a/platform/camera-live-stream.go b/platform/camera-live-stream.go index 232b6819..27ad2570 100644 --- a/platform/camera-live-stream.go +++ b/platform/camera-live-stream.go @@ -923,12 +923,17 @@ func (v *CameraTask) doCameraStreaming(ctx context.Context, input *FFprobeSource // Pull the latest log frame. heartbeat.Polling(ctx, stderr) go func() { + select { + case <-ctx.Done(): + return + case <-heartbeat.firstReadyCtx.Done(): + v.firstReadyTime = &heartbeat.firstReadyTime + } + for { select { case <-ctx.Done(): return - case <-heartbeat.firstReadyCtx.Done(): - v.firstReadyTime = &heartbeat.firstReadyTime case frame := <-heartbeat.FrameLogs: v.updateFrame(frame) } diff --git a/platform/forward.go b/platform/forward.go index a915c1b6..9ed6abfd 100644 --- a/platform/forward.go +++ b/platform/forward.go @@ -672,12 +672,17 @@ func (v *ForwardTask) doForward(ctx context.Context, input *SrsStream) error { // Pull the latest log frame. heartbeat.Polling(ctx, stderr) go func() { + select { + case <-ctx.Done(): + return + case <-heartbeat.firstReadyCtx.Done(): + v.firstReadyTime = &heartbeat.firstReadyTime + } + for { select { case <-ctx.Done(): return - case <-heartbeat.firstReadyCtx.Done(): - v.firstReadyTime = &heartbeat.firstReadyTime case frame := <-heartbeat.FrameLogs: v.updateFrame(frame) } diff --git a/platform/main.go b/platform/main.go index 365b27c3..eb40d067 100644 --- a/platform/main.go +++ b/platform/main.go @@ -7,6 +7,7 @@ import ( "context" "flag" "fmt" + "net/http" "os" "os/signal" "path" @@ -15,6 +16,7 @@ import ( "strings" "syscall" "time" + _ "net/http/pprof" "github.com/ossrs/go-oryx-lib/errors" "github.com/ossrs/go-oryx-lib/logger" @@ -102,6 +104,8 @@ func doMain(ctx context.Context) error { setEnvDefault("PLATFORM_LISTEN", "2024") // Set the default language, en or zh. setEnvDefault("REACT_APP_LOCALE", "en") + // Whether enable the Go pprof. + setEnvDefault("GO_PPROF", "off") // Migrate from mgmt. setEnvDefault("REDIS_DATABASE", "0") @@ -128,14 +132,14 @@ func doMain(ctx context.Context) error { setEnvDefault("SRS_VLIVE_LIMIT", "10") setEnvDefault("SRS_CAMERA_LIMIT", "10") - logger.Tf(ctx, "load .env as MGMT_PASSWORD=%vB, "+ + logger.Tf(ctx, "load .env as MGMT_PASSWORD=%vB, GO_PPROF=%v, "+ "SRS_PLATFORM_SECRET=%vB, CLOUD=%v, REGION=%v, SOURCE=%v, SRT_PORT=%v, RTC_PORT=%v, "+ "NODE_ENV=%v, LOCAL_RELEASE=%v, REDIS_DATABASE=%v, REDIS_HOST=%v, REDIS_PASSWORD=%vB, REDIS_PORT=%v, RTMP_PORT=%v, "+ "PUBLIC_URL=%v, BUILD_PATH=%v, REACT_APP_LOCALE=%v, PLATFORM_LISTEN=%v, HTTP_PORT=%v, "+ "REGISTRY=%v, MGMT_LISTEN=%v, HTTPS_LISTEN=%v, AUTO_SELF_SIGNED_CERTIFICATE=%v, "+ "NAME_LOOKUP=%v, PLATFORM_DOCKER=%v, SRS_FORWARD_LIMIT=%v, SRS_VLIVE_LIMIT=%v, "+ "SRS_CAMERA_LIMIT=%v", - len(envMgmtPassword()), len(envApiSecret()), envCloud(), + len(envMgmtPassword()), os.Getenv("GO_PPROF"), len(envApiSecret()), envCloud(), envRegion(), envSource(), envSrtListen(), envRtcListen(), envNodeEnv(), envLocalRelease(), envRedisDatabase(), envRedisHost(), len(envRedisPassword()), envRedisPort(), @@ -147,6 +151,15 @@ func doMain(ctx context.Context) error { envCameraLimit(), ) + // Start the Go pprof if enabled. + if os.Getenv("GO_PPROF") == "on" { + go func() { + addr := "localhost:6060" + logger.Tf(ctx, "Start Go pprof at %v", addr) + http.ListenAndServe(addr, nil) + } () + } + // Setup the base OS for redis, which should never depends on redis. if err := initMgmtOS(ctx); err != nil { return errors.Wrapf(err, "init mgmt os") diff --git a/platform/virtual-live-stream.go b/platform/virtual-live-stream.go index d6b39f61..2dc55c8d 100644 --- a/platform/virtual-live-stream.go +++ b/platform/virtual-live-stream.go @@ -1144,12 +1144,17 @@ func (v *VLiveTask) doVirtualLiveStream(ctx context.Context, input *FFprobeSourc // Pull the latest log frame. heartbeat.Polling(ctx, stderr) go func() { + select { + case <-ctx.Done(): + return + case <-heartbeat.firstReadyCtx.Done(): + v.firstReadyTime = &heartbeat.firstReadyTime + } + for { select { case <-ctx.Done(): return - case <-heartbeat.firstReadyCtx.Done(): - v.firstReadyTime = &heartbeat.firstReadyTime case frame := <-heartbeat.FrameLogs: v.updateFrame(frame) }