package main import ( "context" "errors" "flag" "fmt" "github.com/gin-gonic/gin" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/soheilhy/cmux" "google.golang.org/grpc" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" "log" "net" "net/http" "net/url" "os" "os/signal" "runtime" "speech-nlu-parse/middleware" "speech-nlu-parse/pkg/database" "speech-nlu-parse/pkg/proto" "speech-nlu-parse/router" "speech-nlu-parse/service" "speech-nlu-parse/service/speechNlu" "strings" "syscall" "time" "gopkg.in/natefinch/lumberjack.v2" "speech-nlu-parse/dao" "speech-nlu-parse/global" "speech-nlu-parse/pkg/consul" "speech-nlu-parse/pkg/logger" "speech-nlu-parse/pkg/setting" ) var ( serverName string serverTag string ip string port int consulAddr string config string runMode string isHelp bool isVersion bool buildTime string buildVersion string author string consulToken string consulUrl string ) func setup() error { // 命令行读取参数 err := setupFlag() if err != nil { log.Fatalf("init.setupFlag err: %v", err) return err } // 判断help if isHelp { fmt.Println("编译后\n" + "Linux可执行: ./tencent-nlu-parse [command=xxx]\n" + "Windows可执行: ./tencent-nlu-parse.exe [command=xxx]\n\n" + "option:") flag.PrintDefaults() return errors.New("missing params.") } if isVersion { fmt.Println("build_time: ", buildTime) fmt.Println("build_version: ", buildVersion) fmt.Println("go_version: ", runtime.Version()) fmt.Println("author: ", author) return errors.New("missing params.") } err = setupSetting() if err != nil { log.Fatalf("init.setupSetting err: %v", err) return err } err = setupLogger() if err != nil { log.Fatalf("init.setupLogger err: %v", err) return err } return nil } func main() { // 初始化配置 if err := setup(); err != nil { log.Fatalf("[FATAL] Setup failed: %v", err) } // 初始化 RabbitMQ 消费者 consumer, err := speechNlu.NewConsumer( global.RabbitMqSetting.Url, global.RabbitMqSetting.ExchangeName, global.RabbitMqSetting.ExchangeType, ) if err != nil { log.Fatalf("[FATAL] Create consumer failed: %v", err) } defer func() { if err := consumer.Shutdown(); err != nil { log.Printf("[WARN] Consumer shutdown error: %v", err) } }() database.InitRedis() // 创建监听 lis, err := net.Listen("tcp", fmt.Sprintf(":%d", global.ServerSetting.Port)) if err != nil { log.Fatalf("[FATAL] Listen failed: %v", err) } // 创建 cmux 多路复用器 m := cmux.New(lis) // 匹配 gRPC 连接 grpcLis := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) // 匹配 HTTP 连接 httpLis := m.Match(cmux.HTTP1Fast()) // 初始化 gRPC 服务器 grpcServer := grpc.NewServer( grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( middleware.StreamGSError500(), )), grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( middleware.UnaryGSError500(), )), ) proto.RegisterTencentNluServer(grpcServer, &service.TencentNlu{}) _ = consul.RegisterService(global.ServerSetting) grpc_health_v1.RegisterHealthServer(grpcServer, &consul.HealthImpl{}) reflection.Register(grpcServer) // 初始化 HTTP 服务器 gin.SetMode(global.ServerSetting.RunMode) engine := router.InitRouter() _ = engine.SetTrustedProxies(nil) httpServer := &http.Server{ Handler: engine, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } // 启动 cmux 多路复用器 go func() { if err := m.Serve(); err != nil { log.Fatalf("[FATAL] cmux serve error: %v", err) } }() // 启动 gRPC 服务 go func() { global.Logger.Info("gRPC service is running...") if err := grpcServer.Serve(grpcLis); err != nil { log.Fatalf("[FATAL] gRPC serve error: %v", err) } }() // 启动 HTTP 服务 go func() { global.Logger.Infof("HTTP service is running on port %d", global.ServerSetting.Port) if err := httpServer.Serve(httpLis); err != nil && err != http.ErrServerClosed { log.Fatalf("[FATAL] HTTP serve error: %v", err) } }() // 阻塞等待信号 stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop // 优雅关闭 log.Println("[INFO] Shutting down...") // 创建上下文用于优雅关闭 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 先关闭HTTP服务 if err := httpServer.Shutdown(ctx); err != nil { log.Printf("[WARN] HTTP server shutdown error: %v", err) } grpcServer.GracefulStop() log.Println("[INFO] Server stopped") } func setupFlag() error { flag.BoolVar(&isHelp, "help", false, "帮助") flag.StringVar(&serverName, "name", "", "注册到注册中心的服务名") flag.StringVar(&serverTag, "tag", "", "注册到注册中心的标签") flag.StringVar(&ip, "ip", "", "IP") flag.IntVar(&port, "port", 0, "端口") flag.StringVar(&consulUrl, "consul", "http://172.28.124.105:8500", "consul服务地址") flag.StringVar(&config, "config", "env/v2,speech_nlu_parse/conf", "指定要使用的配置文件路径") flag.BoolVar(&isVersion, "version", false, "编译信息") flag.StringVar(&consulToken, "token", "092288b5-824f-854c-39aa-a958afd9a633", "consul token") flag.StringVar(&runMode, "mode", "", "启动模式(debug|release|test)") flag.Parse() return nil } func setupSetting() error { // 设置 consul 地址 token及路径 s, err := setting.NewSetting(consulUrl, consulToken, strings.Split(config, ",")...) if err != nil { return err } err = s.ReadSection("Server", &global.ServerSetting) if err != nil { return err } err = s.ReadSection("Logger", &global.LoggerSetting) if err != nil { return err } err = s.ReadSection("Device", &global.DeviceSetting) if err != nil { return err } err = s.ReadSection("gree", &global.InternalRemoteSetting) if err != nil { return err } err = s.ReadSection("DB", &global.DBSetting) if err != nil { return err } err = s.ReadSection("MusicDomain", &global.MusicDomainSetting) if err != nil { return err } err = s.ReadSection("Auth", &global.AuthSetting) if err != nil { return err } err = s.ReadSection("DingDang", &global.TencentSetting) if err != nil { return err } err = s.ReadSection("Others", &global.OthersSetting) if err != nil { return err } err = s.ReadSection("Services", &global.ServiceSetting) if err != nil { return err } // 改由配置文件读取 err = s.ReadSection("DevLimited", &global.DevLimitedArr) if err != nil { return err } err = s.ReadSection("tencentV2", &global.TencentGwSetting) if err != nil { return err } err = s.ReadSection("Speech", &global.SpeechSetting) if err != nil { return err } err = s.ReadSection("RabbitMq", &global.RabbitMqSetting) if err != nil { return err } err = s.ReadSection("Redis", &global.RedisSetting) if err != nil { return err } consulUrlParse, err := url.Parse(consulUrl) if err != nil { return err } global.ServerSetting.ConsulAddr = consulUrlParse.Host // 初始化依赖服务的信息 global.InitServiceMap(consulUrl, consulToken) // global.CheckServiceMap() // global.PrintServiceMap() // 初始化设备差异化配置, 需要先加载设置设置 global.InitLimitedSetting() // 初始化数据库连接, 依赖于设置 err = dao.InitDB(global.DBSetting.Dsn) if err != nil { return err } // 从数据库中加载, 依赖于数据库连接 err = global.InitDingDangBot() if err != nil { return err } global.ServerSetting.Interval *= time.Second global.ServerSetting.Deregister *= time.Minute if serverName != "" { global.ServerSetting.Name = serverName } if serverTag != "" { global.ServerSetting.Tag = strings.Split(serverTag, ",") } if ip != "" { global.ServerSetting.IP = ip } if port != 0 { global.ServerSetting.Port = port } if consulToken != "" { global.ServerSetting.ConsulToken = consulToken } if consulToken != "" { global.ServerSetting.ConsulToken = consulToken } if runMode != "" { global.ServerSetting.RunMode = runMode } global.ConsulObj, err = consul.NewConsulObj(consulUrl, consulToken) if err != nil { return err } return nil } func setupLogger() error { fileName := global.LoggerSetting.LogSavePath + "/" + global.LoggerSetting.LogFileName + "_" + time.Now().Format("20060102150405") + global.LoggerSetting.LogFileExt global.Logger = logger.NewLogger(&lumberjack.Logger{ Filename: fileName, MaxSize: global.LoggerSetting.MaxSize, MaxAge: global.LoggerSetting.MaxAge, MaxBackups: global.LoggerSetting.MaxBackups, Compress: global.LoggerSetting.Compress, }, global.ServerSetting.RunMode, global.ServerSetting.Name) return nil }