perf(runtime): pool the local xray gRPC connection (#6916)

A client per runtime operation made a bulk delete pay a TCP and HTTP/2
handshake for every client. The local runtime now keeps one gRPC client
per API port and replaces it when a core restart moves the port; the
traffic poll keeps its own client, since StatsLastValues is unlocked.

A pooled channel keeps grpc-go's reconnect backoff after the core
restarts, so a call that fails with Unavailable drops the client and
dials again, as a client per call did.
This commit is contained in:
25f2002654
2026-10-24 23:00:09 +05:30
committed by GitHub
parent de21c128fc
commit f105132866
3 changed files with 186 additions and 4 deletions
+17 -4
View File
@@ -8,6 +8,9 @@ import (
"strings"
"sync"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
"github.com/mhsanaei/3x-ui/v3/internal/amneziawgnet"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
@@ -41,12 +44,22 @@ func (l *Local) withAPI(fn func(api *xray.XrayAPI) error) error {
if port <= 0 {
return errors.New("local xray is not running")
}
var api xray.XrayAPI
if err := api.Init(port); err != nil {
api, err := localAPIForPort(port)
if err != nil {
return err
}
defer api.Close()
return fn(&api)
err = fn(api)
if status.Code(err) != codes.Unavailable {
return err
}
// A pooled channel keeps grpc-go's reconnect backoff after a core restart and fails fast for
// seconds; drop it and dial again, as a client per call did.
evictLocalAPIClient(api)
api, err = localAPIForPort(port)
if err != nil {
return err
}
return fn(api)
}
func (l *Local) AddInbound(_ context.Context, ib *model.Inbound) error {