package server import ( "context" "log/slog" "os" "os/signal" "proxy-server/pkg/utils" "proxy-server/server/debug" "proxy-server/server/fwd" "proxy-server/server/pkg/env" "proxy-server/server/pkg/log" "proxy-server/server/pkg/orm" "proxy-server/server/report" "proxy-server/server/web" "sync" "syscall" "time" "github.com/google/uuid" "github.com/joho/godotenv" _ "net/http/pprof" ) const ( Version = 1 RestoreMagic = 0x72 ) type server struct { id int32 name string } func New() *server { return &server{} } func (s *server) Run() (err error) { // 初始化 err = s.init() if err != nil { return err } // 恢复服务状态 err = s.restore() if err != nil { return err } // 准备子服务 ctx, cancel := context.WithCancel(context.Background()) defer cancel() wg := sync.WaitGroup{} // 转发服务 wg.Add(1) fwdQuit := make(chan error, 1) defer close(fwdQuit) go func() { defer wg.Done() err = s.startFwd(ctx) fwdQuit <- err }() // 接口服务 wg.Add(1) apiQuit := make(chan error, 1) defer close(apiQuit) go func() { defer wg.Done() err := s.startWeb(ctx) apiQuit <- err }() // debug go func() { debug.Start(ctx) }() // 性能监控 // go func() { // runtime.SetBlockProfileRate(1) // err := http.ListenAndServe(":6060", nil) // if err != nil { // slog.Error("性能监控服务发生错误", "err", err) // } // }() // 报告上线 slog.Debug("报告服务上线") var reportErrCh = make(chan error, 1) defer close(reportErrCh) go func() { id, err := report.Online(ctx, s.name) if err != nil { reportErrCh <- err return } s.id = id }() // 等待退出信号 osQuit := make(chan os.Signal, 1) signal.Notify(osQuit, os.Interrupt, syscall.SIGTERM) var reportErr error select { case <-osQuit: slog.Info("服务主动退出") case err := <-fwdQuit: slog.Warn("fwd 服务异常退出", "err", err) case err := <-apiQuit: slog.Warn("web 服务异常退出", "err", err) case reportErr = <-reportErrCh: slog.Warn("报告服务上线发生错误", "err", reportErr) } cancel() // 报告下线 if reportErr == nil { slog.Debug("报告服务下线") go func() { err := report.Offline(ctx, s.name) if err != nil { slog.Error("报告服务下线发生错误", "err", err) } }() } // 等待其它服务关闭 timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() select { case <-utils.ChanWgWait(timeout, &wg): slog.Info("服务正常关闭") case <-timeout.Done(): slog.Warn("超时强制关闭") } return nil } func (s *server) init() error { err := godotenv.Load() if err != nil { println("没有本地环境变量文件") } log.Init() env.Init() orm.Init() return nil } func (s *server) restore() error { var file = "proxy.lock" bytes, err := os.ReadFile(file) if err != nil { return err } if len(bytes) == 17 && bytes[0] == RestoreMagic { s.name = uuid.UUID(bytes[1:]).String() slog.Info("恢复服务名称", "name", s.name) } else { var u = uuid.New() s.name = u.String() bytes = make([]byte, 17) bytes[0] = RestoreMagic copy(bytes[1:], u[:]) err := os.WriteFile(file, bytes, 0644) if err != nil { return err } slog.Info("生成服务名称", "name", s.name) } return nil } func (s *server) startFwd(ctx context.Context) error { server := fwd.New(&fwd.Config{ Id: &s.id, }) go func() { <-ctx.Done() server.Stop() }() return server.Run() } func (s *server) startWeb(ctx context.Context) error { server := web.New() go func() { <-ctx.Done() err := server.Stop() if err != nil { slog.Error("web 服务关闭发生错误", "err", err) } }() return server.Run() }