2024-11-22 16:41:04 +08:00
|
|
|
|
// entry function
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"flag"
|
2025-07-31 10:31:26 +08:00
|
|
|
|
"net/http"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"os/signal"
|
2025-10-17 17:10:10 +08:00
|
|
|
|
"path/filepath"
|
2025-07-31 10:31:26 +08:00
|
|
|
|
"syscall"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
2025-01-22 16:38:46 +08:00
|
|
|
|
"modelRT/alert"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"modelRT/config"
|
|
|
|
|
|
"modelRT/database"
|
2025-03-24 16:37:43 +08:00
|
|
|
|
"modelRT/diagram"
|
2025-03-25 17:00:09 +08:00
|
|
|
|
locker "modelRT/distributedlock"
|
2024-12-06 16:13:11 +08:00
|
|
|
|
_ "modelRT/docs"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"modelRT/handler"
|
2024-12-25 16:34:57 +08:00
|
|
|
|
"modelRT/logger"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"modelRT/middleware"
|
2025-10-15 17:08:32 +08:00
|
|
|
|
"modelRT/model"
|
2024-12-05 14:57:23 +08:00
|
|
|
|
"modelRT/pool"
|
2025-11-12 17:34:18 +08:00
|
|
|
|
realtimedata "modelRT/real-time-data"
|
2025-07-31 10:31:26 +08:00
|
|
|
|
"modelRT/router"
|
2025-09-12 17:12:02 +08:00
|
|
|
|
"modelRT/util"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-11-12 17:34:18 +08:00
|
|
|
|
"github.com/confluentinc/confluent-kafka-go/kafka"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
"github.com/panjf2000/ants/v2"
|
2025-06-23 16:00:48 +08:00
|
|
|
|
swaggerFiles "github.com/swaggo/files"
|
|
|
|
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
2024-11-22 16:41:04 +08:00
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var limiter *middleware.Limiter
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
|
limiter = middleware.NewLimiter(10, 1*time.Minute) // 设置限流器,允许每分钟最多请求10次
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
2025-10-17 17:10:10 +08:00
|
|
|
|
modelRTConfigDir = flag.String("modelRT_config_dir", "./configs", "config file dir of model runtime service")
|
2024-11-22 16:41:04 +08:00
|
|
|
|
modelRTConfigName = flag.String("modelRT_config_name", "config", "config file name of model runtime service")
|
|
|
|
|
|
modelRTConfigType = flag.String("modelRT_config_type", "yaml", "config file type of model runtime service")
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
|
modelRTConfig config.ModelRTConfig
|
|
|
|
|
|
postgresDBClient *gorm.DB
|
2025-01-22 16:38:46 +08:00
|
|
|
|
alertManager *alert.EventManager
|
2024-11-22 16:41:04 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2024-12-02 16:13:16 +08:00
|
|
|
|
// TODO 使用 wire 依赖注入管理 DVIE 面板注册的 panel
|
2025-10-20 15:06:23 +08:00
|
|
|
|
// @title ModelRT 实时模型服务 API 文档
|
|
|
|
|
|
// @version 1.0
|
|
|
|
|
|
// @description 实时数据计算和模型运行服务的 API 服务
|
|
|
|
|
|
// TODO termsOfService服务条款待后续优化
|
|
|
|
|
|
// // @termsOfService http://swagger.io/terms/
|
|
|
|
|
|
//
|
|
|
|
|
|
// @contact.name douxu
|
|
|
|
|
|
// TODO 修改支持的文档地址
|
|
|
|
|
|
// @contact.url http://www.swagger.io/support
|
|
|
|
|
|
// @contact.email douxu@clea.com.cn
|
|
|
|
|
|
//
|
|
|
|
|
|
// @license.name Apache 2.0
|
|
|
|
|
|
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
|
|
|
|
|
//
|
|
|
|
|
|
// @host localhost:8080
|
|
|
|
|
|
// @BasePath /api/v1
|
2024-11-22 16:41:04 +08:00
|
|
|
|
func main() {
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
ctx := context.TODO()
|
|
|
|
|
|
|
2025-06-23 16:00:48 +08:00
|
|
|
|
// init logger
|
|
|
|
|
|
logger.InitLoggerInstance(modelRTConfig.LoggerConfig)
|
|
|
|
|
|
|
2025-10-17 17:10:10 +08:00
|
|
|
|
configPath := filepath.Join(*modelRTConfigDir, *modelRTConfigName+"."+*modelRTConfigType)
|
|
|
|
|
|
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
|
|
|
|
logger.Info(ctx, "configuration file not found,checking for example file")
|
|
|
|
|
|
|
|
|
|
|
|
exampleConfigPath := filepath.Join(*modelRTConfigDir, *modelRTConfigName+".example."+*modelRTConfigType)
|
|
|
|
|
|
if _, err := os.Stat(exampleConfigPath); err == nil {
|
|
|
|
|
|
if err := util.CopyFile(exampleConfigPath, configPath); err != nil {
|
|
|
|
|
|
logger.Error(ctx, "failed to copy example config file", "error", err)
|
|
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
logger.Info(ctx, "successfully copied example config to actual config file")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logger.Error(ctx, "No config file and no config example file found.")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-22 16:41:04 +08:00
|
|
|
|
modelRTConfig = config.ReadAndInitConfig(*modelRTConfigDir, *modelRTConfigName, *modelRTConfigType)
|
2025-09-12 17:12:02 +08:00
|
|
|
|
|
|
|
|
|
|
hostName, err := os.Hostname()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "get host name failed", "error", err)
|
|
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 16:12:00 +08:00
|
|
|
|
serviceToken, err := util.GenerateClientToken(hostName, modelRTConfig.ServiceConfig.ServiceName, modelRTConfig.ServiceConfig.SecretKey)
|
2025-09-12 17:12:02 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "generate client token failed", "error", err)
|
|
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-22 16:41:04 +08:00
|
|
|
|
// init postgresDBClient
|
2024-11-28 15:29:34 +08:00
|
|
|
|
postgresDBClient = database.InitPostgresDBInstance(ctx, modelRTConfig.PostgresDBURI)
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
sqlDB, err := postgresDBClient.DB()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
sqlDB.Close()
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
2025-01-22 16:38:46 +08:00
|
|
|
|
// init alert manager
|
|
|
|
|
|
_ = alert.InitAlertEventManager()
|
|
|
|
|
|
|
2024-12-18 16:25:49 +08:00
|
|
|
|
// init model parse ants pool
|
|
|
|
|
|
parsePool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc)
|
2024-11-22 16:41:04 +08:00
|
|
|
|
if err != nil {
|
2025-06-05 15:56:40 +08:00
|
|
|
|
logger.Error(ctx, "init concurrent parse task pool failed", "error", err)
|
2024-11-22 16:41:04 +08:00
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
2024-12-18 16:25:49 +08:00
|
|
|
|
defer parsePool.Release()
|
|
|
|
|
|
|
2025-10-15 17:08:32 +08:00
|
|
|
|
searchPool, err := util.NewRedigoPool(modelRTConfig.StorageRedisConfig)
|
|
|
|
|
|
defer searchPool.Close()
|
|
|
|
|
|
model.InitAutocompleterWithPool(searchPool)
|
|
|
|
|
|
|
2025-09-09 16:02:36 +08:00
|
|
|
|
storageClient := diagram.InitRedisClientInstance(modelRTConfig.StorageRedisConfig)
|
2025-03-24 16:37:43 +08:00
|
|
|
|
defer storageClient.Close()
|
|
|
|
|
|
|
2025-03-25 17:00:09 +08:00
|
|
|
|
lockerClient := locker.InitClientInstance(modelRTConfig.LockerRedisConfig)
|
2025-03-24 16:37:43 +08:00
|
|
|
|
defer lockerClient.Close()
|
|
|
|
|
|
|
2025-01-21 16:35:44 +08:00
|
|
|
|
// init anchor param ants pool
|
2025-01-22 16:38:46 +08:00
|
|
|
|
anchorRealTimePool, err := pool.AnchorPoolInit(modelRTConfig.RTDReceiveConcurrentQuantity)
|
2024-12-18 16:25:49 +08:00
|
|
|
|
if err != nil {
|
2025-06-05 15:56:40 +08:00
|
|
|
|
logger.Error(ctx, "init concurrent anchor param task pool failed", "error", err)
|
2024-12-18 16:25:49 +08:00
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
2025-01-22 16:38:46 +08:00
|
|
|
|
defer anchorRealTimePool.Release()
|
2024-12-18 16:25:49 +08:00
|
|
|
|
|
2025-11-12 17:34:18 +08:00
|
|
|
|
// TODO 配置文件中增加 kafka 配置
|
2025-01-20 16:20:21 +08:00
|
|
|
|
// init cancel context
|
2024-12-18 16:25:49 +08:00
|
|
|
|
cancelCtx, cancel := context.WithCancel(ctx)
|
|
|
|
|
|
defer cancel()
|
2025-11-12 17:34:18 +08:00
|
|
|
|
customerConf := &kafka.ConfigMap{
|
|
|
|
|
|
"bootstrap.servers": modelRTConfig.KafkaConfig.Servers,
|
|
|
|
|
|
"group.id": modelRTConfig.KafkaConfig.GroupID,
|
|
|
|
|
|
"auto.offset.reset": modelRTConfig.KafkaConfig.AutoOffsetReset,
|
|
|
|
|
|
"enable.auto.commit": modelRTConfig.KafkaConfig.EnableAutoCommit,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
go realtimedata.ReceiveChan(cancelCtx, customerConf, []string{modelRTConfig.KafkaConfig.Topic}, modelRTConfig.KafkaConfig.ReadMessageTimeDuration)
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-01-09 15:56:40 +08:00
|
|
|
|
postgresDBClient.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
|
// load circuit diagram from postgres
|
2025-08-15 16:25:48 +08:00
|
|
|
|
// componentTypeMap, err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// logger.Error(ctx, "load circuit diagrams from postgres failed", "error", err)
|
|
|
|
|
|
// panic(err)
|
|
|
|
|
|
// }
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-01-09 15:56:40 +08:00
|
|
|
|
// TODO 暂时屏蔽完成 swagger 启动测试
|
2025-08-15 16:25:48 +08:00
|
|
|
|
tree, err := database.QueryTopologicFromDB(ctx, tx)
|
2025-01-09 15:56:40 +08:00
|
|
|
|
if err != nil {
|
2025-06-05 15:56:40 +08:00
|
|
|
|
logger.Error(ctx, "load topologic info from postgres failed", "error", err)
|
2025-01-09 15:56:40 +08:00
|
|
|
|
panic(err)
|
|
|
|
|
|
}
|
2025-05-20 16:08:17 +08:00
|
|
|
|
diagram.GlobalTree = tree
|
2025-01-09 15:56:40 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
})
|
2024-11-27 09:11:48 +08:00
|
|
|
|
|
2025-10-14 16:12:00 +08:00
|
|
|
|
// use release mode in productio
|
|
|
|
|
|
// gin.SetMode(gin.ReleaseMode)
|
2025-07-31 10:31:26 +08:00
|
|
|
|
engine := gin.New()
|
2025-09-12 17:12:02 +08:00
|
|
|
|
router.RegisterRoutes(engine, serviceToken)
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-01-20 16:20:21 +08:00
|
|
|
|
// real time data api
|
|
|
|
|
|
engine.GET("/ws/rtdatas", handler.RealTimeDataReceivehandler)
|
|
|
|
|
|
|
2024-12-20 16:06:42 +08:00
|
|
|
|
// anchor api
|
|
|
|
|
|
engine.POST("/model/anchor_replace", handler.ComponentAnchorReplaceHandler)
|
|
|
|
|
|
|
2025-01-23 14:56:01 +08:00
|
|
|
|
// alert api
|
|
|
|
|
|
engine.GET("/alert/events/query", handler.QueryAlertEventHandler)
|
|
|
|
|
|
|
|
|
|
|
|
// real time data api
|
|
|
|
|
|
engine.GET("/rt/datas/query", handler.QueryRealTimeDataHandler)
|
|
|
|
|
|
|
2024-12-12 16:17:31 +08:00
|
|
|
|
// dashborad api
|
|
|
|
|
|
dashboard := engine.Group("/dashboard", limiter.Middleware)
|
|
|
|
|
|
{
|
|
|
|
|
|
dashboard.GET("/load", nil)
|
|
|
|
|
|
dashboard.GET("/query", nil)
|
|
|
|
|
|
dashboard.POST("/create", nil)
|
|
|
|
|
|
dashboard.POST("/update", nil)
|
|
|
|
|
|
dashboard.POST("/delete", nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-06 16:13:11 +08:00
|
|
|
|
// Swagger UI
|
|
|
|
|
|
engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
|
|
|
|
|
|
|
|
|
|
// 注册 Swagger UI 路由
|
|
|
|
|
|
// docs.SwaggerInfo.BasePath = "/model"
|
|
|
|
|
|
// v1 := engine.Group("/api/v1")
|
|
|
|
|
|
// {
|
|
|
|
|
|
// eg := v1.Group("/example")
|
|
|
|
|
|
// {
|
|
|
|
|
|
// eg.GET("/helloworld", Helloworld)
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2025-10-14 16:12:00 +08:00
|
|
|
|
server := http.Server{
|
|
|
|
|
|
Addr: ":8080",
|
|
|
|
|
|
Handler: engine,
|
|
|
|
|
|
}
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-10-14 16:12:00 +08:00
|
|
|
|
// creating a System Signal Receiver
|
|
|
|
|
|
done := make(chan os.Signal, 10)
|
|
|
|
|
|
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
<-done
|
|
|
|
|
|
if err := server.Shutdown(context.Background()); err != nil {
|
|
|
|
|
|
logger.Error(ctx, "ShutdownServerError", "err", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2024-11-22 16:41:04 +08:00
|
|
|
|
|
2025-10-17 17:10:10 +08:00
|
|
|
|
logger.Info(ctx, "starting ModelRT server")
|
2025-10-14 16:12:00 +08:00
|
|
|
|
err = server.ListenAndServe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if err == http.ErrServerClosed {
|
|
|
|
|
|
// the service receives the shutdown signal normally and then closes
|
|
|
|
|
|
logger.Info(ctx, "Server closed under request")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// abnormal shutdown of service
|
|
|
|
|
|
logger.Error(ctx, "Server closed unexpected", "err", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-11-22 16:41:04 +08:00
|
|
|
|
}
|