1.4.5.2
This commit is contained in:
984
EdgeAPI/internal/nodes/api_node.go
Normal file
984
EdgeAPI/internal/nodes/api_node.go
Normal file
@@ -0,0 +1,984 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/events"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/setup"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/status"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
// grpc decompression
|
||||
_ "google.golang.org/grpc/encoding/gzip"
|
||||
)
|
||||
|
||||
var sharedAPIConfig *configs.APIConfig = nil
|
||||
|
||||
type APINode struct {
|
||||
serviceInstanceMap map[string]any
|
||||
serviceInstanceLocker sync.Mutex
|
||||
|
||||
sock *gosock.Sock
|
||||
|
||||
isStarting bool
|
||||
|
||||
issues []*StartIssue
|
||||
issuesFile string
|
||||
|
||||
progress *utils.Progress
|
||||
}
|
||||
|
||||
func NewAPINode() *APINode {
|
||||
return &APINode{
|
||||
serviceInstanceMap: map[string]any{},
|
||||
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
||||
|
||||
issues: []*StartIssue{},
|
||||
issuesFile: Tea.LogFile("issues.log"),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *APINode) Start() {
|
||||
this.isStarting = true
|
||||
|
||||
logs.Println("[API_NODE]start api node, pid: " + strconv.Itoa(os.Getpid()))
|
||||
|
||||
// 保存启动过程中的问题,以便于查看
|
||||
defer func() {
|
||||
this.saveIssues()
|
||||
}()
|
||||
|
||||
// 本地Sock
|
||||
logs.Println("[API_NODE]listening sock ...")
|
||||
err := this.listenSock()
|
||||
if err != nil {
|
||||
var errString = "start local sock failed: " + err.Error()
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("sock", errString, "")
|
||||
return
|
||||
}
|
||||
|
||||
// 监听信号
|
||||
this.listenSignals()
|
||||
|
||||
// 启动IP库
|
||||
this.setProgress("IP_LIBRARY", "开始初始化IP库")
|
||||
remotelogs.Println("API_NODE", "initializing ip library ...")
|
||||
err = iplibrary.InitDefault()
|
||||
if err != nil {
|
||||
// IP库初始化失败不应该阻止启动,只记录错误
|
||||
remotelogs.Error("API_NODE", "initialize ip library failed: "+err.Error())
|
||||
logs.Println("[API_NODE]WARNING: initialize ip library failed: " + err.Error() + ", will continue with default library")
|
||||
} else {
|
||||
remotelogs.Println("API_NODE", "ip library initialized successfully")
|
||||
}
|
||||
|
||||
// 检查数据库连接
|
||||
this.setProgress("DATABASE", "正在检查数据库连接")
|
||||
err = this.checkDB()
|
||||
if err != nil {
|
||||
var errString = "check database connection failed: " + err.Error()
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 自动升级
|
||||
logs.Println("[API_NODE]auto upgrading ...")
|
||||
this.setProgress("DATABASE", "正在升级数据库")
|
||||
err = this.autoUpgrade()
|
||||
if err != nil {
|
||||
var errString = "auto upgrade failed: " + err.Error()
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("db", errString, this.dbIssueSuggestion(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 自动设置数据库
|
||||
this.setProgress("DATABASE", "正在设置数据库")
|
||||
logs.Println("[API_NODE]setup database ...")
|
||||
err = this.setupDB()
|
||||
if err != nil {
|
||||
logs.Println("[API_NODE]setup database '" + err.Error() + "'")
|
||||
|
||||
// 不阻断执行
|
||||
}
|
||||
|
||||
// 数据库通知启动
|
||||
this.setProgress("DATABASE", "正在建立数据库模型")
|
||||
logs.Println("[API_NODE]notify ready ...")
|
||||
this.processTableNames()
|
||||
dbs.NotifyReady()
|
||||
|
||||
// 设置时区
|
||||
this.setProgress("TIMEZONE", "正在设置时区")
|
||||
this.setupTimeZone()
|
||||
|
||||
// 读取配置
|
||||
this.setProgress("DATABASE", "正在加载API配置")
|
||||
logs.Println("[API_NODE]reading api config ...")
|
||||
config, err := configs.SharedAPIConfig()
|
||||
if err != nil {
|
||||
var errString = "read api config failed: " + err.Error()
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("config", errString, "")
|
||||
return
|
||||
}
|
||||
sharedAPIConfig = config
|
||||
|
||||
// 校验
|
||||
apiNode, err := models.SharedAPINodeDAO.FindEnabledAPINodeWithUniqueIdAndSecret(nil, config.NodeId, config.Secret)
|
||||
if err != nil {
|
||||
var errString = "start failed: read api node from database failed: " + err.Error()
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("db", errString, "")
|
||||
return
|
||||
}
|
||||
if apiNode == nil {
|
||||
var errString = "can not start node, wrong 'nodeId' or 'secret'"
|
||||
logs.Println("[API_NODE]" + errString)
|
||||
this.addStartIssue("config", errString, "请在api.yaml配置文件中填写正确的`nodeId`和`secret`,如果数据库或者管理节点或API节点是从别的服务器迁移过来的,请将老的系统配置拷贝到当前节点配置下")
|
||||
return
|
||||
}
|
||||
config.SetNumberId(int64(apiNode.Id))
|
||||
|
||||
// 清除上一次启动错误
|
||||
// 这个错误文件可能不存在,不需要处理错误
|
||||
_ = os.Remove(this.issuesFile)
|
||||
|
||||
// 设置rlimit
|
||||
_ = utils.SetRLimit(1024 * 1024)
|
||||
|
||||
// 状态变更计时器
|
||||
goman.New(func() {
|
||||
NewNodeStatusExecutor().Listen()
|
||||
})
|
||||
|
||||
// 访问日志存储管理器
|
||||
this.setProgress("ACCESS_LOG_STORAGES", "正在启动访问日志存储器")
|
||||
this.startAccessLogStorages()
|
||||
|
||||
// 监听RPC服务
|
||||
this.setProgress("LISTEN_PORT", "正在启动监听端口")
|
||||
remotelogs.Println("API_NODE", "starting RPC server ...")
|
||||
|
||||
var isListening = this.listenPorts(apiNode)
|
||||
|
||||
if !isListening {
|
||||
var errString = "the api node require at least one listening address"
|
||||
remotelogs.Error("API_NODE", errString)
|
||||
this.addStartIssue("config", errString, "请给当前API节点设置一个监听端口")
|
||||
return
|
||||
}
|
||||
|
||||
// 结束启动
|
||||
this.isStarting = false
|
||||
this.progress = nil
|
||||
|
||||
// 保持进程
|
||||
select {}
|
||||
}
|
||||
|
||||
// Daemon 实现守护进程
|
||||
func (this *APINode) Daemon() {
|
||||
var path = os.TempDir() + "/" + teaconst.ProcessName + ".sock"
|
||||
var isDebug = lists.ContainsString(os.Args, "debug")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InstallSystemService 安装系统服务
|
||||
func (this *APINode) InstallSystemService() error {
|
||||
var shortName = teaconst.SystemdServiceName
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manager = utils.NewServiceManager(shortName, teaconst.ProductName)
|
||||
err = manager.Install(exe, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 启动RPC监听
|
||||
func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) error {
|
||||
var rpcServer *grpc.Server
|
||||
var options = []grpc.ServerOption{
|
||||
grpc.MaxRecvMsgSize(512 << 20),
|
||||
grpc.MaxSendMsgSize(512 << 20),
|
||||
grpc.UnaryInterceptor(this.unaryInterceptor),
|
||||
}
|
||||
|
||||
if tlsConfig == nil {
|
||||
remotelogs.Println("API_NODE", "listening GRPC http://"+listener.Addr().String()+" ...")
|
||||
rpcServer = grpc.NewServer(options...)
|
||||
} else {
|
||||
logs.Println("[API_NODE]listening GRPC https://" + listener.Addr().String() + " ...")
|
||||
options = append(options, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
||||
rpcServer = grpc.NewServer(options...)
|
||||
}
|
||||
this.registerServices(rpcServer)
|
||||
err := rpcServer.Serve(listener)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[API_NODE]start rpc failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查数据库
|
||||
func (this *APINode) checkDB() error {
|
||||
logs.Println("[API_NODE]checking database connection ...")
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lookup mysqld_safe process
|
||||
go dbutils.FindMySQLPathAndRemember()
|
||||
|
||||
db, err := dbs.Default()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 第一次测试连接
|
||||
_, 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))
|
||||
|
||||
// 决定是否尝试启动本地的MySQL
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
config, _ := db.Config()
|
||||
if config != nil && (strings.Contains(config.Dsn, "tcp(127.0.0.1:") || strings.Contains(config.Dsn, "tcp(localhost:")) && os.Getgid() == 0 /** ROOT 用户 **/ {
|
||||
dbutils.StartLocalMySQL()
|
||||
}
|
||||
}
|
||||
|
||||
// 多次尝试
|
||||
var maxTries = 600
|
||||
if Tea.IsTesting() {
|
||||
maxTries = 600
|
||||
}
|
||||
for i := 0; i <= maxTries; i++ {
|
||||
_, err = db.Exec("SELECT 1")
|
||||
if err != nil {
|
||||
if i == maxTries-1 {
|
||||
return err
|
||||
} else {
|
||||
if i%10 == 0 { // 这让提示不会太多
|
||||
logs.Println("[API_NODE]check database connection failed: " + err.Error() + ", reconnecting to database ...")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
} else {
|
||||
logs.Println("[API_NODE]database connected")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 自动升级
|
||||
func (this *APINode) autoUpgrade() error {
|
||||
if Tea.IsTesting() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行SQL
|
||||
config, err := configs.LoadDBConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode database config failed: %w", err)
|
||||
}
|
||||
var dbConfig = config.DBs[Tea.Env]
|
||||
db, err := dbs.NewInstanceFromConfig(dbConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load database failed: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = db.Close()
|
||||
}()
|
||||
one, err := db.FindOne("SELECT version FROM edgeVersions LIMIT 1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("query version failed: %w", err)
|
||||
}
|
||||
if one != nil {
|
||||
// 如果是同样的版本,则直接认为是最新版本
|
||||
var version = one.GetString("version")
|
||||
if setup.CompareVersion(version, setup.ComposeSQLVersion()) >= 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 不使用remotelog(),因为此时还没有启动完成
|
||||
logs.Println("[API_NODE]upgrade database starting ...")
|
||||
err = setup.NewSQLExecutor(dbConfig).Run(false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execute sql failed: %w", err)
|
||||
}
|
||||
// 不使用remotelog
|
||||
logs.Println("[API_NODE]upgrade database done")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 自动设置数据库
|
||||
func (this *APINode) setupDB() error {
|
||||
db, err := dbs.Default()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查是否为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
|
||||
}
|
||||
|
||||
// 设置Innodb事务提交模式
|
||||
{
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调整预处理语句数量
|
||||
_ = dbutils.SetGlobalVarMin(db, "max_prepared_stmt_count", 65535)
|
||||
|
||||
// 调整binlog过期时间
|
||||
{
|
||||
const binlogExpireDays = 7
|
||||
|
||||
version, err := db.FindCol(0, "SELECT VERSION()")
|
||||
if err == nil {
|
||||
var versionString = types.String(version)
|
||||
if strings.HasPrefix(versionString, "8.") {
|
||||
_ = dbutils.SetGlobalVarMax(db, "binlog_expire_logs_seconds", binlogExpireDays*86400)
|
||||
} else if strings.HasPrefix(versionString, "5.") {
|
||||
_ = dbutils.SetGlobalVarMax(db, "expire_logs_days", binlogExpireDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置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)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理表名兼容
|
||||
func (this *APINode) processTableNames() {
|
||||
dbs.OnDAOInitError(func(dao dbs.DAOInterface, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errors.Is(err, dbs.ErrTableNotFound) {
|
||||
var instance = dao.Object().Instance
|
||||
if instance == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找完全小写的
|
||||
var lowerTableName = strings.ToLower(dao.Object().Table)
|
||||
lowerTable, _ := instance.FindTable(lowerTableName)
|
||||
if lowerTable != nil {
|
||||
_, err = instance.Exec("RENAME TABLE `" + lowerTableName + "` TO `" + dao.Object().Table + "`")
|
||||
if err == nil {
|
||||
logs.Println("[API_NODE]rename table '" + lowerTableName + "' to '" + dao.Object().Table + "'")
|
||||
return dao.Object().Init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// 启动端口
|
||||
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
|
||||
}
|
||||
var ports = []int{}
|
||||
isListening = false
|
||||
if httpConfig != nil && httpConfig.IsOn && len(httpConfig.Listen) > 0 {
|
||||
for _, listen := range httpConfig.Listen {
|
||||
for _, addr := range listen.Addresses() {
|
||||
// 收集Port
|
||||
_, portString, _ := net.SplitHostPort(addr)
|
||||
var port = types.Int(portString)
|
||||
if port > 0 && !lists.ContainsInt(ports, port) {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
||||
|
||||
// 试着只监听端口
|
||||
_, 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")
|
||||
}
|
||||
goman.New(func() {
|
||||
err := this.listenRPC(listener, nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
isListening = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPS
|
||||
httpsConfig, err := apiNode.DecodeHTTPS(nil, nil)
|
||||
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() {
|
||||
// 收集Port
|
||||
_, portString, _ := net.SplitHostPort(addr)
|
||||
var port = types.Int(portString)
|
||||
if port > 0 && !lists.ContainsInt(ports, port) {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' failed: "+err.Error()+", we will try to listen port only")
|
||||
// 试着只监听端口
|
||||
_, 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")
|
||||
}
|
||||
goman.New(func() {
|
||||
err := this.listenRPC(listener, &tls.Config{
|
||||
Certificates: certs,
|
||||
})
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening '"+addr+"' rpc: "+err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
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() {
|
||||
// 收集Port
|
||||
_, portString, _ := net.SplitHostPort(addr)
|
||||
var port = types.Int(portString)
|
||||
if port > 0 && !lists.ContainsInt(ports, port) {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening REST 'http://"+addr+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
goman.New(func() {
|
||||
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
|
||||
}
|
||||
})
|
||||
isListening = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rest HTTPS
|
||||
restHTTPSConfig, err := apiNode.DecodeRestHTTPS(nil, nil)
|
||||
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() {
|
||||
// 收集Port
|
||||
_, portString, _ := net.SplitHostPort(addr)
|
||||
var port = types.Int(portString)
|
||||
if port > 0 && !lists.ContainsInt(ports, port) {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
remotelogs.Error("API_NODE", "listening REST 'https://"+addr+"' failed: "+err.Error())
|
||||
continue
|
||||
}
|
||||
goman.New(func() {
|
||||
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
|
||||
}
|
||||
})
|
||||
isListening = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add to local firewall
|
||||
if len(ports) > 0 {
|
||||
go utils.AddPortsToFirewall(ports)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 监听本地sock
|
||||
func (this *APINode) listenSock() error {
|
||||
// 检查是否在运行
|
||||
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"))
|
||||
} else {
|
||||
return errors.New("error: the process is already running")
|
||||
}
|
||||
}
|
||||
|
||||
// 启动监听
|
||||
goman.New(func() {
|
||||
this.sock.OnCommand(func(cmd *gosock.Command) {
|
||||
switch cmd.Code {
|
||||
case "pid": // 查询PID
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Code: "pid",
|
||||
Params: map[string]any{
|
||||
"pid": os.Getpid(),
|
||||
},
|
||||
})
|
||||
case "info": // 进程相关信息
|
||||
exePath, _ := os.Executable()
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Code: "info",
|
||||
Params: map[string]any{
|
||||
"pid": os.Getpid(),
|
||||
"version": teaconst.Version,
|
||||
"path": exePath,
|
||||
},
|
||||
})
|
||||
case "stop": // 停止
|
||||
_ = cmd.ReplyOk()
|
||||
|
||||
// 退出主进程
|
||||
events.Notify(events.EventQuit)
|
||||
os.Exit(0)
|
||||
case "starting": // 是否正在启动
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Code: "starting",
|
||||
Params: map[string]any{
|
||||
"isStarting": this.isStarting,
|
||||
"progress": this.progress,
|
||||
},
|
||||
})
|
||||
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{
|
||||
Params: map[string]any{
|
||||
"total": runtime.NumGoroutine(),
|
||||
"result": result,
|
||||
},
|
||||
})
|
||||
case "debug": // 进入|取消调试模式
|
||||
teaconst.Debug = !teaconst.Debug
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Params: map[string]any{"debug": teaconst.Debug},
|
||||
})
|
||||
case "db.stmt.prepare": // 显示prepared的语句
|
||||
dbs.ShowPreparedStatements = !dbs.ShowPreparedStatements
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Params: map[string]any{"isOn": dbs.ShowPreparedStatements},
|
||||
})
|
||||
case "db.stmt.count": // 查询prepared语句数量
|
||||
db, _ := dbs.Default()
|
||||
if db != nil {
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Params: map[string]any{"count": db.StmtManager().Len()},
|
||||
})
|
||||
} else {
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Params: map[string]any{"count": 0},
|
||||
})
|
||||
}
|
||||
case "instance": // 获取实例代号
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Params: map[string]any{
|
||||
"code": teaconst.InstanceCode,
|
||||
},
|
||||
})
|
||||
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 + "'",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
err := this.sock.Listen()
|
||||
if err != nil {
|
||||
remotelogs.Println("API_NODE", err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
events.On(events.EventQuit, func() {
|
||||
remotelogs.Println("API_NODE", "quit unix sock")
|
||||
_ = this.sock.Close()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 服务过滤器
|
||||
func (this *APINode) unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||
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
|
||||
}
|
||||
result, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
statusErr, ok := status.FromError(err)
|
||||
if ok {
|
||||
err = status.Error(statusErr.Code(), "'"+info.FullMethod+"()' says: "+err.Error())
|
||||
} else {
|
||||
err = errors.New("'" + info.FullMethod + "()' says: " + err.Error())
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 添加启动相关的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 + ")。"
|
||||
}
|
||||
}
|
||||
|
||||
// 权限错误
|
||||
if strings.Contains(errString, "Error 1045") || strings.Contains(errString, "Error 1044") {
|
||||
return "使用的用户和密码没有权限连接到指定数据库,请检查:1)数据库配置文件中的用户名(" + dsnConfig.User + ")和密码(" + dsnConfig.Passwd + ")是否正确;2)使用的用户是否已经在数据库中设置了正确的权限;(当前数据库配置为:" + dsn + ",配置文件位置:" + dbConfigPath + ")。"
|
||||
}
|
||||
|
||||
// 数据库名称错误
|
||||
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 {
|
||||
_ = os.WriteFile(this.issuesFile, issuesJSON, 0666)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置启动进度
|
||||
func (this *APINode) setProgress(name, description string) {
|
||||
this.progress = &utils.Progress{
|
||||
Name: name,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
|
||||
// 设置时区
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听一些信号
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
8
EdgeAPI/internal/nodes/api_node_ext.go
Normal file
8
EdgeAPI/internal/nodes/api_node_ext.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package nodes
|
||||
|
||||
func (this *APINode) startAccessLogStorages() {
|
||||
|
||||
}
|
||||
15
EdgeAPI/internal/nodes/api_node_ext_plus.go
Normal file
15
EdgeAPI/internal/nodes/api_node_ext_plus.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/accesslogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
)
|
||||
|
||||
func (this *APINode) startAccessLogStorages() {
|
||||
goman.New(func() {
|
||||
accesslogs.SharedStorageManager.Start()
|
||||
})
|
||||
}
|
||||
583
EdgeAPI/internal/nodes/api_node_services.go
Normal file
583
EdgeAPI/internal/nodes/api_node_services.go
Normal file
@@ -0,0 +1,583 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/clients"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/users"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"google.golang.org/grpc"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 注册服务
|
||||
func (this *APINode) registerServices(server *grpc.Server) {
|
||||
{
|
||||
var instance = this.serviceInstance(&services.PingService{}).(*services.PingService)
|
||||
pb.RegisterPingServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.APITokenService{}).(*services.APITokenService)
|
||||
pb.RegisterAPITokenServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.AdminService{}).(*services.AdminService)
|
||||
pb.RegisterAdminServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeGrantService{}).(*services.NodeGrantService)
|
||||
pb.RegisterNodeGrantServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerService{}).(*services.ServerService)
|
||||
pb.RegisterServerServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeService{}).(*services.NodeService)
|
||||
pb.RegisterNodeServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeClusterService{}).(*services.NodeClusterService)
|
||||
pb.RegisterNodeClusterServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeIPAddressService{}).(*services.NodeIPAddressService)
|
||||
pb.RegisterNodeIPAddressServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeIPAddressLogService{}).(*services.NodeIPAddressLogService)
|
||||
pb.RegisterNodeIPAddressLogServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeIPAddressThresholdService{}).(*services.NodeIPAddressThresholdService)
|
||||
pb.RegisterNodeIPAddressThresholdServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.APINodeService{}).(*services.APINodeService)
|
||||
pb.RegisterAPINodeServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.APIMethodStatService{}).(*services.APIMethodStatService)
|
||||
pb.RegisterAPIMethodStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.OriginService{}).(*services.OriginService)
|
||||
pb.RegisterOriginServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPWebService{}).(*services.HTTPWebService)
|
||||
pb.RegisterHTTPWebServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ReverseProxyService{}).(*services.ReverseProxyService)
|
||||
pb.RegisterReverseProxyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPGzipService{}).(*services.HTTPGzipService)
|
||||
pb.RegisterHTTPGzipServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPHeaderPolicyService{}).(*services.HTTPHeaderPolicyService)
|
||||
pb.RegisterHTTPHeaderPolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPHeaderService{}).(*services.HTTPHeaderService)
|
||||
pb.RegisterHTTPHeaderServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPPageService{}).(*services.HTTPPageService)
|
||||
pb.RegisterHTTPPageServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPCachePolicyService{}).(*services.HTTPCachePolicyService)
|
||||
pb.RegisterHTTPCachePolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPFirewallPolicyService{}).(*services.HTTPFirewallPolicyService)
|
||||
pb.RegisterHTTPFirewallPolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.FirewallService{}).(*services.FirewallService)
|
||||
pb.RegisterFirewallServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPLocationService{}).(*services.HTTPLocationService)
|
||||
pb.RegisterHTTPLocationServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPWebsocketService{}).(*services.HTTPWebsocketService)
|
||||
pb.RegisterHTTPWebsocketServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPRewriteRuleService{}).(*services.HTTPRewriteRuleService)
|
||||
pb.RegisterHTTPRewriteRuleServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.SSLCertService{}).(*services.SSLCertService)
|
||||
pb.RegisterSSLCertServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.SSLPolicyService{}).(*services.SSLPolicyService)
|
||||
pb.RegisterSSLPolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.SysSettingService{}).(*services.SysSettingService)
|
||||
pb.RegisterSysSettingServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPFirewallRuleGroupService{}).(*services.HTTPFirewallRuleGroupService)
|
||||
pb.RegisterHTTPFirewallRuleGroupServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPFirewallRuleSetService{}).(*services.HTTPFirewallRuleSetService)
|
||||
pb.RegisterHTTPFirewallRuleSetServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DBNodeService{}).(*services.DBNodeService)
|
||||
pb.RegisterDBNodeServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeLogService{}).(*services.NodeLogService)
|
||||
pb.RegisterNodeLogServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeLoginService{}).(*services.NodeLoginService)
|
||||
pb.RegisterNodeLoginServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPAccessLogService{}).(*services.HTTPAccessLogService)
|
||||
pb.RegisterHTTPAccessLogServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.MessageService{}).(*services.MessageService)
|
||||
pb.RegisterMessageServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeGroupService{}).(*services.NodeGroupService)
|
||||
pb.RegisterNodeGroupServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeRegionService{}).(*services.NodeRegionService)
|
||||
pb.RegisterNodeRegionServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerGroupService{}).(*services.ServerGroupService)
|
||||
pb.RegisterServerGroupServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.IPLibraryService{}).(*services.IPLibraryService)
|
||||
pb.RegisterIPLibraryServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.IPLibraryFileService{}).(*services.IPLibraryFileService)
|
||||
pb.RegisterIPLibraryFileServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.IPLibraryArtifactService{}).(*services.IPLibraryArtifactService)
|
||||
pb.RegisterIPLibraryArtifactServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.FileChunkService{}).(*services.FileChunkService)
|
||||
pb.RegisterFileChunkServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.FileService{}).(*services.FileService)
|
||||
pb.RegisterFileServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.RegionCountryService{}).(*services.RegionCountryService)
|
||||
pb.RegisterRegionCountryServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.RegionProvinceService{}).(*services.RegionProvinceService)
|
||||
pb.RegisterRegionProvinceServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.RegionCityService{}).(*services.RegionCityService)
|
||||
pb.RegisterRegionCityServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.RegionTownService{}).(*services.RegionTownService)
|
||||
pb.RegisterRegionTownServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.RegionProviderService{}).(*services.RegionProviderService)
|
||||
pb.RegisterRegionProviderServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.IPListService{}).(*services.IPListService)
|
||||
pb.RegisterIPListServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.IPItemService{}).(*services.IPItemService)
|
||||
pb.RegisterIPItemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.LogService{}).(*services.LogService)
|
||||
pb.RegisterLogServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DNSProviderService{}).(*services.DNSProviderService)
|
||||
pb.RegisterDNSProviderServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DNSDomainService{}).(*services.DNSDomainService)
|
||||
pb.RegisterDNSDomainServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DNSService{}).(*services.DNSService)
|
||||
pb.RegisterDNSServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ACMEUserService{}).(*services.ACMEUserService)
|
||||
pb.RegisterACMEUserServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ACMETaskService{}).(*services.ACMETaskService)
|
||||
pb.RegisterACMETaskServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ACMEAuthenticationService{}).(*services.ACMEAuthenticationService)
|
||||
pb.RegisterACMEAuthenticationServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ACMEProviderService{}).(*services.ACMEProviderService)
|
||||
pb.RegisterACMEProviderServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ACMEProviderAccountService{}).(*services.ACMEProviderAccountService)
|
||||
pb.RegisterACMEProviderAccountServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&users.UserService{}).(*users.UserService)
|
||||
pb.RegisterUserServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.UserIdentityService{}).(*services.UserIdentityService)
|
||||
pb.RegisterUserIdentityServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerDailyStatService{}).(*services.ServerDailyStatService)
|
||||
pb.RegisterServerDailyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.LoginService{}).(*services.LoginService)
|
||||
pb.RegisterLoginServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.LoginSessionService{}).(*services.LoginSessionService)
|
||||
pb.RegisterLoginSessionServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.LoginTicketService{}).(*services.LoginTicketService)
|
||||
pb.RegisterLoginTicketServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.UserAccessKeyService{}).(*services.UserAccessKeyService)
|
||||
pb.RegisterUserAccessKeyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.SysLockerService{}).(*services.SysLockerService)
|
||||
pb.RegisterSysLockerServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeTaskService{}).(*services.NodeTaskService)
|
||||
pb.RegisterNodeTaskServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeValueService{}).(*services.NodeValueService)
|
||||
pb.RegisterNodeValueServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DBService{}).(*services.DBService)
|
||||
pb.RegisterDBServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerRegionCityMonthlyStatService{}).(*services.ServerRegionCityMonthlyStatService)
|
||||
pb.RegisterServerRegionCityMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerRegionCountryMonthlyStatService{}).(*services.ServerRegionCountryMonthlyStatService)
|
||||
pb.RegisterServerRegionCountryMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerRegionProvinceMonthlyStatService{}).(*services.ServerRegionProvinceMonthlyStatService)
|
||||
pb.RegisterServerRegionProvinceMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerRegionProviderMonthlyStatService{}).(*services.ServerRegionProviderMonthlyStatService)
|
||||
pb.RegisterServerRegionProviderMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&clients.FormalClientSystemService{}).(*clients.FormalClientSystemService)
|
||||
pb.RegisterFormalClientSystemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&clients.FormalClientBrowserService{}).(*clients.FormalClientBrowserService)
|
||||
pb.RegisterFormalClientBrowserServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&clients.ClientAgentIPService{}).(*clients.ClientAgentIPService)
|
||||
pb.RegisterClientAgentIPServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&clients.ClientAgentService{}).(*clients.ClientAgentService)
|
||||
pb.RegisterClientAgentServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerClientSystemMonthlyStatService{}).(*services.ServerClientSystemMonthlyStatService)
|
||||
pb.RegisterServerClientSystemMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerClientBrowserMonthlyStatService{}).(*services.ServerClientBrowserMonthlyStatService)
|
||||
pb.RegisterServerClientBrowserMonthlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerHTTPFirewallDailyStatService{}).(*services.ServerHTTPFirewallDailyStatService)
|
||||
pb.RegisterServerHTTPFirewallDailyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.DNSTaskService{}).(*services.DNSTaskService)
|
||||
pb.RegisterDNSTaskServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeClusterFirewallActionService{}).(*services.NodeClusterFirewallActionService)
|
||||
pb.RegisterNodeClusterFirewallActionServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.AuthorityNodeService{}).(*services.AuthorityNodeService)
|
||||
pb.RegisterAuthorityNodeServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.LatestItemService{}).(*services.LatestItemService)
|
||||
pb.RegisterLatestItemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeThresholdService{}).(*services.NodeThresholdService)
|
||||
pb.RegisterNodeThresholdServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPFastcgiService{}).(*services.HTTPFastcgiService)
|
||||
pb.RegisterHTTPFastcgiServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPAuthPolicyService{}).(*services.HTTPAuthPolicyService)
|
||||
pb.RegisterHTTPAuthPolicyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.MetricItemService{}).(*services.MetricItemService)
|
||||
pb.RegisterMetricItemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.NodeClusterMetricItemService{}).(*services.NodeClusterMetricItemService)
|
||||
pb.RegisterNodeClusterMetricItemServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.MetricStatService{}).(*services.MetricStatService)
|
||||
pb.RegisterMetricStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = this.serviceInstance(&services.MetricChartService{}).(*services.MetricChartService)
|
||||
pb.RegisterMetricChartServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerStatBoardService{}).(*services.ServerStatBoardService)
|
||||
pb.RegisterServerStatBoardServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerStatBoardChartService{}).(*services.ServerStatBoardChartService)
|
||||
pb.RegisterServerStatBoardChartServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.PlanService{}).(*services.PlanService)
|
||||
pb.RegisterPlanServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.UserPlanService{}).(*services.UserPlanService)
|
||||
pb.RegisterUserPlanServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerDomainHourlyStatService{}).(*services.ServerDomainHourlyStatService)
|
||||
pb.RegisterServerDomainHourlyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.TrafficDailyStatService{}).(*services.TrafficDailyStatService)
|
||||
pb.RegisterTrafficDailyStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPCacheTaskKeyService{}).(*services.HTTPCacheTaskKeyService)
|
||||
pb.RegisterHTTPCacheTaskKeyServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.HTTPCacheTaskService{}).(*services.HTTPCacheTaskService)
|
||||
pb.RegisterHTTPCacheTaskServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.ServerBandwidthStatService{}).(*services.ServerBandwidthStatService)
|
||||
pb.RegisterServerBandwidthStatServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
{
|
||||
var instance = this.serviceInstance(&services.UpdatingServerListService{}).(*services.UpdatingServerListService)
|
||||
pb.RegisterUpdatingServerListServiceServer(server, instance)
|
||||
this.rest(instance)
|
||||
}
|
||||
|
||||
APINodeServicesRegister(this, server)
|
||||
|
||||
// TODO check service names
|
||||
for serviceName := range server.GetServiceInfo() {
|
||||
var index = strings.LastIndex(serviceName, ".")
|
||||
if index >= 0 {
|
||||
serviceName = serviceName[index+1:]
|
||||
}
|
||||
_, ok := restServicesMap[serviceName]
|
||||
if !ok {
|
||||
panic("can not find service '" + serviceName + "' in rest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *APINode) rest(instance interface{}) {
|
||||
this.serviceInstanceLocker.Lock()
|
||||
defer this.serviceInstanceLocker.Unlock()
|
||||
|
||||
var name = reflect.TypeOf(instance).String()
|
||||
var index = strings.LastIndex(name, ".")
|
||||
if index >= 0 {
|
||||
name = name[index+1:]
|
||||
}
|
||||
|
||||
_, ok := restServicesMap[name]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
restServicesMap[name] = reflect.ValueOf(instance)
|
||||
}
|
||||
|
||||
func (this *APINode) serviceInstance(instance interface{}) interface{} {
|
||||
this.serviceInstanceLocker.Lock()
|
||||
defer this.serviceInstanceLocker.Unlock()
|
||||
|
||||
var typeName = reflect.TypeOf(instance).String()
|
||||
result, ok := this.serviceInstanceMap[typeName]
|
||||
if ok {
|
||||
return result
|
||||
}
|
||||
|
||||
this.serviceInstanceMap[typeName] = instance
|
||||
return instance
|
||||
}
|
||||
10
EdgeAPI/internal/nodes/api_node_services_hook.go
Normal file
10
EdgeAPI/internal/nodes/api_node_services_hook.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build !plus
|
||||
// +build !plus
|
||||
|
||||
package nodes
|
||||
|
||||
import "google.golang.org/grpc"
|
||||
|
||||
func APINodeServicesRegister(node *APINode, server *grpc.Server) {
|
||||
}
|
||||
316
EdgeAPI/internal/nodes/api_node_services_hook_plus.go
Normal file
316
EdgeAPI/internal/nodes/api_node_services_hook_plus.go
Normal file
@@ -0,0 +1,316 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/accounts"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/anti-ddos"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/nameservers"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/posts"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/reporters"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/tickets"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/trafficpackages"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services/users"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func APINodeServicesRegister(node *APINode, server *grpc.Server) {
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSClusterService{}).(*nameservers.NSClusterService)
|
||||
pb.RegisterNSClusterServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSNodeService{}).(*nameservers.NSNodeService)
|
||||
pb.RegisterNSNodeServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSDomainService{}).(*nameservers.NSDomainService)
|
||||
pb.RegisterNSDomainServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSDomainGroupService{}).(*nameservers.NSDomainGroupService)
|
||||
pb.RegisterNSDomainGroupServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSRecordService{}).(*nameservers.NSRecordService)
|
||||
pb.RegisterNSRecordServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSRouteService{}).(*nameservers.NSRouteService)
|
||||
pb.RegisterNSRouteServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSRouteCategoryService{}).(*nameservers.NSRouteCategoryService)
|
||||
pb.RegisterNSRouteCategoryServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSKeyService{}).(*nameservers.NSKeyService)
|
||||
pb.RegisterNSKeyServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSAccessLogService{}).(*nameservers.NSAccessLogService)
|
||||
pb.RegisterNSAccessLogServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSRecordHourlyStatService{}).(*nameservers.NSRecordHourlyStatService)
|
||||
pb.RegisterNSRecordHourlyStatServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSQuestionOptionService{}).(*nameservers.NSQuestionOptionService)
|
||||
pb.RegisterNSQuestionOptionServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSPlanService{}).(*nameservers.NSPlanService)
|
||||
pb.RegisterNSPlanServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSUserPlanService{}).(*nameservers.NSUserPlanService)
|
||||
pb.RegisterNSUserPlanServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&nameservers.NSService{}).(*nameservers.NSService)
|
||||
pb.RegisterNSServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.AuthorityKeyService{}).(*services.AuthorityKeyService)
|
||||
pb.RegisterAuthorityKeyServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&reporters.ReportNodeService{}).(*reporters.ReportNodeService)
|
||||
pb.RegisterReportNodeServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&reporters.ReportNodeGroupService{}).(*reporters.ReportNodeGroupService)
|
||||
pb.RegisterReportNodeGroupServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&reporters.ReportResultService{}).(*reporters.ReportResultService)
|
||||
pb.RegisterReportResultServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.UserNodeService{}).(*services.UserNodeService)
|
||||
pb.RegisterUserNodeServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&accounts.UserAccountService{}).(*accounts.UserAccountService)
|
||||
pb.RegisterUserAccountServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&accounts.UserAccountLogService{}).(*accounts.UserAccountLogService)
|
||||
pb.RegisterUserAccountLogServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&accounts.UserAccountDailyStatService{}).(*accounts.UserAccountDailyStatService)
|
||||
pb.RegisterUserAccountDailyStatServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&accounts.UserOrderService{}).(*accounts.UserOrderService)
|
||||
pb.RegisterUserOrderServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&accounts.OrderMethodService{}).(*accounts.OrderMethodService)
|
||||
pb.RegisterOrderMethodServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.ScriptService{}).(*services.ScriptService)
|
||||
pb.RegisterScriptServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.UserScriptService{}).(*services.UserScriptService)
|
||||
pb.RegisterUserScriptServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&tickets.UserTicketService{}).(*tickets.UserTicketService)
|
||||
pb.RegisterUserTicketServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&tickets.UserTicketCategoryService{}).(*tickets.UserTicketCategoryService)
|
||||
pb.RegisterUserTicketCategoryServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&tickets.UserTicketLogService{}).(*tickets.UserTicketLogService)
|
||||
pb.RegisterUserTicketLogServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.HTTPAccessLogPolicyService{}).(*services.HTTPAccessLogPolicyService)
|
||||
pb.RegisterHTTPAccessLogPolicyServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.UserBillService{}).(*services.UserBillService)
|
||||
pb.RegisterUserBillServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.ServerBillService{}).(*services.ServerBillService)
|
||||
pb.RegisterServerBillServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.PriceService{}).(*services.PriceService)
|
||||
pb.RegisterPriceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.NodePriceItemService{}).(*services.NodePriceItemService)
|
||||
pb.RegisterNodePriceItemServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.UserTrafficBillService{}).(*services.UserTrafficBillService)
|
||||
pb.RegisterUserTrafficBillServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&trafficpackages.TrafficPackageService{}).(*trafficpackages.TrafficPackageService)
|
||||
pb.RegisterTrafficPackageServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&trafficpackages.TrafficPackagePeriodService{}).(*trafficpackages.TrafficPackagePeriodService)
|
||||
pb.RegisterTrafficPackagePeriodServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&trafficpackages.TrafficPackagePriceService{}).(*trafficpackages.TrafficPackagePriceService)
|
||||
pb.RegisterTrafficPackagePriceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&trafficpackages.UserTrafficPackageService{}).(*trafficpackages.UserTrafficPackageService)
|
||||
pb.RegisterUserTrafficPackageServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&users.UserEmailVerificationService{}).(*users.UserEmailVerificationService)
|
||||
pb.RegisterUserEmailVerificationServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&users.UserMobileVerificationService{}).(*users.UserMobileVerificationService)
|
||||
pb.RegisterUserMobileVerificationServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&users.UserVerifyCodeService{}).(*users.UserVerifyCodeService)
|
||||
pb.RegisterUserVerifyCodeServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageMediaService{}).(*services.MessageMediaService)
|
||||
pb.RegisterMessageMediaServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.ADNetworkService{}).(*antiddos.ADNetworkService)
|
||||
pb.RegisterADNetworkServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.ADPackageService{}).(*antiddos.ADPackageService)
|
||||
pb.RegisterADPackageServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.ADPackageInstanceService{}).(*antiddos.ADPackageInstanceService)
|
||||
pb.RegisterADPackageInstanceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.ADPackagePeriodService{}).(*antiddos.ADPackagePeriodService)
|
||||
pb.RegisterADPackagePeriodServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.ADPackagePriceService{}).(*antiddos.ADPackagePriceService)
|
||||
pb.RegisterADPackagePriceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&antiddos.UserADInstanceService{}).(*antiddos.UserADInstanceService)
|
||||
pb.RegisterUserADInstanceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.NodeActionService{}).(*services.NodeActionService)
|
||||
pb.RegisterNodeActionServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageRecipientService{}).(*services.MessageRecipientService)
|
||||
pb.RegisterMessageRecipientServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageReceiverService{}).(*services.MessageReceiverService)
|
||||
pb.RegisterMessageReceiverServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageRecipientGroupService{}).(*services.MessageRecipientGroupService)
|
||||
pb.RegisterMessageRecipientGroupServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageMediaInstanceService{}).(*services.MessageMediaInstanceService)
|
||||
pb.RegisterMessageMediaInstanceServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageTaskService{}).(*services.MessageTaskService)
|
||||
pb.RegisterMessageTaskServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.MessageTaskLogService{}).(*services.MessageTaskLogService)
|
||||
pb.RegisterMessageTaskLogServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&services.SMSSenderService{}).(*services.SMSSenderService)
|
||||
pb.RegisterSMSSenderServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&posts.PostService{}).(*posts.PostService)
|
||||
pb.RegisterPostServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
{
|
||||
var instance = node.serviceInstance(&posts.PostCategoryService{}).(*posts.PostCategoryService)
|
||||
pb.RegisterPostCategoryServiceServer(server, instance)
|
||||
node.rest(instance)
|
||||
}
|
||||
}
|
||||
187
EdgeAPI/internal/nodes/node_status_executor.go
Normal file
187
EdgeAPI/internal/nodes/node_status_executor.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/events"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NodeStatusExecutor struct {
|
||||
isFirstTime bool
|
||||
|
||||
cpuUpdatedTime time.Time
|
||||
cpuLogicalCount int
|
||||
cpuPhysicalCount int
|
||||
}
|
||||
|
||||
func NewNodeStatusExecutor() *NodeStatusExecutor {
|
||||
return &NodeStatusExecutor{}
|
||||
}
|
||||
|
||||
func (this *NodeStatusExecutor) Listen() {
|
||||
this.isFirstTime = true
|
||||
this.cpuUpdatedTime = time.Now()
|
||||
this.update()
|
||||
|
||||
// TODO 这个时间间隔可以配置
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
|
||||
events.On(events.EventQuit, func() {
|
||||
remotelogs.Println("NODE_STATUS", "quit executor")
|
||||
ticker.Stop()
|
||||
})
|
||||
|
||||
for range ticker.C {
|
||||
this.isFirstTime = false
|
||||
this.update()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *NodeStatusExecutor) update() {
|
||||
if sharedAPIConfig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
status.BuildVersion = teaconst.Version
|
||||
status.BuildVersionCode = utils.VersionToLong(teaconst.Version)
|
||||
status.OS = runtime.GOOS
|
||||
status.Arch = runtime.GOARCH
|
||||
exe, _ := os.Executable()
|
||||
status.ExePath = exe
|
||||
status.ConfigVersion = 0
|
||||
status.IsActive = true
|
||||
status.ConnectionCount = 0 // TODO 实现连接数计算
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
status.Hostname = hostname
|
||||
|
||||
this.updateCPU(status)
|
||||
this.updateMem(status)
|
||||
this.updateLoad(status)
|
||||
this.updateDisk(status)
|
||||
status.UpdatedAt = time.Now().Unix()
|
||||
|
||||
// 发送数据
|
||||
jsonData, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE_STATUS", "serial NodeStatus fail: "+err.Error())
|
||||
return
|
||||
}
|
||||
err = models.SharedAPINodeDAO.UpdateAPINodeStatus(nil, sharedAPIConfig.NumberId(), jsonData)
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE_STATUS", "rpc UpdateNodeStatus() failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 更新CPU
|
||||
func (this *NodeStatusExecutor) updateCPU(status *nodeconfigs.NodeStatus) {
|
||||
duration := time.Duration(0)
|
||||
if this.isFirstTime {
|
||||
duration = 100 * time.Millisecond
|
||||
}
|
||||
percents, err := cpu.Percent(duration, false)
|
||||
if err != nil {
|
||||
status.Error = "cpu.Percent(): " + err.Error()
|
||||
return
|
||||
}
|
||||
if len(percents) == 0 {
|
||||
return
|
||||
}
|
||||
status.CPUUsage = percents[0] / 100
|
||||
|
||||
if time.Since(this.cpuUpdatedTime) > 300*time.Second { // 每隔5分钟才会更新一次
|
||||
this.cpuUpdatedTime = time.Now()
|
||||
|
||||
status.CPULogicalCount, err = cpu.Counts(true)
|
||||
if err != nil {
|
||||
status.Error = "cpu.Counts(): " + err.Error()
|
||||
return
|
||||
}
|
||||
status.CPUPhysicalCount, err = cpu.Counts(false)
|
||||
if err != nil {
|
||||
status.Error = "cpu.Counts(): " + err.Error()
|
||||
return
|
||||
}
|
||||
this.cpuLogicalCount = status.CPULogicalCount
|
||||
this.cpuPhysicalCount = status.CPUPhysicalCount
|
||||
} else {
|
||||
status.CPULogicalCount = this.cpuLogicalCount
|
||||
status.CPUPhysicalCount = this.cpuPhysicalCount
|
||||
}
|
||||
}
|
||||
|
||||
// 更新硬盘
|
||||
func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE_STATUS", err.Error())
|
||||
return
|
||||
}
|
||||
lists.Sort(partitions, func(i int, j int) bool {
|
||||
p1 := partitions[i]
|
||||
p2 := partitions[j]
|
||||
return p1.Mountpoint > p2.Mountpoint
|
||||
})
|
||||
|
||||
// 当前TeaWeb所在的fs
|
||||
var rootFS = ""
|
||||
var rootTotal = uint64(0)
|
||||
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
||||
for _, p := range partitions {
|
||||
if p.Mountpoint == "/" {
|
||||
rootFS = p.Fstype
|
||||
usage, _ := disk.Usage(p.Mountpoint)
|
||||
if usage != nil {
|
||||
rootTotal = usage.Total
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var total = rootTotal
|
||||
var totalUsage = uint64(0)
|
||||
var maxUsage = float64(0)
|
||||
for _, partition := range partitions {
|
||||
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过不同fs的
|
||||
if len(rootFS) > 0 && rootFS != partition.Fstype {
|
||||
continue
|
||||
}
|
||||
|
||||
usage, err := disk.Usage(partition.Mountpoint)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if partition.Mountpoint != "/" && (usage.Total != rootTotal || total == 0) {
|
||||
total += usage.Total
|
||||
}
|
||||
totalUsage += usage.Used
|
||||
if usage.UsedPercent >= maxUsage {
|
||||
maxUsage = usage.UsedPercent
|
||||
status.DiskMaxUsagePartition = partition.Mountpoint
|
||||
}
|
||||
}
|
||||
status.DiskTotal = total
|
||||
if total > 0 {
|
||||
status.DiskUsage = float64(totalUsage) / float64(total)
|
||||
}
|
||||
status.DiskMaxUsage = maxUsage / 100
|
||||
}
|
||||
27
EdgeAPI/internal/nodes/node_status_executor_test.go
Normal file
27
EdgeAPI/internal/nodes/node_status_executor_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNodeStatusExecutor_CPU(t *testing.T) {
|
||||
countLogicCPU, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("logic count:", countLogicCPU)
|
||||
|
||||
countPhysicalCPU, err := cpu.Counts(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("physical count:", countPhysicalCPU)
|
||||
|
||||
percents, err := cpu.Percent(100*time.Millisecond, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(percents)
|
||||
}
|
||||
56
EdgeAPI/internal/nodes/node_status_executor_unix.go
Normal file
56
EdgeAPI/internal/nodes/node_status_executor_unix.go
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/shirou/gopsutil/v3/load"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// 更新内存
|
||||
func (this *NodeStatusExecutor) updateMem(status *nodeconfigs.NodeStatus) {
|
||||
stat, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 重新计算内存
|
||||
if stat.Total > 0 {
|
||||
stat.Used = stat.Total - stat.Free - stat.Buffers - stat.Cached
|
||||
status.MemoryUsage = float64(stat.Used) / float64(stat.Total)
|
||||
}
|
||||
|
||||
status.MemoryTotal = stat.Total
|
||||
|
||||
// 内存严重不足时自动释放内存
|
||||
if stat.Total > 0 {
|
||||
var minFreeMemory = stat.Total / 8
|
||||
if minFreeMemory > 1<<30 {
|
||||
minFreeMemory = 1 << 30
|
||||
}
|
||||
if stat.Available > 0 && stat.Available < minFreeMemory {
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新负载
|
||||
func (this *NodeStatusExecutor) updateLoad(status *nodeconfigs.NodeStatus) {
|
||||
stat, err := load.Avg()
|
||||
if err != nil {
|
||||
status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
if stat == nil {
|
||||
status.Error = "load is nil"
|
||||
return
|
||||
}
|
||||
status.Load1m = stat.Load1
|
||||
status.Load5m = stat.Load5
|
||||
status.Load15m = stat.Load15
|
||||
}
|
||||
102
EdgeAPI/internal/nodes/node_status_executor_windows.go
Normal file
102
EdgeAPI/internal/nodes/node_status_executor_windows.go
Normal file
@@ -0,0 +1,102 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WindowsLoadValue struct {
|
||||
Timestamp int64
|
||||
Value int
|
||||
}
|
||||
|
||||
var windowsLoadValues = []*WindowsLoadValue{}
|
||||
var windowsLoadLocker = &sync.Mutex{}
|
||||
|
||||
// 更新内存
|
||||
func (this *NodeStatusExecutor) updateMem(status *NodeStatus) {
|
||||
stat, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
status.MemoryUsage = stat.UsedPercent
|
||||
status.MemoryTotal = stat.Total
|
||||
}
|
||||
|
||||
// 更新负载
|
||||
func (this *NodeStatusExecutor) updateLoad(status *NodeStatus) {
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
currentLoad := 0
|
||||
info, err := cpu.ProcInfo()
|
||||
if err == nil && len(info) > 0 && info[0].ProcessorQueueLength < 1000 {
|
||||
currentLoad = int(info[0].ProcessorQueueLength)
|
||||
}
|
||||
|
||||
// 删除15分钟之前的数据
|
||||
windowsLoadLocker.Lock()
|
||||
result := []*WindowsLoadValue{}
|
||||
for _, v := range windowsLoadValues {
|
||||
if timestamp-v.Timestamp > 15*60 {
|
||||
continue
|
||||
}
|
||||
result = append(result, v)
|
||||
}
|
||||
result = append(result, &WindowsLoadValue{
|
||||
Timestamp: timestamp,
|
||||
Value: currentLoad,
|
||||
})
|
||||
windowsLoadValues = result
|
||||
|
||||
total1 := 0
|
||||
count1 := 0
|
||||
total5 := 0
|
||||
count5 := 0
|
||||
total15 := 0
|
||||
count15 := 0
|
||||
for _, v := range result {
|
||||
if timestamp-v.Timestamp <= 60 {
|
||||
total1 += v.Value
|
||||
count1++
|
||||
}
|
||||
|
||||
if timestamp-v.Timestamp <= 300 {
|
||||
total5 += v.Value
|
||||
count5++
|
||||
}
|
||||
|
||||
total15 += v.Value
|
||||
count15++
|
||||
}
|
||||
|
||||
load1 := float64(0)
|
||||
load5 := float64(0)
|
||||
load15 := float64(0)
|
||||
if count1 > 0 {
|
||||
load1 = math.Round(float64(total1*100)/float64(count1)) / 100
|
||||
}
|
||||
if count5 > 0 {
|
||||
load5 = math.Round(float64(total5*100)/float64(count5)) / 100
|
||||
}
|
||||
if count15 > 0 {
|
||||
load15 = math.Round(float64(total15*100)/float64(count15)) / 100
|
||||
}
|
||||
|
||||
windowsLoadLocker.Unlock()
|
||||
|
||||
// 在老Windows上不显示错误
|
||||
if err == context.DeadlineExceeded {
|
||||
err = nil
|
||||
}
|
||||
status.Load1m = load1
|
||||
status.Load5m = load5
|
||||
status.Load15m = load15
|
||||
}
|
||||
282
EdgeAPI/internal/nodes/rest_server.go
Normal file
282
EdgeAPI/internal/nodes/rest_server.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
|
||||
rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils/sizes"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var servicePathReg = regexp.MustCompile(`^/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)$`)
|
||||
var restServicesMap = map[string]reflect.Value{
|
||||
"APIAccessTokenService": reflect.ValueOf(new(services.APIAccessTokenService)),
|
||||
"ServerService": reflect.ValueOf(new(services.ServerService)),
|
||||
}
|
||||
|
||||
type RestServer struct{}
|
||||
|
||||
func (this *RestServer) Listen(listener net.Listener) error {
|
||||
var mux = http.NewServeMux()
|
||||
mux.HandleFunc("/", this.handle)
|
||||
var server = &http.Server{}
|
||||
server.Handler = mux
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
func (this *RestServer) ListenHTTPS(listener net.Listener, tlsConfig *tls.Config) error {
|
||||
var mux = http.NewServeMux()
|
||||
mux.HandleFunc("/", this.handle)
|
||||
server := &http.Server{}
|
||||
server.Handler = mux
|
||||
server.TLSConfig = tlsConfig
|
||||
return server.ServeTLS(listener, "", "")
|
||||
}
|
||||
|
||||
func (this *RestServer) handle(writer http.ResponseWriter, req *http.Request) {
|
||||
var path = req.URL.Path
|
||||
|
||||
// 是否显示Pretty后的JSON
|
||||
var shouldPretty = req.Header.Get("X-Edge-Response-Pretty") == "on"
|
||||
|
||||
// 兼容老的Header
|
||||
var oldShouldPretty = req.Header.Get("Edge-Response-Pretty")
|
||||
if len(oldShouldPretty) > 0 {
|
||||
shouldPretty = oldShouldPretty == "on"
|
||||
}
|
||||
|
||||
// 欢迎页
|
||||
if path == "/" {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 200,
|
||||
"message": "Welcome to API",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
var matches = servicePathReg.FindStringSubmatch(path)
|
||||
if len(matches) != 3 {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "invalid api path '" + path + "'",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
var serviceName = matches[1]
|
||||
var methodName = matches[2]
|
||||
|
||||
serviceType, ok := restServicesMap[serviceName]
|
||||
if !ok {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "service '" + serviceName + "' not found",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
if len(methodName) == 0 {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "method '" + methodName + "' not found",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
// 再次查找
|
||||
methodName = strings.ToUpper(string(methodName[0])) + methodName[1:]
|
||||
var method = serviceType.MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
// 兼容Enabled
|
||||
if strings.Contains(methodName, "Enabled") {
|
||||
methodName = strings.Replace(methodName, "Enabled", "", 1)
|
||||
method = serviceType.MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "method '" + methodName + "' not found",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "method '" + methodName + "' not found",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
}
|
||||
if method.Type().NumIn() != 2 || method.Type().NumOut() != 2 {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "method '" + methodName + "' not found",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
if method.Type().In(0).Name() != "Context" {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": "404",
|
||||
"message": "method '" + methodName + "' not found (or invalid context)",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
// 上下文
|
||||
var ctx = context.Background()
|
||||
|
||||
if serviceName != "APIAccessTokenService" || (methodName != "GetAPIAccessToken" && methodName != "getAPIAccessToken") {
|
||||
// 校验TOKEN
|
||||
var token = req.Header.Get("X-Edge-Access-Token")
|
||||
if len(token) == 0 {
|
||||
token = req.Header.Get("Edge-Access-Token")
|
||||
if len(token) == 0 {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"data": maps.Map{},
|
||||
"message": "require 'X-Edge-Access-Token' header",
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
accessToken, err := models.SharedAPIAccessTokenDAO.FindAccessToken(nil, token)
|
||||
if err != nil {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"data": maps.Map{},
|
||||
"message": "server error: " + err.Error(),
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
if accessToken == nil || int64(accessToken.ExpiredAt) < time.Now().Unix() {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"data": maps.Map{},
|
||||
"message": "invalid access token",
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
if accessToken.UserId > 0 {
|
||||
ctx = rpcutils.NewPlainContext("user", int64(accessToken.UserId))
|
||||
} else if accessToken.AdminId > 0 {
|
||||
ctx = rpcutils.NewPlainContext("admin", int64(accessToken.AdminId))
|
||||
} else {
|
||||
// TODO 支持更多类型的角色
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"data": maps.Map{},
|
||||
"message": "not supported role",
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 可以设置最大可接收内容尺寸
|
||||
body, err := io.ReadAll(io.LimitReader(req.Body, 32*sizes.M))
|
||||
if err != nil {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"message": err.Error(),
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果为空,表示传的数据为空
|
||||
if len(body) == 0 {
|
||||
body = []byte("{}")
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
var reqValue = reflect.New(method.Type().In(1).Elem()).Interface()
|
||||
err = json.Unmarshal(body, reqValue)
|
||||
if err != nil {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"message": "Decode request failed: " + err.Error() + ". Request body should be a valid JSON data",
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
return
|
||||
}
|
||||
|
||||
var result = method.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(reqValue)})
|
||||
var resultErr = result[1].Interface()
|
||||
if resultErr != nil {
|
||||
e, ok := resultErr.(error)
|
||||
if ok {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 400,
|
||||
"message": e.Error(),
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
} else {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 500,
|
||||
"message": "server error: server should return a error object, but return a " + result[1].Type().String(),
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
}
|
||||
} else { // 没有返回错误
|
||||
var data = maps.Map{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": result[0].Interface(),
|
||||
}
|
||||
var dataJSON []byte
|
||||
if shouldPretty {
|
||||
dataJSON = data.AsPrettyJSON()
|
||||
} else {
|
||||
dataJSON = data.AsJSON()
|
||||
}
|
||||
if err != nil {
|
||||
this.writeJSON(writer, maps.Map{
|
||||
"code": 500,
|
||||
"message": "server error: marshal json failed: " + err.Error(),
|
||||
"data": maps.Map{},
|
||||
}, shouldPretty)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
_, _ = writer.Write(dataJSON)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RestServer) writeJSON(writer http.ResponseWriter, v maps.Map, pretty bool) {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
if pretty {
|
||||
_, _ = writer.Write(v.AsPrettyJSON())
|
||||
} else {
|
||||
_, _ = writer.Write(v.AsJSON())
|
||||
}
|
||||
}
|
||||
17
EdgeAPI/internal/nodes/start_issue.go
Normal file
17
EdgeAPI/internal/nodes/start_issue.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodes
|
||||
|
||||
type StartIssue struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Suggestion string `json:"suggestion"`
|
||||
}
|
||||
|
||||
func NewStartIssue(code string, message string, suggestion string) *StartIssue {
|
||||
return &StartIssue{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Suggestion: suggestion,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user