main.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package main
  2. import (
  3. "Tech/common"
  4. "Tech/controllers"
  5. _ "Tech/routers"
  6. "os"
  7. "os/signal"
  8. "syscall"
  9. "time"
  10. "github.com/beego/beego/v2/client/orm"
  11. "github.com/beego/beego/v2/core/logs"
  12. "github.com/beego/beego/v2/server/web"
  13. beego "github.com/beego/beego/v2/server/web"
  14. "github.com/beego/beego/v2/server/web/filter/cors"
  15. "github.com/sirupsen/logrus"
  16. )
  17. func init() {
  18. // 注册驱动(可选的,mysql/sqlite3/postgres 默认已注册):cite[4]
  19. // orm.RegisterDriver("mysql", orm.DR_MySQL)
  20. // 从配置文件读取数据库配置
  21. dbAdapter, _ := beego.AppConfig.String("DB.Adapter")
  22. dbURL, _ := beego.AppConfig.String("DB.URL")
  23. // 注册数据库
  24. orm.RegisterDataBase("default", dbAdapter, dbURL)
  25. // 启动数据库连接监控协程
  26. go monitorDatabaseConnection(dbAdapter, dbURL)
  27. // 注册模型(也可以在主程序入口或模型自身的 init 函数中)
  28. // orm.RegisterModel(new(User))
  29. }
  30. // sendHeartbeatPeriodically 定时发送心跳到信息化服务器
  31. func sendHeartbeatPeriodically() {
  32. ticker := time.NewTicker(30 * time.Second) // 每30秒发送一次心跳
  33. defer ticker.Stop()
  34. common.LogInfo("心跳发送任务已启动,间隔30秒")
  35. for range ticker.C {
  36. err := func() error {
  37. defer func() {
  38. if r := recover(); r != nil {
  39. common.LogError("发送心跳时发生异常", nil, logrus.Fields{
  40. "panic": r,
  41. })
  42. }
  43. }()
  44. err := common.SendHeartbeat()
  45. if err != nil {
  46. common.LogError("发送心跳失败", err)
  47. return err
  48. }
  49. common.LogInfo("成功发送心跳到信息化服务器")
  50. return nil
  51. }()
  52. if err != nil {
  53. common.LogError("发送心跳任务出现错误", err)
  54. }
  55. }
  56. }
  57. // monitorDatabaseConnection 定时检查数据库连接并尝试重连
  58. func monitorDatabaseConnection(dbAdapter, dbURL string) {
  59. ticker := time.NewTicker(10 * time.Second) // 每10秒检查一次
  60. defer ticker.Stop()
  61. common.LogInfo("数据库连接监控任务已启动,间隔10秒")
  62. for range ticker.C {
  63. // 使用匿名函数包装可能产生异常的操作,并在每次循环都进行异常处理
  64. func() {
  65. defer func() {
  66. if r := recover(); r != nil {
  67. // 即使NewOrm发生异常,也要尝试重新注册数据库连接
  68. common.LogError("监控协程发生异常", nil, logrus.Fields{
  69. "panic": r,
  70. })
  71. err := orm.RegisterDataBase("default", dbAdapter, dbURL)
  72. if err != nil {
  73. common.LogError("数据库重连失败", err)
  74. } else {
  75. common.LogInfo("数据库重连成功")
  76. }
  77. }
  78. }()
  79. // 尝试执行一个简单的查询来测试连接
  80. startTime := time.Now()
  81. o := orm.NewOrm()
  82. _, err := o.Raw("SELECT 1").Exec()
  83. duration := time.Since(startTime)
  84. if err != nil {
  85. common.LogError("数据库连接异常", err, logrus.Fields{
  86. "adapter": dbAdapter,
  87. "db_check": "SELECT 1",
  88. })
  89. // 尝试重新注册数据库连接
  90. err = orm.RegisterDataBase("default", dbAdapter, dbURL)
  91. if err != nil {
  92. common.LogError("数据库重连失败", err)
  93. } else {
  94. common.LogInfo("数据库重连成功")
  95. }
  96. } else {
  97. // 每100次检查记录一次成功日志(避免日志过多)
  98. if time.Now().Unix()%100 == 0 {
  99. common.LogDebug("数据库连接检查正常", logrus.Fields{
  100. "duration_ms": duration.Milliseconds(),
  101. })
  102. }
  103. }
  104. }()
  105. }
  106. }
  107. func main() {
  108. // 初始化日志系统
  109. logConfig := &common.LogConfig{
  110. LogDir: "./log",
  111. LogLevel: "info",
  112. MaxSize: 100, // 100MB
  113. MaxBackups: 30, // 保留30个旧文件
  114. MaxAge: 90, // 保留90天
  115. Compress: true,
  116. EnableCaller: true,
  117. }
  118. if err := common.InitLogger(logConfig); err != nil {
  119. logs.Error("初始化日志系统失败: %v", err)
  120. // 仍然继续运行,但使用默认日志
  121. } else {
  122. common.LogInfo("日志系统初始化成功")
  123. }
  124. // 设置优雅关闭
  125. setupGracefulShutdown()
  126. // 自动建表。参数说明:数据库别名、是否强制重建(false 则只创建新表)、是否打印日志
  127. // orm.RunSyncdb("default", false, true)
  128. // 可选:开启 ORM 调试模式,打印 SQL 日志
  129. // 将 URL 路径 /static/upload 映射到本地目录 static/upload
  130. // CORS 过滤器
  131. beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
  132. AllowAllOrigins: true,
  133. AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
  134. AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Content-Type"},
  135. ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin"},
  136. AllowCredentials: true,
  137. }))
  138. web.SetStaticPath("/static/upload", "static/upload")
  139. orm.Debug = true
  140. // 初始化用户信息缓存
  141. controllers.InitUserInfoCache()
  142. // 启动定时发送心跳任务
  143. go sendHeartbeatPeriodically()
  144. // 初始化电调数据获取器
  145. common.InitElectricalDataFetcher()
  146. common.LogInfo("SCADA培训系统服务启动中...")
  147. beego.Run()
  148. }
  149. // setupGracefulShutdown 设置优雅关闭
  150. func setupGracefulShutdown() {
  151. c := make(chan os.Signal, 1)
  152. signal.Notify(c, os.Interrupt, syscall.SIGTERM)
  153. go func() {
  154. <-c
  155. common.LogInfo("接收到关闭信号,正在优雅关闭服务...")
  156. common.CloseLogger()
  157. os.Exit(0)
  158. }()
  159. }