Apply .gitignore rules

This commit is contained in:
皮蛋13361098506
2025-01-06 16:21:36 +08:00
parent 1b77f62820
commit ccd2c530cf
580 changed files with 69806 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
package signalMgr
import (
"fmt"
"os"
"os/signal"
"syscall"
"Framework/exitMgr"
"Framework/goroutineMgr"
"Framework/reloadMgr"
"goutil/debugUtil"
"goutil/logUtil"
)
// Start ...启动信号管理器
func Start() {
go func() {
goroutineName := "signalMgr.Start"
goroutineMgr.Monitor(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
for {
// 准备接收信息
sig := <-sigs
// 输出信号
debugUtil.Println("sig:", sig)
if sig == syscall.SIGHUP {
logUtil.InfoLog("收到重启的信号,准备重新加载配置")
// 重新加载
errList := reloadMgr.Reload()
for _, err := range errList {
logUtil.ErrorLog(fmt.Sprintf("重启失败,错误信息为:%s", err))
}
logUtil.InfoLog("收到重启的信号,重新加载配置完成")
} else {
logUtil.InfoLog("收到退出程序的信号,开始退出……")
// 调用退出的方法
exitMgr.Exit()
logUtil.InfoLog("收到退出程序的信号,退出完成……")
// 一旦收到信号,则表明管理员希望退出程序,则先保存信息,然后退出
os.Exit(0)
}
}
}()
}

View File

@@ -0,0 +1,119 @@
package mysqlSync
import (
"database/sql"
"fmt"
"sync"
"Framework/dataSyncMgr/mysqlSync/logSqlSync"
"Framework/dataSyncMgr/mysqlSync/sqlSync"
"goutil/debugUtil"
"goutil/logUtil"
)
// 数据同步管理
type SyncMgr struct {
// 服务器组Id
serverGroupId int32
// 同步数据的存储路径
dirPath string
// 大文件对象size
maxFileSize int
// 数据库对象
dbObj *sql.DB
// 同步对象集合
syncObjMap map[string]*sqlSync.SyncObject
// 同步对象锁
mutex sync.RWMutex
// 新建实例对象的函数
newInstanceFunc func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject
}
// 注册同步对象
// identifier:当前数据的唯一标识(可以使用数据库表名)
func (this *SyncMgr) RegisterSyncObj(identifier string) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 判断是否设置了相同的唯一标识,以免弄混淆
if _, exists := this.syncObjMap[identifier]; exists {
prefix := fmt.Sprintf("%s-%s", identifier, "SyncMgr.RegisterSyncObj")
err := fmt.Errorf("%s has already existed, please change another identifier", prefix)
logUtil.ErrorLog(err.Error())
panic(err)
}
syncObj := this.newInstanceFunc(this, identifier)
syncObj.Init(this.maxFileSize)
this.syncObjMap[identifier] = syncObj
if debugUtil.IsDebug() {
fmt.Printf("%s同步对象成功注册进SyncMgr, 当前有%d个同步对象\n", identifier, len(this.syncObjMap))
}
}
// 保存数据
// identifier:当前数据的唯一标识(可以使用数据库表名)
// command:sql命令
func (this *SyncMgr) Save(identifier string, command string) {
this.mutex.RLock()
defer this.mutex.RUnlock()
syncObj, exists := this.syncObjMap[identifier]
if !exists {
err := fmt.Errorf("syncObj:%s does not existed, please register first", identifier)
logUtil.ErrorLog(err.Error())
panic(err)
}
syncObj.Save(command)
}
// 构造同步管理对象
// serverGroupId:服务器组Id
// dirPath: 文件目录
// maxFileSize: 大文件对象大小
// survivalTime: 同步数据存活时间 (单位hour)
// dbObj: 数据库对象
func NewSyncMgr(serverGroupId int32, dirPath string, maxFileSize int, survivalTime int, dbObj *sql.DB) *SyncMgr {
result := &SyncMgr{
serverGroupId: serverGroupId,
dirPath: dirPath,
maxFileSize: maxFileSize,
dbObj: dbObj,
syncObjMap: make(map[string]*sqlSync.SyncObject),
newInstanceFunc: func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject {
handler := newSyncObject(mgr.dirPath, identifier, mgr.dbObj)
return sqlSync.NewSyncObject(mgr.dirPath, identifier, mgr.dbObj, handler)
},
}
return result
}
// 新建日志同步管理对象
// serverGroupId:服务器组Id
// dirPath: 文件目录
// maxFileSize: 大文件对象大小
// dbObj: 数据库对象
func NewLogSyncMgr(serverGroupId int32, dirPath string, maxFileSize int, dbObj *sql.DB) *SyncMgr {
result := &SyncMgr{
serverGroupId: serverGroupId,
dirPath: dirPath,
maxFileSize: maxFileSize,
dbObj: dbObj,
syncObjMap: make(map[string]*sqlSync.SyncObject),
newInstanceFunc: func(mgr *SyncMgr, identifier string) *sqlSync.SyncObject {
handler := logSqlSync.NewSyncObject(mgr.serverGroupId, mgr.dirPath, identifier, mgr.dbObj)
return sqlSync.NewSyncObject(mgr.dirPath, identifier, mgr.dbObj, handler)
},
}
return result
}

View File

@@ -0,0 +1,119 @@
/*
redisUtil对Redis的连接池进行了一定程度的封装
将常用的方法进行了内部封装,对于不常见的方法,有两种处理方式:
1、向作者提出请求由作者添加到代码中
2、调用GetConnection方法然后自己实现逻辑
在代码中统一将conn.Do的结果和redis.Int,redis.String等类型转换合并处理
redis的命令请参考https://redis.readthedocs.io/en/2.6/index.html
*/
package redisUtil
import (
"fmt"
"time"
"github.com/gomodule/redigo/redis"
)
// 自定义Redis连接池对象
type RedisPool struct {
name string
address string
pool *redis.Pool
}
// 获取自定义Redis连接池对象的名称
// 返回值:
// 自定义Redis连接池对象的名称
func (this *RedisPool) GetName() string {
return this.name
}
// 获取自定义Redis连接池对象的目标地址
// 返回值:
// 自定义Redis连接池对象的目标地址
func (this *RedisPool) GetAddress() string {
return this.address
}
// 从自定义连接池中获取连接在使用后需要调用Close方法
// 返回值:
// 连接对象
func (this *RedisPool) GetConnection() redis.Conn {
return this.pool.Get()
}
// 关闭自定义连接池
func (this *RedisPool) Close() {
this.pool.Close()
}
// 测试连接情况
// 返回值:
// 错误对象
func (this *RedisPool) TestConnection() error {
conn := this.GetConnection()
defer conn.Close()
_, err := conn.Do("PING")
return err
}
// 创建新的Redis连接池对象(obsolete建议使用NewRedisPool2)
// name:连接池对象名称
// connectionString:Redis服务器连接地址
// passwordRedis服务器连接密码
// databaseRedis服务器选择的数据库
// maxActiveRedis连接池允许的最大活跃连接数量
// maxIdleRedis连接池允许的最大空闲数量
// idleTimeout连接被回收前的空闲时间
// dialConnectTimeout连接Redis服务器超时时间
// 返回值:
// Redis连接池对象
func NewRedisPool(name, connectionString, password string, database, maxActive, maxIdle int, idleTimeout, dialConnectTimeout time.Duration) *RedisPool {
redisConfig := NewRedisConfig2(connectionString, password, database, maxActive, maxIdle, idleTimeout, dialConnectTimeout)
return NewRedisPool2(name, redisConfig)
}
// 创建新的Redis连接池对象
// name:连接池对象名称
// redisConfig:Redis配置对象
// 返回值:
// Redis连接池对象
func NewRedisPool2(name string, redisConfig *RedisConfig) *RedisPool {
poolObj := &redis.Pool{
MaxActive: redisConfig.MaxActive,
MaxIdle: redisConfig.MaxIdle,
IdleTimeout: redisConfig.IdleTimeout,
Dial: func() (redis.Conn, error) {
options := make([]redis.DialOption, 0, 4)
options = append(options, redis.DialConnectTimeout(redisConfig.DialConnectTimeout))
options = append(options, redis.DialDatabase(redisConfig.Database))
if redisConfig.Password != "" {
options = append(options, redis.DialPassword(redisConfig.Password))
}
conn, err := redis.Dial("tcp", redisConfig.ConnectionString, options...)
if err != nil {
return nil, fmt.Errorf("Dial failed, err:%s", err)
}
return conn, err
},
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := conn.Do("PING")
return err
},
}
return &RedisPool{
name: name,
address: redisConfig.ConnectionString,
pool: poolObj,
}
}

View File

@@ -0,0 +1,58 @@
package ensureSendUtil
import (
"fmt"
"goutil/fileUtil"
"goutil/logUtil"
"goutil/stringUtil"
)
// 从目录加载缓存数据并发送
func loadData(s EnsureSender, folder string) error {
if fileList, err := fileUtil.GetFileList(folder); err != nil {
return err
} else {
for _, filename := range fileList {
// 读取发送内容
if fileContent, err := fileUtil.ReadFileContent(filename); err != nil {
// 打印错误
log := fmt.Sprintf("ensureSendUtil.loadData: Failed To Read File: %s %s\n", err, filename)
logUtil.NormalLog(log, logUtil.Error)
} else if err = fileUtil.DeleteFile(filename); err != nil {
// 删除文件,如果成功则将内容添加到通道中,否则不处理
log := fmt.Sprintf("ensureSendUtil.loadData: Failed To Delete File: %s %s", err, filename)
logUtil.NormalLog(log, logUtil.Error)
} else {
// 发送数据
s.Write(fileContent)
}
}
}
return nil
}
// 保存数据到文件中(通常在退出时调用)
func saveData(datas <-chan dataItem, folder string) (failed []dataItem, err error) {
defer func() {
if len(failed) > 0 {
err = fmt.Errorf("保存数据时有%d个失败数据", len(failed))
}
}()
for {
select {
case v := <-datas:
filename := stringUtil.GetNewGUID()
if e := fileUtil.WriteFile(folder, filename, false, v.String()); e != nil {
failed = append(failed, v)
log := fmt.Sprintf("ensureSendUtil.saveData: 写入错误\n目录%s文件%s错误信息为%s, Data:%s",
folder, filename, err, v.String())
logUtil.NormalLog(log, logUtil.Error)
}
default:
return
}
}
}

View File

@@ -0,0 +1,321 @@
package configUtil
import (
"fmt"
"testing"
"goutil/xmlUtil"
)
// bool值读取测试
func TestBool(t *testing.T) {
xmlConfigData, errMsg := getxmlConfigData()
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
var ispost bool
ispost, errMsg = xmlConfigData.Bool("html/body", "IsPost")
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
fmt.Println("读取到的值:", ispost)
if ispost == false {
t.Error("html/body的isPost读取错误")
t.Fail()
return
}
ispost = xmlConfigData.DefaultBool("html/body", "IsPost", false)
if ispost == false {
t.Error("html/body的isPost读取错误")
t.Fail()
}
}
// int值读取测试
func TestInt(t *testing.T) {
xmlConfigData, errMsg := getxmlConfigData()
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
var id int
id, errMsg = xmlConfigData.Int("html/body/ul/li/a[@id=1]", "id")
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
if id != 1 {
t.Errorf("html/body的isPost读取错误读取到的值:%v", id)
t.Fail()
return
}
id = xmlConfigData.DefaultInt("html/body", "id", 2)
if id != 2 {
t.Error("TestInt html/body的id读取错误")
t.Fail()
}
}
// int64值读取测试
func TestInt64(t *testing.T) {
xmlConfigData, errMsg := getxmlConfigData()
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
var id int64
id, errMsg = xmlConfigData.Int64("html/body/ul/li/a[@id=1]", "id")
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
if id != 1 {
t.Errorf("TestInt64 html/body/ul/li/a[@id=1]的id读取错误读取到的值:%v", id)
t.Fail()
return
}
id = xmlConfigData.DefaultInt64("html/body", "id", 2)
if id != 2 {
t.Error("TestInt64 html/body的id读取错误")
t.Fail()
}
}
// Float值读取测试
func TestFloat(t *testing.T) {
xmlConfigData, errMsg := getxmlConfigData()
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
var id float64
id, errMsg = xmlConfigData.Float("html/body/ul/li/a[@id=1]", "dd")
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
if id != 1.1 {
t.Errorf("TestFloat html/body/ul/li/a[@id=1]的id读取错误读取到的值:%v", id)
t.Fail()
return
}
id = xmlConfigData.DefaultFloat("html/body", "id", 2)
if id != 2 {
t.Error("TestFloat html/body的id读取错误")
t.Fail()
}
}
// 字符串读取测试
func TestString(t *testing.T) {
xmlConfigData, errMsg := getxmlConfigData()
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
var id string
id, errMsg = xmlConfigData.String("html/body/ul/li/a[@id=1]", "dd")
if errMsg != nil {
t.Error(errMsg)
t.Fail()
return
}
if id != "1.1" {
t.Errorf("TestString html/body/ul/li/a[@id=1]的id读取错误读取到的值:%v", id)
t.Fail()
return
}
id = xmlConfigData.DefaultString("html/body", "id", "2")
if id != "2" {
t.Error("TestString html/body的id读取错误")
t.Fail()
}
}
type HelloStruct struct {
// 连接字符串
ConnectionString string
// 最大开启连接数量
MaxOpenConns int `xml:",attr"`
// 最大空闲连接数量
MaxIdleConns int `xml:",attr"`
}
func (this *HelloStruct) Equal(other *HelloStruct) bool {
return this.MaxOpenConns == other.MaxOpenConns && this.MaxIdleConns == other.MaxIdleConns
}
func TestUnmarshal(t *testing.T) {
data, _ := getxmlConfigData2(`
<DBConnection>
<GameServerCenterDB MaxOpenConns="10" MaxIdleConns="5">
<![CDATA[
root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s
]]>
</GameServerCenterDB>
</DBConnection>
`)
val := &HelloStruct{}
err := data.Unmarshal("/DBConnection/GameServerCenterDB", val)
if err != nil {
t.Error(err)
return
}
want := &HelloStruct{
ConnectionString: "root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s",
MaxOpenConns: 10,
MaxIdleConns: 5,
}
if want.Equal(val) == false {
t.Errorf("Expected %v, but now got %v", want, val)
}
}
type ConnsNum int
type Hello2Struct struct {
// 连接字符串
ConnectionString string
// 最大开启连接数量
MaxOpenConns ConnsNum
// 最大空闲连接数量
MaxIdleConns ConnsNum
}
func (this *Hello2Struct) Equal(other *Hello2Struct) bool {
return this.MaxOpenConns == other.MaxOpenConns && this.MaxIdleConns == other.MaxIdleConns
}
func TestUnmarshal2(t *testing.T) {
data, _ := getxmlConfigData2(`
<DBConnection>
<GameServerCenterDB MaxOpenConns="10" MaxIdleConns="5">
<![CDATA[
root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s
]]>
</GameServerCenterDB>
</DBConnection>
`)
val := &Hello2Struct{}
err := data.Unmarshal("/DBConnection/GameServerCenterDB", val)
if err != nil {
t.Error(err)
return
}
want := &Hello2Struct{
ConnectionString: "root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s",
MaxOpenConns: 10,
MaxIdleConns: 5,
}
if want.Equal(val) == false {
t.Errorf("Expected %v, but now got %v", want, val)
}
}
func TestUnmarshal3(t *testing.T) {
data, _ := getxmlConfigData2(`
<DBConnection>
<GameServerCenterDB MaxOpenConns="10">
<![CDATA[
root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s
]]>
</GameServerCenterDB>
</DBConnection>
`)
val := &HelloStruct{}
err := data.Unmarshal("/DBConnection/GameServerCenterDB", val)
if err != nil {
t.Error(err)
return
}
want := &HelloStruct{
ConnectionString: "root:moqikaka3312@tcp(10.1.0.10:3312)/2_gsc_develop?charset=utf8&parseTime=true&loc=Local&timeout=60s",
MaxOpenConns: 10,
MaxIdleConns: 0,
}
if want.Equal(val) == false {
t.Errorf("Expected %v, but now got %v", want, val)
}
}
func getxmlConfigData() (xmlConfigData *XmlConfig, errMsg error) {
content := `
<html lang="en">
<head>
<title>Hello</title>
<meta name="language" content="en"/>
</head>
<body IsPost='true'>
<h1> This is a H1 </h1>
<ul>
<li><a id="1" dd='1.1' href="/">Home</a></li>
<li><a id="2" href="/about">about</a></li>
<li><a id="3" href="/account">login</a></li>
<li></li>
</ul>
<p>
Hello,This is an example for gxpath.
</p>
<footer>footer script</footer>
</body>
</html>
`
var root *xmlUtil.Node
root, errMsg = xmlUtil.LoadFromString(content)
if errMsg == nil {
xmlConfigData = NewXmlConfig()
xmlConfigData.LoadFromXmlNode(root)
}
return
}
func getxmlConfigData2(xml string) (xmlConfigData *XmlConfig, errMsg error) {
var root *xmlUtil.Node
root, errMsg = xmlUtil.LoadFromString(xml)
if errMsg == nil {
xmlConfigData = NewXmlConfig()
xmlConfigData.LoadFromXmlNode(root)
}
return
}