2020-10-10 11:49:21 +08:00
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2021-01-01 20:49:09 +08:00
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
2021-12-14 10:49:29 +08:00
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
2021-08-30 10:56:31 +08:00
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
2021-12-20 16:20:33 +08:00
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
2021-05-26 14:40:05 +08:00
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
2020-10-10 11:49:21 +08:00
|
|
|
"github.com/iwind/TeaGo/dbs"
|
|
|
|
|
"github.com/iwind/TeaGo/lists"
|
|
|
|
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
|
|
|
|
"hash/crc32"
|
2021-01-01 20:49:09 +08:00
|
|
|
"regexp"
|
2020-10-10 11:49:21 +08:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
var accessLogDBMapping = map[int64]*dbs.DB{} // dbNodeId => DB
|
2020-10-10 11:49:21 +08:00
|
|
|
var accessLogLocker = &sync.RWMutex{}
|
|
|
|
|
|
2021-07-14 22:43:31 +08:00
|
|
|
type httpAccessLogDefinition struct {
|
|
|
|
|
Name string
|
|
|
|
|
HasRemoteAddr bool
|
2021-08-07 22:04:22 +08:00
|
|
|
HasDomain bool
|
2021-07-21 08:08:31 +08:00
|
|
|
Exists bool
|
2021-07-14 22:43:31 +08:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
// HTTP服务访问
|
2022-03-08 19:55:39 +08:00
|
|
|
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
|
2021-06-02 11:53:24 +08:00
|
|
|
|
|
|
|
|
// DNS服务访问
|
|
|
|
|
var nsAccessLogDAOMapping = map[int64]*NSAccessLogDAOWrapper{} // dbNodeId => DAO
|
|
|
|
|
var nsAccessLogTableMapping = map[string]bool{} // tableName_crc(dsn) => true
|
|
|
|
|
|
|
|
|
|
// HTTPAccessLogDAOWrapper HTTP访问日志DAO
|
2020-10-10 19:21:32 +08:00
|
|
|
type HTTPAccessLogDAOWrapper struct {
|
|
|
|
|
DAO *HTTPAccessLogDAO
|
|
|
|
|
NodeId int64
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
// NSAccessLogDAOWrapper NS访问日志DAO
|
|
|
|
|
type NSAccessLogDAOWrapper struct {
|
|
|
|
|
DAO *NSAccessLogDAO
|
|
|
|
|
NodeId int64
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-10 11:49:21 +08:00
|
|
|
func init() {
|
|
|
|
|
initializer := NewDBNodeInitializer()
|
2021-01-19 22:06:10 +08:00
|
|
|
dbs.OnReadyDone(func() {
|
2021-12-14 10:49:29 +08:00
|
|
|
goman.New(func() {
|
|
|
|
|
initializer.Start()
|
|
|
|
|
})
|
2020-10-13 20:05:13 +08:00
|
|
|
})
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取获取DAO
|
2021-06-02 11:53:24 +08:00
|
|
|
func randomHTTPAccessLogDAO() (dao *HTTPAccessLogDAOWrapper) {
|
|
|
|
|
accessLogLocker.RLock()
|
|
|
|
|
if len(httpAccessLogDAOMapping) == 0 {
|
|
|
|
|
dao = nil
|
|
|
|
|
} else {
|
|
|
|
|
for _, d := range httpAccessLogDAOMapping {
|
|
|
|
|
dao = d
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
accessLogLocker.RUnlock()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func randomNSAccessLogDAO() (dao *NSAccessLogDAOWrapper) {
|
2020-10-10 11:49:21 +08:00
|
|
|
accessLogLocker.RLock()
|
2021-06-02 11:53:24 +08:00
|
|
|
if len(nsAccessLogDAOMapping) == 0 {
|
2020-10-10 11:49:21 +08:00
|
|
|
dao = nil
|
|
|
|
|
} else {
|
2021-06-02 11:53:24 +08:00
|
|
|
for _, d := range nsAccessLogDAOMapping {
|
2020-10-10 11:49:21 +08:00
|
|
|
dao = d
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
accessLogLocker.RUnlock()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
|
|
|
|
|
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
|
|
|
|
|
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config, err := db.Config()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tableName = "edgeNSAccessLogs_" + day
|
|
|
|
|
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
|
|
|
|
|
|
|
|
|
accessLogLocker.RLock()
|
|
|
|
|
_, ok = nsAccessLogTableMapping[cacheKey]
|
2020-10-10 19:21:32 +08:00
|
|
|
accessLogLocker.RUnlock()
|
|
|
|
|
if ok {
|
|
|
|
|
return tableName, true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tableNames, err := db.TableNames()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return tableName, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-20 16:20:33 +08:00
|
|
|
return tableName, utils.ContainsStringInsensitive(tableNames, tableName), nil
|
2020-10-10 19:21:32 +08:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
func findNSAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
|
|
|
|
config, err := db.Config()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tableName := "edgeNSAccessLogs_" + day
|
|
|
|
|
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
|
|
|
|
|
|
|
|
|
if !force {
|
|
|
|
|
accessLogLocker.RLock()
|
|
|
|
|
_, ok := nsAccessLogTableMapping[cacheKey]
|
|
|
|
|
accessLogLocker.RUnlock()
|
|
|
|
|
if ok {
|
|
|
|
|
return tableName, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tableNames, err := db.TableNames()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return tableName, err
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-20 16:20:33 +08:00
|
|
|
if utils.ContainsStringInsensitive(tableNames, tableName) {
|
2021-06-02 11:53:24 +08:00
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
nsAccessLogTableMapping[cacheKey] = true
|
|
|
|
|
accessLogLocker.Unlock()
|
|
|
|
|
return tableName, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建表格
|
2021-08-09 15:19:38 +08:00
|
|
|
_, err = db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `domainId` int(11) unsigned DEFAULT '0' COMMENT '域名ID',\n `recordId` int(11) unsigned DEFAULT '0' COMMENT '记录ID',\n `content` json DEFAULT NULL COMMENT '访问数据',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `remoteAddr` varchar(128) DEFAULT NULL COMMENT 'IP',\n PRIMARY KEY (`id`),\n KEY `nodeId` (`nodeId`),\n KEY `domainId` (`domainId`),\n KEY `recordId` (`recordId`),\n KEY `requestId` (`requestId`),\n KEY `remoteAddr` (`remoteAddr`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='域名服务访问日志';")
|
2021-06-02 11:53:24 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return tableName, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
nsAccessLogTableMapping[cacheKey] = true
|
2020-10-10 11:49:21 +08:00
|
|
|
accessLogLocker.Unlock()
|
|
|
|
|
|
|
|
|
|
return tableName, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
// DBNodeInitializer 初始化数据库连接
|
2020-10-10 11:49:21 +08:00
|
|
|
type DBNodeInitializer struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewDBNodeInitializer() *DBNodeInitializer {
|
|
|
|
|
return &DBNodeInitializer{}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
// Start 启动
|
2020-10-10 11:49:21 +08:00
|
|
|
func (this *DBNodeInitializer) Start() {
|
|
|
|
|
// 初始运行
|
|
|
|
|
err := this.loop()
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", err.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定时运行
|
|
|
|
|
ticker := time.NewTicker(60 * time.Second)
|
|
|
|
|
for range ticker.C {
|
|
|
|
|
err := this.loop()
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", err.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 单次运行
|
|
|
|
|
func (this *DBNodeInitializer) loop() error {
|
2021-01-01 23:31:30 +08:00
|
|
|
dbNodes, err := SharedDBNodeDAO.FindAllEnabledAndOnDBNodes(nil)
|
2020-10-10 11:49:21 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nodeIds := []int64{}
|
|
|
|
|
for _, node := range dbNodes {
|
|
|
|
|
nodeIds = append(nodeIds, int64(node.Id))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关掉老的
|
|
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
closingDbs := []*dbs.DB{}
|
|
|
|
|
for nodeId, db := range accessLogDBMapping {
|
2020-10-20 20:18:06 +08:00
|
|
|
if !lists.ContainsInt64(nodeIds, nodeId) {
|
2020-10-10 11:49:21 +08:00
|
|
|
closingDbs = append(closingDbs, db)
|
|
|
|
|
delete(accessLogDBMapping, nodeId)
|
2021-06-02 11:53:24 +08:00
|
|
|
delete(httpAccessLogDAOMapping, nodeId)
|
|
|
|
|
delete(nsAccessLogDAOMapping, nodeId)
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "close db node '"+strconv.FormatInt(nodeId, 10)+"'")
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
accessLogLocker.Unlock()
|
|
|
|
|
for _, db := range closingDbs {
|
|
|
|
|
_ = db.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 启动新的
|
|
|
|
|
for _, node := range dbNodes {
|
|
|
|
|
nodeId := int64(node.Id)
|
|
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
db, ok := accessLogDBMapping[nodeId]
|
|
|
|
|
accessLogLocker.Unlock()
|
|
|
|
|
|
|
|
|
|
dsn := node.Username + ":" + node.Password + "@tcp(" + node.Host + ":" + fmt.Sprintf("%d", node.Port) + ")/" + node.Database + "?charset=utf8mb4&timeout=10s"
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
// 检查配置是否有变化
|
|
|
|
|
oldConfig, err := db.Config()
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "read database old config failed: "+err.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果有变化则关闭
|
|
|
|
|
if oldConfig.Dsn != dsn {
|
|
|
|
|
_ = db.Close()
|
|
|
|
|
db = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if db == nil {
|
2022-04-08 14:15:45 +08:00
|
|
|
var config = &dbs.DBConfig{
|
2020-10-10 11:49:21 +08:00
|
|
|
Driver: "mysql",
|
|
|
|
|
Dsn: dsn,
|
|
|
|
|
Prefix: "edge",
|
|
|
|
|
}
|
|
|
|
|
db, err := dbs.NewInstanceFromConfig(config)
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "initialize database config failed: "+err.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查表是否存在
|
2021-06-02 11:53:24 +08:00
|
|
|
// httpAccessLog
|
|
|
|
|
{
|
2022-03-08 19:55:39 +08:00
|
|
|
tableDef, err := SharedHTTPAccessLogManager.FindTable(db, timeutil.Format("Ymd"), true)
|
2021-06-02 11:53:24 +08:00
|
|
|
if err != nil {
|
2022-03-08 19:55:39 +08:00
|
|
|
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
2021-06-02 11:53:24 +08:00
|
|
|
|
2022-03-08 19:55:39 +08:00
|
|
|
// 创建节点日志
|
|
|
|
|
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
|
|
|
|
if createLogErr != nil {
|
|
|
|
|
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
2022-03-08 19:55:39 +08:00
|
|
|
|
|
|
|
|
continue
|
2021-06-02 11:53:24 +08:00
|
|
|
}
|
2020-10-10 11:49:21 +08:00
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
daoObject := dbs.DAOObject{
|
|
|
|
|
Instance: db,
|
|
|
|
|
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
|
2021-07-14 22:43:31 +08:00
|
|
|
Table: tableDef.Name,
|
2021-06-02 11:53:24 +08:00
|
|
|
PkName: "id",
|
|
|
|
|
Model: new(HTTPAccessLog),
|
|
|
|
|
}
|
|
|
|
|
err = daoObject.Init()
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "initialize dao failed: "+err.Error())
|
2020-10-10 11:49:21 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
accessLogDBMapping[nodeId] = db
|
|
|
|
|
dao := &HTTPAccessLogDAO{
|
|
|
|
|
DAOObject: daoObject,
|
|
|
|
|
}
|
|
|
|
|
httpAccessLogDAOMapping[nodeId] = &HTTPAccessLogDAOWrapper{
|
|
|
|
|
DAO: dao,
|
|
|
|
|
NodeId: nodeId,
|
|
|
|
|
}
|
|
|
|
|
accessLogLocker.Unlock()
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:53:24 +08:00
|
|
|
// nsAccessLog
|
|
|
|
|
{
|
|
|
|
|
tableName, err := findNSAccessLogTable(db, timeutil.Format("Ymd"), false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
2021-06-02 11:53:24 +08:00
|
|
|
|
|
|
|
|
// 创建节点日志
|
2021-11-30 16:43:16 +08:00
|
|
|
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
2021-06-02 11:53:24 +08:00
|
|
|
if createLogErr != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
2021-06-02 11:53:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
err = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
daoObject := dbs.DAOObject{
|
|
|
|
|
Instance: db,
|
|
|
|
|
DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")",
|
|
|
|
|
Table: tableName,
|
|
|
|
|
PkName: "id",
|
|
|
|
|
Model: new(NSAccessLog),
|
|
|
|
|
}
|
|
|
|
|
err = daoObject.Init()
|
|
|
|
|
if err != nil {
|
2021-08-30 10:56:31 +08:00
|
|
|
remotelogs.Error("DB_NODE", "initialize dao failed: "+err.Error())
|
2021-06-02 11:53:24 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
accessLogLocker.Lock()
|
|
|
|
|
accessLogDBMapping[nodeId] = db
|
|
|
|
|
dao := &NSAccessLogDAO{
|
|
|
|
|
DAOObject: daoObject,
|
|
|
|
|
}
|
|
|
|
|
nsAccessLogDAOMapping[nodeId] = &NSAccessLogDAOWrapper{
|
|
|
|
|
DAO: dao,
|
|
|
|
|
NodeId: nodeId,
|
|
|
|
|
}
|
|
|
|
|
accessLogLocker.Unlock()
|
2020-10-10 19:21:32 +08:00
|
|
|
}
|
2020-10-10 11:49:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|