2020-10-04 14:27:14 +08:00
|
|
|
|
package nodes
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2022-01-19 16:53:52 +08:00
|
|
|
|
"context"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"crypto/tls"
|
2022-06-08 15:13:24 +08:00
|
|
|
|
"encoding/json"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"errors"
|
2023-08-11 16:13:33 +08:00
|
|
|
|
"fmt"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/configs"
|
2020-11-17 10:26:31 +08:00
|
|
|
|
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
2022-09-25 20:34:19 +08:00
|
|
|
|
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
|
2021-01-12 11:49:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/events"
|
2021-12-14 10:49:29 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
2020-12-30 22:01:01 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
2022-01-19 16:53:52 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/rpc"
|
2020-11-17 10:26:31 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/setup"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
2022-08-21 23:09:59 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
2022-09-09 10:28:44 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
2022-06-08 15:13:24 +08:00
|
|
|
|
"github.com/go-sql-driver/mysql"
|
2020-11-17 15:42:42 +08:00
|
|
|
|
"github.com/iwind/TeaGo/Tea"
|
2020-10-13 20:05:13 +08:00
|
|
|
|
"github.com/iwind/TeaGo/dbs"
|
2021-01-12 11:49:14 +08:00
|
|
|
|
"github.com/iwind/TeaGo/lists"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"github.com/iwind/TeaGo/logs"
|
2021-07-25 17:46:47 +08:00
|
|
|
|
"github.com/iwind/TeaGo/maps"
|
2021-03-25 11:48:45 +08:00
|
|
|
|
"github.com/iwind/TeaGo/types"
|
2021-07-25 17:46:47 +08:00
|
|
|
|
"github.com/iwind/gosock/pkg/gosock"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
|
"google.golang.org/grpc/credentials"
|
2023-06-16 08:17:00 +08:00
|
|
|
|
"google.golang.org/grpc/status"
|
2021-01-12 11:49:14 +08:00
|
|
|
|
"log"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"net"
|
|
|
|
|
|
"os"
|
2021-01-12 11:49:14 +08:00
|
|
|
|
"os/exec"
|
2024-04-13 17:14:58 +08:00
|
|
|
|
"os/signal"
|
2021-12-14 10:49:29 +08:00
|
|
|
|
"runtime"
|
|
|
|
|
|
"sort"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
"strconv"
|
2022-06-08 15:13:24 +08:00
|
|
|
|
"strings"
|
2021-06-20 19:22:24 +08:00
|
|
|
|
"sync"
|
2024-04-13 17:14:58 +08:00
|
|
|
|
"syscall"
|
2021-01-12 11:49:14 +08:00
|
|
|
|
"time"
|
2022-03-20 11:28:45 +08:00
|
|
|
|
|
|
|
|
|
|
// grpc decompression
|
|
|
|
|
|
_ "google.golang.org/grpc/encoding/gzip"
|
2020-10-04 14:27:14 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var sharedAPIConfig *configs.APIConfig = nil
|
|
|
|
|
|
|
|
|
|
|
|
type APINode struct {
|
2022-09-07 15:57:31 +08:00
|
|
|
|
serviceInstanceMap map[string]any
|
2021-06-20 19:22:24 +08:00
|
|
|
|
serviceInstanceLocker sync.Mutex
|
2021-07-25 17:46:47 +08:00
|
|
|
|
|
|
|
|
|
|
sock *gosock.Sock
|
2021-11-21 19:27:27 +08:00
|
|
|
|
|
|
|
|
|
|
isStarting bool
|
2022-06-08 15:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
issues []*StartIssue
|
|
|
|
|
|
issuesFile string
|
2022-09-07 15:57:31 +08:00
|
|
|
|
|
|
|
|
|
|
progress *utils.Progress
|
2020-10-04 14:27:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewAPINode() *APINode {
|
2021-06-20 19:22:24 +08:00
|
|
|
|
return &APINode{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
serviceInstanceMap: map[string]any{},
|
2021-07-25 17:46:47 +08:00
|
|
|
|
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
2022-06-08 15:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
issues: []*StartIssue{},
|
|
|
|
|
|
issuesFile: Tea.LogFile("issues.log"),
|
2021-06-20 19:22:24 +08:00
|
|
|
|
}
|
2020-10-04 14:27:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *APINode) Start() {
|
2021-11-21 19:27:27 +08:00
|
|
|
|
this.isStarting = true
|
|
|
|
|
|
|
2020-11-17 10:26:31 +08:00
|
|
|
|
logs.Println("[API_NODE]start api node, pid: " + strconv.Itoa(os.Getpid()))
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
// 保存启动过程中的问题,以便于查看
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
this.saveIssues()
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// 本地Sock
|
|
|
|
|
|
logs.Println("[API_NODE]listening sock ...")
|
|
|
|
|
|
err := this.listenSock()
|
2021-04-15 11:16:58 +08:00
|
|
|
|
if err != nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "start local sock failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("sock", errString, "")
|
2021-04-15 11:16:58 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-13 17:14:58 +08:00
|
|
|
|
// 监听信号
|
|
|
|
|
|
this.listenSignals()
|
|
|
|
|
|
|
2022-08-21 23:09:59 +08:00
|
|
|
|
// 启动IP库
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("IP_LIBRARY", "开始初始化IP库")
|
2022-08-21 23:09:59 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "initializing ip library ...")
|
2022-08-23 19:40:17 +08:00
|
|
|
|
err = iplibrary.InitDefault()
|
2022-08-21 23:09:59 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "initialize ip library failed: "+err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
// 检查数据库连接
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("DATABASE", "正在检查数据库连接")
|
2022-06-08 15:13:24 +08:00
|
|
|
|
err = this.checkDB()
|
2021-01-12 11:49:14 +08:00
|
|
|
|
if err != nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "check database connection failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
|
2021-01-12 11:49:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-17 10:26:31 +08:00
|
|
|
|
// 自动升级
|
2021-11-21 19:27:27 +08:00
|
|
|
|
logs.Println("[API_NODE]auto upgrading ...")
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("DATABASE", "正在升级数据库")
|
2021-01-12 11:49:14 +08:00
|
|
|
|
err = this.autoUpgrade()
|
2020-11-17 10:26:31 +08:00
|
|
|
|
if err != nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "auto upgrade failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
|
2020-11-17 10:26:31 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-10-04 14:27:14 +08:00
|
|
|
|
|
2021-03-25 11:48:45 +08:00
|
|
|
|
// 自动设置数据库
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("DATABASE", "正在设置数据库")
|
2021-11-21 19:27:27 +08:00
|
|
|
|
logs.Println("[API_NODE]setup database ...")
|
2021-03-25 11:48:45 +08:00
|
|
|
|
err = this.setupDB()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Println("[API_NODE]setup database '" + err.Error() + "'")
|
|
|
|
|
|
|
|
|
|
|
|
// 不阻断执行
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-13 20:05:13 +08:00
|
|
|
|
// 数据库通知启动
|
2022-09-26 11:00:58 +08:00
|
|
|
|
this.setProgress("DATABASE", "正在建立数据库模型")
|
2021-11-21 19:27:27 +08:00
|
|
|
|
logs.Println("[API_NODE]notify ready ...")
|
2020-10-13 20:05:13 +08:00
|
|
|
|
dbs.NotifyReady()
|
|
|
|
|
|
|
2022-09-09 10:28:44 +08:00
|
|
|
|
// 设置时区
|
2022-09-26 11:00:58 +08:00
|
|
|
|
this.setProgress("TIMEZONE", "正在设置时区")
|
2022-09-09 10:28:44 +08:00
|
|
|
|
this.setupTimeZone()
|
|
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
// 读取配置
|
2022-09-26 11:00:58 +08:00
|
|
|
|
this.setProgress("DATABASE", "正在加载API配置")
|
2021-11-21 19:27:27 +08:00
|
|
|
|
logs.Println("[API_NODE]reading api config ...")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
config, err := configs.SharedAPIConfig()
|
|
|
|
|
|
if err != nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "read api config failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("config", errString, "")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
sharedAPIConfig = config
|
|
|
|
|
|
|
|
|
|
|
|
// 校验
|
2021-01-01 23:31:30 +08:00
|
|
|
|
apiNode, err := models.SharedAPINodeDAO.FindEnabledAPINodeWithUniqueIdAndSecret(nil, config.NodeId, config.Secret)
|
2020-10-04 14:27:14 +08:00
|
|
|
|
if err != nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "start failed: read api node from database failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("db", errString, "")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if apiNode == nil {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "can not start node, wrong 'nodeId' or 'secret'"
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("config", errString, "请在api.yaml配置文件中填写正确的`nodeId`和`secret`,如果数据库或者管理节点或API节点是从别的服务器迁移过来的,请将老的系统配置拷贝到当前节点配置下")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
config.SetNumberId(int64(apiNode.Id))
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
// 清除上一次启动错误
|
|
|
|
|
|
// 这个错误文件可能不存在,不需要处理错误
|
|
|
|
|
|
_ = os.Remove(this.issuesFile)
|
|
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
// 设置rlimit
|
|
|
|
|
|
_ = utils.SetRLimit(1024 * 1024)
|
|
|
|
|
|
|
2020-12-29 18:28:07 +08:00
|
|
|
|
// 状态变更计时器
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
|
|
|
|
|
NewNodeStatusExecutor().Listen()
|
|
|
|
|
|
})
|
2020-12-29 18:28:07 +08:00
|
|
|
|
|
2021-07-29 16:50:59 +08:00
|
|
|
|
// 访问日志存储管理器
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("ACCESS_LOG_STORAGES", "正在启动访问日志存储器")
|
2022-08-09 17:35:35 +08:00
|
|
|
|
this.startAccessLogStorages()
|
2021-07-29 16:50:59 +08:00
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
// 监听RPC服务
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.setProgress("LISTEN_PORT", "正在启动监听端口")
|
2020-12-30 22:01:01 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "starting RPC server ...")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var isListening = this.listenPorts(apiNode)
|
2020-12-29 18:28:07 +08:00
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
if !isListening {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var errString = "the api node require at least one listening address"
|
|
|
|
|
|
remotelogs.Error("API_NODE", errString)
|
|
|
|
|
|
this.addStartIssue("config", errString, "请给当前API节点设置一个监听端口")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 19:27:27 +08:00
|
|
|
|
// 结束启动
|
|
|
|
|
|
this.isStarting = false
|
2022-09-07 15:57:31 +08:00
|
|
|
|
this.progress = nil
|
2021-11-21 19:27:27 +08:00
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
// 保持进程
|
|
|
|
|
|
select {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-15 11:16:58 +08:00
|
|
|
|
// Daemon 实现守护进程
|
2021-01-12 11:49:14 +08:00
|
|
|
|
func (this *APINode) Daemon() {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var path = os.TempDir() + "/" + teaconst.ProcessName + ".sock"
|
|
|
|
|
|
var isDebug = lists.ContainsString(os.Args, "debug")
|
2021-01-12 11:49:14 +08:00
|
|
|
|
for {
|
|
|
|
|
|
conn, err := net.DialTimeout("unix", path, 1*time.Second)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if isDebug {
|
|
|
|
|
|
log.Println("[DAEMON]starting ...")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试启动
|
|
|
|
|
|
err = func() error {
|
|
|
|
|
|
exe, err := os.Executable()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd := exec.Command(exe)
|
|
|
|
|
|
err = cmd.Start()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
err = cmd.Wait()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if isDebug {
|
|
|
|
|
|
log.Println("[DAEMON]", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_ = conn.Close()
|
|
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-15 11:16:58 +08:00
|
|
|
|
// InstallSystemService 安装系统服务
|
2021-01-12 11:49:14 +08:00
|
|
|
|
func (this *APINode) InstallSystemService() error {
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var shortName = teaconst.SystemdServiceName
|
2021-01-12 11:49:14 +08:00
|
|
|
|
|
|
|
|
|
|
exe, err := os.Executable()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
var manager = utils.NewServiceManager(shortName, teaconst.ProductName)
|
2021-01-12 11:49:14 +08:00
|
|
|
|
err = manager.Install(exe, []string{})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
// 启动RPC监听
|
|
|
|
|
|
func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) error {
|
|
|
|
|
|
var rpcServer *grpc.Server
|
2022-09-12 22:00:12 +08:00
|
|
|
|
var options = []grpc.ServerOption{
|
2023-03-18 22:44:04 +08:00
|
|
|
|
grpc.MaxRecvMsgSize(512 << 20),
|
|
|
|
|
|
grpc.MaxSendMsgSize(512 << 20),
|
2022-09-12 22:00:12 +08:00
|
|
|
|
grpc.UnaryInterceptor(this.unaryInterceptor),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-04 14:27:14 +08:00
|
|
|
|
if tlsConfig == nil {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "listening GRPC http://"+listener.Addr().String()+" ...")
|
2022-09-12 22:00:12 +08:00
|
|
|
|
rpcServer = grpc.NewServer(options...)
|
2020-10-04 14:27:14 +08:00
|
|
|
|
} else {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
logs.Println("[API_NODE]listening GRPC https://" + listener.Addr().String() + " ...")
|
2022-09-12 22:00:12 +08:00
|
|
|
|
options = append(options, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
|
|
|
|
|
rpcServer = grpc.NewServer(options...)
|
2020-10-04 14:27:14 +08:00
|
|
|
|
}
|
2021-05-25 15:49:13 +08:00
|
|
|
|
this.registerServices(rpcServer)
|
2020-10-04 14:27:14 +08:00
|
|
|
|
err := rpcServer.Serve(listener)
|
|
|
|
|
|
if err != nil {
|
2023-08-11 16:13:33 +08:00
|
|
|
|
return fmt.Errorf("[API_NODE]start rpc failed: %w", err)
|
2020-11-17 10:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-15 11:16:58 +08:00
|
|
|
|
// 检查数据库
|
|
|
|
|
|
func (this *APINode) checkDB() error {
|
2021-04-15 11:18:40 +08:00
|
|
|
|
logs.Println("[API_NODE]checking database connection ...")
|
2021-04-15 11:16:58 +08:00
|
|
|
|
|
2024-05-06 17:31:08 +08:00
|
|
|
|
// generate .db.yaml
|
|
|
|
|
|
{
|
|
|
|
|
|
data, err := os.ReadFile(Tea.ConfigFile("db.yaml"))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("could not find database config file 'db.yaml' (at " + Tea.ConfigFile("db.yaml") + ")")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
simpleConfig, err := configs.ParseSimpleDBConfig(data)
|
|
|
|
|
|
if err == nil && len(simpleConfig.Host) > 0 {
|
|
|
|
|
|
err = simpleConfig.GenerateOldConfig()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-05 11:14:51 +08:00
|
|
|
|
// lookup mysqld_safe process
|
|
|
|
|
|
go dbutils.FindMySQLPathAndRemember()
|
|
|
|
|
|
|
2021-04-15 11:16:58 +08:00
|
|
|
|
db, err := dbs.Default()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
// 第一次测试连接
|
|
|
|
|
|
_, err = db.Exec("SELECT 1")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
var errString = "check database connection failed: " + err.Error()
|
|
|
|
|
|
logs.Println("[API_NODE]" + errString)
|
|
|
|
|
|
this.addStartIssue("db", errString, this.dbIssueSuggestion(errString))
|
|
|
|
|
|
|
2023-03-17 16:02:37 +08:00
|
|
|
|
// 决定是否尝试启动本地的MySQL
|
|
|
|
|
|
if strings.Contains(err.Error(), "connection refused") {
|
|
|
|
|
|
config, _ := db.Config()
|
2023-05-20 16:58:14 +08:00
|
|
|
|
if config != nil && (strings.Contains(config.Dsn, "tcp(127.0.0.1:") || strings.Contains(config.Dsn, "tcp(localhost:")) && os.Getgid() == 0 /** ROOT 用户 **/ {
|
2023-07-05 11:14:51 +08:00
|
|
|
|
dbutils.StartLocalMySQL()
|
2023-03-17 16:02:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-08 15:13:24 +08:00
|
|
|
|
// 多次尝试
|
|
|
|
|
|
var maxTries = 600
|
|
|
|
|
|
if Tea.IsTesting() {
|
|
|
|
|
|
maxTries = 600
|
|
|
|
|
|
}
|
|
|
|
|
|
for i := 0; i <= maxTries; i++ {
|
2023-03-17 16:02:37 +08:00
|
|
|
|
_, err = db.Exec("SELECT 1")
|
2022-06-08 15:13:24 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
if i == maxTries-1 {
|
|
|
|
|
|
return err
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if i%10 == 0 { // 这让提示不会太多
|
2022-06-08 19:55:06 +08:00
|
|
|
|
logs.Println("[API_NODE]check database connection failed: " + err.Error() + ", reconnecting to database ...")
|
2022-06-08 15:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
time.Sleep(1 * time.Second)
|
2021-04-15 11:16:58 +08:00
|
|
|
|
}
|
2022-06-08 15:13:24 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
logs.Println("[API_NODE]database connected")
|
|
|
|
|
|
return nil
|
2021-04-15 11:16:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-17 10:26:31 +08:00
|
|
|
|
// 自动升级
|
|
|
|
|
|
func (this *APINode) autoUpgrade() error {
|
2020-11-17 15:42:42 +08:00
|
|
|
|
if Tea.IsTesting() {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行SQL
|
2024-05-06 17:31:08 +08:00
|
|
|
|
config, err := configs.LoadDBConfig()
|
2020-11-17 15:42:42 +08:00
|
|
|
|
if err != nil {
|
2023-08-11 16:13:33 +08:00
|
|
|
|
return fmt.Errorf("decode database config failed: %w", err)
|
2020-11-17 15:42:42 +08:00
|
|
|
|
}
|
2022-03-14 16:06:25 +08:00
|
|
|
|
var dbConfig = config.DBs[Tea.Env]
|
2020-11-17 15:42:42 +08:00
|
|
|
|
db, err := dbs.NewInstanceFromConfig(dbConfig)
|
|
|
|
|
|
if err != nil {
|
2023-08-11 16:13:33 +08:00
|
|
|
|
return fmt.Errorf("load database failed: %w", err)
|
2020-11-17 10:26:31 +08:00
|
|
|
|
}
|
2022-04-08 14:15:45 +08:00
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = db.Close()
|
|
|
|
|
|
}()
|
2020-11-17 10:26:31 +08:00
|
|
|
|
one, err := db.FindOne("SELECT version FROM edgeVersions LIMIT 1")
|
|
|
|
|
|
if err != nil {
|
2023-08-11 16:13:33 +08:00
|
|
|
|
return fmt.Errorf("query version failed: %w", err)
|
2020-11-17 10:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
if one != nil {
|
|
|
|
|
|
// 如果是同样的版本,则直接认为是最新版本
|
2022-03-14 16:06:25 +08:00
|
|
|
|
var version = one.GetString("version")
|
2024-04-14 11:47:10 +08:00
|
|
|
|
if setup.CompareVersion(version, setup.ComposeSQLVersion()) >= 0 {
|
2020-11-17 10:26:31 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2020-10-04 14:27:14 +08:00
|
|
|
|
}
|
2021-07-21 08:08:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 不使用remotelog(),因为此时还没有启动完成
|
2020-11-17 10:26:31 +08:00
|
|
|
|
logs.Println("[API_NODE]upgrade database starting ...")
|
2021-08-05 19:53:54 +08:00
|
|
|
|
err = setup.NewSQLExecutor(dbConfig).Run(false)
|
2020-11-17 10:26:31 +08:00
|
|
|
|
if err != nil {
|
2023-08-11 16:13:33 +08:00
|
|
|
|
return fmt.Errorf("execute sql failed: %w", err)
|
2020-11-17 10:26:31 +08:00
|
|
|
|
}
|
2021-07-21 08:08:31 +08:00
|
|
|
|
// 不使用remotelog
|
2020-11-17 10:26:31 +08:00
|
|
|
|
logs.Println("[API_NODE]upgrade database done")
|
2020-10-04 14:27:14 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2021-01-01 20:49:09 +08:00
|
|
|
|
|
2021-03-25 11:48:45 +08:00
|
|
|
|
// 自动设置数据库
|
|
|
|
|
|
func (this *APINode) setupDB() error {
|
|
|
|
|
|
db, err := dbs.Default()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-26 12:16:08 +08:00
|
|
|
|
// 检查是否为root用户
|
|
|
|
|
|
config, _ := db.Config()
|
|
|
|
|
|
if config == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
dsnConfig, err := mysql.ParseDSN(config.Dsn)
|
|
|
|
|
|
if err != nil || dsnConfig == nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if dsnConfig.User != "root" {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-25 20:34:19 +08:00
|
|
|
|
// 设置Innodb事务提交模式
|
2021-03-25 11:48:45 +08:00
|
|
|
|
{
|
2022-09-25 20:34:19 +08:00
|
|
|
|
result, err := db.FindOne("SHOW VARIABLES WHERE variable_name='innodb_flush_log_at_trx_commit'")
|
|
|
|
|
|
if err == nil && result != nil {
|
|
|
|
|
|
var oldValue = result.GetInt("Value")
|
|
|
|
|
|
if oldValue == 1 {
|
|
|
|
|
|
_, _ = db.Exec("SET GLOBAL innodb_flush_log_at_trx_commit=2")
|
2021-03-25 11:48:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-25 20:34:19 +08:00
|
|
|
|
// 调整预处理语句数量
|
|
|
|
|
|
_ = dbutils.SetGlobalVarMin(db, "max_prepared_stmt_count", 65535)
|
|
|
|
|
|
|
2022-09-16 10:39:17 +08:00
|
|
|
|
// 调整binlog过期时间
|
|
|
|
|
|
{
|
|
|
|
|
|
const binlogExpireDays = 7
|
|
|
|
|
|
|
|
|
|
|
|
version, err := db.FindCol(0, "SELECT VERSION()")
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
var versionString = types.String(version)
|
|
|
|
|
|
if strings.HasPrefix(versionString, "8.") {
|
2022-09-25 20:34:19 +08:00
|
|
|
|
_ = dbutils.SetGlobalVarMax(db, "binlog_expire_logs_seconds", binlogExpireDays*86400)
|
2022-09-16 10:39:17 +08:00
|
|
|
|
} else if strings.HasPrefix(versionString, "5.") {
|
2022-09-25 20:34:19 +08:00
|
|
|
|
_ = dbutils.SetGlobalVarMax(db, "expire_logs_days", binlogExpireDays)
|
2022-09-16 10:39:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-25 20:34:19 +08:00
|
|
|
|
// 设置binlog_cache_size
|
|
|
|
|
|
_ = dbutils.SetGlobalVarMin(db, "binlog_cache_size", 1*1024*1024)
|
|
|
|
|
|
|
|
|
|
|
|
// 设置binlog_stmt_cache_size
|
|
|
|
|
|
_ = dbutils.SetGlobalVarMin(db, "binlog_stmt_cache_size", 1*1024*1024)
|
|
|
|
|
|
|
|
|
|
|
|
// 设置thread_cache_size
|
|
|
|
|
|
_ = dbutils.SetGlobalVarMin(db, "thread_cache_size", 32)
|
|
|
|
|
|
|
2021-03-25 11:48:45 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
// 启动端口
|
|
|
|
|
|
func (this *APINode) listenPorts(apiNode *models.APINode) (isListening bool) {
|
|
|
|
|
|
// HTTP
|
|
|
|
|
|
httpConfig, err := apiNode.DecodeHTTP()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "decode http config: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-04-19 19:35:50 +08:00
|
|
|
|
var ports = []int{}
|
2021-01-01 20:49:09 +08:00
|
|
|
|
isListening = false
|
|
|
|
|
|
if httpConfig != nil && httpConfig.IsOn && len(httpConfig.Listen) > 0 {
|
|
|
|
|
|
for _, listen := range httpConfig.Listen {
|
|
|
|
|
|
for _, addr := range listen.Addresses() {
|
2022-04-19 19:35:50 +08:00
|
|
|
|
// 收集Port
|
2022-04-19 19:48:37 +08:00
|
|
|
|
_, portString, _ := net.SplitHostPort(addr)
|
|
|
|
|
|
var port = types.Int(portString)
|
|
|
|
|
|
if port > 0 && !lists.ContainsInt(ports, port) {
|
|
|
|
|
|
ports = append(ports, port)
|
2022-04-19 19:35:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
|
|
|
|
if err != nil {
|
2021-07-21 09:01:37 +08:00
|
|
|
|
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
2021-07-06 15:19:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 试着只监听端口
|
|
|
|
|
|
_, port, err := net.SplitHostPort(addr)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
|
|
|
|
|
|
listener, err = net.Listen("tcp", ":"+port)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
|
2021-01-01 20:49:09 +08:00
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
err := this.listenRPC(listener, nil)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
})
|
2021-01-01 20:49:09 +08:00
|
|
|
|
isListening = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HTTPS
|
2021-08-22 11:35:33 +08:00
|
|
|
|
httpsConfig, err := apiNode.DecodeHTTPS(nil, nil)
|
2021-01-01 20:49:09 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "decode https config: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if httpsConfig != nil &&
|
|
|
|
|
|
httpsConfig.IsOn &&
|
|
|
|
|
|
len(httpsConfig.Listen) > 0 &&
|
|
|
|
|
|
httpsConfig.SSLPolicy != nil &&
|
|
|
|
|
|
httpsConfig.SSLPolicy.IsOn &&
|
|
|
|
|
|
len(httpsConfig.SSLPolicy.Certs) > 0 {
|
|
|
|
|
|
certs := []tls.Certificate{}
|
|
|
|
|
|
for _, cert := range httpsConfig.SSLPolicy.Certs {
|
|
|
|
|
|
certs = append(certs, *cert.CertObject())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, listen := range httpsConfig.Listen {
|
|
|
|
|
|
for _, addr := range listen.Addresses() {
|
2022-04-19 19:35:50 +08:00
|
|
|
|
// 收集Port
|
2022-04-19 19:48:37 +08:00
|
|
|
|
_, portString, _ := net.SplitHostPort(addr)
|
|
|
|
|
|
var port = types.Int(portString)
|
|
|
|
|
|
if port > 0 && !lists.ContainsInt(ports, port) {
|
|
|
|
|
|
ports = append(ports, port)
|
2022-04-19 19:35:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
|
|
|
|
if err != nil {
|
2021-07-21 09:01:37 +08:00
|
|
|
|
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
2021-07-06 15:19:39 +08:00
|
|
|
|
// 试着只监听端口
|
|
|
|
|
|
_, port, err := net.SplitHostPort(addr)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ...")
|
|
|
|
|
|
listener, err = net.Listen("tcp", ":"+port)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening ':"+port+"' failed: "+err.Error())
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
remotelogs.Println("API_NODE", "retry listening port ':"+port+"' only ok")
|
2021-01-01 20:49:09 +08:00
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
err := this.listenRPC(listener, &tls.Config{
|
|
|
|
|
|
Certificates: certs,
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
})
|
2021-01-01 20:49:09 +08:00
|
|
|
|
isListening = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Rest HTTP
|
|
|
|
|
|
restHTTPConfig, err := apiNode.DecodeRestHTTP()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "decode REST http config: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if restHTTPConfig != nil && restHTTPConfig.IsOn && len(restHTTPConfig.Listen) > 0 {
|
|
|
|
|
|
for _, listen := range restHTTPConfig.Listen {
|
|
|
|
|
|
for _, addr := range listen.Addresses() {
|
2022-04-19 19:35:50 +08:00
|
|
|
|
// 收集Port
|
2022-04-19 19:48:37 +08:00
|
|
|
|
_, portString, _ := net.SplitHostPort(addr)
|
|
|
|
|
|
var port = types.Int(portString)
|
|
|
|
|
|
if port > 0 && !lists.ContainsInt(ports, port) {
|
|
|
|
|
|
ports = append(ports, port)
|
2022-04-19 19:35:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening REST 'http://"+addr+"' failed: "+err.Error())
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "listening REST http://"+addr+" ...")
|
|
|
|
|
|
server := &RestServer{}
|
|
|
|
|
|
err := server.Listen(listener)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening REST 'http://"+addr+"' failed: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
})
|
2021-01-01 20:49:09 +08:00
|
|
|
|
isListening = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Rest HTTPS
|
2021-08-22 11:35:33 +08:00
|
|
|
|
restHTTPSConfig, err := apiNode.DecodeRestHTTPS(nil, nil)
|
2021-01-01 20:49:09 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "decode REST https config: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if restHTTPSConfig != nil &&
|
|
|
|
|
|
restHTTPSConfig.IsOn &&
|
|
|
|
|
|
len(restHTTPSConfig.Listen) > 0 &&
|
|
|
|
|
|
restHTTPSConfig.SSLPolicy != nil &&
|
|
|
|
|
|
restHTTPSConfig.SSLPolicy.IsOn &&
|
|
|
|
|
|
len(restHTTPSConfig.SSLPolicy.Certs) > 0 {
|
|
|
|
|
|
for _, listen := range restHTTPSConfig.Listen {
|
|
|
|
|
|
for _, addr := range listen.Addresses() {
|
2022-04-19 19:35:50 +08:00
|
|
|
|
// 收集Port
|
2022-04-19 19:48:37 +08:00
|
|
|
|
_, portString, _ := net.SplitHostPort(addr)
|
|
|
|
|
|
var port = types.Int(portString)
|
|
|
|
|
|
if port > 0 && !lists.ContainsInt(ports, port) {
|
|
|
|
|
|
ports = append(ports, port)
|
2022-04-19 19:35:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening REST 'https://"+addr+"' failed: "+err.Error())
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
2021-01-01 20:49:09 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "listening REST https://"+addr+" ...")
|
|
|
|
|
|
server := &RestServer{}
|
|
|
|
|
|
|
|
|
|
|
|
certs := []tls.Certificate{}
|
|
|
|
|
|
for _, cert := range httpsConfig.SSLPolicy.Certs {
|
|
|
|
|
|
certs = append(certs, *cert.CertObject())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := server.ListenHTTPS(listener, &tls.Config{
|
|
|
|
|
|
Certificates: certs,
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "listening REST 'https://"+addr+"' failed: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
})
|
2021-01-01 20:49:09 +08:00
|
|
|
|
isListening = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-19 19:35:50 +08:00
|
|
|
|
// add to local firewall
|
|
|
|
|
|
if len(ports) > 0 {
|
2022-09-04 06:36:22 +08:00
|
|
|
|
go utils.AddPortsToFirewall(ports)
|
2022-04-19 19:35:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-01 20:49:09 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-01-12 11:49:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听本地sock
|
|
|
|
|
|
func (this *APINode) listenSock() error {
|
2021-07-25 17:46:47 +08:00
|
|
|
|
// 检查是否在运行
|
|
|
|
|
|
if this.sock.IsListening() {
|
|
|
|
|
|
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
|
2021-01-12 11:49:14 +08:00
|
|
|
|
} else {
|
2021-07-25 17:46:47 +08:00
|
|
|
|
return errors.New("error: the process is already running")
|
2021-01-12 11:49:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-25 17:46:47 +08:00
|
|
|
|
// 启动监听
|
2021-12-14 10:49:29 +08:00
|
|
|
|
goman.New(func() {
|
2021-07-25 17:46:47 +08:00
|
|
|
|
this.sock.OnCommand(func(cmd *gosock.Command) {
|
|
|
|
|
|
switch cmd.Code {
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "pid": // 查询PID
|
2021-07-25 17:46:47 +08:00
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Code: "pid",
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{
|
2021-07-25 17:46:47 +08:00
|
|
|
|
"pid": os.Getpid(),
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "info": // 进程相关信息
|
2021-11-04 11:15:22 +08:00
|
|
|
|
exePath, _ := os.Executable()
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Code: "info",
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{
|
2021-11-04 11:15:22 +08:00
|
|
|
|
"pid": os.Getpid(),
|
|
|
|
|
|
"version": teaconst.Version,
|
|
|
|
|
|
"path": exePath,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "stop": // 停止
|
2021-07-25 17:46:47 +08:00
|
|
|
|
_ = cmd.ReplyOk()
|
|
|
|
|
|
|
|
|
|
|
|
// 退出主进程
|
|
|
|
|
|
events.Notify(events.EventQuit)
|
|
|
|
|
|
os.Exit(0)
|
2021-11-21 19:27:27 +08:00
|
|
|
|
case "starting": // 是否正在启动
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Code: "starting",
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{
|
2021-11-21 19:27:27 +08:00
|
|
|
|
"isStarting": this.isStarting,
|
2022-09-07 15:57:31 +08:00
|
|
|
|
"progress": this.progress,
|
2021-11-21 19:27:27 +08:00
|
|
|
|
},
|
|
|
|
|
|
})
|
2021-12-14 10:49:29 +08:00
|
|
|
|
case "goman":
|
|
|
|
|
|
var posMap = map[string]maps.Map{} // file#line => Map
|
|
|
|
|
|
for _, instance := range goman.List() {
|
|
|
|
|
|
var pos = instance.File + "#" + types.String(instance.Line)
|
|
|
|
|
|
m, ok := posMap[pos]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
m["count"] = m["count"].(int) + 1
|
|
|
|
|
|
} else {
|
|
|
|
|
|
m = maps.Map{
|
|
|
|
|
|
"pos": pos,
|
|
|
|
|
|
"count": 1,
|
|
|
|
|
|
}
|
|
|
|
|
|
posMap[pos] = m
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = []maps.Map{}
|
|
|
|
|
|
for _, m := range posMap {
|
|
|
|
|
|
result = append(result, m)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
|
|
|
|
return result[i]["count"].(int) > result[j]["count"].(int)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{
|
2021-12-14 10:49:29 +08:00
|
|
|
|
"total": runtime.NumGoroutine(),
|
|
|
|
|
|
"result": result,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "debug": // 进入|取消调试模式
|
2022-01-19 16:53:52 +08:00
|
|
|
|
teaconst.Debug = !teaconst.Debug
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{"debug": teaconst.Debug},
|
2022-01-19 16:53:52 +08:00
|
|
|
|
})
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "db.stmt.prepare": // 显示prepared的语句
|
2022-04-08 15:09:33 +08:00
|
|
|
|
dbs.ShowPreparedStatements = !dbs.ShowPreparedStatements
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{"isOn": dbs.ShowPreparedStatements},
|
2022-04-08 15:09:33 +08:00
|
|
|
|
})
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "db.stmt.count": // 查询prepared语句数量
|
2022-04-08 15:09:33 +08:00
|
|
|
|
db, _ := dbs.Default()
|
|
|
|
|
|
if db != nil {
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{"count": db.StmtManager().Len()},
|
2022-04-08 15:09:33 +08:00
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{"count": 0},
|
2022-04-08 15:09:33 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2022-09-07 15:57:31 +08:00
|
|
|
|
case "instance": // 获取实例代号
|
2022-07-21 19:21:11 +08:00
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
2022-09-07 15:57:31 +08:00
|
|
|
|
Params: map[string]any{
|
2022-07-21 19:21:11 +08:00
|
|
|
|
"code": teaconst.InstanceCode,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2023-03-19 21:24:24 +08:00
|
|
|
|
case "lookupToken":
|
|
|
|
|
|
var role = maps.NewMap(cmd.Params).GetString("role")
|
|
|
|
|
|
switch role {
|
|
|
|
|
|
case "admin", "user", "api":
|
|
|
|
|
|
tokens, err := models.SharedApiTokenDAO.FindAllEnabledAPITokens(nil, role)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Params: map[string]any{
|
|
|
|
|
|
"isOk": false,
|
|
|
|
|
|
"err": err.Error(),
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
var tokenMaps = []maps.Map{}
|
|
|
|
|
|
for _, token := range tokens {
|
|
|
|
|
|
tokenMaps = append(tokenMaps, maps.Map{
|
|
|
|
|
|
"nodeId": token.NodeId,
|
|
|
|
|
|
"secret": token.Secret,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Params: map[string]any{
|
|
|
|
|
|
"isOk": true,
|
|
|
|
|
|
"tokens": tokenMaps,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
_ = cmd.Reply(&gosock.Command{
|
|
|
|
|
|
Params: map[string]any{
|
|
|
|
|
|
"isOk": false,
|
|
|
|
|
|
"err": "unsupported role '" + role + "'",
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2021-01-12 11:49:14 +08:00
|
|
|
|
}
|
2021-07-25 17:46:47 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
err := this.sock.Listen()
|
|
|
|
|
|
if err != nil {
|
2022-08-15 15:17:09 +08:00
|
|
|
|
remotelogs.Println("API_NODE", err.Error())
|
2021-01-12 11:49:14 +08:00
|
|
|
|
}
|
2021-12-14 10:49:29 +08:00
|
|
|
|
})
|
2021-01-12 11:49:14 +08:00
|
|
|
|
|
2021-07-25 17:46:47 +08:00
|
|
|
|
events.On(events.EventQuit, func() {
|
2022-08-15 15:17:09 +08:00
|
|
|
|
remotelogs.Println("API_NODE", "quit unix sock")
|
2021-07-25 17:46:47 +08:00
|
|
|
|
_ = this.sock.Close()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2021-01-12 11:49:14 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2022-01-19 16:53:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 服务过滤器
|
2022-09-07 15:57:31 +08:00
|
|
|
|
func (this *APINode) unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
2022-01-19 16:53:52 +08:00
|
|
|
|
if teaconst.Debug {
|
|
|
|
|
|
var before = time.Now()
|
|
|
|
|
|
var traceCtx = rpc.NewContext(ctx)
|
|
|
|
|
|
resp, err = handler(traceCtx, req)
|
|
|
|
|
|
|
|
|
|
|
|
var costMs = time.Since(before).Seconds() * 1000
|
|
|
|
|
|
statErr := models.SharedAPIMethodStatDAO.CreateStat(nil, info.FullMethod, "", costMs)
|
|
|
|
|
|
if statErr != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "create method stat failed: "+statErr.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var tagMap = traceCtx.TagMap()
|
|
|
|
|
|
for tag, tagCostMs := range tagMap {
|
|
|
|
|
|
statErr = models.SharedAPIMethodStatDAO.CreateStat(nil, info.FullMethod, tag, tagCostMs)
|
|
|
|
|
|
if statErr != nil {
|
|
|
|
|
|
remotelogs.Error("API_NODE", "create method stat failed: "+statErr.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-03-31 15:30:04 +08:00
|
|
|
|
result, err := handler(ctx, req)
|
|
|
|
|
|
if err != nil {
|
2023-06-16 08:17:00 +08:00
|
|
|
|
statusErr, ok := status.FromError(err)
|
|
|
|
|
|
if ok {
|
2023-07-05 11:14:51 +08:00
|
|
|
|
err = status.Error(statusErr.Code(), "'"+info.FullMethod+"()' says: "+err.Error())
|
2023-06-16 08:17:00 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
err = errors.New("'" + info.FullMethod + "()' says: " + err.Error())
|
|
|
|
|
|
}
|
2022-03-31 15:30:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
return result, err
|
2022-01-19 16:53:52 +08:00
|
|
|
|
}
|
2022-06-08 15:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加启动相关的Issue
|
|
|
|
|
|
func (this *APINode) addStartIssue(code string, message string, suggestion string) {
|
|
|
|
|
|
this.issues = append(this.issues, NewStartIssue(code, message, suggestion))
|
|
|
|
|
|
this.saveIssues()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 增加数据库建议
|
|
|
|
|
|
func (this *APINode) dbIssueSuggestion(errString string) string {
|
|
|
|
|
|
// 数据库配置
|
|
|
|
|
|
db, err := dbs.Default()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
config, err := db.Config()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var dsn = config.Dsn
|
|
|
|
|
|
dsnConfig, err := mysql.ParseDSN(dsn)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
var addr = dsnConfig.Addr
|
|
|
|
|
|
|
|
|
|
|
|
// 配置文件位置
|
|
|
|
|
|
var dbConfigPath = Tea.ConfigFile("db.yaml")
|
|
|
|
|
|
|
|
|
|
|
|
// 连接被拒绝
|
|
|
|
|
|
if strings.Contains(errString, "connection refused") {
|
|
|
|
|
|
// 本机
|
|
|
|
|
|
if strings.HasPrefix(addr, "127.0.0.1:") || strings.HasPrefix(addr, "localhost:") {
|
|
|
|
|
|
return "试图连接到数据库被拒绝,请检查:1)本地数据库服务是否已经启动;2)数据库IP和端口(" + addr + ")是否正确;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return "试图连接到数据库被拒绝,请检查:1)数据库服务是否已经启动;2)数据库IP和端口(" + addr + ")是否正确;3)防火墙设置;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 权限错误
|
2022-06-08 19:55:06 +08:00
|
|
|
|
if strings.Contains(errString, "Error 1045") || strings.Contains(errString, "Error 1044") {
|
|
|
|
|
|
return "使用的用户和密码没有权限连接到指定数据库,请检查:1)数据库配置文件中的用户名(" + dsnConfig.User + ")和密码(" + dsnConfig.Passwd + ")是否正确;2)使用的用户是否已经在数据库中设置了正确的权限;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
|
2022-06-08 15:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数据库名称错误
|
|
|
|
|
|
if strings.Contains(errString, "Error 1049") {
|
|
|
|
|
|
return "数据库名称配置错误,请检查:数据库配置文件中数据库名称(" + dsnConfig.DBName + ")是否正确;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存issues
|
|
|
|
|
|
func (this *APINode) saveIssues() {
|
|
|
|
|
|
issuesJSON, err := json.Marshal(this.issues)
|
|
|
|
|
|
if err == nil {
|
2022-08-04 11:41:42 +08:00
|
|
|
|
_ = os.WriteFile(this.issuesFile, issuesJSON, 0666)
|
2022-06-08 15:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-09-07 15:57:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置启动进度
|
|
|
|
|
|
func (this *APINode) setProgress(name, description string) {
|
|
|
|
|
|
this.progress = &utils.Progress{
|
|
|
|
|
|
Name: name,
|
|
|
|
|
|
Description: description,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-09-09 10:28:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置时区
|
|
|
|
|
|
func (this *APINode) setupTimeZone() {
|
|
|
|
|
|
config, err := models.SharedSysSettingDAO.ReadAdminUIConfig(nil, nil)
|
|
|
|
|
|
if err == nil && config != nil {
|
|
|
|
|
|
if len(config.TimeZone) == 0 {
|
|
|
|
|
|
config.TimeZone = nodeconfigs.DefaultTimeZoneLocation
|
|
|
|
|
|
}
|
|
|
|
|
|
location, err := time.LoadLocation(config.TimeZone)
|
|
|
|
|
|
if err == nil && time.Local != location {
|
|
|
|
|
|
time.Local = location
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-13 17:14:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听一些信号
|
|
|
|
|
|
func (this *APINode) listenSignals() {
|
|
|
|
|
|
var queue = make(chan os.Signal, 8)
|
|
|
|
|
|
signal.Notify(queue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
|
|
|
|
|
|
goman.New(func() {
|
|
|
|
|
|
for range queue {
|
|
|
|
|
|
events.Notify(events.EventQuit)
|
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|