fix: start timeout

This commit is contained in:
VaalaCat
2025-08-10 15:45:41 +00:00
parent 6e9d2d6c25
commit c0eb0c07f2
6 changed files with 70 additions and 15 deletions

View File

@@ -41,6 +41,7 @@ func BuildCommand(fs embed.FS) *cobra.Command {
logger.UpdateLoggerOpt(
cfg.Logger.FRPLoggerLevel,
cfg.Logger.DefaultLoggerLevel,
cfg.IsDebug,
)
return NewRootCmd(
@@ -152,6 +153,7 @@ func NewMasterCmd(cfg conf.Config, fs embed.FS) *cobra.Command {
warnDepParam(cmd)
opts := []fx.Option{
fx.StartTimeout(defs.AppStartTimeout),
commonMod,
masterMod,
serverMod,
@@ -198,6 +200,7 @@ func NewClientCmd(cfg conf.Config) *cobra.Command {
warnDepParam(cmd)
opts := []fx.Option{
fx.StartTimeout(defs.AppStartTimeout),
clientMod,
commonMod,
fx.Supply(
@@ -243,6 +246,7 @@ func NewServerCmd(cfg conf.Config) *cobra.Command {
warnDepParam(cmd)
opts := []fx.Option{
fx.StartTimeout(defs.AppStartTimeout),
serverMod,
commonMod,
fx.Supply(

View File

@@ -71,6 +71,7 @@ const (
PullConfigDuration = 30 * time.Second
PushProxyInfoDuration = 30 * time.Second
PullClientWorkersDuration = 30 * time.Second
AppStartTimeout = 5 * time.Minute
)
const (

View File

@@ -1,8 +1,11 @@
package clientrpc
import (
"context"
"github.com/VaalaCat/frp-panel/pb"
"github.com/VaalaCat/frp-panel/services/app"
"github.com/VaalaCat/frp-panel/utils/logger"
)
type ClientRPCHandler interface {
@@ -42,6 +45,12 @@ func NewClientRPCHandler(
}
func (s *clientRPCHandler) Run() {
defer func() {
if err := recover(); err != nil {
logger.Logger(context.Background()).Fatalf("client rpc handler panic: %v", err)
}
}()
startClientRpcHandler(s.appInstance, s.rpcClient, s.done, s.clientID, s.clientSecret, s.event, s.handerFunc)
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/VaalaCat/frp-panel/defs"
"github.com/VaalaCat/frp-panel/pb"
"github.com/VaalaCat/frp-panel/services/app"
"github.com/VaalaCat/frp-panel/utils"
"github.com/VaalaCat/frp-panel/utils/logger"
"github.com/VaalaCat/frp-panel/utils/wsgrpc"
"github.com/imroc/req/v3"
@@ -34,6 +35,7 @@ func (m *masterClient) Call() pb.MasterClient {
}
func NewMasterCli(appInstance app.Application) *masterClient {
logger.Logger(context.Background()).Debugf("creating new master client")
return &masterClient{
inited: false,
appInstance: appInstance,
@@ -60,16 +62,23 @@ func newMasterCli(appInstance app.Application) pb.MasterClient {
wsURL := fmt.Sprintf("%s://%s/wsgrpc", connInfo.Scheme, connInfo.Host)
header := http.Header{}
wsDialer := wsgrpc.WebsocketDialer(wsURL, header, appInstance.GetConfig().Client.TLSInsecureSkipVerify)
wsDialer := wsgrpc.WebsocketDialer(wsURL,
header,
appInstance.GetConfig().Client.TLSInsecureSkipVerify,
logger.Logger(ctx),
)
opt = append(opt, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(wsDialer))
}
logger.Logger(ctx).Debugf("creating new grpc client to [%s]", utils.MarshalForJson(connInfo))
conn, err := grpc.NewClient(connInfo.Host, opt...)
if err != nil {
logger.Logger(ctx).Fatalf("did not connect: %v", err)
}
logger.Logger(ctx).Debugf("grpc client created")
return pb.NewMasterClient(conn)
}

View File

@@ -34,18 +34,26 @@ func InitLogger() {
logrus.SetFormatter(NewCustomFormatter(false, true))
}
func UpdateLoggerOpt(frpLogLevel string, logrusLevel string) {
func UpdateLoggerOpt(frpLogLevel string, logrusLevel string, isDebug bool) {
ctx := context.Background()
frpLogLevel = strings.ToLower(frpLogLevel)
logrusLevel = strings.ToLower(logrusLevel)
if frpLogLevel == "" {
if isDebug {
frpLogLevel = "trace"
} else {
frpLogLevel = "info"
}
}
if logrusLevel == "" {
if isDebug {
logrusLevel = "trace"
} else {
logrusLevel = "info"
}
}
frpLv, err := log.ParseLevel(frpLogLevel)
if err != nil {

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
@@ -14,6 +15,10 @@ import (
"github.com/gorilla/websocket"
)
var (
WSGrpcError = errors.New("wsgrpc error")
)
// ---------------------------------------
// 通用 websocketConn 实现 net.Conn 接口
// ---------------------------------------
@@ -35,16 +40,20 @@ func (c *websocketConn) Read(p []byte) (int, error) {
if c.readBuffer.Len() == 0 {
messageType, data, err := c.ws.ReadMessage()
if err != nil {
return 0, err
return 0, errors.Join(err, errors.New("wsgrpc read message error"), WSGrpcError)
}
// 只接受二进制数据
if messageType != websocket.BinaryMessage {
return 0, fmt.Errorf("unexpected message type: %d", messageType)
return 0, errors.Join(fmt.Errorf("unexpected message type: %d", messageType), WSGrpcError)
}
c.readBuffer.Write(data)
}
return c.readBuffer.Read(p)
if n, err := c.readBuffer.Read(p); err != nil {
return n, errors.Join(err, WSGrpcError)
} else {
return n, nil
}
}
// Write 将数据作为单条二进制消息发送
@@ -54,14 +63,18 @@ func (c *websocketConn) Write(p []byte) (int, error) {
err := c.ws.WriteMessage(websocket.BinaryMessage, p)
if err != nil {
return 0, err
return 0, errors.Join(err, errors.New("wsgrpc write message error"), WSGrpcError)
}
return len(p), nil
}
// Close 关闭 websocket 连接
func (c *websocketConn) Close() error {
return c.ws.Close()
err := c.ws.Close()
if err != nil {
return errors.Join(err, errors.New("wsgrpc close error"), WSGrpcError)
}
return nil
}
// LocalAddr 返回本地地址,通过 websocket 底层连接获取
@@ -83,9 +96,12 @@ func (c *websocketConn) RemoteAddr() net.Addr {
// SetDeadline 同时设置读写超时
func (c *websocketConn) SetDeadline(t time.Time) error {
if err := c.ws.SetReadDeadline(t); err != nil {
return err
return errors.Join(err, errors.New("wsgrpc set read deadline error"), WSGrpcError)
}
return c.ws.SetWriteDeadline(t)
if err := c.ws.SetWriteDeadline(t); err != nil {
return errors.Join(err, errors.New("wsgrpc set write deadline error"), WSGrpcError)
}
return nil
}
// SetReadDeadline 设置读超时
@@ -101,18 +117,26 @@ func (c *websocketConn) SetWriteDeadline(t time.Time) error {
// ---------------------------------------
// 客户端 WebSocket Dialer
// ---------------------------------------
type LogInterface interface {
Infof(format string, args ...interface{})
Errorf(format string, args ...interface{})
Tracef(format string, args ...interface{})
}
// WebsocketDialer 返回一个可以用于 grpc.WithContextDialer 的拨号函数;该函数通过 websocket 建立连接。
// 参数 url 表示 websocket 服务器地址header 可用于传递额外的 header 参数。
func WebsocketDialer(url string, header http.Header, insecure bool) func(ctx context.Context, addr string) (net.Conn, error) {
func WebsocketDialer(url string, header http.Header, insecure bool, log LogInterface) func(ctx context.Context, addr string) (net.Conn, error) {
return func(ctx context.Context, addr string) (net.Conn, error) {
dialer := websocket.Dialer{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
}
log.Tracef("dialing websocket server [%s]", url)
ws, _, err := dialer.DialContext(ctx, url, header)
if err != nil {
return nil, err
log.Errorf("wsgrpc dialer error: %v", err)
return nil, errors.Join(err, errors.New("wsgrpc dialer error"), WSGrpcError)
}
log.Tracef("websocket connection connect done")
return &websocketConn{ws: ws}, nil
}
}
@@ -160,11 +184,11 @@ func (l *WSListener) Accept() (net.Conn, error) {
select {
case conn, ok := <-l.connCh:
if !ok {
return nil, fmt.Errorf("listener closed")
return nil, errors.Join(fmt.Errorf("listener closed"), WSGrpcError)
}
return conn, nil
case <-l.done:
return nil, fmt.Errorf("listener closed")
return nil, errors.Join(fmt.Errorf("listener closed"), WSGrpcError)
}
}