初始化项目
This commit is contained in:
13
trunk/framework/dataSyncMgr/mysqlSync/doc.go
Normal file
13
trunk/framework/dataSyncMgr/mysqlSync/doc.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package mysqlSync
|
||||
|
||||
/*
|
||||
提供数据同步到mysql的方法。基本逻辑如下:
|
||||
1、对外接收数据,以追加的方式保存到大文件中。数据的格式为:header(4bytes)+content。
|
||||
2、启动独立的goroutine来从大文件中读取数据,并保存到数据库中。
|
||||
3、使用syncInfo.txt文件保存当前已经处理的文件的路径,以及下一次将要读取的文件的Offset。为了降低向syncInfo.txt文件中写入失败,
|
||||
导致需要从头开始同步数据,所以采用了在指定数目的范围内以追加形式来写入数据的方式;只有达到了指定数量才会将整个文件清空。
|
||||
|
||||
对于错误的处理方式,分为以下两种:
|
||||
1、文件错误:由于文件系统是本系统的核心,所以如果出现文件的读写出错,则需要终止整个进程,所以需要抛出panic。
|
||||
2、数据库错误:当数据库不可访问时,为了不影响整个外部进程的运行,故而不抛出panic,而只是通过monitorNewMgr.Report的方式来报告故障。
|
||||
*/
|
||||
100
trunk/framework/dataSyncMgr/mysqlSync/errorHandle.go
Normal file
100
trunk/framework/dataSyncMgr/mysqlSync/errorHandle.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package mysqlSync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"goutil/fileUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
// 记录错误sql命令的文件名
|
||||
con_Error_FileName = "errorFile.txt"
|
||||
)
|
||||
|
||||
// 定义处理错误命令的文件对象
|
||||
type errorFile struct {
|
||||
// 错误文件
|
||||
file *os.File
|
||||
|
||||
// 文件路径
|
||||
filePath string
|
||||
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
}
|
||||
|
||||
// 保存命令到错误文件
|
||||
// command: sql命令
|
||||
func (this *errorFile) SaveCommand(command string) {
|
||||
this.open()
|
||||
defer this.close()
|
||||
|
||||
// 覆盖写入
|
||||
this.file.Seek(0, 0)
|
||||
|
||||
// 写入命令
|
||||
_, err := this.file.WriteString(command)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.SaveCommand")
|
||||
err = fmt.Errorf("%s-Write %s to file failed:%s", prefix, command, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 清理残留数据
|
||||
this.file.Truncate(int64(len(command)))
|
||||
}
|
||||
|
||||
// 读取文件中命令
|
||||
func (this *errorFile) ReadCommand() string {
|
||||
this.open()
|
||||
defer this.close()
|
||||
|
||||
this.file.Seek(0, 0)
|
||||
content, err := ioutil.ReadAll(this.file)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.ReadCommand")
|
||||
err = fmt.Errorf("%s-Read command failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
func (this *errorFile) open() {
|
||||
// 打开errorFile文件, 如果没有就创建
|
||||
var err error
|
||||
this.file, err = os.OpenFile(this.filePath, os.O_CREATE|os.O_RDWR, os.ModePerm|os.ModeTemporary)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "errorFile.newErrorFile.os.OpenFile")
|
||||
err = fmt.Errorf("%s-Open File failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭文件
|
||||
func (this *errorFile) close() {
|
||||
this.file.Close()
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
func (this *errorFile) Delete() {
|
||||
fileUtil.DeleteFile(this.filePath)
|
||||
}
|
||||
|
||||
// 构造错误文件对象
|
||||
// _dirPath:文件路径
|
||||
// _identifier:唯一标识
|
||||
func newErrorFile(_dirPath string, _identifier string) *errorFile {
|
||||
_filePath := filepath.Join(_dirPath, con_Error_FileName)
|
||||
return &errorFile{
|
||||
filePath: _filePath,
|
||||
identifier: _identifier,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package logSqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 错误信息记录表是否已经初始化
|
||||
var ifSyncErrorInfoTableInited bool = false
|
||||
|
||||
// 同步的错误信息处理对象
|
||||
type syncErrorInfo struct {
|
||||
// 数据库连接对象
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// 初始化表信息
|
||||
func (this *syncErrorInfo) init() error {
|
||||
// 初始化表结构
|
||||
if ifSyncErrorInfoTableInited == false {
|
||||
err := this.initTable(this.db)
|
||||
if err == nil {
|
||||
ifSyncErrorInfoTableInited = true
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 把同步信息更新到数据库
|
||||
// data:待更新的数据
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncErrorInfo) AddErrorSql(tran *sql.Tx, data string, errMsg string) error {
|
||||
updateSql := "INSERT INTO `sync_error_info` (`SqlString`,`ExecuteTime`,`RetryCount`,`ErrMessage`) VALUES(?,?,?,?);"
|
||||
|
||||
var err error
|
||||
if tran != nil {
|
||||
_, err = tran.Exec(updateSql, data, time.Now(), 0, errMsg)
|
||||
} else {
|
||||
_, err = this.db.Exec(updateSql, data, time.Now(), 0, errMsg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncErrorInfo.AddErrorSql Error:%s", err.Error()))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 初始化同步信息表结构
|
||||
// db:数据库连接对象
|
||||
func (this *syncErrorInfo) initTable(db *sql.DB) error {
|
||||
// 创建同步信息表
|
||||
createTableSql := `CREATE TABLE IF NOT EXISTS sync_error_info (
|
||||
Id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增Id',
|
||||
SqlString varchar(1024) NOT NULL COMMENT '执行的sql',
|
||||
ExecuteTime datetime NOT NULL COMMENT '最近一次执行时间',
|
||||
RetryCount int NOT NULL COMMENT '重试次数',
|
||||
ErrMessage text NULL COMMENT '执行错误的信息',
|
||||
PRIMARY KEY (Id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='未执行成功的sql数据';`
|
||||
if _, err := db.Exec(createTableSql); err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncErrorInfo.initTable Error:%s", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建同步信息对象
|
||||
// _db:数据库连接对象
|
||||
// 返回值:
|
||||
// 同步信息对象
|
||||
func newSyncErrorInfoObject(_db *sql.DB) (result *syncErrorInfo, err error) {
|
||||
result = &syncErrorInfo{
|
||||
db: _db,
|
||||
}
|
||||
|
||||
err = result.init()
|
||||
|
||||
return result, err
|
||||
}
|
||||
204
trunk/framework/dataSyncMgr/mysqlSync/logSqlSync/syncObject.go
Normal file
204
trunk/framework/dataSyncMgr/mysqlSync/logSqlSync/syncObject.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package logSqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"Framework/dataSyncMgr/mysqlSync/sqlSync"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 同步对象定义
|
||||
type SyncObject struct {
|
||||
// 服务器组Id
|
||||
serverGroupId int32
|
||||
|
||||
// 同步数据的存储路径
|
||||
dirPath string
|
||||
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
|
||||
// 数据库对象
|
||||
dbObj *sql.DB
|
||||
|
||||
// 同步信息对象
|
||||
syncingInfoObj *syncingInfo
|
||||
|
||||
// 错误处理对象
|
||||
errorHandleObj *syncErrorInfo
|
||||
|
||||
// 同步对象
|
||||
syncObj *sqlSync.SyncObject
|
||||
}
|
||||
|
||||
// 初始化
|
||||
// baseObj:基础同步对象
|
||||
func (this *SyncObject) Init(baseObj *sqlSync.SyncObject) {
|
||||
this.syncObj = baseObj
|
||||
|
||||
// 初始化同步信息对象
|
||||
syncingInfoObj, err := newSyncingInfoObject(this.serverGroupId, this.dbObj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
//// 初始化错误处理对象
|
||||
errorHandleObj, err := newSyncErrorInfoObject(this.dbObj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
this.syncingInfoObj = syncingInfoObj
|
||||
this.errorHandleObj = errorHandleObj
|
||||
|
||||
// 初始化当前处理的文件
|
||||
fileList := sqlSync.GetDataFileList(this.dirPath)
|
||||
filePath, _ := this.syncingInfoObj.GetSyncingInfo()
|
||||
if len(filePath) < 0 && len(fileList) > 0 {
|
||||
this.syncingInfoObj.Update(fileList[0], 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取正在同步的信息
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
func (this *SyncObject) GetSyncingInfo() (filePath string, offset int64) {
|
||||
return this.syncingInfoObj.GetSyncingInfo()
|
||||
}
|
||||
|
||||
// 更新
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
// tran:事务对象
|
||||
// 返回值:
|
||||
// error:错误对象
|
||||
func (this *SyncObject) Update(filePath string, offset int64, tx *sql.Tx) error {
|
||||
return this.syncingInfoObj.Update(filePath, offset, tx)
|
||||
}
|
||||
|
||||
// 同步一条sql语句
|
||||
// command:待执行的命令
|
||||
// filePath:保存路径
|
||||
// offset:文件偏移量
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) SyncOneSql(command string, filePath string, offset int64) {
|
||||
var err error
|
||||
for {
|
||||
err = sqlSync.ExecuteByTran(this.dbObj, func(tran *sql.Tx) (isCommit bool, err error) {
|
||||
// 保存sql到数据库
|
||||
err = this.syncToMysql(command, tran)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存进度信息到数据库
|
||||
err = this.syncingInfoObj.Update(filePath, offset, tran)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isCommit = true
|
||||
return
|
||||
})
|
||||
|
||||
// 如果是连接出错,则仍然循环执行
|
||||
if err != nil {
|
||||
if sqlSync.CheckIfConnectionError(err.Error()) {
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是数据库连接出错,则算是执行完成
|
||||
break
|
||||
}
|
||||
|
||||
// 如果存在错误,则循环尝试执行
|
||||
if err != nil {
|
||||
this.recordSqlError(command, filePath, offset, err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 同步数据到mysql中
|
||||
// command:sql语句
|
||||
// tx:事务处理对象
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
|
||||
_, err := tx.Exec(command)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/logSqlSync/syncObject.syncToMysql error:%s", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 错误处理
|
||||
// cmd:待执行的命令
|
||||
// filePath:保存路径
|
||||
// offset:文件偏移量
|
||||
// errMsg:错误信息
|
||||
func (this *SyncObject) recordSqlError(command string, filePath string, offset int64, errMsg string) {
|
||||
errMsg = sqlSync.GetSimpleErrorMessage(errMsg)
|
||||
|
||||
for {
|
||||
err := sqlSync.ExecuteByTran(this.dbObj, func(tran *sql.Tx) (isCommit bool, err error) {
|
||||
// 保存sql到数据库
|
||||
err = this.errorHandleObj.AddErrorSql(tran, command, errMsg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存进度信息到数据库
|
||||
err = this.syncingInfoObj.Update(filePath, offset, tran)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isCommit = true
|
||||
return
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// 创新新的mysql同步对象
|
||||
// dirPath:存放数据的目录
|
||||
// identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// dbObj:数据库对象
|
||||
// syncingInfoObj:同步信息记录对象
|
||||
// errorHandleObj:错误处理对象
|
||||
// 返回值:
|
||||
// mysql同步对象
|
||||
func NewSyncObject(serverGroupId int32, dirPath, identifier string, dbObj *sql.DB) *SyncObject {
|
||||
dirPath = filepath.Join(dirPath, identifier)
|
||||
|
||||
// 创建更新目录
|
||||
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 构造同步信息对象
|
||||
result := &SyncObject{
|
||||
serverGroupId: serverGroupId,
|
||||
dirPath: dirPath,
|
||||
identifier: identifier,
|
||||
dbObj: dbObj,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
186
trunk/framework/dataSyncMgr/mysqlSync/logSqlSync/syncingInfo.go
Normal file
186
trunk/framework/dataSyncMgr/mysqlSync/logSqlSync/syncingInfo.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package logSqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 同步信息表是否已经被初始化
|
||||
var ifSyncingTableInited bool = false
|
||||
|
||||
// 同步信息项,保存已经处理过的文件的信息
|
||||
type syncingModel struct {
|
||||
// 服务器组Id
|
||||
ServerGroupId int32
|
||||
|
||||
// 待处理文件的绝对路径
|
||||
FilePath string
|
||||
|
||||
// 待处理文件的偏移量
|
||||
FileOffset int64
|
||||
|
||||
// 更新时间
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
||||
// 同步信息对象
|
||||
type syncingInfo struct {
|
||||
// 服务器组Id
|
||||
ServerGroupId int32
|
||||
|
||||
// 同步信息项
|
||||
item *syncingModel
|
||||
|
||||
// 数据库连接对象
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// 获取同步信息
|
||||
// filePath:正在同步的文件
|
||||
// fileOffset:同步到的位置
|
||||
func (this *syncingInfo) GetSyncingInfo() (filePath string, fileOffset int64) {
|
||||
return this.item.FilePath, this.item.FileOffset
|
||||
}
|
||||
|
||||
// 更新正在同步的位置和文件信息
|
||||
// filePath:文件路径
|
||||
// offset:当前同步到的位置
|
||||
// tran:事务对象,可以为nil
|
||||
// 返回值:
|
||||
// error:处理的错误信息
|
||||
func (this *syncingInfo) Update(filePath string, offset int64, tran *sql.Tx) error {
|
||||
this.item.FilePath = filePath
|
||||
this.item.FileOffset = offset
|
||||
this.item.UpdateTime = time.Now()
|
||||
|
||||
// 更新到数据库
|
||||
return this.update(this.item, tran)
|
||||
}
|
||||
|
||||
// 初始化同步信息
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncingInfo) init() error {
|
||||
// 数据表初始化
|
||||
if ifSyncingTableInited == false {
|
||||
if err := this.initSyncingInfoTable(this.db); err == nil {
|
||||
ifSyncingTableInited = true
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 获取此表的同步信息
|
||||
data, exist, err := this.get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 如果同步信息不存在,则初始化一条到此表
|
||||
if exist == false {
|
||||
data = &syncingModel{
|
||||
ServerGroupId: this.ServerGroupId,
|
||||
FilePath: "",
|
||||
FileOffset: 0,
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
this.item = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// 初始化同步信息表结构
|
||||
// db:数据库连接对象
|
||||
func (this *syncingInfo) initSyncingInfoTable(db *sql.DB) error {
|
||||
// 创建同步信息表
|
||||
createTableSql := `CREATE TABLE IF NOT EXISTS syncing_info (
|
||||
ServerGroupId int NOT NULL COMMENT '服务器组Id',
|
||||
FilePath varchar(500) NOT NULL COMMENT '正在同步的文件路径',
|
||||
FileOffset bigint(20) NOT NULL COMMENT '偏移量',
|
||||
UpdateTime datetime NOT NULL COMMENT '最后一次更新时间',
|
||||
PRIMARY KEY (ServerGroupId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='正在同步的文件信息';`
|
||||
if _, err := db.Exec(createTableSql); err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.initSyncingInfoTable Error:%s", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 从数据库获取数据
|
||||
// 返回值:
|
||||
// data:获取到的数据
|
||||
// exist:是否存在此数据
|
||||
// err:错误信息
|
||||
func (this *syncingInfo) get() (data *syncingModel, exist bool, err error) {
|
||||
//// 从数据库查询
|
||||
querySql := fmt.Sprintf("SELECT FilePath,FileOffset,UpdateTime FROM syncing_info WHERE ServerGroupId ='%v'", this.ServerGroupId)
|
||||
var rows *sql.Rows
|
||||
rows, err = this.db.Query(querySql)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.get.Query ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() == false {
|
||||
exist = false
|
||||
return
|
||||
}
|
||||
exist = true
|
||||
|
||||
// 读取数据
|
||||
data = &syncingModel{
|
||||
ServerGroupId: this.ServerGroupId,
|
||||
}
|
||||
err = rows.Scan(&data.FilePath, &data.FileOffset, &data.UpdateTime)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.get.Query ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 把同步信息更新到数据库
|
||||
// data:待更新的数据
|
||||
// tran:事务处理对象
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncingInfo) update(data *syncingModel, tran *sql.Tx) error {
|
||||
updateSql := "REPLACE INTO `syncing_info` SET `ServerGroupId` = ?, `FilePath` = ?,`FileOffset` = ?, `UpdateTime` = ?;"
|
||||
var err error
|
||||
if tran != nil {
|
||||
_, err = tran.Exec(updateSql, data.ServerGroupId, data.FilePath, data.FileOffset, data.UpdateTime)
|
||||
} else {
|
||||
_, err = this.db.Exec(updateSql, data.ServerGroupId, data.FilePath, data.FileOffset, data.UpdateTime)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("logSqlSync/syncingInfo.update ServerGroupId:%v error:%s", this.ServerGroupId, err.Error()))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建同步信息对象
|
||||
// _dirPath:目录的路径
|
||||
// _identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// _db:数据库连接对象
|
||||
// 返回值:
|
||||
// 同步信息对象
|
||||
func newSyncingInfoObject(serverGroupId int32, _db *sql.DB) (result *syncingInfo, err error) {
|
||||
result = &syncingInfo{
|
||||
ServerGroupId: serverGroupId,
|
||||
db: _db,
|
||||
}
|
||||
|
||||
err = result.init()
|
||||
|
||||
return result, err
|
||||
}
|
||||
62
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/dal.go
Normal file
62
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/dal.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/gorm"
|
||||
"Framework/dataSyncMgr/mysqlSync"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var _ = mysql.DeregisterLocalFile
|
||||
|
||||
var (
|
||||
connectionString = "root:moqikaka3309@tcp(10.1.0.10:3309)/develop_liujun?charset=utf8&parseTime=true&loc=Local&timeout=60s"
|
||||
maxOpenConns = 10
|
||||
maxIdleConns = 10
|
||||
|
||||
syncFileSize = 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
// 数据库对象
|
||||
dbObj *gorm.DB
|
||||
|
||||
// 同步管理对象
|
||||
syncMgr *mysqlSync.SyncMgr
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 初始化数据库连接
|
||||
dbObj = initMysql()
|
||||
|
||||
// 构造同步管理对象
|
||||
syncMgr = mysqlSync.NewLogSyncMgr(1, "Sync", syncFileSize, dbObj.DB())
|
||||
}
|
||||
|
||||
// 初始化Mysql
|
||||
func initMysql() *gorm.DB {
|
||||
dbObj, err := gorm.Open("mysql", connectionString)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("初始化数据库:%s失败,错误信息为:%s", connectionString, err))
|
||||
}
|
||||
logUtil.DebugLog(fmt.Sprintf("连接mysql:%s成功", connectionString))
|
||||
|
||||
if maxOpenConns > 0 && maxIdleConns > 0 {
|
||||
dbObj.DB().SetMaxOpenConns(maxOpenConns)
|
||||
dbObj.DB().SetMaxIdleConns(maxIdleConns)
|
||||
}
|
||||
|
||||
return dbObj
|
||||
}
|
||||
|
||||
// 注册同步对象
|
||||
func registerSyncObj(identifier string) {
|
||||
syncMgr.RegisterSyncObj(identifier)
|
||||
}
|
||||
|
||||
// 保存sql数据
|
||||
func save(identifier string, command string) {
|
||||
syncMgr.Save(identifier, command)
|
||||
}
|
||||
82
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/main.go
Normal file
82
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/main.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
wg.Add(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
playerMgr := newPlayerMgr()
|
||||
|
||||
// insert
|
||||
go func() {
|
||||
for {
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s", id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
|
||||
insert(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
/*
|
||||
// update
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
suffix := mathUtil.GetRandInt(1000)
|
||||
newName := fmt.Sprintf("Hero_%d", suffix)
|
||||
obj.resetName(newName)
|
||||
|
||||
update(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// delete
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
playerMgr.delete(obj)
|
||||
|
||||
clear(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// errorFile
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(1 * time.Hour)
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s%s", id, id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
print("errorFile")
|
||||
|
||||
insert(obj)
|
||||
}
|
||||
|
||||
}()
|
||||
*/
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
64
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/player.go
Normal file
64
trunk/framework/dataSyncMgr/mysqlSync/logSyncTest/player.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type player struct {
|
||||
// 玩家id
|
||||
Id string `gorm:"column:Id;primary_key"`
|
||||
|
||||
// 玩家名称
|
||||
Name string `gorm:"column:Name"`
|
||||
}
|
||||
|
||||
func (this *player) resetName(name string) {
|
||||
this.Name = name
|
||||
}
|
||||
|
||||
func (this *player) tableName() string {
|
||||
return "player"
|
||||
}
|
||||
|
||||
func newPlayer(id, name string) *player {
|
||||
return &player{
|
||||
Id: id,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
type playerMgr struct {
|
||||
playerMap map[string]*player
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (this *playerMgr) insert(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
this.playerMap[obj.Id] = obj
|
||||
}
|
||||
|
||||
func (this *playerMgr) delete(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
delete(this.playerMap, obj.Id)
|
||||
}
|
||||
|
||||
func (this *playerMgr) randomSelect() *player {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
for _, obj := range this.playerMap {
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPlayerMgr() *playerMgr {
|
||||
return &playerMgr{
|
||||
playerMap: make(map[string]*player),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//package test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
con_player_tableName = "player"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerSyncObj(con_player_tableName)
|
||||
}
|
||||
|
||||
func insert(obj *player) {
|
||||
command := fmt.Sprintf("INSERT INTO `%s` (`Id`,`Name`) VALUES ('%v','%v') ", con_player_tableName, obj.Id, obj.Name)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
|
||||
func update(obj *player) {
|
||||
command := fmt.Sprintf("UPDATE `%s` SET `Name` = '%v' WHERE `Id` = '%v';", con_player_tableName, obj.Name, obj.Id)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
|
||||
func clear(obj *player) {
|
||||
command := fmt.Sprintf("DELETE FROM %s where Id = '%v';", con_player_tableName, obj.Id)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package sqlSync
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"goutil/fileUtil"
|
||||
"goutil/intAndBytesUtil"
|
||||
)
|
||||
|
||||
const (
|
||||
// 头部字节长度
|
||||
con_Header_Length = 4
|
||||
)
|
||||
|
||||
var (
|
||||
// 字节的大小端顺序
|
||||
byteOrder = binary.LittleEndian
|
||||
)
|
||||
|
||||
// 按照指定方式读取文本内容
|
||||
// fileObj:大文件对象
|
||||
// data:待写入的数据
|
||||
// 返回值:
|
||||
// error:写入是否存在异常
|
||||
func Write(fileObj *fileUtil.BigFile, data string) error {
|
||||
// 获得数据内容的长度
|
||||
dataLength := len(data)
|
||||
|
||||
// 将长度转化为字节数组
|
||||
header := intAndBytesUtil.Int32ToBytes(int32(dataLength), byteOrder)
|
||||
|
||||
// 将头部与内容组合在一起
|
||||
message := append(header, data...)
|
||||
|
||||
// 写入数据
|
||||
return fileObj.WriteMessage(message)
|
||||
}
|
||||
|
||||
// 从文件读取一条数据
|
||||
// fileObj:文件对象
|
||||
// 返回值:
|
||||
// result:读取到的字符串
|
||||
// err:错误信息
|
||||
func Read(fileObj *os.File) (result string, readLen int64, err error) {
|
||||
// 1. 读取头部内容
|
||||
header := make([]byte, 4)
|
||||
var n int
|
||||
n, err = fileObj.Read(header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n < con_Header_Length {
|
||||
err = errors.New("can not read 4 byte for read len")
|
||||
readLen = int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
dataLength := intAndBytesUtil.BytesToInt32(header, byteOrder)
|
||||
|
||||
// 2. 读取指定长度的内容
|
||||
data := make([]byte, dataLength)
|
||||
n, err = fileObj.Read(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
readLen = int64(len(header) + int(dataLength))
|
||||
result = string(data)
|
||||
return
|
||||
}
|
||||
139
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/sqlFile.go
Normal file
139
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/sqlFile.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package sqlSync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"goutil/fileUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
const (
|
||||
// 第一个文件名
|
||||
con_Default_FileName = "00000000"
|
||||
|
||||
// 文件名后缀
|
||||
con_FileName_Suffix = "data"
|
||||
)
|
||||
|
||||
// 同步数据对象(用于往文件中写入sql语句)
|
||||
type SqlFile struct {
|
||||
// 存放同步数据的文件夹路径
|
||||
dirPath string
|
||||
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
|
||||
// 保存数据的大文件对象
|
||||
bigFileObj *fileUtil.BigFile
|
||||
|
||||
// 数据同步对象
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// 将数据写入同步数据对象
|
||||
// data:待写入的数据
|
||||
func (this *SqlFile) Write(data string) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
// 写入数据
|
||||
err := Write(this.bigFileObj, data)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SqlFile.write.bigFileObj.WriteMessage")
|
||||
err = fmt.Errorf("%s-Write message to big file object failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取大文件对象的文件绝对路径
|
||||
func (this *SqlFile) FileFullName() string {
|
||||
return filepath.Join(this.dirPath, this.bigFileObj.FileName())
|
||||
}
|
||||
|
||||
// 当前读写的文件名
|
||||
func (this *SqlFile) FileName() string {
|
||||
return this.bigFileObj.FileName()
|
||||
}
|
||||
|
||||
// 创建同步数据对象
|
||||
// _dirPath:目录的路径
|
||||
// _identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// _maxFileSize:每个大文件的最大写入值(单位:Byte)
|
||||
// 返回值:
|
||||
// 同步数据对象
|
||||
func NewSqlFile(dirPath, identifier, fileName string, maxFileSize int) *SqlFile {
|
||||
result := &SqlFile{
|
||||
dirPath: dirPath,
|
||||
identifier: identifier,
|
||||
}
|
||||
|
||||
// 初始化大文件对象
|
||||
if fileName == "" {
|
||||
fileName = con_Default_FileName
|
||||
}
|
||||
bigFileObj, err := fileUtil.NewBigFileWithNewFileNameFunc2(dirPath, "", fileName, maxFileSize, NewFileName)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", result.identifier, "SqlFile.newSqlFile.fileUtil.NewBigFileWithNewFileNameFunc")
|
||||
err = fmt.Errorf("%s-Create big file object failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
result.bigFileObj = bigFileObj
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 根据当前文件名生成下一个sql文件名
|
||||
// prefix:文件名前缀
|
||||
// path:当前文件的路径
|
||||
// 返回值:
|
||||
// string:下一个文件的完整路径
|
||||
func NewFileName(prefix, path string) string {
|
||||
fullName := filepath.Base(path)
|
||||
curFileName := strings.Split(fullName, ".")[0]
|
||||
curFileId, err := strconv.Atoi(curFileName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s-Convert newFileName:%s to int failed:%s", prefix, curFileName, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
newFileId := curFileId + 1
|
||||
newFileName := fmt.Sprintf("%08d", newFileId)
|
||||
|
||||
// 加上文件后缀
|
||||
newFileName = fmt.Sprintf("%s.%s", newFileName, con_FileName_Suffix)
|
||||
|
||||
return newFileName
|
||||
}
|
||||
|
||||
// 获取文件夹下所有的sql文件
|
||||
// dirPath:指定要获取的文件夹路径
|
||||
// 返回值:
|
||||
// []string:sql文件列表
|
||||
func GetDataFileList(dirPath string) []string {
|
||||
// 获取当前目录中所有的数据文件列表
|
||||
fileList, err := fileUtil.GetFileList2(dirPath, "", con_FileName_Suffix)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
} else {
|
||||
err = fmt.Errorf("%s/*.%s-Get file list failed:%s", dirPath, con_FileName_Suffix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果文件数量大于1,则进行排序,以便于后续处理
|
||||
if len(fileList) > 1 {
|
||||
sort.Strings(fileList)
|
||||
}
|
||||
|
||||
return fileList
|
||||
}
|
||||
271
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/syncObject.go
Normal file
271
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/syncObject.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package sqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"goutil/debugUtil"
|
||||
"goutil/fileUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 同步对象定义
|
||||
type SyncObject struct {
|
||||
// 同步数据的存储路径
|
||||
dirPath string
|
||||
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
|
||||
// 数据库对象
|
||||
dbObj *sql.DB
|
||||
|
||||
// 处理数据写入的文件
|
||||
sqlFileObj *SqlFile
|
||||
|
||||
// 同步处理对象
|
||||
syncHandleObj SyncHandler
|
||||
}
|
||||
|
||||
// 进行同步对象初始化
|
||||
// maxFileSize:每个大文件的最大写入值(单位:Byte)
|
||||
func (this *SyncObject) Init(maxFileSize int) {
|
||||
// 启动时同步所有数据(然后才能从数据库中查询数据,以免数据丢失)
|
||||
this.syncHandleObj.Init(this)
|
||||
|
||||
// 构造同步数据对象
|
||||
fileName, _ := this.syncHandleObj.GetSyncingInfo()
|
||||
this.sqlFileObj = NewSqlFile(this.dirPath, this.identifier, fileName, maxFileSize)
|
||||
|
||||
// 当前没有正在同步的文件,则指向当前正在写的文件
|
||||
if len(fileName) <= 0 {
|
||||
this.syncHandleObj.Update(this.sqlFileObj.FileFullName(), 0, nil)
|
||||
}
|
||||
|
||||
// 启动一个新goroutine来负责同步数据
|
||||
go func() {
|
||||
/* 此处不使用goroutineMgr.Monitor/ReleaseMonitor,因为此处不能捕获panic,需要让外部进程终止执行,
|
||||
因为此模块的文件读写为核心逻辑,一旦出现问题必须停止进程,否则会造成脏数据
|
||||
*/
|
||||
this.Sync()
|
||||
}()
|
||||
}
|
||||
|
||||
// 保存数据到本地文件
|
||||
// command:待保存的指令
|
||||
func (this *SyncObject) Save(command string) {
|
||||
this.sqlFileObj.Write(command)
|
||||
}
|
||||
|
||||
// 循环同步多个文件
|
||||
func (this *SyncObject) Sync() {
|
||||
// 开始循环同步
|
||||
for {
|
||||
// 同步当前文件
|
||||
this.syncOneFile()
|
||||
|
||||
// 当前文件同步完成,记录同步日志
|
||||
nowFilePath, _ := this.syncHandleObj.GetSyncingInfo()
|
||||
|
||||
// 删除已同步完成的文件
|
||||
WaitForOk(func() bool {
|
||||
fileExist, err := fileUtil.IsFileExists(nowFilePath)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject IsFileExists error:%s", err.Error()))
|
||||
return false
|
||||
}
|
||||
if fileExist == false {
|
||||
return true
|
||||
}
|
||||
|
||||
err = fileUtil.DeleteFile(nowFilePath)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject delete file error:%s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, 10*time.Second)
|
||||
|
||||
// 当前文件同步完成,获取下个文件
|
||||
nextFileName := NewFileName("", nowFilePath)
|
||||
filePath := filepath.Join(this.dirPath, nextFileName)
|
||||
exist, err := fileUtil.IsFileExists(filePath)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncObject IsFileExists error:%s", err.Error()))
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 如果文件不存在,退出
|
||||
if !exist {
|
||||
// fmt.Println("协程退出了")
|
||||
return
|
||||
}
|
||||
|
||||
// 更新同步的位置信息 此处忽略错误是因为,哪怕是出错了,也不会影响整体逻辑
|
||||
this.syncHandleObj.Update(filePath, 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// 同步单个文件
|
||||
func (this *SyncObject) syncOneFile() {
|
||||
// 获取信息同步项对象
|
||||
filePath, offset := this.syncHandleObj.GetSyncingInfo()
|
||||
|
||||
// 打开待读取的文件
|
||||
f, exist := this.openFile(filePath)
|
||||
if exist == false {
|
||||
// logUtil.WarnLog(fmt.Sprintf("待同步的文件不存在,跳过此文件:%s", filePath),"")
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for {
|
||||
// 移动到需要读取的位置
|
||||
if _, err := f.Seek(offset, io.SeekStart); err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.Seek")
|
||||
err = fmt.Errorf("%s-Seek offset for header failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
command, readLen, err := Read(f)
|
||||
if err != nil {
|
||||
// 如果读取到文件末尾,判断是否等待
|
||||
if err == io.EOF {
|
||||
if this.sqlFileObj != nil && strings.Contains(filePath, this.sqlFileObj.FileName()) {
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果该文件是空文件,同步更新信息
|
||||
return
|
||||
}
|
||||
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncOneFile.f.Read")
|
||||
err = fmt.Errorf("%s-Read header failed:%s", prefix, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 3. 同步到mysql中,并更新同步位置
|
||||
this.syncHandleObj.SyncOneSql(command, filePath, offset+readLen)
|
||||
|
||||
// 4. 更新内存中的同步位置
|
||||
offset += readLen
|
||||
}
|
||||
}
|
||||
|
||||
// 打开待读取的文件
|
||||
// filePath:待打开的文件
|
||||
// 返回值:
|
||||
// *os.File:文件句柄,
|
||||
func (this *SyncObject) openFile(filePath string) (f *os.File, exist bool) {
|
||||
var err error
|
||||
for {
|
||||
exist, err = fileUtil.IsFileExists(filePath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("check file error,filePath:%v error:%v", filePath, err.Error())
|
||||
logUtil.ErrorLog(err.Error())
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
if exist == false {
|
||||
// 如果文件不存在,则跳过此文件
|
||||
logUtil.WarnLog(fmt.Sprintf("file no exist, skip file:%v", filePath))
|
||||
exist = false
|
||||
return
|
||||
}
|
||||
exist = true
|
||||
|
||||
// 打开当前处理文件
|
||||
f, err = os.OpenFile(filePath, os.O_RDONLY, os.ModePerm|os.ModeTemporary)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncOneFile.os.OpenFile")
|
||||
err = fmt.Errorf("%s-Open file:%s failed:%s", prefix, filePath, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 同步数据到mysql中
|
||||
// command:sql语句
|
||||
// tx:事务处理对象
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
|
||||
_, err := tx.Exec(command)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncToMysql")
|
||||
err = fmt.Errorf("%s-%s Update to mysql failed:%s", prefix, command, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
debugUtil.Printf("fatal Error:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创新新的mysql同步对象
|
||||
// dirPath:存放数据的目录
|
||||
// identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// dbObj:数据库对象
|
||||
// _syncHandleObj:同步处理对象
|
||||
// 返回值:
|
||||
// mysql同步对象
|
||||
func NewSyncObject(dirPath, identifier string, dbObj *sql.DB, _syncHandleObj SyncHandler) *SyncObject {
|
||||
dirPath = filepath.Join(dirPath, identifier)
|
||||
|
||||
// 创建更新目录
|
||||
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 构造同步信息对象
|
||||
result := &SyncObject{
|
||||
dirPath: dirPath,
|
||||
identifier: identifier,
|
||||
dbObj: dbObj,
|
||||
syncHandleObj: _syncHandleObj,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 同步处理接口
|
||||
type SyncHandler interface {
|
||||
// 初始化
|
||||
Init(baseObj *SyncObject)
|
||||
|
||||
// 获取正在同步的信息
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
GetSyncingInfo() (filePath string, offset int64)
|
||||
|
||||
// 更新
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
// tran:事务对象
|
||||
// 返回值:
|
||||
// error:错误对象
|
||||
Update(filePath string, offset int64, tran *sql.Tx) error
|
||||
|
||||
// 同步一条sql
|
||||
// command:指令数据
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
SyncOneSql(command string, filePath string, offset int64)
|
||||
}
|
||||
99
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/utility.go
Normal file
99
trunk/framework/dataSyncMgr/mysqlSync/sqlSync/utility.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package sqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 以事务的方式执行
|
||||
// db:数据库对象
|
||||
// funcObj:对应的具体处理函数
|
||||
// 返回值:
|
||||
// error:处理是否存在错误
|
||||
func ExecuteByTran(db *sql.DB, funcObj func(tran *sql.Tx) (isCommit bool, err error)) error {
|
||||
tran, err := db.Begin()
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("start transaction error:%v", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
// 事务处理
|
||||
isCommit := false
|
||||
defer func() {
|
||||
if isCommit {
|
||||
err = tran.Commit()
|
||||
} else {
|
||||
err = tran.Rollback()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("transaction end error:%v", err.Error()))
|
||||
}
|
||||
}()
|
||||
|
||||
isCommit, err = funcObj(tran)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 循环执行知道返回成功为止
|
||||
// funcObj:待执行的函数
|
||||
// interval:执行间隔时间
|
||||
func WaitForOk(funcObj func() bool, interval time.Duration) {
|
||||
for {
|
||||
if funcObj() == false {
|
||||
time.Sleep(interval)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是连接错误
|
||||
// errMsg:错误信息
|
||||
// 返回值:
|
||||
// bool:true:连接错误 false:其他异常
|
||||
func CheckIfConnectionError(errMsg string) bool {
|
||||
//// 连接被关闭
|
||||
ifConnectionClose := strings.Contains(errMsg, "A connection attempt failed because the connected party did not properly respond")
|
||||
if ifConnectionClose {
|
||||
return true
|
||||
}
|
||||
|
||||
// 使用过程中连接断开
|
||||
ifConnectionClose = strings.Contains(errMsg, "No connection could be made")
|
||||
if ifConnectionClose {
|
||||
return true
|
||||
}
|
||||
|
||||
// 事务处理过程中连接断开的提示
|
||||
ifConnectionClose = strings.Contains(errMsg, "bad connection")
|
||||
if ifConnectionClose {
|
||||
return true
|
||||
}
|
||||
|
||||
// socket压根儿连不上的处理
|
||||
ifConnectionClose = strings.Contains(errMsg, "A socket operation was attempted to an unreachable network")
|
||||
if ifConnectionClose {
|
||||
return true
|
||||
}
|
||||
|
||||
// 用户无法访问
|
||||
return strings.Contains(errMsg, "Access denied for user")
|
||||
}
|
||||
|
||||
// 获取比较简洁的错误信息
|
||||
// errMsg:错误信息
|
||||
// 返回值:
|
||||
// string:比较简洁的错误信息
|
||||
func GetSimpleErrorMessage(errMsg string) string {
|
||||
if strings.Contains(errMsg, "Error 1064: You have an error in your SQL syntax") {
|
||||
return "SqlError"
|
||||
}
|
||||
|
||||
return errMsg
|
||||
}
|
||||
119
trunk/framework/dataSyncMgr/mysqlSync/syncMgr.go
Normal file
119
trunk/framework/dataSyncMgr/mysqlSync/syncMgr.go
Normal 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
|
||||
}
|
||||
210
trunk/framework/dataSyncMgr/mysqlSync/syncObject.go
Normal file
210
trunk/framework/dataSyncMgr/mysqlSync/syncObject.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package mysqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"Framework/dataSyncMgr/mysqlSync/sqlSync"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 同步对象定义
|
||||
type SyncObject struct {
|
||||
// 同步数据的存储路径
|
||||
dirPath string
|
||||
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
|
||||
// 数据库对象
|
||||
dbObj *sql.DB
|
||||
|
||||
// 同步信息对象
|
||||
syncingInfoObj *syncingInfo
|
||||
|
||||
// 错误处理对象
|
||||
errorHandleObj *errorFile
|
||||
|
||||
// 同步对象
|
||||
syncObj *sqlSync.SyncObject
|
||||
}
|
||||
|
||||
// 进行同步对象初始化
|
||||
// maxFileSize:每个大文件的最大写入值(单位:Byte)
|
||||
func (this *SyncObject) Init(baseObj *sqlSync.SyncObject) {
|
||||
this.syncObj = baseObj
|
||||
|
||||
// 创建同步信息记录对象
|
||||
syncingInfoObj, err := newSyncingInfoObject(this.identifier, this.dbObj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
this.syncingInfoObj = syncingInfoObj
|
||||
|
||||
// 启动时同步所有数据(然后才能从数据库中查询数据,以免数据丢失)
|
||||
this.syncOldData()
|
||||
}
|
||||
|
||||
// 同步完成之前未同步完的数据
|
||||
func (this *SyncObject) syncOldData() {
|
||||
// 获取文件列表(有序的列表)
|
||||
fileList := sqlSync.GetDataFileList(this.dirPath)
|
||||
filePath, _ := this.syncingInfoObj.GetSyncingInfo()
|
||||
|
||||
// 判断是否有文件
|
||||
if len(fileList) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 判断当前文件是否为空,如果为空则将第一个文件赋给它
|
||||
if filePath == "" {
|
||||
this.syncingInfoObj.Update(fileList[0], 0, nil)
|
||||
}
|
||||
|
||||
// 开始同步数据
|
||||
this.syncObj.Sync()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 获取正在同步的信息
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
func (this *SyncObject) GetSyncingInfo() (filePath string, offset int64) {
|
||||
return this.syncingInfoObj.GetSyncingInfo()
|
||||
}
|
||||
|
||||
// 更新
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
// tran:事务对象
|
||||
// 返回值:
|
||||
// error:错误对象
|
||||
func (this *SyncObject) Update(filePath string, offset int64, tran *sql.Tx) error {
|
||||
return this.syncingInfoObj.Update(filePath, offset, tran)
|
||||
}
|
||||
|
||||
// 同步一条sql语句
|
||||
// command:待执行的命令
|
||||
// filePath:保存路径
|
||||
// offset:文件偏移量
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) SyncOneSql(command string, filePath string, offset int64) {
|
||||
err := this.syncOneSqlDetail(command, filePath, offset)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 发送监控报警
|
||||
this.handleError(command, filePath, offset, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 同步一条sql语句的具体逻辑
|
||||
// command:待执行的命令
|
||||
// filePath:保存路径
|
||||
// offset:文件偏移量
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) syncOneSqlDetail(command string, filePath string, offset int64) error {
|
||||
return sqlSync.ExecuteByTran(this.dbObj, func(tx *sql.Tx) (isCommit bool, err error) {
|
||||
// 保存sql到数据库
|
||||
err = this.syncToMysql(command, tx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 保存进度信息到数据库
|
||||
err = this.syncingInfoObj.Update(filePath, offset, tx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// 同步数据到mysql中
|
||||
// command:待执行的命令
|
||||
// tx:事务对象
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *SyncObject) syncToMysql(command string, tx *sql.Tx) error {
|
||||
_, err := tx.Exec(command)
|
||||
if err != nil {
|
||||
prefix := fmt.Sprintf("%s-%s", this.identifier, "SyncObject.syncToMysql")
|
||||
err = fmt.Errorf("%s-%s Update to mysql failed:%s", prefix, command, err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
debugUtil.Printf("fatal Error:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 进行错误处理
|
||||
// command:存在异常的数据
|
||||
// filePath:文件路径
|
||||
// offset:文件偏移量
|
||||
// err:错误信息
|
||||
func (this *SyncObject) handleError(command string, filePath string, offset int64, err error) {
|
||||
defer this.errorHandleObj.Delete()
|
||||
|
||||
// 保存当前sql命令
|
||||
this.errorHandleObj.SaveCommand(command)
|
||||
|
||||
// 循环处理当前命令,直到没有错误
|
||||
beginTime := time.Now().Unix()
|
||||
for {
|
||||
// 每隔5分钟,发送警报
|
||||
if time.Now().Unix()-beginTime > 5*60 {
|
||||
beginTime = time.Now().Unix()
|
||||
}
|
||||
|
||||
// 每次循环休眠20秒
|
||||
time.Sleep(5 * time.Second)
|
||||
command = this.errorHandleObj.ReadCommand()
|
||||
err = this.syncOneSqlDetail(command, filePath, offset)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 创新新的mysql同步对象
|
||||
// dirPath:存放数据的目录
|
||||
// identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// dbObj:数据库对象
|
||||
// syncingInfoObj:同步信息记录对象
|
||||
// errorHandleObj:错误处理对象
|
||||
// 返回值:
|
||||
// mysql同步对象
|
||||
func newSyncObject(dirPath, identifier string, dbObj *sql.DB) *SyncObject {
|
||||
dirPath = filepath.Join(dirPath, identifier)
|
||||
|
||||
// 创建更新目录
|
||||
err := os.MkdirAll(dirPath, os.ModePerm|os.ModeTemporary)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s-%s-make dir failed:%s", identifier, "SyncObject.newSyncObject.os.MkdirAll", err)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 构造同步信息对象
|
||||
result := &SyncObject{
|
||||
dirPath: dirPath,
|
||||
identifier: identifier,
|
||||
dbObj: dbObj,
|
||||
errorHandleObj: newErrorFile(dirPath, identifier),
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
62
trunk/framework/dataSyncMgr/mysqlSync/syncTest/dal.go
Normal file
62
trunk/framework/dataSyncMgr/mysqlSync/syncTest/dal.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/gorm"
|
||||
"Framework/dataSyncMgr/mysqlSync"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var _ = mysql.DeregisterLocalFile
|
||||
|
||||
var (
|
||||
connectionString = "root:moqikaka3309@tcp(10.1.0.10:3309)/develop_liujun?charset=utf8&parseTime=true&loc=Local&timeout=60s"
|
||||
maxOpenConns = 10
|
||||
maxIdleConns = 10
|
||||
|
||||
syncFileSize = 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
// 数据库对象
|
||||
dbObj *gorm.DB
|
||||
|
||||
// 同步管理对象
|
||||
syncMgr *mysqlSync.SyncMgr
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 初始化数据库连接
|
||||
dbObj = initMysql()
|
||||
|
||||
// 构造同步管理对象
|
||||
syncMgr = mysqlSync.NewSyncMgr(1, "Sync", syncFileSize, 1, dbObj.DB())
|
||||
}
|
||||
|
||||
// 初始化Mysql
|
||||
func initMysql() *gorm.DB {
|
||||
dbObj, err := gorm.Open("mysql", connectionString)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("初始化数据库:%s失败,错误信息为:%s", connectionString, err))
|
||||
}
|
||||
logUtil.DebugLog(fmt.Sprintf("连接mysql:%s成功", connectionString))
|
||||
|
||||
if maxOpenConns > 0 && maxIdleConns > 0 {
|
||||
dbObj.DB().SetMaxOpenConns(maxOpenConns)
|
||||
dbObj.DB().SetMaxIdleConns(maxIdleConns)
|
||||
}
|
||||
|
||||
return dbObj
|
||||
}
|
||||
|
||||
// 注册同步对象
|
||||
func registerSyncObj(identifier string) {
|
||||
syncMgr.RegisterSyncObj(identifier)
|
||||
}
|
||||
|
||||
// 保存sql数据
|
||||
func save(identifier string, command string) {
|
||||
syncMgr.Save(identifier, command)
|
||||
}
|
||||
82
trunk/framework/dataSyncMgr/mysqlSync/syncTest/main.go
Normal file
82
trunk/framework/dataSyncMgr/mysqlSync/syncTest/main.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"goutil/mathUtil"
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
wg.Add(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
playerMgr := newPlayerMgr()
|
||||
|
||||
// insert
|
||||
go func() {
|
||||
for {
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s", id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
|
||||
insert(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// update
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
suffix := mathUtil.GetRand().GetRandInt(1000)
|
||||
newName := fmt.Sprintf("Hero_%d", suffix)
|
||||
obj.resetName(newName)
|
||||
|
||||
update(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// delete
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
playerMgr.delete(obj)
|
||||
|
||||
clear(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// errorFile
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(1 * time.Hour)
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s%s", id, id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
print("errorFile")
|
||||
|
||||
insert(obj)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
64
trunk/framework/dataSyncMgr/mysqlSync/syncTest/player.go
Normal file
64
trunk/framework/dataSyncMgr/mysqlSync/syncTest/player.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type player struct {
|
||||
// 玩家id
|
||||
Id string `gorm:"column:Id;primary_key"`
|
||||
|
||||
// 玩家名称
|
||||
Name string `gorm:"column:Name"`
|
||||
}
|
||||
|
||||
func (this *player) resetName(name string) {
|
||||
this.Name = name
|
||||
}
|
||||
|
||||
func (this *player) tableName() string {
|
||||
return "player"
|
||||
}
|
||||
|
||||
func newPlayer(id, name string) *player {
|
||||
return &player{
|
||||
Id: id,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
type playerMgr struct {
|
||||
playerMap map[string]*player
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (this *playerMgr) insert(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
this.playerMap[obj.Id] = obj
|
||||
}
|
||||
|
||||
func (this *playerMgr) delete(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
delete(this.playerMap, obj.Id)
|
||||
}
|
||||
|
||||
func (this *playerMgr) randomSelect() *player {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
for _, obj := range this.playerMap {
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPlayerMgr() *playerMgr {
|
||||
return &playerMgr{
|
||||
playerMap: make(map[string]*player),
|
||||
}
|
||||
}
|
||||
29
trunk/framework/dataSyncMgr/mysqlSync/syncTest/player_dal.go
Normal file
29
trunk/framework/dataSyncMgr/mysqlSync/syncTest/player_dal.go
Normal file
@@ -0,0 +1,29 @@
|
||||
//package test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
con_player_tableName = "player"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerSyncObj(con_player_tableName)
|
||||
}
|
||||
|
||||
func insert(obj *player) {
|
||||
command := fmt.Sprintf("INSERT INTO `%s` (`Id`,`Name`) VALUES ('%v','%v') ", con_player_tableName, obj.Id, obj.Name)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
|
||||
func update(obj *player) {
|
||||
command := fmt.Sprintf("UPDATE `%s` SET `Name` = '%v' WHERE `Id` = '%v';", con_player_tableName, obj.Name, obj.Id)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
|
||||
func clear(obj *player) {
|
||||
command := fmt.Sprintf("DELETE FROM %s where Id = '%v';", con_player_tableName, obj.Id)
|
||||
save(con_player_tableName, command)
|
||||
}
|
||||
222
trunk/framework/dataSyncMgr/mysqlSync/syncingInfo.go
Normal file
222
trunk/framework/dataSyncMgr/mysqlSync/syncingInfo.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package mysqlSync
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
// 是否已经初始化了正在同步的表信息
|
||||
ifSyncingInfoTableInited = false
|
||||
|
||||
// 表初始化错误信息
|
||||
initTableError error = nil
|
||||
|
||||
// 表是否已经初始化
|
||||
isTableInited bool = false
|
||||
)
|
||||
|
||||
// 同步信息项,保存已经处理过的文件的信息
|
||||
type syncingModel struct {
|
||||
// 唯一标识
|
||||
Identifier string
|
||||
|
||||
// 待处理文件的绝对路径
|
||||
FilePath string
|
||||
|
||||
// 待处理文件的偏移量
|
||||
FileOffset int64
|
||||
|
||||
// 更新时间
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
||||
// 同步信息对象
|
||||
type syncingInfo struct {
|
||||
// 同步数据对象的唯一标识,用于进行重复判断
|
||||
identifier string
|
||||
|
||||
// 同步信息项
|
||||
item *syncingModel
|
||||
|
||||
// 数据库连接对象
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// 获取同步信息
|
||||
// filePath:正在同步的文件
|
||||
// fileOffset:同步到的位置
|
||||
func (this *syncingInfo) GetSyncingInfo() (filePath string, fileOffset int64) {
|
||||
return this.item.FilePath, this.item.FileOffset
|
||||
}
|
||||
|
||||
// 更新正在同步的位置和文件信息
|
||||
// filePath:文件路径
|
||||
// offset:当前同步到的位置
|
||||
// tran:事务对象,可以为nil
|
||||
// 返回值:
|
||||
// error:处理的错误信息
|
||||
func (this *syncingInfo) Update(filePath string, offset int64, tran *sql.Tx) error {
|
||||
this.item.FilePath = filePath
|
||||
this.item.FileOffset = offset
|
||||
this.item.UpdateTime = time.Now()
|
||||
|
||||
// 更新到数据库
|
||||
return this.update(this.item, tran)
|
||||
}
|
||||
|
||||
// 初始化同步信息
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncingInfo) init() error {
|
||||
if ifSyncingInfoTableInited == false {
|
||||
err := initSyncingInfoTable(this.db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ifSyncingInfoTableInited = true
|
||||
}
|
||||
|
||||
// 获取此表的同步信息
|
||||
data, exist, err := this.get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 如果同步信息不存在,则初始化一条到此表
|
||||
if exist == false {
|
||||
data = &syncingModel{
|
||||
Identifier: this.identifier,
|
||||
FilePath: "",
|
||||
FileOffset: 0,
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
err = this.insert(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
this.item = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// 从数据库获取数据
|
||||
// 返回值:
|
||||
// data:获取到的数据
|
||||
// exist:是否存在此数据
|
||||
// err:错误信息
|
||||
func (this *syncingInfo) get() (data *syncingModel, exist bool, err error) {
|
||||
//// 从数据库查询
|
||||
querySql := fmt.Sprintf("SELECT FilePath,FileOffset,UpdateTime FROM syncing_info WHERE Identifier ='%v';", this.identifier)
|
||||
var rows *sql.Rows
|
||||
rows, err = this.db.Query(querySql)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo get.query error:%v", err.Error()))
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() == false {
|
||||
exist = false
|
||||
return
|
||||
}
|
||||
exist = true
|
||||
|
||||
// 读取数据
|
||||
data = &syncingModel{
|
||||
Identifier: this.identifier,
|
||||
}
|
||||
err = rows.Scan(&data.FilePath, &data.FileOffset, &data.UpdateTime)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo get.scan error:%v", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 把同步信息写入到数据库
|
||||
// data:待插入的数据
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncingInfo) insert(data *syncingModel) error {
|
||||
insertSql := "INSERT INTO syncing_info(Identifier,FilePath,FileOffset,UpdateTime) VALUES(?,?,?,?);"
|
||||
_, err := this.db.Exec(insertSql, data.Identifier, data.FilePath, data.FileOffset, data.UpdateTime)
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo insert error:%v", err.Error()))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 把同步信息更新到数据库
|
||||
// data:待更新的数据
|
||||
// tran:事务对象
|
||||
// 返回值:
|
||||
// error:错误信息
|
||||
func (this *syncingInfo) update(data *syncingModel, tran *sql.Tx) error {
|
||||
updateSql := "UPDATE syncing_info SET FilePath=?, FileOffset=?, UpdateTime=? WHERE Identifier=?;"
|
||||
var err error
|
||||
if tran != nil {
|
||||
_, err = tran.Exec(updateSql, data.FilePath, data.FileOffset, data.UpdateTime, data.Identifier)
|
||||
} else {
|
||||
_, err = this.db.Exec(updateSql, data.FilePath, data.FileOffset, data.UpdateTime, data.Identifier)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo update error:%v", err.Error()))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建同步信息对象
|
||||
// _dirPath:目录的路径
|
||||
// _identifier:当前数据的唯一标识(可以使用数据库表名)
|
||||
// _db:数据库连接对象
|
||||
// 返回值:
|
||||
// 同步信息对象
|
||||
func newSyncingInfoObject(identifier string, _db *sql.DB) (result *syncingInfo, err error) {
|
||||
result = &syncingInfo{
|
||||
identifier: identifier,
|
||||
db: _db,
|
||||
}
|
||||
|
||||
err = result.init()
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 初始化同步信息表结构
|
||||
// db:数据库连接对象
|
||||
func initSyncingInfoTable(db *sql.DB) error {
|
||||
if isTableInited {
|
||||
return initTableError
|
||||
}
|
||||
|
||||
defer func() {
|
||||
isTableInited = true
|
||||
}()
|
||||
|
||||
// 创建同步信息表
|
||||
createTableSql := `CREATE TABLE IF NOT EXISTS syncing_info (
|
||||
Identifier varchar(30) NOT NULL COMMENT '同步唯一标识(数据库表名)',
|
||||
FilePath varchar(500) NOT NULL COMMENT '正在同步的文件路径',
|
||||
FileOffset bigint NOT NULL COMMENT '文件偏移量',
|
||||
UpdateTime datetime NOT NULL COMMENT '最后一次更新时间',
|
||||
PRIMARY KEY (Identifier)
|
||||
) COMMENT 'P表同步信息';`
|
||||
if _, initTableError = db.Exec(createTableSql); initTableError != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("mysqlSync/syncingInfo initSyncingInfoTable error:%v", initTableError.Error()))
|
||||
return initTableError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user