+ Hello,This is an example for gxpath. +
+ + + + ` + var root *xmlUtil.Node + root, errMsg = xmlUtil.LoadFromString(content) + if errMsg == nil { + xmlConfigData = NewXmlConfig() + xmlConfigData.LoadFromXmlNode(root) + } + + return +} diff --git a/trunk/goutil/configUtil/xmlConfig_test.go b/trunk/goutil/configUtil/xmlConfig_test.go new file mode 100644 index 0000000..6790f46 --- /dev/null +++ b/trunk/goutil/configUtil/xmlConfig_test.go @@ -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(` ++ Hello,This is an example for gxpath. +
+ + + + ` + 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 +} diff --git a/trunk/goutil/coroutine-timer/.gitignore b/trunk/goutil/coroutine-timer/.gitignore new file mode 100644 index 0000000..1a1566e --- /dev/null +++ b/trunk/goutil/coroutine-timer/.gitignore @@ -0,0 +1,2 @@ +Log/* +logs/* \ No newline at end of file diff --git a/trunk/goutil/coroutine-timer/cmd.go b/trunk/goutil/coroutine-timer/cmd.go new file mode 100644 index 0000000..1ddcb42 --- /dev/null +++ b/trunk/goutil/coroutine-timer/cmd.go @@ -0,0 +1,47 @@ +package coroutine_timer + +const ( + // 添加 + cmd_add = 1 + + // 删除 + cmd_del = 2 +) + +// cmdModel +// @description: 命令对象 +type cmdModel struct { + // cmd 指令 + cmd int + + // paramObj 指令参数 + paramObj interface{} + + // resObj 指令返回对象 + resObj interface{} + + // err 指令返回的错误 + err error + + // waitChan 等待channel + waitChan chan struct{} +} + +// newCmdModel +// @description: 创建cmd模型对象 +// parameter: +// @c:cmd命令 +// @po:参数 +// return: +// @*cmdModel: +func newCmdModel(c int, po interface{}) *cmdModel { + result := &cmdModel{ + cmd: c, + paramObj: po, + resObj: nil, + err: nil, + waitChan: make(chan struct{}, 1), + } + + return result +} diff --git a/trunk/goutil/coroutine-timer/model.go b/trunk/goutil/coroutine-timer/model.go new file mode 100644 index 0000000..58209b8 --- /dev/null +++ b/trunk/goutil/coroutine-timer/model.go @@ -0,0 +1,73 @@ +package coroutine_timer + +// timersModel +// @description: timer卡槽对象 +type timersModel struct { + timers map[string]*timerObj +} + +// newTimersModel +// @description: 构造timer卡槽对象 +// parameter: +// return: +// @*timersModel: +func newTimersModel() *timersModel { + return &timersModel{timers: map[string]*timerObj{}} +} + +// addTimer +// @description: 添加定时器 +// parameter: +// @receiver this: +// @t: +// return: +func (this *timersModel) addTimer(t *timerObj) { + this.timers[t.id] = t +} + +// delTimer +// @description: 删除定时器 +// parameter: +// @receiver this: +// @id: +// return: +func (this *timersModel) delTimer(id string) { + delete(this.timers, id) +} + +// exist +// @description: 判断id是否存在 +// parameter: +// @receiver this: +// @id: +// return: +// @exist: +func (this *timersModel) exist(id string) (exist bool) { + _, exist = this.timers[id] + return +} + +// getAllTimers +// @description: 获取所有定时器 +// parameter: +// @receiver this: +// return: +// @map[string]*timerObj: +func (this *timersModel) getAllTimers() map[string]*timerObj { + return this.timers +} + +// getAllTimers2 +// @description: 获取所有定时器 +// parameter: +// @receiver this: +// return: +// @result: +func (this *timersModel) getAllTimers2() (result []*timerObj) { + result = make([]*timerObj, 0, len(this.timers)) + for _, v := range this.timers { + result = append(result, v) + } + + return +} diff --git a/trunk/goutil/coroutine-timer/readme.md b/trunk/goutil/coroutine-timer/readme.md new file mode 100644 index 0000000..02eaad6 --- /dev/null +++ b/trunk/goutil/coroutine-timer/readme.md @@ -0,0 +1,20 @@ +coroutine-timer支持如下工作: +定时触发设定的回调,最小精度秒级 + +## 使用方式 + +### 增加回调 + +> 1. 导入包 +> 2. 调用AddTimerx添加定时回调,传入相关参数 + +ps: +> 1. AddTimer1,AddTimer2,AddTimer3是内部自动生成的id,内部保证唯一性。外部如果后续要删除该添加的timer,需要持有返回的id信息 +> 2. AddTimer4 需要外部传入id,外部需要保证id的唯一性。并且这个方法会在内部校验id是否已经存在,所以性能上会比其他AddTimer方法慢 + +### 删除回调 + +```go + DeleteTimer(id) +``` + diff --git a/trunk/goutil/coroutine-timer/timer-mgr.go b/trunk/goutil/coroutine-timer/timer-mgr.go new file mode 100644 index 0000000..5808105 --- /dev/null +++ b/trunk/goutil/coroutine-timer/timer-mgr.go @@ -0,0 +1,409 @@ +package coroutine_timer + +import ( + "fmt" + "math" + "time" + + "goutil/logUtil" + "goutil/stringUtil" +) + +const ( + // 启动暂停时间 + con_STAR_SLEEP_NUM = 3 + + // 秒级定时器卡槽数量 + con_SECOND_SLOT_NUM = 60 + + //分钟级定时器卡槽数量 + con_MINUTES_SLOT_NUM = 60 +) + +var ( + // 秒级定时器下标 + secIndex = 0 + + // 秒级定时器当前开始时间 + secondStarTime int64 + + // 秒级定时器槽 + secondsTimers [con_SECOND_SLOT_NUM]*timersModel + + // 分钟级定时器下标 + minIndex = 0 + + // 分钟级定时器当前开始时间 + minStarTime int64 + + // 分钟级定时器槽 + minutesTimers [con_MINUTES_SLOT_NUM]*timersModel + + // 其他定时器存放槽 + otherTimers *timersModel + + // 操作通道 + cmdChan chan *cmdModel +) + +func init() { + for i := 0; i < con_SECOND_SLOT_NUM; i++ { + secondsTimers[i] = newTimersModel() + } + for i := 0; i < con_MINUTES_SLOT_NUM; i++ { + minutesTimers[i] = newTimersModel() + } + + otherTimers = newTimersModel() + cmdChan = make(chan *cmdModel, 1000) + secondStarTime = time.Now().Unix() + minStarTime = secondStarTime + con_SECOND_SLOT_NUM + + go chanHandler() +} + +// AddTimer +// @description: 添加定时回调 +// parameter: +// +// @afterSecond:延后多少时间执行 +// @exfun:执行方法 +// @obj:执行传入的参数 +// +// return: +// +// @string: +func AddTimer(afterSecond int, exfun func(interface{}), obj interface{}) string { + tick := time.Now().Unix() + int64(afterSecond) + return AddTimer3(tick, exfun, obj) +} + +// AddTimer2 +// @description: 添加定时回调 +// parameter: +// +// @t:执行时间点 +// @exfun:执行方法 +// @obj:执行传入的参数 +// +// return: +// +// @string: +func AddTimer2(t time.Time, exfun func(interface{}), obj interface{}) string { + tick := t.Unix() + return AddTimer3(tick, exfun, obj) +} + +// AddTimer3 +// @description: 添加定时回调 +// parameter: +// +// @tick:执行时间点 +// @exfun:执行方法 +// @obj:执行传入的参数 +// +// return: +// +// @newId: +func AddTimer3(tick int64, exfun func(interface{}), obj interface{}) (newId string) { + newId = stringUtil.GetNewUUID() + newObj := newTimerObj(newId, tick, exfun, obj) + + cnm := newCmdModel(cmd_add, newObj) + cmdChan <- cnm + + return +} + +// AddTimer4 +// @description: 添加定时回调(此方法会在内部校验id,所以性能会比其他AddTimer方法低) +// parameter: +// +// @id:定时id(外部需要自行保证id唯一) +// @tick:执行时间点 +// @exfun:执行方法 +// @obj:执行传入的参数 +// +// return: +// +// @err: +func AddTimer4(id string, tick int64, exfun func(interface{}), obj interface{}) (err error) { + newObj := newTimerObj(id, tick, exfun, obj) + newObj.needCheckId = true + + // 加入处理队列 + cnm := newCmdModel(cmd_add, newObj) + cmdChan <- cnm + + // 等待处理结束 + <-cnm.waitChan + + // 返回处理结果 + err = cnm.err + + return +} + +// DeleteTimer +// @description: 删除定时器 +// parameter: +// +// @id: +// +// return: +func DeleteTimer(id string) { + cnm := newCmdModel(cmd_del, id) + cmdChan <- cnm +} + +// chanHandler +// @description: channel处理 +// parameter: +// return: +func chanHandler() { + defer func() { + if err := recover(); err != nil { + logUtil.ErrorLog("coroutine-timer.excute err:%s", err) + } + }() + + // 暂停一下再处理,避免启动立即处理,其他数据还没准备好 + time.Sleep(con_STAR_SLEEP_NUM * time.Second) + + at := time.After(time.Second * 1) + for { + select { + case cm := <-cmdChan: + switch cm.cmd { + case cmd_add: + cmdAdd(cm) + case cmd_del: + cmdDel(cm) + } + case <-at: + // byron:需要处理时间后调导致跳时间的问题:调整后应该马上执行的 + + // 计算需要执行的次数 + n := time.Now().Unix() - secondStarTime - int64(secIndex) + if n > 0 { + + // 执行对应次数的方法 --- 正常应该只执行1此,调时间后,此处会追时间 + var i int64 + for i = 0; i < n; i++ { + cmdRun() + } + } + + at = time.After(time.Second * 1) + } + } +} + +// cmdAdd +// @description: 添加定时器 +// parameter: +// +// @cm: +// +// return: +func cmdAdd(cm *cmdModel) { + newObj := cm.paramObj.(*timerObj) + if newObj.needCheckId && checkTimerExist(newObj.id) { + cm.err = fmt.Errorf("已经存在id=%s的timer", newObj.id) + cm.waitChan <- struct{}{} + return + } + + // 如果执行时间比当前时间小,则放入最近的调度卡槽,以便尽快执行 + tick := newObj.tick + if tick <= (secondStarTime + int64(secIndex)) { + tick = (secondStarTime + int64(secIndex)) + 1 + } + + // 落在秒钟级别定时器上 + if tick < (secondStarTime + con_SECOND_SLOT_NUM) { + index := (int)(tick - secondStarTime) + secondsTimers[index].addTimer(newObj) + cm.waitChan <- struct{}{} + return + } + + // 落在分钟级别定时器上 + if tick < (minStarTime + con_MINUTES_SLOT_NUM*con_SECOND_SLOT_NUM) { + index := (int)(tick-minStarTime) / con_SECOND_SLOT_NUM + minutesTimers[index].addTimer(newObj) + cm.waitChan <- struct{}{} + return + } + + //落在小时级别定时器上 + otherTimers.addTimer(newObj) + + // 返回操作完成 + cm.waitChan <- struct{}{} +} + +// cmdDel +// @description: 删除timer +// parameter: +// +// @cm: +// +// return: +func cmdDel(cm *cmdModel) { + id := cm.paramObj.(string) + + // 移除秒级别定时器 + for _, item := range secondsTimers { + item.delTimer(id) + } + + // 移除分种级定时器 + for _, item := range minutesTimers { + item.delTimer(id) + } + + // 移除时钟级定时器 + otherTimers.delTimer(id) + + // 返回操作完成 + cm.waitChan <- struct{}{} +} + +// cmdRun +// @description: 运行定时器 +// parameter: +// return: +func cmdRun() { + defer func() { + if err := recover(); err != nil { + logUtil.ErrorLog("coroutine-timer.inExcute err:%s", err) + } + }() + + // 执行秒级定时器 + timers := getSencondTimers() + if len(timers) == 0 { + return + } + + for _, t := range timers { + go safeRun(t) + } +} + +// checkTimerExist +// @description: 校验timer是否存在 +// parameter: +// +// @id:id +// +// return: +// +// @bool: +func checkTimerExist(id string) bool { + // 秒级别定时器检测 + for _, item := range secondsTimers { + if item.exist(id) { + return true + } + } + + // 分种级定时器检测 + for _, item := range minutesTimers { + if item.exist(id) { + return true + } + } + + // 时钟级定时器检测 + return otherTimers.exist(id) +} + +// getSencondTimers +// @description: 获取秒级定时器 +// parameter: +// return: +// +// @result: +func getSencondTimers() (result []*timerObj) { + // 获取对应slot里面的定时对象 + result = secondsTimers[secIndex].getAllTimers2() + secondsTimers[secIndex] = newTimersModel() + secIndex++ + + // 如果达到最大,则重新填装新的调度对象 + if secIndex == con_SECOND_SLOT_NUM { + secIndex = 0 + secondStarTime = secondStarTime + con_SECOND_SLOT_NUM + minTaskList := getMinutesTasks() + for _, t := range minTaskList { + index := t.tick - secondStarTime + secondsTimers[index].addTimer(t) + } + } + + return +} + +// getMinutesTasks +// @description: 获取分钟级定时器 +// parameter: +// return: +// +// @result: +func getMinutesTasks() (result []*timerObj) { + // 获取对应slot里面的定时对象 + result = minutesTimers[minIndex].getAllTimers2() + minutesTimers[minIndex] = newTimersModel() + minIndex++ + + // 如果达到最大,则重新填装新的调度对象 + if minIndex == con_MINUTES_SLOT_NUM { + reInputMin() + } + + return +} + +// reInputMin +// @description: 重新填入分钟级定时器 +// parameter: +// return: +func reInputMin() { + minIndex = 0 + minStarTime = minStarTime + con_MINUTES_SLOT_NUM*con_SECOND_SLOT_NUM + + delMap := make(map[string]struct{}) + for _, t := range otherTimers.getAllTimers() { + index := (t.tick - minStarTime) / con_SECOND_SLOT_NUM + if index > math.MaxInt || index >= con_MINUTES_SLOT_NUM { + continue + } + minutesTimers[index].addTimer(t) + delMap[t.id] = struct{}{} + } + + if len(delMap) > 0 { + for k := range delMap { + otherTimers.delTimer(k) + } + } +} + +// safeRun +// @description: 安全运行定时器回调 +// parameter: +// +// @t: +// +// return: +func safeRun(t *timerObj) { + defer func() { + if err := recover(); err != nil { + logUtil.ErrorLog("coroutine-timer.safeRun id:%s err:%s", t.id, err) + } + }() + + t.excuteAction(t.paramObj) +} diff --git a/trunk/goutil/coroutine-timer/timer-obj.go b/trunk/goutil/coroutine-timer/timer-obj.go new file mode 100644 index 0000000..a60cc69 --- /dev/null +++ b/trunk/goutil/coroutine-timer/timer-obj.go @@ -0,0 +1,40 @@ +package coroutine_timer + +// timerObj +// @description: 定时调度对象 +type timerObj struct { + // id 调度id + id string + + // tick 执行时间 + tick int64 + + // excuteAction 执行方法 + excuteAction func(interface{}) + + // paramObj 携带的参数 + paramObj interface{} + + // needCheckId 是否需要校验id + needCheckId bool +} + +// newTimerObj +// @description: 构造调度对象 +// parameter: +// @_id:id +// @t:调度时间 +// @ea:调度方法 +// @pm:调度参数 +// return: +// @*timerObj: +func newTimerObj(_id string, t int64, ea func(interface{}), pm interface{}) *timerObj { + result := &timerObj{ + id: _id, + tick: t, + excuteAction: ea, + paramObj: pm, + } + + return result +} diff --git a/trunk/goutil/coroutine-timer/timer_test.go b/trunk/goutil/coroutine-timer/timer_test.go new file mode 100644 index 0000000..869b391 --- /dev/null +++ b/trunk/goutil/coroutine-timer/timer_test.go @@ -0,0 +1,114 @@ +package coroutine_timer + +import ( + "sync" + "testing" + "time" + + "goutil/mathUtil" + "goutil/stringUtil" +) + +func init() { +} + +func Test_Method1(t *testing.T) { + imap := make(map[int]struct{}) + var lockObj sync.Mutex + + cb := func(obj interface{}) { + i := obj.(int) + + lockObj.Lock() + defer lockObj.Unlock() + + if _, exist := imap[i]; exist == false { + t.Error(i, "应该删除,不应该回调 Test_Method1") + } + + delete(imap, i) + } + + for i := 0; i < 20000; i++ { + tick := i % 20 + isdel := false + if tick > 1 { + isdel = mathUtil.GetRand().GetRandInt(100) < 50 + } + if isdel == false { + lockObj.Lock() + imap[i] = struct{}{} + lockObj.Unlock() + } + id := AddTimer(tick, cb, i) + if isdel { + DeleteTimer(id) + } + } + + newN := 10000000 + newId := stringUtil.GetNewUUID() + + lockObj.Lock() + imap[newN] = struct{}{} + lockObj.Unlock() + + err := AddTimer4(newId, 3, cb, newN) + if err != nil { + t.Error(err) + } + + err = AddTimer4(newId, 3, cb, newN) + if err == nil { + t.Error("未检测到重复id") + } + + for { + if len(imap) == 0 { + break + } + + t.Log("剩余回调次数:", len(imap)) + time.Sleep(time.Second) + } +} + +func Test_Method2(t *testing.T) { + imap := make(map[int64]struct{}) + var lockObj sync.Mutex + + cb := func(obj interface{}) { + i := obj.(int64) + n := time.Now().Unix() + x := n - i + // 此处因为启动有暂停5s,所以启动后最近的执行偏差在5s内 + if x > 6 || x < -6 { + t.Errorf("错误的时间执行了回调函数 tick:%v now:%v", i, n) + } + + lockObj.Lock() + defer lockObj.Unlock() + + if _, exist := imap[i]; exist == false { + t.Error(i, "应该删除,不应该回调 Test_Method2") + } + + delete(imap, i) + } + + for i := 0; i < 20; i++ { + tick := time.Now().Unix() + int64(i) + imap[tick] = struct{}{} + AddTimer3(tick, cb, tick) + } + + for { + if len(imap) == 0 { + break + } + + t.Log("剩余回调次数:", len(imap)) + time.Sleep(time.Second) + } + +} diff --git a/trunk/goutil/counter-util/counter.go b/trunk/goutil/counter-util/counter.go new file mode 100644 index 0000000..4250b89 --- /dev/null +++ b/trunk/goutil/counter-util/counter.go @@ -0,0 +1,60 @@ +package counter_util + +import ( + "time" +) + +// CounterUtil +// @description: 固定窗口计数器辅助类 +type CounterUtil struct { + tag string // tag counter标识 + num int // num 当前计数 + warnNum int // warnNum 警告数量 + windowsTime time.Time // windowsTime 窗口时间 + checkSameWindowsFun func(t1, t2 time.Time) bool // checkSameWindowsFun 比较是否同一个时间窗口 + warnAction func(tag string, num int, t time.Time) // warnAction 监控回调方法 +} + +// NewCounterUtil +// @description: 构造计数器 +// parameter: +// @_tag:tag标识,会在WarnAction中传递回来 +// @_warnNum:告警数量 +// @_checkWindowFun:比较是否同一个时间窗口 +// @_warnAction:指定数量触发回调 +// return: +// @*CounterUtil:固定窗口计数器辅助类 +func NewCounterUtil(_tag string, _warnNum int, _checkWindowFun func(t1, t2 time.Time) bool, _warnAction func(tag string, num int, t time.Time)) *CounterUtil { + r := &CounterUtil{ + tag: _tag, + warnNum: _warnNum, + windowsTime: time.Now(), + checkSameWindowsFun: _checkWindowFun, + warnAction: _warnAction, + } + + return r +} + +// AddNum +// @description: 添加数量 +// parameter: +// @receiver c:计数器 +// @n:增加的数量 +// return: +// @int:计数器当前的数量 +func (c *CounterUtil) AddNum(n int) int { + if !c.checkSameWindowsFun(c.windowsTime, time.Now()) { + c.num = 0 + c.windowsTime = time.Now() + } + + // 增加次数 + c.num += n + if c.num >= c.warnNum { + c.warnAction(c.tag, c.num, time.Now()) + c.num = 0 + } + + return c.num +} diff --git a/trunk/goutil/counter-util/counter_test.go b/trunk/goutil/counter-util/counter_test.go new file mode 100644 index 0000000..0bcfe2e --- /dev/null +++ b/trunk/goutil/counter-util/counter_test.go @@ -0,0 +1,33 @@ +package counter_util + +import ( + "fmt" + "testing" + "time" +) + +func TestInfoLog(t *testing.T) { + var iserr bool = true + c := NewCounterUtil("test", 2, checkId, func(tag string, num int, ti time.Time) { + msg := fmt.Sprintf("tag:%s 当前数量为num:%v ti:%v", tag, num, ti) + if iserr { + t.Error(msg) + } else { + t.Log(msg) + } + + }) + + c.AddNum(1) + iserr = false + c.AddNum(1) + time.Sleep(time.Second * 1) + iserr = true + c.AddNum(1) + time.Sleep(time.Second * 1) + c.AddNum(1) +} + +func checkId(t1, t2 time.Time) bool { + return t1.Second() == t2.Second() +} diff --git a/trunk/goutil/counter-util/readme.md b/trunk/goutil/counter-util/readme.md new file mode 100644 index 0000000..316ff00 --- /dev/null +++ b/trunk/goutil/counter-util/readme.md @@ -0,0 +1,35 @@ +### 窗口周期计数器 +窗口周期计数类,用于记录一个窗口周期数量,并且触发某个操作的场景。 +在下一个窗口周期会自动重置次数 + +#### =======================>使用方法说明<========================= + +1.引入包 +2.构造对象并次有 +3.调用对象的增加次数方法 + +```go +package demo + +import ( + "time" + + "goutil/counter_util" +) + +func main() { + // 构造名字叫test的,窗口间隔为1s,计数达到2就会触发警告的窗口计数器 + c := counter_util.NewCounterUtil("test", 2, checkId, func(tag string, num int, ti time.Time) { + //自定义触发动作 + }) + + c.AddNum(1) + c.AddNum(10) +} + +// 窗口周期设定为1s +func checkId(t1, t2 time.Time) bool { + return t1.Second() == t2.Second() +} + +``` \ No newline at end of file diff --git a/trunk/goutil/dbUtil/dataRow.go b/trunk/goutil/dbUtil/dataRow.go new file mode 100644 index 0000000..36cc8d9 --- /dev/null +++ b/trunk/goutil/dbUtil/dataRow.go @@ -0,0 +1,104 @@ +package dbUtil + +import ( + "errors" + "time" +) + +// 数据行结果 +type DataRow struct { + // 所属数据表 + table *DataTable + + // 行的所有值 + cells []interface{} +} + +// 行的所有原始值 +func (this *DataRow) CellOriginValues() []interface{} { + return this.cells +} + +// 值的个数 +func (this *DataRow) Len() int { + return len(this.cells) +} + +// 单元格的字符串值(可能为nil),如果有设置连接字符串:parseTime=true,则会有time.Time +// celIndex:单元格序号 +// 返回值: +// interface{}:单元格的字符串值 +// error:错误信息 +func (this *DataRow) CellValue(celIndex int) (interface{}, error) { + if len(this.cells) <= celIndex { + return nil, errors.New("cell out of range") + } + + // 检查是否为nil + if this.cells[celIndex] == nil { + return nil, nil + } + + // 转换为字符串 + switch this.cells[celIndex].(type) { + case []byte: + return string(this.cells[celIndex].([]byte)), nil + case string: + return this.cells[celIndex].(string), nil + case time.Time: + return this.cells[celIndex].(time.Time), nil + } + + return nil, errors.New("unknown value type") +} + +// 单元格的字符串值(可能为nil),如果有设置连接字符串:parseTime=true,则会有time.Time +// cellName:单元格名称 +// 返回值: +// interface{}:单元格的字符串值 +// error:错误信息 +func (this *DataRow) CellValueByName(cellName string) (interface{}, error) { + celIndex := this.table.cellIndex(cellName) + if celIndex < 0 { + return nil, errors.New("cell name no exist") + } + + return this.CellValue(celIndex) +} + +// 单元格的原始值 +// celIndex:单元格序号 +// 返回值: +// interface{}:单元格的字符串值 +// error:错误信息 +func (this *DataRow) OriginCellValue(celIndex int) (interface{}, error) { + if len(this.cells) <= celIndex { + return nil, errors.New("cell out of range") + } + + return this.cells[celIndex], nil +} + +// 单元格的原始值 +// cellName:单元格名称 +// 返回值: +// interface{}:单元格的字符串值 +// error:错误信息 +func (this *DataRow) OriginCellValueByName(cellName string) (interface{}, error) { + celIndex := this.table.cellIndex(cellName) + if celIndex < 0 { + return nil, errors.New("cell name no exist") + } + + return this.OriginCellValue(celIndex) +} + +// 创建单元格对象 +// _table:所属表对象 +// _cells:单元格的值集合 +func newDataRow(_table *DataTable, _cells []interface{}) *DataRow { + return &DataRow{ + table: _table, + cells: _cells, + } +} diff --git a/trunk/goutil/dbUtil/dataTable.go b/trunk/goutil/dbUtil/dataTable.go new file mode 100644 index 0000000..6e7f3c5 --- /dev/null +++ b/trunk/goutil/dbUtil/dataTable.go @@ -0,0 +1,189 @@ +package dbUtil + +import ( + "database/sql" + "errors" +) + +// 数据表结构 +type DataTable struct { + // 行对象集合 + rowData []*DataRow + + // 列名称集合 + columnNames map[string]int +} + +// 数据表初始化 +// rows:原始的数据行信息 +// 返回值: +// error:初始化的错误信息 +func (this *DataTable) init(rows *sql.Rows) error { + defer func() { + rows.Close() + }() + + // 读取列信息和保存列名称 + tmpColumns, errMsg := rows.Columns() + if errMsg != nil { + return errMsg + } + this.columnNames = make(map[string]int) + for index, val := range tmpColumns { + this.columnNames[val] = index + } + + // 读取行数据 + this.rowData = make([]*DataRow, 0) + columnCount := len(this.columnNames) + + args := make([]interface{}, columnCount) + for rows.Next() { + values := make([]interface{}, columnCount) + for i := 0; i < columnCount; i++ { + args[i] = &values[i] + } + rows.Scan(args...) + + this.rowData = append(this.rowData, newDataRow(this, values)) + } + + return nil +} + +// 获取原始单元格值(一般为:string或[]byte) +// rowIndex:行序号 +// cellIndex:单元格序号 +// 返回值: +// interface{}:原始单元格值(一般为:string或[]byte) +// error:获取错误信息 +func (this *DataTable) OriginCellValueByIndex(rowIndex int, cellIndex int) (interface{}, error) { + if len(this.rowData) <= rowIndex { + return nil, errors.New("row out of range") + } + + rowItem := this.rowData[rowIndex] + if len(rowItem.cells) <= cellIndex { + return nil, errors.New("column out of range") + } + + return rowItem.OriginCellValue(cellIndex) +} + +// 获取原始单元格值(一般为:string或[]byte) +// rowIndex:行序号 +// cellIndex:单元格序号 +// 返回值: +// interface{}:原始单元格值(一般为:string或[]byte) +// error:获取错误信息 +func (this *DataTable) OriginCellValueByCellName(rowIndex int, cellName string) (interface{}, error) { + if len(this.rowData) <= rowIndex { + return nil, errors.New("row out of range") + } + + rowItem := this.rowData[rowIndex] + + return rowItem.OriginCellValueByName(cellName) +} + +// 获取字符串的单元格值(有可能为nil) +// rowIndex:行序号 +// cellIndex:单元格序号 +// 返回值: +// interface{}:字符串的单元格值(有可能为nil) +// error:获取错误信息 +func (this *DataTable) CellValueByIndex(rowIndex int, cellIndex int) (interface{}, error) { + if len(this.rowData) <= rowIndex { + return nil, errors.New("row out of range") + } + + rowItem := this.rowData[rowIndex] + if len(rowItem.cells) <= cellIndex { + return nil, errors.New("column out of range") + } + + return rowItem.CellValue(cellIndex) +} + +// 获取字符串的单元格值(有可能为nil) +// rowIndex:行序号 +// cellIndex:单元格序号 +// 返回值: +// interface{}:字符串的单元格值(有可能为nil) +// error:获取错误信息 +func (this *DataTable) CellValueByName(rowIndex int, cellName string) (interface{}, error) { + if len(this.rowData) <= rowIndex { + return nil, errors.New("row out of range") + } + + rowItem := this.rowData[rowIndex] + + return rowItem.CellValueByName(cellName) +} + +// 获取行对象 +// rowIndex:行序号 +// 返回值: +// *DataRow:行对象 +// error:错误信息 +func (this *DataTable) Row(rowIndex int) (*DataRow, error) { + if len(this.rowData) <= rowIndex { + return nil, errors.New("row out of range") + } + + return this.rowData[rowIndex], nil +} + +// 根据列名获取列序号 +// cellName:列名 +// 返回值: +// int:列序号 +func (this *DataTable) cellIndex(cellName string) int { + cellIndex, isExist := this.columnNames[cellName] + if isExist == false { + return -1 + } + + return cellIndex +} + +// 获取所有列的名字 +// 返回值: +// []string:列字段名集合 +func (this *DataTable) Columns() []string { + result := make([]string, len(this.columnNames)) + for key, val := range this.columnNames { + result[val] = key + } + + return result +} + +// 获取列数量 +// 返回值: +// int:列数量 +func (this *DataTable) ColumnCount() int { + return len(this.columnNames) +} + +// 获取数据行数 +// 返回值: +// int:行数 +func (this *DataTable) RowCount() int { + return len(this.rowData) +} + +// 新建数据表对象 +// rows:数据行对象 +// 返回值: +// *DataTable:数据表对象 +// error:错误信息 +func NewDataTable(rows *sql.Rows) (*DataTable, error) { + table := &DataTable{} + errMsg := table.init(rows) + if errMsg != nil { + return nil, errMsg + } + + return table, nil +} diff --git a/trunk/goutil/dbUtil/valueConvert.go b/trunk/goutil/dbUtil/valueConvert.go new file mode 100644 index 0000000..3d0d496 --- /dev/null +++ b/trunk/goutil/dbUtil/valueConvert.go @@ -0,0 +1,195 @@ +package dbUtil + +import ( + "fmt" + "time" + + "goutil/typeUtil" +) + +// 类型转换为byte +// 返回值: +// byte:结果 +// error:错误数据 +func Byte(row *DataRow, key string) (byte, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Byte(val) +} + +// 类型转换为int +// 返回值: +// int:结果 +// error:错误数据 +func Int32(row *DataRow, key string) (int32, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Int32(val) +} + +// 类型转换为uint32 +// 返回值: +// int:结果 +// error:错误数据 +func Uint32(row *DataRow, key string) (uint32, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Uint32(val) +} + +// 类型转换为int +// 返回值: +// int:结果 +// error:错误数据 +func Int(row *DataRow, key string) (int, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Int(val) +} + +// 类型转换为int +// 返回值: +// int:结果 +// error:错误数据 +func Uint(row *DataRow, key string) (uint, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Uint(val) +} + +// 类型转换为int +// 返回值: +// int:结果 +// error:错误数据 +func Int64(row *DataRow, key string) (int64, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Int64(val) +} + +// 类型转换为int +// 返回值: +// int:结果 +// error:错误数据 +func Uint64(row *DataRow, key string) (uint64, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Uint64(val) +} + +// 类型转换为int +// 返回值: +// float64:结果 +// error:错误数据 +func Float64(row *DataRow, key string) (float64, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return 0, errMsg + } + + if val == nil { + return 0, fmt.Errorf("value is nil") + } + + return typeUtil.Float64(val) +} + +// 类型转换为bool +// 返回值: +// bool:结果 +// error:错误信息 +func Bool(row *DataRow, key string) (bool, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return false, errMsg + } + + if val == nil { + return false, fmt.Errorf("value is nil") + } + + return typeUtil.Bool(val) +} + +// 类型转换为字符串 +// 返回值: +// string:结果 +// error:错误信息 +func String(row *DataRow, key string) (string, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return "", errMsg + } + + if val == nil { + return "", fmt.Errorf("value is nil") + } + + return typeUtil.String(val) +} + +// 转换为时间格式,如果是字符串,则要求内容格式形如:2017-02-14 05:20:00 +// 返回值: +// bool:结果 +// error:错误信息 +func DateTime(row *DataRow, key string) (time.Time, error) { + val, errMsg := row.CellValueByName(key) + if errMsg != nil { + return time.Time{}, errMsg + } + + if val == nil { + return time.Time{}, fmt.Errorf("value is nil") + } + + return typeUtil.DateTime(val) +} diff --git a/trunk/goutil/debugUtil/debug.go b/trunk/goutil/debugUtil/debug.go new file mode 100644 index 0000000..0b20060 --- /dev/null +++ b/trunk/goutil/debugUtil/debug.go @@ -0,0 +1,66 @@ +package debugUtil + +import ( + "github.com/fatih/color" +) + +var ( + isDebug = false + code Code = Code_Bold + foregroundColor ForegroundColor = Foreground_Purple + backgroundColor BackgroundColor = BackgroundColor_Black + + colorObj = color.New(foregroundColor, code) +) + +// 设置DEBUG状态 +// _isDebug:是否是DEBUG +func SetDebug(_isDebug bool) { + isDebug = _isDebug +} + +// 是否处于调试状态 +func IsDebug() bool { + return isDebug +} + +// 设置显示信息 +func SetDisplayInfo(_code Code, _foregroundColor ForegroundColor, _backgroundColor BackgroundColor) { + code = _code + foregroundColor = _foregroundColor + backgroundColor = _backgroundColor + + colorObj = color.New(foregroundColor, code) +} + +// Print formats using the default formats for its operands and writes to standard output. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +func Print(a ...interface{}) { + if !isDebug { + return + } + + _, _ = colorObj.Print(a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +func Printf(format string, a ...interface{}) { + if !isDebug { + return + } + + _, _ = colorObj.Printf(format, a...) +} + +// Println formats using the default formats for its operands and writes to standard output. +// Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. +func Println(a ...interface{}) { + if !isDebug { + return + } + + _, _ = colorObj.Println(a...) +} diff --git a/trunk/goutil/debugUtil/display.go b/trunk/goutil/debugUtil/display.go new file mode 100644 index 0000000..071d1a4 --- /dev/null +++ b/trunk/goutil/debugUtil/display.go @@ -0,0 +1,47 @@ +package debugUtil + +import "github.com/fatih/color" + +// Code 显示代码 +type Code = color.Attribute + +const ( + Code_Reset Code = color.Reset + Code_Bold Code = color.Bold + Code_Faint Code = color.Faint + Code_Italic Code = color.Italic + Code_Underline Code = color.Underline + Code_BlinkSlow Code = color.BlinkSlow + Code_BlinkRapid Code = color.BlinkRapid + Code_ReverseVideo Code = color.ReverseVideo + Code_Concealed Code = color.Concealed + Code_CrossedOut Code = color.CrossedOut +) + +// ForegroundColor 前景色 +type ForegroundColor = color.Attribute + +const ( + Foreground_Black ForegroundColor = color.FgBlack + Foreground_Red ForegroundColor = color.FgRed + Foreground_Green ForegroundColor = color.FgGreen + Foreground_Yellow ForegroundColor = color.FgYellow + Foreground_Blue ForegroundColor = color.FgBlue + Foreground_Purple ForegroundColor = color.FgMagenta + Foreground_Cyan ForegroundColor = color.FgCyan + Foreground_White ForegroundColor = color.FgWhite +) + +// BackgroundColor 背景色 +type BackgroundColor = color.Attribute + +const ( + BackgroundColor_Black = color.BgBlack + BackgroundColor_Red = color.BgRed + BackgroundColor_Green = color.BgGreen + BackgroundColor_Yellow = color.BgYellow + BackgroundColor_Blue = color.BgBlue + BackgroundColor_Purple = color.BgMagenta + BackgroundColor_Cyan = color.BgCyan + BackgroundColor_White = color.BgWhite +) diff --git a/trunk/goutil/debugUtil/doc.go b/trunk/goutil/debugUtil/doc.go new file mode 100644 index 0000000..6e476fb --- /dev/null +++ b/trunk/goutil/debugUtil/doc.go @@ -0,0 +1,4 @@ +/* +提供调试功能的助手包 +*/ +package debugUtil diff --git a/trunk/goutil/deviceUtil/deviceUtil.go b/trunk/goutil/deviceUtil/deviceUtil.go new file mode 100644 index 0000000..28fa721 --- /dev/null +++ b/trunk/goutil/deviceUtil/deviceUtil.go @@ -0,0 +1,81 @@ +package deviceUtil + +import ( + "strings" +) + +// 将MAC地址转化为标准格式 +func ConvertMacToStandardFormat(mac string) string { + if mac == "" || mac == "00:00:00:00:00:00" || mac == "02:00:00:00:00:00" { + return "" + } + + //如果mac的长度不为12或17,则是不正确的格式 + if len(mac) != 12 && len(mac) != 17 { + return "" + } + + + //转化为大写 + mac = strings.ToUpper(mac) + + //如果mac地址的长度为17(已经有:),则直接返回 + if len(mac) == 17 { + return mac + } + + //如果没有分隔符,则添加分隔符 + newMac := make([]rune, 0, 17) + for i, v := range []rune(mac) { + newMac = append(newMac, v) + if i < len(mac) - 1 && i % 2 == 1 { + newMac = append(newMac, ':') + } + } + + return string(newMac) +} + +func ConvertIdfaToStandardFormat(idfa string) string { + //如果是空或默认值,则返回String.Empty + if idfa == "" || idfa == "00000000-0000-0000-0000-000000000000" { + return "" + } + + //如果idfa的长度不为32或36,则代表是Android的数据,则可以直接返回 + if len(idfa) != 32 && len(idfa) != 36 { + return idfa + } + + //转化为大写 + idfa = strings.ToUpper(idfa); + + //如果idfa地址的长度为36(已经有:),则直接返回 + if len(idfa) == 36 { + return idfa + } + + //如果没有分隔符,则添加分隔符 + newIdfa := make([]rune, 0, 36) + for i, v := range []rune(idfa) { + newIdfa = append(newIdfa, v) + if i == 7 || i == 11 || i == 15 || i == 19 { + newIdfa = append(newIdfa, '-') + } + } + + return string(newIdfa) +} + +// 根据MAC和IDFA获取唯一标识 +func GetIdentifier(mac, idfa string) string { + mac = ConvertMacToStandardFormat(mac) + idfa = ConvertIdfaToStandardFormat(idfa); + + //如果idfa不为空,则使用idfa,否则使用mac + if idfa != "" { + return idfa + } else { + return mac + } +} diff --git a/trunk/goutil/deviceUtil/deviceUtil_test.go b/trunk/goutil/deviceUtil/deviceUtil_test.go new file mode 100644 index 0000000..271273f --- /dev/null +++ b/trunk/goutil/deviceUtil/deviceUtil_test.go @@ -0,0 +1,131 @@ +package deviceUtil + +import ( + "testing" +) + +func TestConvertMacToStarndardFormat(t *testing.T) { + mac := "" + expected := "" + got := ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "00:00:00:00:00:00" + expected = "" + got = ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "02:00:00:00:00:00" + expected = "" + got = ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "02:00:00:00:00" + expected = "" + got = ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "020000000020" + expected = "02:00:00:00:00:20" + got = ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "02:00:00:00:00:20" + expected = "02:00:00:00:00:20" + got = ConvertMacToStandardFormat(mac) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } +} + +func TestConvertIdfaToStandardFormat(t *testing.T) { + idfa := "" + expected := "" + got := ConvertIdfaToStandardFormat(idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + idfa = "00000000-0000-0000-0000-000000000000" + expected = "" + got = ConvertIdfaToStandardFormat(idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + idfa = "00000000-0000-0000-0000-000000000000-123" + expected = "00000000-0000-0000-0000-000000000000-123" + got = ConvertIdfaToStandardFormat(idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + idfa = "00000000-1234-5678-0000-000000000000" + expected = "00000000-1234-5678-0000-000000000000" + got = ConvertIdfaToStandardFormat(idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + idfa = "00000000123456780000000000000000" + expected = "00000000-1234-5678-0000-000000000000" + got = ConvertIdfaToStandardFormat(idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } +} + +func TestGetIdentifier(t *testing.T) { + mac := "" + idfa := "" + expected := "" + got := GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "00:00:00:00:00:00" + expected = "" + got = GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "02:00:00:00:00:00" + expected = "" + got = GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "020000000020" + expected = "02:00:00:00:00:20" + got = GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + mac = "02:00:00:00:00:20" + expected = "02:00:00:00:00:20" + got = GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } + + idfa = "00000000123456780000000000000000" + expected = "00000000-1234-5678-0000-000000000000" + got = GetIdentifier(mac, idfa) + if got != expected { + t.Errorf("Expected: %s, but got:%s", expected, got) + } +} diff --git a/trunk/goutil/dfaExUtil/dfaEx.go b/trunk/goutil/dfaExUtil/dfaEx.go new file mode 100644 index 0000000..98967e3 --- /dev/null +++ b/trunk/goutil/dfaExUtil/dfaEx.go @@ -0,0 +1,240 @@ +package dfaExUtil + +/* + * 扩展DFA算法 + * + * 一种二层树实现类DFA算法(DFA为多层树结构;go语言特性中的map结构过于“重量级”导致内存占用很大;此外还可能存在大量相同字符结点) + * + * 第一层map为所有字母/汉字作为key;value为第二层map + * 第二层map为第一层冲突字母/汉字的自定义hash作为key;value指示是否为敏感词结束标识 + * + * 测试结果:50万+的敏感词: + * 构造树耗时稍优于原始DFA; + * 内存使用为原DFA的不到1/4:原DFA占用495M内存,此算法使用111M; + * 查询效率比原DFA低10%~20%左右;主要是多一次map查询和多一次hash计算; + * + */ + +/* + * 注意使用[]rune的问题(此问题已通过使用固定位数hash解决): + * []rune中文使用的是unicode编码;若“中”编码为#4E2D;而#4E2D对应“N-”; + * 即:"N-"与"中"unicode编码均为#4E2D,即会产生hash冲突 + * + */ + +import ( + "fmt" + "strings" + + hash "goutil/dfaExUtil/hash64" +) + +// hash使用的类型(uint64对应hash64函数;uint32对应hash32函数) +type hashType = uint64 + +type DFAEx struct { + // 忽略大小写;true-忽略大小写;false-大小写敏感 + ignoreCase bool + // hash冲突个数 + hashCollisions int + // 树根 + // 字符/hash/uint8(b10000000)(最高字符表示是否结束/低7位表示字符位置) + root map[rune]map[hashType]uint8 +} + +// 新建敏感词对象 +// wordList - 敏感词列表 +// ignoreCase - [可选;默认false] 是否忽略大小写 +func NewDFAEx(wordList []string, ignoreCase ...bool) (dfaEx *DFAEx) { + var iCase bool + var mapSize int + + if len(ignoreCase) > 0 { + iCase = ignoreCase[0] + } + + mapSize = len(wordList) * 10 + // 防止过小 + if mapSize < 1_000 { + mapSize = 1_000 + } + // 通常各语言的单rune非重复数不超过1万 + if mapSize > 10_000 { + mapSize = 10_000 + } + + dfaEx = &DFAEx{ + ignoreCase: iCase, + root: make(map[rune]map[hashType]uint8, mapSize), + } + + for _, v := range wordList { + word := v + if iCase { + // 忽略大小写;所有字母转大写 + word = strings.ToUpper(word) + } + wordRune := []rune(word) + if len(wordRune) > 0 { + dfaEx.InsertWord(wordRune) + } + } + + return dfaEx +} + +// 添加敏感词 +func (dfaEx *DFAEx) InsertWord(word []rune) { + var hs hashType + var lastWord rune + var lastHash hashType + for i, v := range word { + lastWord = v + lastHash = hs + if wdInfo, ok := dfaEx.root[v]; ok { + // "字"已存在 + if hsV, ok := wdInfo[hs]; !ok { + // hash不存在,添加hash + wdInfo[hs] = uint8(i & 0x7F) // 第i位 + } else { + // hash已存在,检测是否冲突 + if (hsV & 0x7F) != uint8(i) { + // hash冲突 + dfaEx.hashCollisions++ + // fmt.Printf("hash冲突 %s %016X %d %d\n", string(v), hs, i+1, hsV&0x7F+1) + } + } + } else { + // "字"不存在,添加"字"和hash + wdInfo = make(map[hashType]uint8) + wdInfo[hs] = uint8(i & 0x7F) // 第i位 + dfaEx.root[v] = wdInfo + } + hs = hash.FastSumByRune2(v, hs) // hash更新 + } + + // 敏感词结束标志(uint8最高位置1) + dfaEx.root[lastWord][lastHash] |= 0x80 +} + +// 字符串查找敏感词 +func (dfaEx *DFAEx) IsMatch(str string) bool { + starts, _ := dfaEx.SearchSentence(str, true) + return len(starts) > 0 +} + +// 指定字符替换敏感词 +func (dfaEx *DFAEx) HandleWord(str string, replace rune) string { + starts, ends := dfaEx.SearchSentence(str) + if len(starts) == 0 { + return str + } + + strRune := []rune(str) + for i := 0; i < len(starts); i++ { + for idx := starts[i]; idx <= ends[i]; idx++ { + strRune[idx] = replace + } + } + + return string(strRune) +} + +// 字符串查找敏感词 +func (dfaEx *DFAEx) SearchSentence(str string, firstOpt ...bool) (starts, ends []int) { + var first bool // 是否首次匹配就返回 + if len(firstOpt) > 0 { + first = firstOpt[0] + } + strBak := str + if dfaEx.ignoreCase { + // 忽略大小写;所有字母转大写 + strBak = strings.ToUpper(str) + } + runeStr := []rune(strBak) + for i := 0; i < len(runeStr); { + end := dfaEx.searchByStart(i, runeStr) + if end < 0 { + // 继续下一个进行匹配 + i++ + } else { + // 记录匹配位置;从匹配到的下一个位置继续 + starts = append(starts, i) + ends = append(ends, end) + if first { + // 首次匹配就返回 + break + } + + i = end + 1 + } + } + + return +} + +// 从指定的开始位置搜索语句 +// start - 开始匹配的位置 +// str - 待检测字符串 +// 返回:匹配到的结束位置,未匹配到返回-1 +func (dfaEx *DFAEx) searchByStart(start int, runeStr []rune) (end int) { + var hs hashType + end = -1 // 未匹配到返回值 + + for i := start; i < len(runeStr); i++ { + wd := runeStr[i] + wdInfo, ok := dfaEx.root[wd] + if !ok { + // "字"不存在 + break + } + + hsV, ok := wdInfo[hs] + if !ok { + // hash不存在 + break + } + + // 检测是否句尾 + if (hsV & 0x80) != 0 { + // 找到句尾,继续匹配,直到匹配到最长敏感词为止 + end = i + } + + hs = hash.FastSumByRune2(wd, hs) // hash更新 + } + + return +} + +// 获取hash冲突数 +func (dfaEx *DFAEx) GetHashCollisions() int { + return dfaEx.hashCollisions +} + +// 调试接口 +func (dfaEx *DFAEx) Print() { + fmt.Println(dfaEx) +} + +// 调试接口 +func (dfaEx *DFAEx) PrintFmt(verbose bool) { + var keys int + var hashs int + for k, v := range dfaEx.root { + keys++ + if verbose { + fmt.Println("---------------------------") + fmt.Println(string(k)) + } + for kk, vv := range v { + hashs++ + if verbose { + fmt.Printf("%016X %02X\n", kk, vv) + } + } + } + + fmt.Println("================================") + fmt.Println("keys:", keys, "hashs", hashs, "map count", keys+1, "hashCollisions Count", dfaEx.hashCollisions) +} diff --git a/trunk/goutil/dfaExUtil/dfaEx_test.go b/trunk/goutil/dfaExUtil/dfaEx_test.go new file mode 100644 index 0000000..e7496b7 --- /dev/null +++ b/trunk/goutil/dfaExUtil/dfaEx_test.go @@ -0,0 +1,15 @@ +package dfaExUtil + +import "testing" + +func TestHandleWord(t *testing.T) { + strs := []string{"ABC", "1234", "测试", "测试代码", "测试一下"} + + dfaEx1 := NewDFAEx(strs) + str := dfaEx1.HandleWord("abc按了数字12345来测试代码是否正常,结果测试出了bug", '*') + t.Log(str) + + dfaEx2 := NewDFAEx(strs, true) + str = dfaEx2.HandleWord("abc按了数字12345来测试代码是否正常,结果测试出了bug", '*') + t.Log(str) +} diff --git a/trunk/goutil/dfaExUtil/hash32/hash32.go b/trunk/goutil/dfaExUtil/hash32/hash32.go new file mode 100644 index 0000000..fa84949 --- /dev/null +++ b/trunk/goutil/dfaExUtil/hash32/hash32.go @@ -0,0 +1,98 @@ +package hash32 + +/* + * ***注意*** + * + * Sum 使用的是[]byte参数;string中文是utf-8编码 + * SumByRune 使用的是[]rune参数;中文使用的是unicode编码 + * + * 两种参数中文编码不同;同一个string调用两个接口得到的hash是不同的!!! 这是要特别注意的 + * + * 对string进行for range操作会自动被[]rune化;一定要注意!!! + * + */ + +const shiftBit = 11 // 每个字节移位数(测试经验值11) +const reverseBit = 32 - shiftBit + +// 快速Hash计算(*** 注意:只取了rune低16位进行hash计算;计算结果与SumByRune不一致 ***) +func FastSumByRune2(in rune, hs uint32) (out uint32) { + out = ((hs << shiftBit) | (hs >> reverseBit)) + uint32(byte(in>>8)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in)) + return +} + +// 快速Hash计算(*** 此计算结果与SumByRune一致 ***) +func FastSumByRune4(in rune, hs uint32) (out uint32) { + out = ((hs << shiftBit) | (hs >> reverseBit)) + uint32(byte(in>>24)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in>>16)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in>>8)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint32(byte(in)) + return +} + +// 原Hash值参数重 +func hsArg(hsOpt ...uint32) (out uint32) { + out = uint32(0) + if len(hsOpt) > 0 { + out = hsOpt[0] + } + return +} + +// Hash计算 +// in - 待hash串 +// hsOpt - 原hash值(在此基础上继续hash) +func Sum(in []byte, hsOpt ...uint32) (out uint32) { + out = hsArg(hsOpt...) + for _, v := range in { + out = ((out << shiftBit) | (out >> reverseBit)) + uint32(v) + } + + return +} + +// Hash计算 +func SumByRune(in rune, hsOpt ...uint32) (out uint32) { + // rune转[]byte + inVal := make([]byte, 4) + inVal[0] = byte(in >> 24) + inVal[1] = byte(in >> 16) + inVal[2] = byte(in >> 8) + inVal[3] = byte(in) + + // *** 经实际测试:不加以下代码运行效率更高 *** + + // 去除前面多余的\x00 + // for { + // if len(inVal) <= 1 { + // // 以免全0异常;至少要保留1位 + // break + // } + // if inVal[0] == 0 { + // inVal = inVal[1:] + // } else { + // break + // } + // } + // 规避hash冲突,如:"N-"与"中"unicode编码均为#4E2D,即会产生hash冲突 + // 若长度>1(即非常规ASCII),所有字节最高位置1 + // if len(inVal) > 1 { + // for i := 0; i < len(inVal); i++ { + // inVal[i] |= 0x80 + // } + // } + out = Sum(inVal, hsOpt...) + + return +} + +// Hash计算 +func SumByRunes(in []rune, hsOpt ...uint32) (out uint32) { + out = hsArg(hsOpt...) + for _, v := range in { + out = SumByRune(v, out) + } + + return +} diff --git a/trunk/goutil/dfaExUtil/hash64/hash64.go b/trunk/goutil/dfaExUtil/hash64/hash64.go new file mode 100644 index 0000000..17eb0fa --- /dev/null +++ b/trunk/goutil/dfaExUtil/hash64/hash64.go @@ -0,0 +1,98 @@ +package hash64 + +/* + * ***注意*** + * + * Sum 使用的是[]byte参数;string中文是utf-8编码 + * SumByRune 使用的是[]rune参数;中文使用的是unicode编码 + * + * 两种参数中文编码不同;同一个string调用两个接口得到的hash是不同的!!! 这是要特别注意的 + * + * 对string进行for range操作会自动被[]rune化;一定要注意!!! + * + */ + +const shiftBit = 23 // 每个字节移位数(测试经验值23) +const reverseBit = 64 - shiftBit + +// 快速Hash计算(*** 注意:只取了rune低16位进行hash计算;计算结果与SumByRune不一致 ***) +func FastSumByRune2(in rune, hs uint64) (out uint64) { + out = ((hs << shiftBit) | (hs >> reverseBit)) + uint64(byte(in>>8)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint64(byte(in)) + return +} + +// 快速Hash计算(*** 此计算结果与SumByRune一致 ***) +func FastSumByRune4(in rune, hs uint64) (out uint64) { + out = ((hs << shiftBit) | (hs >> reverseBit)) + uint64(byte(in>>24)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint64(byte(in>>16)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint64(byte(in>>8)) + out = ((out << shiftBit) | (out >> reverseBit)) + uint64(byte(in)) + return +} + +// 原Hash值参数重 +func hsArg(hsOpt ...uint64) (out uint64) { + out = uint64(0) + if len(hsOpt) > 0 { + out = hsOpt[0] + } + return +} + +// Hash计算 +// in - 待hash串 +// hsOpt - 原hash值(在此基础上继续hash) +func Sum(in []byte, hsOpt ...uint64) (out uint64) { + out = hsArg(hsOpt...) + for _, v := range in { + out = ((out << shiftBit) | (out >> reverseBit)) + uint64(v) + } + + return +} + +// Hash计算 +func SumByRune(in rune, hsOpt ...uint64) (out uint64) { + // rune转[]byte + inVal := make([]byte, 4) + inVal[0] = byte(in >> 24) + inVal[1] = byte(in >> 16) + inVal[2] = byte(in >> 8) + inVal[3] = byte(in) + + // *** 经实际测试:不加以下代码运行效率更高 *** + + // 去除前面多余的\x00 + // for { + // if len(inVal) <= 1 { + // // 以免全0异常;至少要保留1位 + // break + // } + // if inVal[0] == 0 { + // inVal = inVal[1:] + // } else { + // break + // } + // } + // 规避hash冲突,如:"N-"与"中"unicode编码均为#4E2D,即会产生hash冲突 + // 若长度>1(即非常规ASCII),所有字节最高位置1 + // if len(inVal) > 1 { + // for i := 0; i < len(inVal); i++ { + // inVal[i] |= 0x80 + // } + // } + out = Sum(inVal, hsOpt...) + + return +} + +// Hash计算 +func SumByRunes(in []rune, hsOpt ...uint64) (out uint64) { + out = hsArg(hsOpt...) + for _, v := range in { + out = SumByRune(v, out) + } + + return +} diff --git a/trunk/goutil/dfaExUtil/三维树转二维树.bmp b/trunk/goutil/dfaExUtil/三维树转二维树.bmp new file mode 100644 index 0000000..f3af9dd Binary files /dev/null and b/trunk/goutil/dfaExUtil/三维树转二维树.bmp differ diff --git a/trunk/goutil/dfaUtil/dfa.go b/trunk/goutil/dfaUtil/dfa.go new file mode 100644 index 0000000..f093bfa --- /dev/null +++ b/trunk/goutil/dfaUtil/dfa.go @@ -0,0 +1,219 @@ +package dfaUtil + +import "strings" + +/* +DFA util, is used to verify whether a sentence has invalid words. +The underlying data structure is trie. +https://en.wikipedia.org/wiki/Trie +*/ + +// dfa util +type DFAUtil struct { + // The root node + root *trieNode +} + +// 搜索语句 +// 由于go不支持tuple,所以为了避免定义多余的struct,特别使用两个list来分别返回匹配的索引的上界和下界 +// 在处理此方法的返回值时,需要两者配合使用 +// 参数: +// +// sentence:语句字符串 +// +// 返回: +// +// 搜索到的开始位置列表 +// 搜索到的结束位置列表 +func (this *DFAUtil) SearchSentence(sentence string) (startIndexList, endIndexList []int) { + sentenceRuneList := []rune(sentence) + for i := 0; i < len(sentenceRuneList); { + //按序匹配每个字 + end := this.searchSentenceByStart(i, sentenceRuneList) + if end < 0 { + //匹配失败,继续匹配下一个字 + i++ + } else { + //匹配成功,记录索引位置 + startIndexList = append(startIndexList, i) + endIndexList = append(endIndexList, end) + + //从匹配到的字后面开始找 + i = end + 1 + } + } + + return +} + +// 从指定的开始位置搜索语句 +// 参数: +// +// start:开始匹配的位置 +// sentenceRuneList:语句字列表 +// +// 返回: +// +// 匹配到的结束位置,未匹配到返回-1 +func (this *DFAUtil) searchSentenceByStart(start int, sentenceRuneList []rune) (endIndex int) { + //当前节点,从根节点开始找 + currNode := this.root + //是否匹配到 + var isMatched bool + + //按顺序匹配字 + for i := start; i < len(sentenceRuneList); { + child, exists := currNode.children[sentenceRuneList[i]] + + //未匹配到则结束,跳出循环(可能匹配到过词结尾) + if !exists { + break + } + + //是否是词末尾,如果是则先记录下来,因为还可能匹配到更长的词 + //比如["金鳞"、"金鳞岂是池中物"] => 匹配"金鳞岂是池中物",匹配到"金鳞"不应该停下来,应继续匹配更长的词 + if child.isEndOfWord { + endIndex = i + isMatched = true + } + + //是否已经到词末尾 + if len(child.children) == 0 { + return endIndex + } else { + //继续与后面的字匹配 + currNode = child + } + + //增加索引匹配下一个位置 + i++ + } + + //匹配结束,若曾经匹配到词末尾,则直接返回匹配到的位置 + if isMatched { + return endIndex + } else { + //没有匹配到词末尾,则返回匹配失败 + return -1 + } +} + +// Insert new word into object +func (this *DFAUtil) InsertWord(word []rune) { + currNode := this.root + for _, c := range word { + if cildNode, exist := currNode.children[c]; !exist { + cildNode = newtrieNode() + currNode.children[c] = cildNode + currNode = cildNode + } else { + currNode = cildNode + } + } + + currNode.isEndOfWord = true +} + +// Check if there is any word in the trie that starts with the given prefix. +func (this *DFAUtil) StartsWith(prefix []rune) bool { + currNode := this.root + for _, c := range prefix { + if childNode, exist := currNode.children[c]; !exist { + return false + } else { + currNode = childNode + } + } + + return true +} + +// Judge if input sentence contains some special caracter +// Return: +// Matc or not +func (this *DFAUtil) IsMatch(sentence string) bool { + startIndexList, _ := this.SearchSentence(sentence) + return len(startIndexList) > 0 +} + +// Handle sentence. Use specified caracter to replace those sensitive caracters. +// input: Input sentence +// replaceCh: candidate +// Return: +// Sentence after manipulation +func (this *DFAUtil) HandleWord(sentence string, replaceCh rune) string { + startIndexList, endIndexList := this.SearchSentence(sentence) + if len(startIndexList) == 0 { + return sentence + } + + // Manipulate + sentenceList := []rune(sentence) + for i := 0; i < len(startIndexList); i++ { + for index := startIndexList[i]; index <= endIndexList[i]; index++ { + sentenceList[index] = replaceCh + } + } + + return string(sentenceList) +} + +// Handle sentence. Use specified caracter to replace those sensitive caracters. +// input: Input sentence +// replaceCh: candidate +// Return: +// Sentence after manipulation +func (this *DFAUtil) HandleWordUseStr(input string, replaceCh string) string { + input2 := strings.ToUpper(input) + + startIndexList, endIndexList := this.SearchSentence(input2) + if len(startIndexList) == 0 { + return input + } + + // Manipulate + inputRune := []rune(input) + replaceChList := []rune(replaceCh) + + //上一次替换掉的数量 + lastReplaceCount := 0 + + for i := 0; i < len(startIndexList); i++ { + + //替换字的索引 + index := len(replaceChList) + + //开始位置--加上替换的词的索引 + starIndex := startIndexList[i] + (i * index) - lastReplaceCount + + //结束位置 + endIndex := endIndexList[i] + (i * index) - lastReplaceCount + + //结束字符串 + sentenceAttr := string(inputRune[endIndex+1:]) + + //替换范围字符串 + inputRune = append(inputRune[:starIndex], replaceChList...) + inputRune = append(inputRune, []rune(sentenceAttr)...) + lastReplaceCount = endIndex + 1 - starIndex + } + + return string(inputRune) +} + +// Create new DfaUtil object +// wordList:word list +func NewDFAUtil(wordList []string) *DFAUtil { + this := &DFAUtil{ + root: newtrieNode(), + } + + for _, word := range wordList { + wordRuneList := []rune(word) + if len(wordRuneList) > 0 { + this.InsertWord(wordRuneList) + } + } + + return this +} diff --git a/trunk/goutil/dfaUtil/dfa_test.go b/trunk/goutil/dfaUtil/dfa_test.go new file mode 100644 index 0000000..362ce75 --- /dev/null +++ b/trunk/goutil/dfaUtil/dfa_test.go @@ -0,0 +1,2088 @@ +package dfaUtil + +import ( + "testing" +) + +var ( + sensitiveList = make([]string, 0, 1024) +) + +func init() { + sensitiveList = append(sensitiveList, "&") + sensitiveList = append(sensitiveList, "*鏉?娲?蹇?闃挎墎") + sensitiveList = append(sensitiveList, "01gh.com") + sensitiveList = append(sensitiveList, "025game.cn") + sensitiveList = append(sensitiveList, "0571qq.com") + sensitiveList = append(sensitiveList, "09city.com") + sensitiveList = append(sensitiveList, "1000scarf.com") + sensitiveList = append(sensitiveList, "10mb.cn") + sensitiveList = append(sensitiveList, "1100y.com") + sensitiveList = append(sensitiveList, "11xp.1243.net.cn") + sensitiveList = append(sensitiveList, "1234chengren.1249.net.cn") + sensitiveList = append(sensitiveList, "131.com") + sensitiveList = append(sensitiveList, "13888wg.com") + sensitiveList = append(sensitiveList, "13ml.net") + sensitiveList = append(sensitiveList, "158le.com") + sensitiveList = append(sensitiveList, "15wy.com") + sensitiveList = append(sensitiveList, "166578.cn") + sensitiveList = append(sensitiveList, "16dy-鍥惧簱") + sensitiveList = append(sensitiveList, "16dy-鍦栧韩") + sensitiveList = append(sensitiveList, "16澶?17173dl.net") + sensitiveList = append(sensitiveList, "17173yxdl.cn") + sensitiveList = append(sensitiveList, "173at.com") + sensitiveList = append(sensitiveList, "17youle.com") + sensitiveList = append(sensitiveList, "18900.com") + sensitiveList = append(sensitiveList, "18鎽?18绂?1999浜氬お鏂版柊闂?1qmsj.com") + sensitiveList = append(sensitiveList, "1t1t.com") + sensitiveList = append(sensitiveList, "202333.com") + sensitiveList = append(sensitiveList, "208.43.198.56") + sensitiveList = append(sensitiveList, "21涓栫邯涓浗鍩洪噾浼?222se鍥剧墖") + sensitiveList = append(sensitiveList, "222se鍦栫墖") + sensitiveList = append(sensitiveList, "2feiche.cn") + sensitiveList = append(sensitiveList, "30鏉傚織") + sensitiveList = append(sensitiveList, "33bbb璧板厜") + sensitiveList = append(sensitiveList, "365gn") + sensitiveList = append(sensitiveList, "365tttyx.com") + sensitiveList = append(sensitiveList, "37447.cn") + sensitiveList = append(sensitiveList, "3kwow.com") + sensitiveList = append(sensitiveList, "3p鐐浘") + sensitiveList = append(sensitiveList, "3p鐐湒") + sensitiveList = append(sensitiveList, "47513.cn") + sensitiveList = append(sensitiveList, "51hdw.com") + sensitiveList = append(sensitiveList, "51jiafen.cn") + sensitiveList = append(sensitiveList, "51juezhan.com") + sensitiveList = append(sensitiveList, "52ppsa.cn") + sensitiveList = append(sensitiveList, "52sxhy.cn") + sensitiveList = append(sensitiveList, "54hero.com") + sensitiveList = append(sensitiveList, "55sss鍋锋媿鍖?55sss鍋锋媿鍗€") + sensitiveList = append(sensitiveList, "567567aa.cn") + sensitiveList = append(sensitiveList, "58.253.67.74") + sensitiveList = append(sensitiveList, "597ft.com") + sensitiveList = append(sensitiveList, "5d6d.com") + sensitiveList = append(sensitiveList, "5m5m5m.com") + sensitiveList = append(sensitiveList, "6-4tianwang") + sensitiveList = append(sensitiveList, "661661.com") + sensitiveList = append(sensitiveList, "69nb.com") + sensitiveList = append(sensitiveList, "71ka.com") + sensitiveList = append(sensitiveList, "77bbb") + sensitiveList = append(sensitiveList, "7gg") + sensitiveList = append(sensitiveList, "7mmo.com") + sensitiveList = append(sensitiveList, "8 浠?8188mu.com") + sensitiveList = append(sensitiveList, "888895.com") + sensitiveList = append(sensitiveList, "88kx.com") + sensitiveList = append(sensitiveList, "88mysf.com") + sensitiveList = append(sensitiveList, "89-64cdjp") + sensitiveList = append(sensitiveList, "8浠?91bysd.cn") + sensitiveList = append(sensitiveList, "92ey.com") + sensitiveList = append(sensitiveList, "92klgh.cn") + sensitiveList = append(sensitiveList, "92wydl.com") + sensitiveList = append(sensitiveList, "97sese") + sensitiveList = append(sensitiveList, "991game.com") + sensitiveList = append(sensitiveList, "999鏃ユ湰濡?99sa.com") + sensitiveList = append(sensitiveList, "a33.com") + sensitiveList = append(sensitiveList, "a4u") + sensitiveList = append(sensitiveList, "a4y") + sensitiveList = append(sensitiveList, "aa.yazhousetu.hi.9705.net.cn") + sensitiveList = append(sensitiveList, "aaac.s.51524.com") + sensitiveList = append(sensitiveList, "aaad.s.59764.com") + sensitiveList = append(sensitiveList, "abian") + sensitiveList = append(sensitiveList, "abianwansui") + sensitiveList = append(sensitiveList, "admin") + sensitiveList = append(sensitiveList, "administrator") + sensitiveList = append(sensitiveList, "aids") + sensitiveList = append(sensitiveList, "aiort澧撳湴") + sensitiveList = append(sensitiveList, "aisenhaoweier") + sensitiveList = append(sensitiveList, "ai婊?alafate") + sensitiveList = append(sensitiveList, "aluoyue") + sensitiveList = append(sensitiveList, "anbeijinsan") + sensitiveList = append(sensitiveList, "annan") + sensitiveList = append(sensitiveList, "any2000.com") + sensitiveList = append(sensitiveList, "aobama") + sensitiveList = append(sensitiveList, "aomaer") + sensitiveList = append(sensitiveList, "arqus浼氳鍦?asgardcn.com") + sensitiveList = append(sensitiveList, "asshole") + sensitiveList = append(sensitiveList, "atan鐨勭Щ鍔ㄧ煶") + sensitiveList = append(sensitiveList, "AV") + sensitiveList = append(sensitiveList, "a鐗?baichi") + sensitiveList = append(sensitiveList, "baijie.1249.net.cn") + sensitiveList = append(sensitiveList, "banchan") + sensitiveList = append(sensitiveList, "baopi") + sensitiveList = append(sensitiveList, "baoweier") + sensitiveList = append(sensitiveList, "bao鐨?bastard") + sensitiveList = append(sensitiveList, "bazhuwg.cn") + sensitiveList = append(sensitiveList, "BBC") + sensitiveList = append(sensitiveList, "bbs.766.com") + sensitiveList = append(sensitiveList, "bbs.7gg.cn") + sensitiveList = append(sensitiveList, "bbs.butcn.com") + sensitiveList = append(sensitiveList, "bbs.pkmmo") + sensitiveList = append(sensitiveList, "bc") + sensitiveList = append(sensitiveList, "bcd.s.59764.com") + sensitiveList = append(sensitiveList, "benladeng") + sensitiveList = append(sensitiveList, "biaozi") + sensitiveList = append(sensitiveList, "biao瀛?bibidu.com") + sensitiveList = append(sensitiveList, "bignews") + sensitiveList = append(sensitiveList, "bitch") + sensitiveList = append(sensitiveList, "bi鏍?bl62.com") + sensitiveList = append(sensitiveList, "blacksee.com.cn") + sensitiveList = append(sensitiveList, "blowjob") + sensitiveList = append(sensitiveList, "blowjobs") + sensitiveList = append(sensitiveList, "bobaoping") + sensitiveList = append(sensitiveList, "boxilai") + sensitiveList = append(sensitiveList, "boxun") + sensitiveList = append(sensitiveList, "bulaier") + sensitiveList = append(sensitiveList, "bulaoge.com") + sensitiveList = append(sensitiveList, "bushi") + sensitiveList = append(sensitiveList, "bwowd.com") + sensitiveList = append(sensitiveList, "bz176.com") + sensitiveList = append(sensitiveList, "b鏍?c a o") + sensitiveList = append(sensitiveList, "C-SPAN") + sensitiveList = append(sensitiveList, "c5c8.cn") + sensitiveList = append(sensitiveList, "canyaa.com") + sensitiveList = append(sensitiveList, "cao") + sensitiveList = append(sensitiveList, "caob") + sensitiveList = append(sensitiveList, "caobi") + sensitiveList = append(sensitiveList, "caogangchuan") + sensitiveList = append(sensitiveList, "cao浣?cao浣犲ぇ鐖?cao浣犲") + sensitiveList = append(sensitiveList, "carrefour") + sensitiveList = append(sensitiveList, "cc灏忛洩") + sensitiveList = append(sensitiveList, "cdream.com") + sensitiveList = append(sensitiveList, "chailing") + sensitiveList = append(sensitiveList, "cha浣?chenduxiu") + sensitiveList = append(sensitiveList, "chengrenmanhua.1242.net.cn") + sensitiveList = append(sensitiveList, "chengrenwangzhi.1242.net.cn") + sensitiveList = append(sensitiveList, "chenliangyu") + sensitiveList = append(sensitiveList, "chenyi") + sensitiveList = append(sensitiveList, "chenyun") + sensitiveList = append(sensitiveList, "chenzhili") + sensitiveList = append(sensitiveList, "chinaliberal") + sensitiveList = append(sensitiveList, "chinamz") + sensitiveList = append(sensitiveList, "chinavfx.net") + sensitiveList = append(sensitiveList, "chinesenewsnet") + sensitiveList = append(sensitiveList, "chongxianmu.cn") + sensitiveList = append(sensitiveList, "chunyilang") + sensitiveList = append(sensitiveList, "cikcatv.2om") + sensitiveList = append(sensitiveList, "client") + sensitiveList = append(sensitiveList, "clockgemstone") + sensitiveList = append(sensitiveList, "cnaicheng.1174.net.cn") + sensitiveList = append(sensitiveList, "cnd") + sensitiveList = append(sensitiveList, "CND鍒婄墿鍜岃鍧?CNN/NHK") + sensitiveList = append(sensitiveList, "consignment5173") + sensitiveList = append(sensitiveList, "creaders") + sensitiveList = append(sensitiveList, "crestbone") + sensitiveList = append(sensitiveList, "cs") + sensitiveList = append(sensitiveList, "cvceo.com") + sensitiveList = append(sensitiveList, "d666.com") + sensitiveList = append(sensitiveList, "dadati.com") + sensitiveList = append(sensitiveList, "dafa") + sensitiveList = append(sensitiveList, "dajiyuan") + sensitiveList = append(sensitiveList, "dalai") + sensitiveList = append(sensitiveList, "dalailama") + sensitiveList = append(sensitiveList, "damn") + sensitiveList = append(sensitiveList, "dang") + sensitiveList = append(sensitiveList, "dengxiaoping") + sensitiveList = append(sensitiveList, "dfdz") + sensitiveList = append(sensitiveList, "dick") + sensitiveList = append(sensitiveList, "digitallongking.com") + sensitiveList = append(sensitiveList, "dingxiang.1243.net.cn") + sensitiveList = append(sensitiveList, "dingxiang.1249.net") + sensitiveList = append(sensitiveList, "Discuss") + sensitiveList = append(sensitiveList, "dishun") + sensitiveList = append(sensitiveList, "DJY") + sensitiveList = append(sensitiveList, "dl.com") + sensitiveList = append(sensitiveList, "dolbbs.com") + sensitiveList = append(sensitiveList, "dongtiaoyingji") + sensitiveList = append(sensitiveList, "dpp") + sensitiveList = append(sensitiveList, "dulumen") + sensitiveList = append(sensitiveList, "duowan") + sensitiveList = append(sensitiveList, "duowan.com") + sensitiveList = append(sensitiveList, "e7sw.cn") + sensitiveList = append(sensitiveList, "eee.xaoh.cn") + sensitiveList = append(sensitiveList, "eerdeni") + sensitiveList = append(sensitiveList, "Engadget涓枃缃?engesi") + sensitiveList = append(sensitiveList, "ent365.com") + sensitiveList = append(sensitiveList, "event") + sensitiveList = append(sensitiveList, "falu") + sensitiveList = append(sensitiveList, "falun") + sensitiveList = append(sensitiveList, "falundafa") + sensitiveList = append(sensitiveList, "falundafahao") + sensitiveList = append(sensitiveList, "falungong") + sensitiveList = append(sensitiveList, "fa杞?feelmistone") + sensitiveList = append(sensitiveList, "feitengdl.com") + sensitiveList = append(sensitiveList, "fisonet.com") + sensitiveList = append(sensitiveList, "fku") + sensitiveList = append(sensitiveList, "flg") + sensitiveList = append(sensitiveList, "freechina") + sensitiveList = append(sensitiveList, "freedom") + sensitiveList = append(sensitiveList, "freenet") + sensitiveList = append(sensitiveList, "fuck") + sensitiveList = append(sensitiveList, "fulankelin") + sensitiveList = append(sensitiveList, "fxjsqc.com") + sensitiveList = append(sensitiveList, "fy371.com") + sensitiveList = append(sensitiveList, "g365.net") + sensitiveList = append(sensitiveList, "game") + sensitiveList = append(sensitiveList, "gamemaster") + sensitiveList = append(sensitiveList, "gangcunningci") + sensitiveList = append(sensitiveList, "gangcunxiushu") + sensitiveList = append(sensitiveList, "gan浣?gaolipiao") + sensitiveList = append(sensitiveList, "gcd") + sensitiveList = append(sensitiveList, "geocities.com") + sensitiveList = append(sensitiveList, "gigabyte.cn") + sensitiveList = append(sensitiveList, "gm") + sensitiveList = append(sensitiveList, "gogo.net") + sensitiveList = append(sensitiveList, "gongchandang") + sensitiveList = append(sensitiveList, "gruepin") + sensitiveList = append(sensitiveList, "gssmtt.com") + sensitiveList = append(sensitiveList, "guobaxiong") + sensitiveList = append(sensitiveList, "guojia") + sensitiveList = append(sensitiveList, "g鍏?g鍙?g宸?g鎺?g鐐?g鐗?g鑺?g榛?hacking") + sensitiveList = append(sensitiveList, "hao1788.co") + sensitiveList = append(sensitiveList, "haog8.cn") + sensitiveList = append(sensitiveList, "haosilu.com") + sensitiveList = append(sensitiveList, "haouse.cn") + sensitiveList = append(sensitiveList, "heguoqiang") + sensitiveList = append(sensitiveList, "helong") + sensitiveList = append(sensitiveList, "heluxiaofu") + sensitiveList = append(sensitiveList, "hexun.com") + sensitiveList = append(sensitiveList, "homexf.cn") + sensitiveList = append(sensitiveList, "Hong Kong Herald. 鍙版咕鎶ョ焊") + sensitiveList = append(sensitiveList, "hongzhi") + sensitiveList = append(sensitiveList, "host800.com") + sensitiveList = append(sensitiveList, "hrichina") + sensitiveList = append(sensitiveList, "http") + sensitiveList = append(sensitiveList, "huajianmin") + sensitiveList = append(sensitiveList, "huanet") + sensitiveList = append(sensitiveList, "huangju") + sensitiveList = append(sensitiveList, "huangsexiaoshuo.1242.net.cn") + sensitiveList = append(sensitiveList, "huashengdun") + sensitiveList = append(sensitiveList, "huihuangtx.com") + sensitiveList = append(sensitiveList, "huiliangyu") + sensitiveList = append(sensitiveList, "huimin") + sensitiveList = append(sensitiveList, "hujintao") + sensitiveList = append(sensitiveList, "huyaobang") + sensitiveList = append(sensitiveList, "huzhiming") + sensitiveList = append(sensitiveList, "hypermart.net") + sensitiveList = append(sensitiveList, "h鍔ㄦ极") + sensitiveList = append(sensitiveList, "h鍕曟极") + sensitiveList = append(sensitiveList, "h绔?i9game.com") + sensitiveList = append(sensitiveList, "ice") + sensitiveList = append(sensitiveList, "icpcn.com") + sensitiveList = append(sensitiveList, "id666.uqc.cn") + sensitiveList = append(sensitiveList, "ieboy.cn") + sensitiveList = append(sensitiveList, "incest") + sensitiveList = append(sensitiveList, "ising99.com") + sensitiveList = append(sensitiveList, "islam") + sensitiveList = append(sensitiveList, "item") + sensitiveList = append(sensitiveList, "j8") + sensitiveList = append(sensitiveList, "jb") + sensitiveList = append(sensitiveList, "jdyou.com") + sensitiveList = append(sensitiveList, "jiangdongriji") + sensitiveList = append(sensitiveList, "jiangjieshi") + sensitiveList = append(sensitiveList, "jiangjingguo") + sensitiveList = append(sensitiveList, "jiangqing") + sensitiveList = append(sensitiveList, "jiangzemin") + sensitiveList = append(sensitiveList, "jiangzhongzheng") + sensitiveList = append(sensitiveList, "jian浣?jiaochuang") + sensitiveList = append(sensitiveList, "jiaochun") + sensitiveList = append(sensitiveList, "jiaqinglin") + sensitiveList = append(sensitiveList, "jiayyou.cn") + sensitiveList = append(sensitiveList, "jiayyou.com") + sensitiveList = append(sensitiveList, "jiba") + sensitiveList = append(sensitiveList, "jinpaopao.com") + sensitiveList = append(sensitiveList, "jinricheng") + sensitiveList = append(sensitiveList, "jinv") + sensitiveList = append(sensitiveList, "jinzhengen") + sensitiveList = append(sensitiveList, "jinzhengri") + sensitiveList = append(sensitiveList, "ji濂?jjdlw.com") + sensitiveList = append(sensitiveList, "jooplay.com") + sensitiveList = append(sensitiveList, "kao") + sensitiveList = append(sensitiveList, "kartt.cn") + sensitiveList = append(sensitiveList, "kasiteluo") + sensitiveList = append(sensitiveList, "kefu") + sensitiveList = append(sensitiveList, "kelindun") + sensitiveList = append(sensitiveList, "kissmyass") + sensitiveList = append(sensitiveList, "kkk.xaoh.cn") + sensitiveList = append(sensitiveList, "ko180") + sensitiveList = append(sensitiveList, "kod920.cn") + sensitiveList = append(sensitiveList, "k绮?l2jsom.cn") + sensitiveList = append(sensitiveList, "laichangxing") + sensitiveList = append(sensitiveList, "lamusifeierde") + sensitiveList = append(sensitiveList, "langyou.info") + sensitiveList = append(sensitiveList, "laola") + sensitiveList = append(sensitiveList, "lianggezhongguo") + sensitiveList = append(sensitiveList, "lianzhan") + sensitiveList = append(sensitiveList, "lichangchun") + sensitiveList = append(sensitiveList, "lidashi") + sensitiveList = append(sensitiveList, "lidazhao") + sensitiveList = append(sensitiveList, "lidenghui") + sensitiveList = append(sensitiveList, "liening") + sensitiveList = append(sensitiveList, "ligen") + sensitiveList = append(sensitiveList, "lihongzhi") + sensitiveList = append(sensitiveList, "likeko.com") + sensitiveList = append(sensitiveList, "likeqiang") + sensitiveList = append(sensitiveList, "lilanqing") + sensitiveList = append(sensitiveList, "linbiao") + sensitiveList = append(sensitiveList, "lingjihua") + sensitiveList = append(sensitiveList, "linken") + sensitiveList = append(sensitiveList, "lipeng") + sensitiveList = append(sensitiveList, "liruihuan") + sensitiveList = append(sensitiveList, "liubocheng") + sensitiveList = append(sensitiveList, "liulijun") + sensitiveList = append(sensitiveList, "liuqi") + sensitiveList = append(sensitiveList, "liushaoqi") + sensitiveList = append(sensitiveList, "liusi") + sensitiveList = append(sensitiveList, "liuyunshan") + sensitiveList = append(sensitiveList, "lll.xaoh.cn") + sensitiveList = append(sensitiveList, "lordren.com") + sensitiveList = append(sensitiveList, "lunlidianying.1274.net.cn") + sensitiveList = append(sensitiveList, "lunlidianyingxiazai.1174.net.cn") + sensitiveList = append(sensitiveList, "luogan") + sensitiveList = append(sensitiveList, "luoronghuan") + sensitiveList = append(sensitiveList, "luoxialu.cn") + sensitiveList = append(sensitiveList, "luxiulian") + sensitiveList = append(sensitiveList, "luyi") + sensitiveList = append(sensitiveList, "lxsm888.com") + sensitiveList = append(sensitiveList, "lybbs.info") + sensitiveList = append(sensitiveList, "lyx-game.cn") + sensitiveList = append(sensitiveList, "mai楠?majiajue") + sensitiveList = append(sensitiveList, "makesi") + sensitiveList = append(sensitiveList, "making") + sensitiveList = append(sensitiveList, "mandela") + sensitiveList = append(sensitiveList, "maozedong") + sensitiveList = append(sensitiveList, "master") + sensitiveList = append(sensitiveList, "mayingjiu") + sensitiveList = append(sensitiveList, "meideweijiefu") + sensitiveList = append(sensitiveList, "meiguiqingren.1274.net.cn") + sensitiveList = append(sensitiveList, "mgjzybj.com") + sensitiveList = append(sensitiveList, "minghui") + sensitiveList = append(sensitiveList, "minghuinews") + sensitiveList = append(sensitiveList, "mm灞?mm缇庡浘") + sensitiveList = append(sensitiveList, "mm缇庡湒") + sensitiveList = append(sensitiveList, "mohanmode") + sensitiveList = append(sensitiveList, "mokeer") + sensitiveList = append(sensitiveList, "mosuolini") + sensitiveList = append(sensitiveList, "moyi520.cn") + sensitiveList = append(sensitiveList, "mpceggs.cn") + sensitiveList = append(sensitiveList, "my3q.com") + sensitiveList = append(sensitiveList, "MyRadio") + sensitiveList = append(sensitiveList, "nacb") + sensitiveList = append(sensitiveList, "naive") + sensitiveList = append(sensitiveList, "napolun") + sensitiveList = append(sensitiveList, "narcotics") + sensitiveList = append(sensitiveList, "nbfib.cn") + sensitiveList = append(sensitiveList, "ncyh.cn") + sensitiveList = append(sensitiveList, "neckromancer") + sensitiveList = append(sensitiveList, "newsmth.net") + sensitiveList = append(sensitiveList, "nierongzhen") + sensitiveList = append(sensitiveList, "nikesong") + sensitiveList = append(sensitiveList, "niuniujidi.1174.net.cn") + sensitiveList = append(sensitiveList, "njgamecollege.org") + sensitiveList = append(sensitiveList, "nmis") + sensitiveList = append(sensitiveList, "npc") + sensitiveList = append(sensitiveList, "okaygood.cn") + sensitiveList = append(sensitiveList, "ooo.xaoh.cn") + sensitiveList = append(sensitiveList, "osisa.cn") + sensitiveList = append(sensitiveList, "paper64") + sensitiveList = append(sensitiveList, "parke888.com") + sensitiveList = append(sensitiveList, "pcikchina.com") + sensitiveList = append(sensitiveList, "peacehall") + sensitiveList = append(sensitiveList, "pengdehuai") + sensitiveList = append(sensitiveList, "pengliyuan") + sensitiveList = append(sensitiveList, "penis") + sensitiveList = append(sensitiveList, "petgirl") + sensitiveList = append(sensitiveList, "pk200.com") + sensitiveList = append(sensitiveList, "pkmmo.com") + sensitiveList = append(sensitiveList, "playboy") + sensitiveList = append(sensitiveList, "porn") + sensitiveList = append(sensitiveList, "ppt.cc") + sensitiveList = append(sensitiveList, "prettyirene.net") + sensitiveList = append(sensitiveList, "pujing") + sensitiveList = append(sensitiveList, "pussy") + sensitiveList = append(sensitiveList, "qgqm.org") + sensitiveList = append(sensitiveList, "qiangjian") + sensitiveList = append(sensitiveList, "qingsewuyuetian.1174.net.cn") + sensitiveList = append(sensitiveList, "qixingnet.com") + sensitiveList = append(sensitiveList, "qqsg.org") + sensitiveList = append(sensitiveList, "qtoy.com") + sensitiveList = append(sensitiveList, "quannengshenjiao") + sensitiveList = append(sensitiveList, "rape") + sensitiveList = append(sensitiveList, "reddidi.com.cn") + sensitiveList = append(sensitiveList, "renaya") + sensitiveList = append(sensitiveList, "renminbao") + sensitiveList = append(sensitiveList, "renmindahuitang") + sensitiveList = append(sensitiveList, "renmingbao") + sensitiveList = append(sensitiveList, "rfa") + sensitiveList = append(sensitiveList, "RFI") + sensitiveList = append(sensitiveList, "rivals") + sensitiveList = append(sensitiveList, "rrr.xaoh.cn") + sensitiveList = append(sensitiveList, "sadamu") + sensitiveList = append(sensitiveList, "safeweb") + sensitiveList = append(sensitiveList, "samalanqi") + sensitiveList = append(sensitiveList, "saobi") + sensitiveList = append(sensitiveList, "saqieer") + sensitiveList = append(sensitiveList, "sb") + sensitiveList = append(sensitiveList, "server") + sensitiveList = append(sensitiveList, "sewuyuetian.1174.net.cn") + sensitiveList = append(sensitiveList, "sewuyuetian.1274.net.cn") + sensitiveList = append(sensitiveList, "sex") + sensitiveList = append(sensitiveList, "sex鑱婂ぉ瀹?sf") + sensitiveList = append(sensitiveList, "shanbenwushiliu") + sensitiveList = append(sensitiveList, "shenycs.co") + sensitiveList = append(sensitiveList, "shijiamouni") + sensitiveList = append(sensitiveList, "shit") + sensitiveList = append(sensitiveList, "showka8.com") + sensitiveList = append(sensitiveList, "sidalin") + sensitiveList = append(sensitiveList, "simple") + sensitiveList = append(sensitiveList, "siwameitui.1274.net.cn") + sensitiveList = append(sensitiveList, "sjlike.com") + sensitiveList = append(sensitiveList, "sm") + sensitiveList = append(sensitiveList, "sm濂崇帇") + sensitiveList = append(sensitiveList, "sm鎻翠氦") + sensitiveList = append(sensitiveList, "sm瑾挎暀") + sensitiveList = append(sensitiveList, "sm璋冩暀") + sensitiveList = append(sensitiveList, "songchuyu") + sensitiveList = append(sensitiveList, "stlmbbs.cn") + sensitiveList = append(sensitiveList, "suck") + sensitiveList = append(sensitiveList, "sucker") + sensitiveList = append(sensitiveList, "sunwen") + sensitiveList = append(sensitiveList, "sunyixian") + sensitiveList = append(sensitiveList, "sunzhongshan") + sensitiveList = append(sensitiveList, "svdc") + sensitiveList = append(sensitiveList, "system") + sensitiveList = append(sensitiveList, "szlmgh.cn") + sensitiveList = append(sensitiveList, "t.m.d") + sensitiveList = append(sensitiveList, "t9wg.cn") + sensitiveList = append(sensitiveList, "taidu") + sensitiveList = append(sensitiveList, "taip") + sensitiveList = append(sensitiveList, "Taipei Times") + sensitiveList = append(sensitiveList, "taiwanduli") + sensitiveList = append(sensitiveList, "tangjiaxuan") + sensitiveList = append(sensitiveList, "taobao") + sensitiveList = append(sensitiveList, "tb.hi.4024.net.cn") + sensitiveList = append(sensitiveList, "TD") + sensitiveList = append(sensitiveList, "teen") + sensitiveList = append(sensitiveList, "test") + sensitiveList = append(sensitiveList, "The Standard") + sensitiveList = append(sensitiveList, "the9") + sensitiveList = append(sensitiveList, "the9city") + sensitiveList = append(sensitiveList, "thec.cn") + sensitiveList = append(sensitiveList, "tiananmen") + sensitiveList = append(sensitiveList, "tianlong4f.cn") + sensitiveList = append(sensitiveList, "tibetalk") + sensitiveList = append(sensitiveList, "tmd") + sensitiveList = append(sensitiveList, "tnnd") + sensitiveList = append(sensitiveList, "tont.cn") + sensitiveList = append(sensitiveList, "triangle") + sensitiveList = append(sensitiveList, "triangleboy") + sensitiveList = append(sensitiveList, "tringel") + sensitiveList = append(sensitiveList, "txwswind") + sensitiveList = append(sensitiveList, "u r") + sensitiveList = append(sensitiveList, "u-r") + sensitiveList = append(sensitiveList, "u/r") + sensitiveList = append(sensitiveList, "ul86.com") + sensitiveList = append(sensitiveList, "ultrasurf") + sensitiveList = append(sensitiveList, "unixbox") + sensitiveList = append(sensitiveList, "ur") + sensitiveList = append(sensitiveList, "urban") + sensitiveList = append(sensitiveList, "urban-rivals") + sensitiveList = append(sensitiveList, "ustibet") + sensitiveList = append(sensitiveList, "uu1001.com") + sensitiveList = append(sensitiveList, "vip886.com") + sensitiveList = append(sensitiveList, "voa") + sensitiveList = append(sensitiveList, "voachinese") + sensitiveList = append(sensitiveList, "wangce") + sensitiveList = append(sensitiveList, "wangdan") + sensitiveList = append(sensitiveList, "wanggang") + sensitiveList = append(sensitiveList, "wanghongwen") + sensitiveList = append(sensitiveList, "wanglequan") + sensitiveList = append(sensitiveList, "wanglijun") + sensitiveList = append(sensitiveList, "wangqishan") + sensitiveList = append(sensitiveList, "wangyoudl.com") + sensitiveList = append(sensitiveList, "wangzhaoguo") + sensitiveList = append(sensitiveList, "webgame.com.cn") + sensitiveList = append(sensitiveList, "webzen") + sensitiveList = append(sensitiveList, "web鎴樼墝") + sensitiveList = append(sensitiveList, "web鐗屾埌") + sensitiveList = append(sensitiveList, "weijianxing") + sensitiveList = append(sensitiveList, "wenjiabao") + sensitiveList = append(sensitiveList, "wg") + sensitiveList = append(sensitiveList, "wg7766.cn") + sensitiveList = append(sensitiveList, "wgbobo.cn") + sensitiveList = append(sensitiveList, "wgxiaowu.com") + sensitiveList = append(sensitiveList, "whoyo.com") + sensitiveList = append(sensitiveList, "woerkaixi") + sensitiveList = append(sensitiveList, "woyinwose.1243.net.cn") + sensitiveList = append(sensitiveList, "wstaiji") + sensitiveList = append(sensitiveList, "wubangguo") + sensitiveList = append(sensitiveList, "wuguanzheng") + sensitiveList = append(sensitiveList, "wuyi") + sensitiveList = append(sensitiveList, "www") + sensitiveList = append(sensitiveList, "www.") + sensitiveList = append(sensitiveList, "www.23nice.com.cn") + sensitiveList = append(sensitiveList, "www.2q5q.com") + sensitiveList = append(sensitiveList, "www.315ts.net") + sensitiveList = append(sensitiveList, "www.365jw.com") + sensitiveList = append(sensitiveList, "www.50spcombaidu1828adyou97sace.co.cc") + sensitiveList = append(sensitiveList, "www.567yx.com") + sensitiveList = append(sensitiveList, "www.70yx.com") + sensitiveList = append(sensitiveList, "www.766.com") + sensitiveList = append(sensitiveList, "www.91wangyou.com") + sensitiveList = append(sensitiveList, "www.976543.com") + sensitiveList = append(sensitiveList, "www.978808.com") + sensitiveList = append(sensitiveList, "www.99game.net") + sensitiveList = append(sensitiveList, "www.anule.cn") + sensitiveList = append(sensitiveList, "www.bin5.cn") + sensitiveList = append(sensitiveList, "www.dy6789.cn") + sensitiveList = append(sensitiveList, "www.foyeye.com") + sensitiveList = append(sensitiveList, "www.gamelifeclub.cn") + sensitiveList = append(sensitiveList, "www.gardcn.com") + sensitiveList = append(sensitiveList, "www.gw17173.cn") + sensitiveList = append(sensitiveList, "www.legu.com") + sensitiveList = append(sensitiveList, "www.maituan.com") + sensitiveList = append(sensitiveList, "www.neicehao.cn") + sensitiveList = append(sensitiveList, "www.sanjidianying.com.cn") + sensitiveList = append(sensitiveList, "www.sexwyt.com") + sensitiveList = append(sensitiveList, "www.sf766.com.cn") + sensitiveList = append(sensitiveList, "www.wang567.com") + sensitiveList = append(sensitiveList, "www.wow366.cn") + sensitiveList = append(sensitiveList, "www.xaoh.cn") + sensitiveList = append(sensitiveList, "www.yhzt.org") + sensitiveList = append(sensitiveList, "www.ylteam.cn") + sensitiveList = append(sensitiveList, "www.yxnpc.com") + sensitiveList = append(sensitiveList, "www.zsyxhd.cn") + sensitiveList = append(sensitiveList, "www.zxkaku.cn") + sensitiveList = append(sensitiveList, "www.zy528.com") + sensitiveList = append(sensitiveList, "wy33.com") + sensitiveList = append(sensitiveList, "wy3868.com") + sensitiveList = append(sensitiveList, "wy724.com") + sensitiveList = append(sensitiveList, "xdns.eu") + sensitiveList = append(sensitiveList, "xiao77") + sensitiveList = append(sensitiveList, "xiao77.1243.net.cn") + sensitiveList = append(sensitiveList, "xiao77luntan.1249.net.cn") + sensitiveList = append(sensitiveList, "xiaoqinzaixian.cn") + sensitiveList = append(sensitiveList, "xiaoquan") + sensitiveList = append(sensitiveList, "xiejiao") + sensitiveList = append(sensitiveList, "xihanuke") + sensitiveList = append(sensitiveList, "xijinping") + sensitiveList = append(sensitiveList, "xilake") + sensitiveList = append(sensitiveList, "xingqingzhongren.1174.net.cn") + sensitiveList = append(sensitiveList, "xinjiangduli") + sensitiveList = append(sensitiveList, "xinsheng") + sensitiveList = append(sensitiveList, "xinwenguanzhi") + sensitiveList = append(sensitiveList, "xitele") + sensitiveList = append(sensitiveList, "xiuau.cn") + sensitiveList = append(sensitiveList, "xizangduli") + sensitiveList = append(sensitiveList, "xl517.com") + sensitiveList = append(sensitiveList, "xq-wl.cn") + sensitiveList = append(sensitiveList, "xtl") + sensitiveList = append(sensitiveList, "xucaihou") + sensitiveList = append(sensitiveList, "xuxiangqian") + sensitiveList = append(sensitiveList, "xya3.cn") + sensitiveList = append(sensitiveList, "xyq2sf.com") + sensitiveList = append(sensitiveList, "xyxgh.com") + sensitiveList = append(sensitiveList, "yalishanda") + sensitiveList = append(sensitiveList, "yaokong7.com") + sensitiveList = append(sensitiveList, "yaowenyuan") + sensitiveList = append(sensitiveList, "yejianying") + sensitiveList = append(sensitiveList, "yeswm.com") + sensitiveList = append(sensitiveList, "yidangzhuanzheng") + sensitiveList = append(sensitiveList, "yisilan") + sensitiveList = append(sensitiveList, "youxiji888.cn") + sensitiveList = append(sensitiveList, "ys168.com") + sensitiveList = append(sensitiveList, "yujiankang") + sensitiveList = append(sensitiveList, "yuming") + sensitiveList = append(sensitiveList, "yumuming") + sensitiveList = append(sensitiveList, "yuyongkang") + sensitiveList = append(sensitiveList, "yuzhengsheng") + sensitiveList = append(sensitiveList, "yz55.cn") + sensitiveList = append(sensitiveList, "zaixu.net") + sensitiveList = append(sensitiveList, "zengpeiyan") + sensitiveList = append(sensitiveList, "zengqinghong") + sensitiveList = append(sensitiveList, "zhangchunqiao") + sensitiveList = append(sensitiveList, "zhangdejiang") + sensitiveList = append(sensitiveList, "zhanggaoli") + sensitiveList = append(sensitiveList, "zhaoziyang") + sensitiveList = append(sensitiveList, "zhengjian") + sensitiveList = append(sensitiveList, "zhengjianwang") + sensitiveList = append(sensitiveList, "zhenshanren") + sensitiveList = append(sensitiveList, "zhenzhu") + sensitiveList = append(sensitiveList, "zhidian8.com") + sensitiveList = append(sensitiveList, "zhonghuaminguo") + sensitiveList = append(sensitiveList, "zhouenlai") + sensitiveList = append(sensitiveList, "zhoujiankang") + sensitiveList = append(sensitiveList, "zhouxiaokang") + sensitiveList = append(sensitiveList, "zhouyongkang") + sensitiveList = append(sensitiveList, "zhuanfalun") + sensitiveList = append(sensitiveList, "zhuanfalunadmin") + sensitiveList = append(sensitiveList, "zhude") + sensitiveList = append(sensitiveList, "zhurongji") + sensitiveList = append(sensitiveList, "zuanshi1000.cn") + sensitiveList = append(sensitiveList, "zx002.com") + sensitiveList = append(sensitiveList, "zxsj188.com") + sensitiveList = append(sensitiveList, "zy666.net") + sensitiveList = append(sensitiveList, "zzmysf.com") + sensitiveList = append(sensitiveList, "zzz.xaoh.cn") + sensitiveList = append(sensitiveList, "銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銊?銏?銏?銏?銏?銏?銏?銏?銏?銏?銑?銕?銕?銕?涓€涓竴鍙?涓€鍏氫笓鍒?涓€鍏氫笓鏀?涓€鍥戒袱鍒?涓€澶滄€х恫") + sensitiveList = append(sensitiveList, "涓€澶滄€х綉") + sensitiveList = append(sensitiveList, "涓€澶滄儏") + sensitiveList = append(sensitiveList, "涓€澶滄") + sensitiveList = append(sensitiveList, "涓€澶滄") + sensitiveList = append(sensitiveList, "涓€鏈亾") + sensitiveList = append(sensitiveList, "涓€璨亾") + sensitiveList = append(sensitiveList, "涓€璐亾") + sensitiveList = append(sensitiveList, "涓€杈逛竴鍥?涓€閭婁竴鍦?涓€闄€绯?涓€榛ㄥ皥鏀?涓佸叧鏍?涓佸瓙闇?涓佸瓧瑁ょ繕鑷€") + sensitiveList = append(sensitiveList, "涓佸瓧瑜茬抗鑷€") + sensitiveList = append(sensitiveList, "涓侀棞鏍?涓侀绀惧尯") + sensitiveList = append(sensitiveList, "涓侀绀惧崁") + sensitiveList = append(sensitiveList, "涓囨帆鍫?涓囩◣") + sensitiveList = append(sensitiveList, "涓囩淮鍗氬") + sensitiveList = append(sensitiveList, "涓囩淮璇昏€呯綉") + sensitiveList = append(sensitiveList, "涓塳榛?涓塸") + sensitiveList = append(sensitiveList, "涓変釜浠e") + sensitiveList = append(sensitiveList, "涓変釜鍛嗗") + sensitiveList = append(sensitiveList, "涓夊€嬩唬琛?涓夊叓娣?涓夊幓杌婁緰宸ュ姏") + sensitiveList = append(sensitiveList, "涓夊幓杞︿粦") + sensitiveList = append(sensitiveList, "涓夊攽浠?涓夋皯涓讳箟") + sensitiveList = append(sensitiveList, "涓夌礆鐗?涓夌骇鐗?涓夐櫔") + sensitiveList = append(sensitiveList, "涓夐櫔濂?涓婁綘") + sensitiveList = append(sensitiveList, "涓婂Τ") + sensitiveList = append(sensitiveList, "涓婃捣瀛ゅ厭闄?涓婃捣骞?涓婅í") + sensitiveList = append(sensitiveList, "涓嬫敞") + sensitiveList = append(sensitiveList, "涓嬫祦鍦板甫") + sensitiveList = append(sensitiveList, "涓嬫祦鍦板付") + sensitiveList = append(sensitiveList, "涓嬭长") + sensitiveList = append(sensitiveList, "涓嬭幢") + sensitiveList = append(sensitiveList, "涓嬮珨") + sensitiveList = append(sensitiveList, "涓?涓嶆粎甯濈帇") + sensitiveList = append(sensitiveList, "涓嶇埥涓嶈閷?涓嶇帺浜?涓嶈壇") + sensitiveList = append(sensitiveList, "涓撳埗") + sensitiveList = append(sensitiveList, "涓撳疇") + sensitiveList = append(sensitiveList, "涓撴斂") + sensitiveList = append(sensitiveList, "涓栫晫鎶ョ焊") + sensitiveList = append(sensitiveList, "涓栫晫鏂伴椈濯掍綋缃?涓栫晫鏂伴椈缃?涓栫晫鏃ユ姤") + sensitiveList = append(sensitiveList, "涓栫晫鐢靛奖(鍙版咕)") + sensitiveList = append(sensitiveList, "涓栫晫缍撴繜灏庡牨") + sensitiveList = append(sensitiveList, "涓栫淮浼?涓樺瀭璨?涓滀簹鏃ユ姤") + sensitiveList = append(sensitiveList, "涓滀簹鐥呭か") + sensitiveList = append(sensitiveList, "涓滀含鐑?涓滀紛杩?涓滃寳xx缃?涓滃寳鐙珛") + sensitiveList = append(sensitiveList, "涓滃崌") + sensitiveList = append(sensitiveList, "涓滄柟鏃ユ姤") + sensitiveList = append(sensitiveList, "涓滄潯鑻辨満") + sensitiveList = append(sensitiveList, "涓滄鏁?涓滅儹绌哄") + sensitiveList = append(sensitiveList, "涓滅獊") + sensitiveList = append(sensitiveList, "涓滅獊鏆村姩鍜岀嫭绔?涓滅獊缁勭粐") + sensitiveList = append(sensitiveList, "涓滅綉") + sensitiveList = append(sensitiveList, "涓濊") + sensitiveList = append(sensitiveList, "涓濊鍐欑湡") + sensitiveList = append(sensitiveList, "涓濊娣") + sensitiveList = append(sensitiveList, "涓濊瓒充氦") + sensitiveList = append(sensitiveList, "涓濊楂樿窡") + sensitiveList = append(sensitiveList, "涓濊") + sensitiveList = append(sensitiveList, "涓や釜涓浗") + sensitiveList = append(sensitiveList, "涓ゅ浗璁?涓よ吙涔嬮棿") + sensitiveList = append(sensitiveList, "涓ユ柟鍐?涓?涓縿閭婄晫") + sensitiveList = append(sensitiveList, "涓叡") + sensitiveList = append(sensitiveList, "涓崕鏃ユ姤") + sensitiveList = append(sensitiveList, "涓崕姘戝浗") + sensitiveList = append(sensitiveList, "涓崕鐢佃鍏徃") + sensitiveList = append(sensitiveList, "涓崡娴?涓浗涔嬫槬") + sensitiveList = append(sensitiveList, "涓浗浜烘潈鍙屽懆鍒?涓浗瀵嗘姤") + sensitiveList = append(sensitiveList, "涓浗鎬х埍鍩?涓浗鎭愭€栫粍缁?涓浗鎴愪汉璁哄潧") + sensitiveList = append(sensitiveList, "涓浗鏃舵姤") + sensitiveList = append(sensitiveList, "涓浗鏃舵姤鏂扮鍒嗙ぞ") + sensitiveList = append(sensitiveList, "涓浗鐚?涓浗鑼夎帀鑺遍潻鍛?涓湅涔嬫槬") + sensitiveList = append(sensitiveList, "涓湅鍏辩敚榛?涓湅鍜屽钩") + sensitiveList = append(sensitiveList, "涓湅鍟忛璜栧") + sensitiveList = append(sensitiveList, "涓湅濞佽剠璜?涓湅瀛ゅ厭闄?涓湅寰╄垐璜栧") + sensitiveList = append(sensitiveList, "涓湅鎬ф剾鍩?涓湅鎴愪汉璜栧") + sensitiveList = append(sensitiveList, "涓湅鐚?涓湅鐪熷鍏у") + sensitiveList = append(sensitiveList, "涓湅绀炬渻璜栧") + sensitiveList = append(sensitiveList, "涓湅绀炬渻閫叉榛?涓湅绉诲嫊閫氫俊") + sensitiveList = append(sensitiveList, "涓湅璜栧") + sensitiveList = append(sensitiveList, "涓湅璞?涓ぎ鏃ユ姤缃戠粶鎶?涓ぎ鐢佃鍙?涓ぎ绀?涓ぎ绀炬柊闂荤綉") + sensitiveList = append(sensitiveList, "涓ぎ闆昏鑷?涓閮?涓勾缇庡") + sensitiveList = append(sensitiveList, "涓勾缇庡│") + sensitiveList = append(sensitiveList, "涓枃鎼滄€х恫") + sensitiveList = append(sensitiveList, "涓枃鎼滄€х綉") + sensitiveList = append(sensitiveList, "涓椂鐢靛瓙鎶?涓鏂伴椈缃?涓瘨鍦栭ò") + sensitiveList = append(sensitiveList, "涓瘨鐨勫湒楱?涓彲浜烘皯瀵﹁┍瀵﹁") + sensitiveList = append(sensitiveList, "涓彲浜烘皯姝i偑") + sensitiveList = append(sensitiveList, "涓彲澶у湴") + sensitiveList = append(sensitiveList, "涓彲澶ц") + sensitiveList = append(sensitiveList, "涓彲鏅備簨") + sensitiveList = append(sensitiveList, "涓彲姘戝湅") + sensitiveList = append(sensitiveList, "涓彲鐪熷鍫遍亾") + sensitiveList = append(sensitiveList, "涓彲璎涙竻") + sensitiveList = append(sensitiveList, "涓彲椁婄敓鐩婃櫤鍔?涓颁钩") + sensitiveList = append(sensitiveList, "涓板攪鑹冲К") + sensitiveList = append(sensitiveList, "涓讳經") + sensitiveList = append(sensitiveList, "涓诲満鏂伴椈") + sensitiveList = append(sensitiveList, "涓诲腑") + sensitiveList = append(sensitiveList, "涓绘敾鎸囨彯瀹?涓荤京") + sensitiveList = append(sensitiveList, "涓芥槬鑻?涔旂煶") + sensitiveList = append(sensitiveList, "涔犱富甯?涔犲ぇ澶?涔犺繎骞?涔犺繙骞?涔″反浣?涔?涔板崠鏋敮") + sensitiveList = append(sensitiveList, "涔版槬") + sensitiveList = append(sensitiveList, "涔版槬鍫?涔拌储瀵?涔变鸡") + sensitiveList = append(sensitiveList, "涔变鸡鐔熷コ缃?涔?涔充氦") + sensitiveList = append(sensitiveList, "涔冲ご") + sensitiveList = append(sensitiveList, "涔冲皠") + sensitiveList = append(sensitiveList, "涔冲盎") + sensitiveList = append(sensitiveList, "涔虫埧") + sensitiveList = append(sensitiveList, "涔虫殘") + sensitiveList = append(sensitiveList, "涔虫涓濊") + sensitiveList = append(sensitiveList, "涔虫涓版弧") + sensitiveList = append(sensitiveList, "涔虫鍔ㄤ汉") + sensitiveList = append(sensitiveList, "涔虫鍕曚汉") + sensitiveList = append(sensitiveList, "涔虫绲茶オ") + sensitiveList = append(sensitiveList, "涔虫璞愭豢") + sensitiveList = append(sensitiveList, "涔虫矡") + sensitiveList = append(sensitiveList, "涔虫尝") + sensitiveList = append(sensitiveList, "涔虫尝鑷€娴?涔虫簼") + sensitiveList = append(sensitiveList, "涔崇垎") + sensitiveList = append(sensitiveList, "涔抽湼") + sensitiveList = append(sensitiveList, "浜備氦") + sensitiveList = append(sensitiveList, "浜傚€?浜傚€啛濂崇恫") + sensitiveList = append(sensitiveList, "浜傝吉") + sensitiveList = append(sensitiveList, "浜夐福鏉傚織") + sensitiveList = append(sensitiveList, "浜嬪鐛ㄧ珛") + sensitiveList = append(sensitiveList, "浜宐") + sensitiveList = append(sensitiveList, "浜屼箼鍩洪叞鑳哄彂鎶?浜屽眲") + sensitiveList = append(sensitiveList, "浜岀┐涓嚭") + sensitiveList = append(sensitiveList, "浜岄€?浜庡辜杌?浜掓帆") + sensitiveList = append(sensitiveList, "浜掕垟娣┐") + sensitiveList = append(sensitiveList, "浜旀槦绾㈡棗") + sensitiveList = append(sensitiveList, "浜旀湀澶?浜氬巻灞卞ぇ") + sensitiveList = append(sensitiveList, "浜氭儏") + sensitiveList = append(sensitiveList, "浜氭床鍛ㄥ垔") + sensitiveList = append(sensitiveList, "浜氭床鎬ц檺") + sensitiveList = append(sensitiveList, "浜氭床鎯呰壊缃?浜氭床鏈夌爜") + sensitiveList = append(sensitiveList, "浜氭床娣▋") + sensitiveList = append(sensitiveList, "浜氭床鑹?浜炴儏") + sensitiveList = append(sensitiveList, "浜炴灞卞ぇ") + sensitiveList = append(sensitiveList, "浜炴床鎬ц檺") + sensitiveList = append(sensitiveList, "浜炴床鎯呰壊缍?浜炴床鏈夌⒓") + sensitiveList = append(sensitiveList, "浜炴床娣▋") + sensitiveList = append(sensitiveList, "浜炴床鑹?浜ゅ") + sensitiveList = append(sensitiveList, "浜ゆ崲澶") + sensitiveList = append(sensitiveList, "浜ゆ彌澶") + sensitiveList = append(sensitiveList, "浜ゆ竻鐢靛瓙鎶?浜ら€氶儴") + sensitiveList = append(sensitiveList, "浜ら厤") + sensitiveList = append(sensitiveList, "浜ф潈灞€") + sensitiveList = append(sensitiveList, "浜眲") + sensitiveList = append(sensitiveList, "浜┐") + sensitiveList = append(sensitiveList, "浜叉皯鍏?浜轰唬浼?浜轰綋鎽勫奖") + sensitiveList = append(sensitiveList, "浜轰綋鐐稿脊") + sensitiveList = append(sensitiveList, "浜哄ぇ") + sensitiveList = append(sensitiveList, "浜哄ぇ浠h〃") + sensitiveList = append(sensitiveList, "浜哄") + sensitiveList = append(sensitiveList, "浜哄浜ゆ崲") + sensitiveList = append(sensitiveList, "浜哄浜ゆ彌") + sensitiveList = append(sensitiveList, "浜哄鍋氭剾") + sensitiveList = append(sensitiveList, "浜哄鍋氱埍") + sensitiveList = append(sensitiveList, "浜哄姒ㄤ钩") + sensitiveList = append(sensitiveList, "浜哄鐔熷コ") + sensitiveList = append(sensitiveList, "浜哄鑷媿") + sensitiveList = append(sensitiveList, "浜哄鑹茶獦") + sensitiveList = append(sensitiveList, "浜哄鑹茶") + sensitiveList = append(sensitiveList, "浜哄睜") + sensitiveList = append(sensitiveList, "浜哄脊") + sensitiveList = append(sensitiveList, "浜烘€ф湰鑹?浜烘瑠") + sensitiveList = append(sensitiveList, "浜烘皯") + sensitiveList = append(sensitiveList, "浜烘皯涔嬭伈璜栧") + sensitiveList = append(sensitiveList, "浜烘皯鍏ф儏鐪熺浉") + sensitiveList = append(sensitiveList, "浜烘皯鍫?浜烘皯澶т細鍫?浜烘皯澶ф渻鍫?浜烘皯骞垮満") + sensitiveList = append(sensitiveList, "浜烘皯鎶?浜烘皯鏃ユ姤") + sensitiveList = append(sensitiveList, "浜烘皯鐪熷") + sensitiveList = append(sensitiveList, "浜烘皯閾惰") + sensitiveList = append(sensitiveList, "浜洪棿绂忔姤") + sensitiveList = append(sensitiveList, "浜洪珨鏀濆奖") + sensitiveList = append(sensitiveList, "浠?浠嗚") + sensitiveList = append(sensitiveList, "浠婃棩鎮夊凹") + sensitiveList = append(sensitiveList, "浠栧棊鐨?浠栧") + sensitiveList = append(sensitiveList, "浠栧鐨?浠栧") + sensitiveList = append(sensitiveList, "浠栧鐨?浠栨瘝浜?浠栨瘝瑕?浠栫埞") + sensitiveList = append(sensitiveList, "浠栫瀹?浠栭┈鐨?浠よ▓鐣?浠よ鍒?浠ヨ寧鑷虫礊") + sensitiveList = append(sensitiveList, "浠?浼婃媺鍏?浼婃柉鍏?浼婃柉铇?浼婃柉铇簽鏍兼灄灏兼柉") + sensitiveList = append(sensitiveList, "浼婃湕") + sensitiveList = append(sensitiveList, "浼婅帋璨濈埦") + sensitiveList = append(sensitiveList, "浼卞") + sensitiveList = append(sensitiveList, "浼烘湇鍣?浣撳ジ") + sensitiveList = append(sensitiveList, "浣旈牁涓挵") + sensitiveList = append(sensitiveList, "浣曞€欏崕") + sensitiveList = append(sensitiveList, "浣欒嫳鏅?浣涘睍鍗冩墜娉?浣涙暀") + sensitiveList = append(sensitiveList, "浣涚") + sensitiveList = append(sensitiveList, "浣涜槶鍏嬫灄") + sensitiveList = append(sensitiveList, "浣滄剾") + sensitiveList = append(sensitiveList, "浣滅埍") + sensitiveList = append(sensitiveList, "浣滅") + sensitiveList = append(sensitiveList, "浣犱簩澶х埛") + sensitiveList = append(sensitiveList, "浣犲ぇ鐖?浣犲ぇ鐖?浣犲") + sensitiveList = append(sensitiveList, "浣犲鐨?浣犲閫?浣犲Д") + sensitiveList = append(sensitiveList, "浣犲Д濮ョ殑") + sensitiveList = append(sensitiveList, "浣犲") + sensitiveList = append(sensitiveList, "浣犲") + sensitiveList = append(sensitiveList, "浣犲浜嗗") + sensitiveList = append(sensitiveList, "浣犲姣?浣犲鐨?浣犲閫?浣犵埛鐖风殑") + sensitiveList = append(sensitiveList, "浣犵埜") + sensitiveList = append(sensitiveList, "浣犵埡") + sensitiveList = append(sensitiveList, "浣犺€佸懗") + sensitiveList = append(sensitiveList, "浣犺€佹瘝") + sensitiveList = append(sensitiveList, "浣犺壊鍚?浣犺壊鍡?浣犺鎴戣璜栧") + sensitiveList = append(sensitiveList, "浣?渚嶅緸鍊柉闊?渚嶅緸璨濊但鐖剧壒") + sensitiveList = append(sensitiveList, "渚︽帰璁惧") + sensitiveList = append(sensitiveList, "渚ㄥ姙") + sensitiveList = append(sensitiveList, "淇勫湅") + sensitiveList = append(sensitiveList, "淇忚噣鎽勯瓌") + sensitiveList = append(sensitiveList, "淇忚噣鏀濋瓌") + sensitiveList = append(sensitiveList, "淇濆瘑灞€") + sensitiveList = append(sensitiveList, "淇濈洃浼?淇濋嚕") + sensitiveList = append(sensitiveList, "淇濋挀") + sensitiveList = append(sensitiveList, "淇炴澹?淇炴鑱?淇″緬") + sensitiveList = append(sensitiveList, "淇℃姤鏈堝垔") + sensitiveList = append(sensitiveList, "淇℃姤璐㈢粡鏂伴椈") + sensitiveList = append(sensitiveList, "淇$敤鍗辨") + sensitiveList = append(sensitiveList, "淇厜") + sensitiveList = append(sensitiveList, "鍊嶅彲浜?鍊偛璩?鍊姛") + sensitiveList = append(sensitiveList, "鍊?鍊浗") + sensitiveList = append(sensitiveList, "鍊瘒") + sensitiveList = append(sensitiveList, "鍋囧簡鏋?鍋囨叾鏋?鍋囬槼鍏锋彃绌?鍋囬櫧鍏锋彃绌?鍋囬潚鏋?鍋氭剾") + sensitiveList = append(sensitiveList, "鍋氭剾鍦栫墖") + sensitiveList = append(sensitiveList, "鍋氭剾鑷媿") + sensitiveList = append(sensitiveList, "鍋氭剾闆诲奖") + sensitiveList = append(sensitiveList, "鍋氱埍") + sensitiveList = append(sensitiveList, "鍋氱埍鍥剧墖") + sensitiveList = append(sensitiveList, "鍋氱埍濮垮娍") + sensitiveList = append(sensitiveList, "鍋氱埍鐢靛奖") + sensitiveList = append(sensitiveList, "鍋氱埍鑷媿") + sensitiveList = append(sensitiveList, "鍋?鍋锋媿") + sensitiveList = append(sensitiveList, "鍋锋媿缇庣┐") + sensitiveList = append(sensitiveList, "鍋锋") + sensitiveList = append(sensitiveList, "鍋锋") + sensitiveList = append(sensitiveList, "鍋风鍥剧墖") + sensitiveList = append(sensitiveList, "鍋风瑙嗛") + sensitiveList = append(sensitiveList, "鍋风鍦栫墖") + sensitiveList = append(sensitiveList, "鍋风瑕栭牷") + sensitiveList = append(sensitiveList, "鍋风簿") + sensitiveList = append(sensitiveList, "鍌呬綔涔?鍌呬綔缇?鍌呴箯") + sensitiveList = append(sensitiveList, "鍌儏鑽?鍌虫煋鎬х梾") + sensitiveList = append(sensitiveList, "鍌?鍌?鍌籦") + sensitiveList = append(sensitiveList, "鍌诲嵉") + sensitiveList = append(sensitiveList, "鍌诲悐") + sensitiveList = append(sensitiveList, "鍌诲瓙") + sensitiveList = append(sensitiveList, "鍌诲眲") + sensitiveList = append(sensitiveList, "鍌绘瘮") + sensitiveList = append(sensitiveList, "鍌婚€?鍌婚偿") + sensitiveList = append(sensitiveList, "鍍?鍍佃硦") + sensitiveList = append(sensitiveList, "鍍佃硦姘?鍍佃醇姘?鍎傝憲鍗垫媼") + sensitiveList = append(sensitiveList, "鍎傝憲宀″窉") + sensitiveList = append(sensitiveList, "鍎?鍎垮瓙") + sensitiveList = append(sensitiveList, "鍏冭€佽槶鎻?娌冨痉锛?鍏堝ジ鍚庢潃") + sensitiveList = append(sensitiveList, "鍏堝ジ寰屾") + sensitiveList = append(sensitiveList, "鍏嬪嫗妫?鍏嬫灄闋?鍏嬫灄椤?鍏嬭悐鎭?鍏嬭悐鐗?鍏嶈不a鐗?鍏嶈不鍋风缍?鍏嶈不鎴愪汉缍茬珯") + sensitiveList = append(sensitiveList, "鍏嶈垂a鐗?鍏嶈垂鍋风缃?鍏嶈垂鎴愪汉缃戠珯") + sensitiveList = append(sensitiveList, "鍏掑瓙") + sensitiveList = append(sensitiveList, "鍏氫富甯?鍏ョ┐涓€娓?鍏у皠") + sensitiveList = append(sensitiveList, "鍏у皠缇庡│") + sensitiveList = append(sensitiveList, "鍏ц。") + sensitiveList = append(sensitiveList, "鍏цげ") + sensitiveList = append(sensitiveList, "鍏ㄥ浗浜哄ぇ") + sensitiveList = append(sensitiveList, "鍏ㄥ湅浜哄ぇ") + sensitiveList = append(sensitiveList, "鍏ㄥ湅鍏╂渻") + sensitiveList = append(sensitiveList, "鍏ㄨ兘绁?鍏ㄨ8") + sensitiveList = append(sensitiveList, "鍏╁€嬩腑鍦?鍏╁湅璜?鍏╁哺涓夊湴璜栧") + sensitiveList = append(sensitiveList, "鍏╁哺闂滀總") + sensitiveList = append(sensitiveList, "鍏╂渻") + sensitiveList = append(sensitiveList, "鍏╂渻鍫遍亾") + sensitiveList = append(sensitiveList, "鍏╂渻鏂拌仦") + sensitiveList = append(sensitiveList, "鍏節") + sensitiveList = append(sensitiveList, "鍏節椋庢尝") + sensitiveList = append(sensitiveList, "鍏柟鍥介檯璧勮") + sensitiveList = append(sensitiveList, "鍏涔?鍏浜?鍏畨") + sensitiveList = append(sensitiveList, "鍏畨灞€") + sensitiveList = append(sensitiveList, "鍏畨閮?鍏暀鎶?鍏?鍥?鍏?鍥?鍏?鍥?鍏€傚洓") + sensitiveList = append(sensitiveList, "鍏悎褰?鍏洓") + sensitiveList = append(sensitiveList, "鍏洓浜嬩欢") + sensitiveList = append(sensitiveList, "鍏洓鐪熺浉") + sensitiveList = append(sensitiveList, "鍏洓杩愬姩") + sensitiveList = append(sensitiveList, "鍏洓閬嬪嫊") + sensitiveList = append(sensitiveList, "鍏湀鑱旂洘") + sensitiveList = append(sensitiveList, "鍏湀鑱洘") + sensitiveList = append(sensitiveList, "鍏变骇涓讳箟") + sensitiveList = append(sensitiveList, "鍏变骇鍏?鍏卞厷") + sensitiveList = append(sensitiveList, "鍏卞尓") + sensitiveList = append(sensitiveList, "鍏辨Ξ鍦?鍏辩嫍") + sensitiveList = append(sensitiveList, "鍏辩敘榛?鍏辩敚") + sensitiveList = append(sensitiveList, "鍏辩敚涓荤京") + sensitiveList = append(sensitiveList, "鍏辩敚榛?鍏辫粛") + sensitiveList = append(sensitiveList, "鍏遍花") + sensitiveList = append(sensitiveList, "鍏村鍓?鍏戒氦") + sensitiveList = append(sensitiveList, "鍏藉吔闂?鍏藉ジ") + sensitiveList = append(sensitiveList, "鍏芥") + sensitiveList = append(sensitiveList, "鍐?鍐呭皠") + sensitiveList = append(sensitiveList, "鍐呭皠缇庡") + sensitiveList = append(sensitiveList, "鍐呭箷") + sensitiveList = append(sensitiveList, "鍐呭箷绗?8鏈?鍐堟潙瀹佹") + sensitiveList = append(sensitiveList, "鍐堟潙绉€鏍?鍐岄偅濞橀姣?鍐欑湡") + sensitiveList = append(sensitiveList, "鍐涘浗涓讳箟") + sensitiveList = append(sensitiveList, "鍐涘") + sensitiveList = append(sensitiveList, "鍐涢暱鍙戝▉") + sensitiveList = append(sensitiveList, "鍐滀笟鐢靛瓙鎶?鍐滀笟閮?鍐鐑?鍐鐔?鍐板緦") + sensitiveList = append(sensitiveList, "鍐版瘨") + sensitiveList = append(sensitiveList, "鍐扮矇") + sensitiveList = append(sensitiveList, "鍐?鍑岃颈") + sensitiveList = append(sensitiveList, "鍑卞ェ鍕掑凹浠€") + sensitiveList = append(sensitiveList, "鍑辩埦鏈?鍑辩埦闆锋柉") + sensitiveList = append(sensitiveList, "鍑辩壒鍒囩埦") + sensitiveList = append(sensitiveList, "鍑?鍑镐汉涔?鍑镐粬") + sensitiveList = append(sensitiveList, "鍑镐綘") + sensitiveList = append(sensitiveList, "鍑稿コ涔?鍑稿ス") + sensitiveList = append(sensitiveList, "鍑稿Τ") + sensitiveList = append(sensitiveList, "鍑告垜") + sensitiveList = append(sensitiveList, "鍑歌倝浼?鍑歌倝鍎?鍑哄敭鍋囧竵") + sensitiveList = append(sensitiveList, "鍑哄敭鎵嬫灙") + sensitiveList = append(sensitiveList, "鍑哄敭鏋敮") + sensitiveList = append(sensitiveList, "鍑哄敭姝ゅ彿") + sensitiveList = append(sensitiveList, "鍑哄敭绁炵") + sensitiveList = append(sensitiveList, "鍑哄敭璐﹀彿") + sensitiveList = append(sensitiveList, "鍑鸿建") + sensitiveList = append(sensitiveList, "鍒嗚绁栧浗") + sensitiveList = append(sensitiveList, "鍒嗛殜闀锋柦钂傛枃") + sensitiveList = append(sensitiveList, "鍒囦竷") + sensitiveList = append(sensitiveList, "鍒楀畞") + sensitiveList = append(sensitiveList, "鍒楀") + sensitiveList = append(sensitiveList, "鍒樸€€娣?鍒樹簯灞?鍒樺啗") + sensitiveList = append(sensitiveList, "鍒樺钁?鍒樺皯濂?鍒樺欢涓?鍒樻穱") + sensitiveList = append(sensitiveList, "鍒?鍙?鍒犲彿") + sensitiveList = append(sensitiveList, "鍒舵湇鐙?鍒舵湇缇庡") + sensitiveList = append(sensitiveList, "鍒舵湇缇庡│") + sensitiveList = append(sensitiveList, "鍒舵湇瑾樻儜") + sensitiveList = append(sensitiveList, "鍒?鍓囨皯") + sensitiveList = append(sensitiveList, "鍓嶅嚫鍚庣繕") + sensitiveList = append(sensitiveList, "鍓嶅嚫寰岀抗") + sensitiveList = append(sensitiveList, "鍓涙瘮") + sensitiveList = append(sensitiveList, "鍓涙瘮妯e瓙") + sensitiveList = append(sensitiveList, "鍔変集鎵?鍔変繆鍦?鍔夊嚤涓?鍔夊墰") + sensitiveList = append(sensitiveList, "鍔夊崈鐭?鍔夊湅鍑?鍔夊+璩?鍔夊皯濂?鍔夊北闈?鍔夊欢鏉?鍔夋枃鍕?鍔夋枃闆?鍔夋泬娉?鍔夋泬绔?鍔夋案宸?鍔夋穱") + sensitiveList = append(sensitiveList, "鍔夎彲娓?鍔夎硴娣?鍔夎硴闆?鍔夐洸灞?鍔夐潚") + sensitiveList = append(sensitiveList, "鍔?鍔?鍔炵悊鏂囧嚟") + sensitiveList = append(sensitiveList, "鍔炵悊璇佷欢") + sensitiveList = append(sensitiveList, "鍔犳嬁澶у浗闄呭箍鎾數鍙?鍔ㄥ悜鏉傚織") + sensitiveList = append(sensitiveList, "鍔ㄦ极鑹插浘") + sensitiveList = append(sensitiveList, "鍔╂墜濞佺埦鐗?鍔╃悊") + sensitiveList = append(sensitiveList, "鍔?鍔?鏁?鎵€") + sensitiveList = append(sensitiveList, "鍔冲姩淇濋殰閮?鍔虫媺") + sensitiveList = append(sensitiveList, "鍕曚簜") + sensitiveList = append(sensitiveList, "鍕曟极鑹插湒") + sensitiveList = append(sensitiveList, "鍕炲嫊鏁欓鎵€") + sensitiveList = append(sensitiveList, "鍕炴敼") + sensitiveList = append(sensitiveList, "鍕炴暀") + sensitiveList = append(sensitiveList, "鍕?鍕惧紩") + sensitiveList = append(sensitiveList, "鍕鹃瓊灏戝") + sensitiveList = append(sensitiveList, "鍕鹃瓊灏戝│") + sensitiveList = append(sensitiveList, "鍖呬簩濂?鍖呯毊") + sensitiveList = append(sensitiveList, "鍖椾含xx缍?鍖椾含xx缃?鍖椾含涔嬫槬") + sensitiveList = append(sensitiveList, "鍖椾含鐣跺眬") + sensitiveList = append(sensitiveList, "鍖楀ぇ涓夎鍦拌珫澹?鍖楃編涓枃缃?鍖楃編鑷敱璜栧") + sensitiveList = append(sensitiveList, "鍖楅煋") + sensitiveList = append(sensitiveList, "鍗佸叓鎽?鍗佸勾鍔ㄤ贡鐭宠繘") + sensitiveList = append(sensitiveList, "鍗囧ぉ") + sensitiveList = append(sensitiveList, "鍗庝汉浠婃棩缃?鍗庡浗閿?鍗庡瘜璐㈢粡") + sensitiveList = append(sensitiveList, "鍗庡皵琛楁棩鎶?鍗庡缓鏁?鍗庣洓椤?鍗撲集婧?鍗?鍥?鍗朾") + sensitiveList = append(sensitiveList, "鍗杋d") + sensitiveList = append(sensitiveList, "鍗杚q") + sensitiveList = append(sensitiveList, "鍗栧厷姹傝崳") + sensitiveList = append(sensitiveList, "鍗栧崱") + sensitiveList = append(sensitiveList, "鍗栧彿") + sensitiveList = append(sensitiveList, "鍗栧浗") + sensitiveList = append(sensitiveList, "鍗栧浗姹傝崳") + sensitiveList = append(sensitiveList, "鍗栨灙鏀脊鑽?鍗栨瘮") + sensitiveList = append(sensitiveList, "鍗栨帆") + sensitiveList = append(sensitiveList, "鍗栬储瀵?鍗栬韩") + sensitiveList = append(sensitiveList, "鍗栬蒋浠?鍗栭€?鍗楀崕鏃╂姤") + sensitiveList = append(sensitiveList, "鍗楀ぇ鑷敱璜栧") + sensitiveList = append(sensitiveList, "鍗楁棭涓枃缃?鍗楁磱瑙嗙晫") + sensitiveList = append(sensitiveList, "鍗楄仈鐩?鍗楄彲鏃╁牨") + sensitiveList = append(sensitiveList, "鍗楄洰") + sensitiveList = append(sensitiveList, "鍗楄洰瀛?鍗楄牷") + sensitiveList = append(sensitiveList, "鍗楄牷瀛?鍗氬澶ц禌缃戠珯") + sensitiveList = append(sensitiveList, "鍗氳▕") + sensitiveList = append(sensitiveList, "鍗氳") + sensitiveList = append(sensitiveList, "鍗氳缃?鍗氳皥缃?鍗犻涓幆") + sensitiveList = append(sensitiveList, "鍗℃柉妤?鍗℃柉鐗圭綏") + sensitiveList = append(sensitiveList, "鍗фЫ") + sensitiveList = append(sensitiveList, "鍗敓閮?鍗?鍗靛瓙") + sensitiveList = append(sensitiveList, "鍘曞ゴ") + sensitiveList = append(sensitiveList, "鍘曟墍鍋锋媿") + sensitiveList = append(sensitiveList, "鍘曟墍鐩楁憚") + sensitiveList = append(sensitiveList, "鍘?鍘熶綇姘戞棌鐢佃鍙?鍘熷懗涓濊") + sensitiveList = append(sensitiveList, "鍘熷懗绲茶オ") + sensitiveList = append(sensitiveList, "鍘熷瓙寮?鍘熷瓙褰?鍘熷瓙鑳芥満鏋?鍘讳綘濡堢殑") + sensitiveList = append(sensitiveList, "鍘讳綘濯界殑") + sensitiveList = append(sensitiveList, "鍘绘") + sensitiveList = append(sensitiveList, "鍙備簨瀹?鍙嬪ソ鐨勯寰?鍙屽嘲寰ⅳ") + sensitiveList = append(sensitiveList, "鍙岃瀛︾敓閭姤") + sensitiveList = append(sensitiveList, "鍙岄緳鍏ユ礊") + sensitiveList = append(sensitiveList, "鍙嶄汉椤?鍙嶅厷") + sensitiveList = append(sensitiveList, "鍙嶅叡") + sensitiveList = append(sensitiveList, "鍙嶅姩") + sensitiveList = append(sensitiveList, "鍙嶅崕") + sensitiveList = append(sensitiveList, "鍙嶅皝閹?鍙嶅皝閹栨妧琛?鍙嶆亹濮斿憳浼?鍙嶆斂搴?鍙嶆棩") + sensitiveList = append(sensitiveList, "鍙嶇ぞ鏈?鍙嶈厫鏁楄珫澹?鍙嶉潻鍛?鍙嶉花") + sensitiveList = append(sensitiveList, "鍙戜鸡") + sensitiveList = append(sensitiveList, "鍙戜鸡鍔?鍙戞姟鍔?鍙戞氮") + sensitiveList = append(sensitiveList, "鍙戠エ") + sensitiveList = append(sensitiveList, "鍙戣") + sensitiveList = append(sensitiveList, "鍙戣鍏?鍙戣鍔?鍙戣疆") + sensitiveList = append(sensitiveList, "鍙戦獨") + sensitiveList = append(sensitiveList, "鍙斿珎鑲夋") + sensitiveList = append(sensitiveList, "鍙楄檺鐙?鍙樻€?鍙d氦") + sensitiveList = append(sensitiveList, "鍙d氦鏀惧翱") + sensitiveList = append(sensitiveList, "鍙e収鐖嗗皠") + sensitiveList = append(sensitiveList, "鍙e唴鐖嗗皠") + sensitiveList = append(sensitiveList, "鍙e皠") + sensitiveList = append(sensitiveList, "鍙f椿") + sensitiveList = append(sensitiveList, "鍙f帆") + sensitiveList = append(sensitiveList, "鍙g垎") + sensitiveList = append(sensitiveList, "鍙g垎鍚炵簿") + sensitiveList = append(sensitiveList, "鍙ら緧绁") + sensitiveList = append(sensitiveList, "鍙皬濮?鍙簥") + sensitiveList = append(sensitiveList, "鍙洖") + sensitiveList = append(sensitiveList, "鍙浮") + sensitiveList = append(sensitiveList, "鍙崱鍙?鍙崱鍥?鍙緟鍥?鍙板姙") + sensitiveList = append(sensitiveList, "鍙版咕鍏?鍙版咕鍏变骇鍏?鍙版咕鍏卞拰鍥?鍙版咕鍥?鍙版咕宀涘浗") + sensitiveList = append(sensitiveList, "鍙版咕甯濆浗") + sensitiveList = append(sensitiveList, "鍙版咕鏃烘姤") + sensitiveList = append(sensitiveList, "鍙版咕姘戝浗") + sensitiveList = append(sensitiveList, "鍙版咕鐙?鍙版咕鐙珛") + sensitiveList = append(sensitiveList, "鍙版咕璁拌€呭崗浼?鍙扮嫭") + sensitiveList = append(sensitiveList, "鍙扮嫭涓囧瞾") + sensitiveList = append(sensitiveList, "鍙扮嫭鍒嗗瓙") + sensitiveList = append(sensitiveList, "鍙扮崹") + sensitiveList = append(sensitiveList, "鍙扮崹鍒嗗瓙") + sensitiveList = append(sensitiveList, "鍙扮崹钀") + sensitiveList = append(sensitiveList, "鍙拌仈") + sensitiveList = append(sensitiveList, "鍙拌伅") + sensitiveList = append(sensitiveList, "鍙拌嫳绀?鍙茶悐濮?鍙茶悐濮嗙帇") + sensitiveList = append(sensitiveList, "鍙查仈鏋?鍙崇考") + sensitiveList = append(sensitiveList, "鍙跺墤鑻?鍙稿緬鑿?鍙告硶閮?鍙搁Μ鏅?鍙搁Μ鐠?鍙间綘") + sensitiveList = append(sensitiveList, "鍙间綘濡?鍙间綘濯?鍚冨ぇ渚?鍚冨睅") + sensitiveList = append(sensitiveList, "鍚冪簿") + sensitiveList = append(sensitiveList, "鍚冮洖宸?鍚冮浮宸?鍚屽眳") + sensitiveList = append(sensitiveList, "鍚屽眳涓囧瞾") + sensitiveList = append(sensitiveList, "鍚庡涵") + sensitiveList = append(sensitiveList, "鍚曠鑾?鍚楀暋") + sensitiveList = append(sensitiveList, "鍚楀暋鐗?鍚楀暋纰?鍚楃殑") + sensitiveList = append(sensitiveList, "鍚炵簿") + sensitiveList = append(sensitiveList, "鍚炵簿楱峰") + sensitiveList = append(sensitiveList, "鍚炵簿楠氬") + sensitiveList = append(sensitiveList, "鍚睂") + sensitiveList = append(sensitiveList, "鍚充粊鑿?鍚冲剙") + sensitiveList = append(sensitiveList, "鍚冲鐕?鍚冲鐠?鍚冲畯閬?鍚冲畼姝?鍚冲公鍦?鍚冲紭閬?鍚冲織鑺?鍚虫暒缇?鍚虫柟鍩?鍚虫窇鐝?鍚崇櫨鐩?鍚宠偛鍗?鍚抽偊鍦?鍚淬€€浠?鍚翠华") + sensitiveList = append(sensitiveList, "鍚村畼姝?鍚村府鍥?鍚撮偊鍥?鍚告敹鐨勫湒楱?鍚告瘨") + sensitiveList = append(sensitiveList, "鍚哥簿") + sensitiveList = append(sensitiveList, "鍚哥簿灏戝コ") + sensitiveList = append(sensitiveList, "鍚歌鐛?鍚瑰枃鍙?鍚圭") + sensitiveList = append(sensitiveList, "鍚圭矮") + sensitiveList = append(sensitiveList, "鍚硅惂") + sensitiveList = append(sensitiveList, "鍛備含鑺?鍛傜钃?鍛嗗嵉") + sensitiveList = append(sensitiveList, "鍛?鍛ㄥ仴搴?鍛ㄥ叚鎬у惂") + sensitiveList = append(sensitiveList, "鍛ㄥ畧瑷?鍛ㄥ皬搴?鍛ㄦ€荤悊") + sensitiveList = append(sensitiveList, "鍛ㄦ仼渚?鍛ㄦ仼鏉?鍛ㄦ案搴?鍛ㄩ嫆閹?鍛诲悷") + sensitiveList = append(sensitiveList, "鍜岄崑妲?鍜岄攨鏋?鍜挭") + sensitiveList = append(sensitiveList, "鍜挭鍥剧墖") + sensitiveList = append(sensitiveList, "鍜挭鍦栫墖") + sensitiveList = append(sensitiveList, "鍜潃榫熷ご") + sensitiveList = append(sensitiveList, "鍜憲榫滈牠") + sensitiveList = append(sensitiveList, "鍜?鍝佺┐") + sensitiveList = append(sensitiveList, "鍝佽壊鍫?鍝侀鍫?鍝堢埦缇呭凹") + sensitiveList = append(sensitiveList, "鍝瘝") + sensitiveList = append(sensitiveList, "鍝埜") + sensitiveList = append(sensitiveList, "鍞愬鐠?鍞愬鐠?鍞愭煆姗?鍞?鍞甶d") + sensitiveList = append(sensitiveList, "鍞彿") + sensitiveList = append(sensitiveList, "鍞蒋浠?鍟嗗姟閮?鍟婄劇鍗?鍠勬儭鏈夊牨") + sensitiveList = append(sensitiveList, "鍠?鍠紛") + sensitiveList = append(sensitiveList, "鍠煶") + sensitiveList = append(sensitiveList, "鍠蜂綘") + sensitiveList = append(sensitiveList, "鍠风簿") + sensitiveList = append(sensitiveList, "鍡庡暋") + sensitiveList = append(sensitiveList, "鍡庣殑") + sensitiveList = append(sensitiveList, "鍡戣嵂") + sensitiveList = append(sensitiveList, "鍡戣棩") + sensitiveList = append(sensitiveList, "鍢?鍢?鍢?鍢?鍢绘父涓浗") + sensitiveList = append(sensitiveList, "鍢婚亰涓湅") + sensitiveList = append(sensitiveList, "鍣?鍣翠綘") + sensitiveList = append(sensitiveList, "鍣寸簿") + sensitiveList = append(sensitiveList, "鍤?鍤村鍏?鍤村绁?鍥?鍥涗汉甯?鍥涗汉骞?鍥涘窛鐙珛") + sensitiveList = append(sensitiveList, "鍥涘窛鐛?鍥涘窛鐛ㄧ珛") + sensitiveList = append(sensitiveList, "鍥?鍥炲洖") + sensitiveList = append(sensitiveList, "鍥炴暀") + sensitiveList = append(sensitiveList, "鍥炴棌浜哄悆鐚倝") + sensitiveList = append(sensitiveList, "鍥炴皯鍚冪尓鑲?鍥炴皯鍚冭爆鑲?鍥炴皯鏆村姩") + sensitiveList = append(sensitiveList, "鍥炴皯鏆村嫊") + sensitiveList = append(sensitiveList, "鍥炶壇鐜?鍥戒骇av") + sensitiveList = append(sensitiveList, "鍥戒骇鍋锋媿") + sensitiveList = append(sensitiveList, "鍥戒骇瀚栧") + sensitiveList = append(sensitiveList, "鍥戒骇鏃犵爜") + sensitiveList = append(sensitiveList, "鍥戒骇楠氳揣") + sensitiveList = append(sensitiveList, "鍥藉唴鍔ㄦ€佹竻鏍?鍥藉畨灞€") + sensitiveList = append(sensitiveList, "鍥藉涓诲腑") + sensitiveList = append(sensitiveList, "鍥藉涓昏閮ㄥ") + sensitiveList = append(sensitiveList, "鍥藉姘戝") + sensitiveList = append(sensitiveList, "鍥芥皯鍏?鍥芥皯鍏氫竾宀?鍥界灞€") + sensitiveList = append(sensitiveList, "鍥借鏃ユ姤") + sensitiveList = append(sensitiveList, "鍥介槻绉戝伐濮?鍥介槻閮?鍥介檯骞挎挱鐨勭數瑙嗚妭鐩?鍥介檯娉曢櫌") + sensitiveList = append(sensitiveList, "鍦嗘弧") + sensitiveList = append(sensitiveList, "鍦嬪収鍕曟厠娓呮ǎ") + sensitiveList = append(sensitiveList, "鍦嬪瀹夊叏") + sensitiveList = append(sensitiveList, "鍦嬪姗熷瘑") + sensitiveList = append(sensitiveList, "鍦嬬敘av") + sensitiveList = append(sensitiveList, "鍦嬬敘鍋锋媿") + sensitiveList = append(sensitiveList, "鍦嬬敘瀚栧") + sensitiveList = append(sensitiveList, "鍦嬬敘鐒$⒓") + sensitiveList = append(sensitiveList, "鍦嬬敘楱疯波") + sensitiveList = append(sensitiveList, "鍦嬭硦") + sensitiveList = append(sensitiveList, "鍦嬭粛") + sensitiveList = append(sensitiveList, "鍦撴豢") + sensitiveList = append(sensitiveList, "鍦栨浉绠$悊鍝″崱鐗?鍦樺摗棣埦姹€") + sensitiveList = append(sensitiveList, "鍦橀暦鎴堢櫥") + sensitiveList = append(sensitiveList, "鍦eコ宄?鍦f垬") + sensitiveList = append(sensitiveList, "鍦f瘝") + sensitiveList = append(sensitiveList, "鍦f硥瀛︽帆") + sensitiveList = append(sensitiveList, "鍦鼎") + sensitiveList = append(sensitiveList, "鍦颁笅鏁欐渻") + sensitiveList = append(sensitiveList, "鍦拌棌") + sensitiveList = append(sensitiveList, "鍧愬彴") + sensitiveList = append(sensitiveList, "鍧愬彴鐨?鍧愬簞") + sensitiveList = append(sensitiveList, "鍧愯帄") + sensitiveList = append(sensitiveList, "鍨冨溇娓告垙") + sensitiveList = append(sensitiveList, "鍨?鍩冩柉钀?鍩冭鍏嬭槆鐗瑰嫟") + sensitiveList = append(sensitiveList, "鍩?鍩哄湴绲勭箶") + sensitiveList = append(sensitiveList, "鍩哄湴缁勭粐") + sensitiveList = append(sensitiveList, "鍩虹潱") + sensitiveList = append(sensitiveList, "鍩虹潱鏁?鍫曟帆") + sensitiveList = append(sensitiveList, "濉斿収") + sensitiveList = append(sensitiveList, "濉斿埄鐝?濉旂儚") + sensitiveList = append(sensitiveList, "濉炰綘鍏?濉炰綘濞?濉炰綘姣?濉炰綘鐖?濉炰綘鑰佸笀") + sensitiveList = append(sensitiveList, "濉炰綘鑰佹瘝") + sensitiveList = append(sensitiveList, "濉炵櫧") + sensitiveList = append(sensitiveList, "澧ㄧ储閲屽凹") + sensitiveList = append(sensitiveList, "澧帆") + sensitiveList = append(sensitiveList, "澹叺绠$悊鍝$摝鐖捐嚕") + sensitiveList = append(sensitiveList, "澹瑰懆鍒?澹瑰懆鍒?鍙版咕)") + sensitiveList = append(sensitiveList, "澹圭數瑙") + sensitiveList = append(sensitiveList, "澶栥€€鎸?澶栦笓灞€") + sensitiveList = append(sensitiveList, "澶栦氦鑸囨柟鐣?澶栦氦璜栧") + sensitiveList = append(sensitiveList, "澶栦氦閮?澶栨寕") + sensitiveList = append(sensitiveList, "澶栨帥") + sensitiveList = append(sensitiveList, "澶栨眹灞€") + sensitiveList = append(sensitiveList, "澶栭亣") + sensitiveList = append(sensitiveList, "澶氫汉鎬ф剾") + sensitiveList = append(sensitiveList, "澶氫汉杓?澶氫汉杞?澶氱董") + sensitiveList = append(sensitiveList, "澶氱淮") + sensitiveList = append(sensitiveList, "澶氱淮鏂伴椈") + sensitiveList = append(sensitiveList, "澶氱淮鏂伴椈缃?澶氱淮缃?澶滄儏") + sensitiveList = append(sensitiveList, "澶滆┍绱鍩?澶") + sensitiveList = append(sensitiveList, "澶т腑鍦嬭珫澹?澶т腑鑿珫澹?澶т贡浜?澶т钩") + sensitiveList = append(sensitiveList, "澶т钩闋?澶т簜浜?澶т簨浠?澶т紬鑹叉儏鎴愪汉缃?澶т究") + sensitiveList = append(sensitiveList, "澶у姏鎶介€?澶у崼鏁?澶у弬鑰?澶у弮鑰?澶у彶绱€") + sensitiveList = append(sensitiveList, "澶у彶瑷?澶уザ澶?澶уザ濯?澶уザ缇庨€?澶уザ楱峰コ") + sensitiveList = append(sensitiveList, "澶уザ楠氬コ") + sensitiveList = append(sensitiveList, "澶у楠氫贡") + sensitiveList = append(sensitiveList, "澶у璜栧") + sensitiveList = append(sensitiveList, "澶у笀") + sensitiveList = append(sensitiveList, "澶ф澅浜?澶ф澅浜炲叡姒?澶ф瘮") + sensitiveList = append(sensitiveList, "澶ф硶") + sensitiveList = append(sensitiveList, "澶ф尝") + sensitiveList = append(sensitiveList, "澶ф尝娉?澶ф尝绮塨") + sensitiveList = append(sensitiveList, "澶ф尝楱峰│") + sensitiveList = append(sensitiveList, "澶ф尝楠氬") + sensitiveList = append(sensitiveList, "澶х溇鑹叉儏鎴愪汉缍?澶х磤鍏?澶х磤鍏冩柊鑱炵恫") + sensitiveList = append(sensitiveList, "澶х磤鍦?澶х邯鍏?澶х邯鍏冩柊闂荤綉") + sensitiveList = append(sensitiveList, "澶х邯鍏冩椂鎶?澶х邯鍏冪綉") + sensitiveList = append(sensitiveList, "澶ц儐鍑轰綅") + sensitiveList = append(sensitiveList, "澶ц儐灏戝コ") + sensitiveList = append(sensitiveList, "澶ц喗鍑轰綅") + sensitiveList = append(sensitiveList, "澶ц喗灏戝コ") + sensitiveList = append(sensitiveList, "澶ц姳閫?澶цb") + sensitiveList = append(sensitiveList, "澶ц姣?澶ц閫?澶ц鐪熶汉鐪熶簨") + sensitiveList = append(sensitiveList, "澶ц鏁?澶ч€?澶ч洖宸?澶ч稄宸?澶ч浮宸?澶ч夯") + sensitiveList = append(sensitiveList, "澶ч夯鏍戣剛") + sensitiveList = append(sensitiveList, "澶ч夯娌?澶╀笅鏉傚織") + sensitiveList = append(sensitiveList, "澶╀富鏁?澶╁ぉ骞?澶╁ぉ骞茶布鍦?澶╁ぉ骞茶创鍥?澶╁ぉ鎯呰壊") + sensitiveList = append(sensitiveList, "澶╁畨闁€") + sensitiveList = append(sensitiveList, "澶╁畨闁€涓€浠?澶╁畨闁€浜嬩欢") + sensitiveList = append(sensitiveList, "澶╁畨闁€灞犳") + sensitiveList = append(sensitiveList, "澶╁畨闁€閷勫奖甯?澶╁畨闂ㄤ簨浠?澶╃殗闄涗笅") + sensitiveList = append(sensitiveList, "澶╃溂鏃ユ姤绀?澶╄棌") + sensitiveList = append(sensitiveList, "澶╅柟") + sensitiveList = append(sensitiveList, "澶瓙鍏?澶洃") + sensitiveList = append(sensitiveList, "澶洠") + sensitiveList = append(sensitiveList, "澶槼鎶?澶櫧鍫?澶3p") + sensitiveList = append(sensitiveList, "澶涔变氦") + sensitiveList = append(sensitiveList, "澶浜備氦") + sensitiveList = append(sensitiveList, "澶淇变箰閮?澶淇辨▊閮?澶澶歱") + sensitiveList = append(sensitiveList, "澶鑷媿") + sensitiveList = append(sensitiveList, "澶鍏ч儴鏅氭渻") + sensitiveList = append(sensitiveList, "澶?澶辩") + sensitiveList = append(sensitiveList, "澶辫韩") + sensitiveList = append(sensitiveList, "澶存潯鏃ユ姤") + sensitiveList = append(sensitiveList, "濂?濂囨帆瀹濋壌") + sensitiveList = append(sensitiveList, "濂囨帆瀵堕憭") + sensitiveList = append(sensitiveList, "濂ュ反椹?濂ラ┈灏?濂у€?濂у厠鎷?濂у埄寮?濂у反棣?濂ф媺寰?濂х壒铇?濂чΜ鐖?濂ч濂?濂?濂充富浜虹緟濮Μ鑾?濂充紭") + sensitiveList = append(sensitiveList, "濂冲劒") + sensitiveList = append(sensitiveList, "濂冲盎") + sensitiveList = append(sensitiveList, "濂冲眲") + sensitiveList = append(sensitiveList, "濂冲共") + sensitiveList = append(sensitiveList, "濂冲构") + sensitiveList = append(sensitiveList, "濂抽Μ銊?濂抽Μ鐧藉嫼") + sensitiveList = append(sensitiveList, "濂抽Μ鐨?濂抽┈銊?濂抽┈鐧藉嫼") + sensitiveList = append(sensitiveList, "濂抽┈鐨?濂寸暅鎶?濂撮毞瑾挎暀") + sensitiveList = append(sensitiveList, "濂撮毞璋冩暀") + sensitiveList = append(sensitiveList, "濂撮毟榄旀棌澹叺") + sensitiveList = append(sensitiveList, "濂?濂跺ぇ灞勮偉") + sensitiveList = append(sensitiveList, "濂跺ザ鐨?濂跺瓙") + sensitiveList = append(sensitiveList, "濂舵尯鑷€缈?濂舵尯鑷€缈?濂堕牠") + sensitiveList = append(sensitiveList, "濂?濂镐綘") + sensitiveList = append(sensitiveList, "濂稿か娣") + sensitiveList = append(sensitiveList, "濂稿辜") + sensitiveList = append(sensitiveList, "濂告儏") + sensitiveList = append(sensitiveList, "濂告帆") + sensitiveList = append(sensitiveList, "濂告帆鐢佃溅") + sensitiveList = append(sensitiveList, "濂瑰") + sensitiveList = append(sensitiveList, "濂瑰鐨勯噾鏃ユ垚") + sensitiveList = append(sensitiveList, "濂瑰") + sensitiveList = append(sensitiveList, "濂藉") + sensitiveList = append(sensitiveList, "濂借壊cc") + sensitiveList = append(sensitiveList, "濡備締") + sensitiveList = append(sensitiveList, "濡傛潵") + sensitiveList = append(sensitiveList, "濡?濡坆") + sensitiveList = append(sensitiveList, "濡堝崠濡堝眮") + sensitiveList = append(sensitiveList, "濡堟壒") + sensitiveList = append(sensitiveList, "濡堟瘮") + sensitiveList = append(sensitiveList, "濡堢櫧鍕?濡堢殑") + sensitiveList = append(sensitiveList, "濡堢") + sensitiveList = append(sensitiveList, "濡堥€?濡?濡撳コ") + sensitiveList = append(sensitiveList, "濡撻櫌") + sensitiveList = append(sensitiveList, "濡栧獨鐔熸瘝") + sensitiveList = append(sensitiveList, "濡╁獨鎸戦€?濡冲鐨?濡冲鐨?濡宠€佹瘝鐨?濡抽┈鐨?濡瑰闃存瘺") + sensitiveList = append(sensitiveList, "濡瑰闄版瘺") + sensitiveList = append(sensitiveList, "濡瑰楱峰湒") + sensitiveList = append(sensitiveList, "濡瑰楠氬浘") + sensitiveList = append(sensitiveList, "濮氭枃鍏?濮氭湀璎?濮氱緟") + sensitiveList = append(sensitiveList, "濮hタ") + sensitiveList = append(sensitiveList, "濮︽儏") + sensitiveList = append(sensitiveList, "濮︽煋") + sensitiveList = append(sensitiveList, "濮︽帆") + sensitiveList = append(sensitiveList, "濮︽帆闆昏粖") + sensitiveList = append(sensitiveList, "濮颈") + sensitiveList = append(sensitiveList, "濮嫕寰?濞佽€屾煍") + sensitiveList = append(sensitiveList, "濞佽€岄挗") + sensitiveList = append(sensitiveList, "濞樼殑") + sensitiveList = append(sensitiveList, "濞橀姣?濞?濠?濠婂瓙") + sensitiveList = append(sensitiveList, "濠婂瓙椁婄殑") + sensitiveList = append(sensitiveList, "濠贡鍐涘洟") + sensitiveList = append(sensitiveList, "濠簜杌嶅湗") + sensitiveList = append(sensitiveList, "濯掍綋鍏皯琛屽姩缃?濯氳嵂灏戝勾") + sensitiveList = append(sensitiveList, "濯氳棩灏戝勾") + sensitiveList = append(sensitiveList, "濯?濯絙") + sensitiveList = append(sensitiveList, "濯芥壒") + sensitiveList = append(sensitiveList, "濯芥瘮") + sensitiveList = append(sensitiveList, "濯界櫧鍕?濯界殑") + sensitiveList = append(sensitiveList, "濯介€?瀚愬眲") + sensitiveList = append(sensitiveList, "瀚栧鎸囧崡") + sensitiveList = append(sensitiveList, "瀚栧") + sensitiveList = append(sensitiveList, "瀚?瀚゜") + sensitiveList = append(sensitiveList, "瀚゜b") + sensitiveList = append(sensitiveList, "瀚╁コ") + sensitiveList = append(sensitiveList, "瀚╁ザ") + sensitiveList = append(sensitiveList, "瀚╁眲") + sensitiveList = append(sensitiveList, "瀚╃┐") + sensitiveList = append(sensitiveList, "瀚╃┐鑲夌斧") + sensitiveList = append(sensitiveList, "瀚╃┐鑲夌紳") + sensitiveList = append(sensitiveList, "瀚╃斧") + sensitiveList = append(sensitiveList, "瀚╃紳") + sensitiveList = append(sensitiveList, "瀚╅€?瀚╅畱") + sensitiveList = append(sensitiveList, "瀚╅畱榄?瀚╅矋") + sensitiveList = append(sensitiveList, "瀚╅矋楸?瀚靛獨鎸戦€?瀣€") + sensitiveList = append(sensitiveList, "瀛愬コ浠昏亴鍚嶅崟") + sensitiveList = append(sensitiveList, "瀛愬") + sensitiveList = append(sensitiveList, "瀛愬") + sensitiveList = append(sensitiveList, "瀛欎腑灞?瀛欐斂鎵?瀛欐枃") + sensitiveList = append(sensitiveList, "瀛欐槬鍏?瀛欓€镐粰") + sensitiveList = append(sensitiveList, "瀛熷缓鏌?瀛︽疆") + sensitiveList = append(sensitiveList, "瀛︾敓濡?瀛﹁繍") + sensitiveList = append(sensitiveList, "瀛腑灞?瀛ぇ鍗?瀛枃") + sensitiveList = append(sensitiveList, "瀛€镐粰") + sensitiveList = append(sensitiveList, "瀛?瀛告疆") + sensitiveList = append(sensitiveList, "瀛哥敓濡?瀛歌伅") + sensitiveList = append(sensitiveList, "瀛歌嚜鑱?瀛搁亱") + sensitiveList = append(sensitiveList, "瀹?瀹囨槑缍?瀹夊€嶆檳涓?瀹夊€嶆檵涓?瀹夊叏灞€") + sensitiveList = append(sensitiveList, "瀹夊崡") + sensitiveList = append(sensitiveList, "瀹夋媺") + sensitiveList = append(sensitiveList, "瀹夐潪浠栧懡") + sensitiveList = append(sensitiveList, "瀹嬫浉鍏?瀹嬫鐟?瀹嬬鑻?瀹岃泲鎿?瀹楁暀") + sensitiveList = append(sensitiveList, "瀹樺晢鍕剧粨") + sensitiveList = append(sensitiveList, "瀹樻柟") + sensitiveList = append(sensitiveList, "瀹橀€兼皯鍙?瀹℃煡") + sensitiveList = append(sensitiveList, "瀹㈠鐢佃鍙?瀹㈡埗鏈嶅嫏") + sensitiveList = append(sensitiveList, "瀹㈡埛鏈嶅姟") + sensitiveList = append(sensitiveList, "瀹㈡湇") + sensitiveList = append(sensitiveList, "瀹崇緸") + sensitiveList = append(sensitiveList, "瀹惧懆") + sensitiveList = append(sensitiveList, "瀹鹃濂抽儙") + sensitiveList = append(sensitiveList, "瀵傚癁鑷懜") + sensitiveList = append(sensitiveList, "瀵嗗畻") + sensitiveList = append(sensitiveList, "瀵嗗娣") + sensitiveList = append(sensitiveList, "瀵嗘礊") + sensitiveList = append(sensitiveList, "瀵嗙┐") + sensitiveList = append(sensitiveList, "瀵嗙┐璨煎湒") + sensitiveList = append(sensitiveList, "瀵嗙┐璐村浘") + sensitiveList = append(sensitiveList, "瀵囨檽浼?瀵屽叞鍏嬫灄") + sensitiveList = append(sensitiveList, "瀵╂煡") + sensitiveList = append(sensitiveList, "瀵湡") + sensitiveList = append(sensitiveList, "瀵剁煶鍟嗕汉") + sensitiveList = append(sensitiveList, "瀵绘") + sensitiveList = append(sensitiveList, "瀵煎脊") + sensitiveList = append(sensitiveList, "灏佸嵃鐨勯潏榄傞◣澹?灏佸緸寰?灏佹") + sensitiveList = append(sensitiveList, "灏勪簡閭勮瑕?灏勫ザ") + sensitiveList = append(sensitiveList, "灏勭埥") + sensitiveList = append(sensitiveList, "灏勭簿") + sensitiveList = append(sensitiveList, "灏勯") + sensitiveList = append(sensitiveList, "灏勯") + sensitiveList = append(sensitiveList, "灏囧墖姘?灏堝埗") + sensitiveList = append(sensitiveList, "灏堟斂") + sensitiveList = append(sensitiveList, "灏夊仴琛?灏庡斧") + sensitiveList = append(sensitiveList, "灏庡綀") + sensitiveList = append(sensitiveList, "灏廱") + sensitiveList = append(sensitiveList, "灏廱妯?灏忎钩澶?灏忎究") + sensitiveList = append(sensitiveList, "灏忓弬鑰?灏忓弮鑰?灏忓彈") + sensitiveList = append(sensitiveList, "灏忓彸") + sensitiveList = append(sensitiveList, "灏忓") + sensitiveList = append(sensitiveList, "灏忓鍏艰亴") + sensitiveList = append(sensitiveList, "灏忓鎵撻姗?灏忓鎵撻鏈?灏忓瑁歌亰") + sensitiveList = append(sensitiveList, "灏忓闆?灏忓楦?灏忓紵寮?灏忔敾") + sensitiveList = append(sensitiveList, "灏忔棩鏈?灏忔瘮妯?灏忔硥") + sensitiveList = append(sensitiveList, "灏忔硥绱斾竴閮?灏忔硥绾竴閮?灏忕┐") + sensitiveList = append(sensitiveList, "灏忚倝绮?灏忛€?灏忛洖闆?灏忛潏閫?灏忛稄槎?灏忛浮楦?灏戜慨姝?灏戝コ琚彃") + sensitiveList = append(sensitiveList, "灏戝鍋锋儏") + sensitiveList = append(sensitiveList, "灏戝│鍋锋儏") + sensitiveList = append(sensitiveList, "灏ゆ瘮浜?灏卞幓鏃?灏卞幓鑹茶壊") + sensitiveList = append(sensitiveList, "灏卞幓瑾樻儜") + sensitiveList = append(sensitiveList, "灏卞幓璇辨儜") + sensitiveList = append(sensitiveList, "灏规叾姘?灏?灏煎厠鏉?灏煎厠妫?灏煎ェ澶?灏肩帥") + sensitiveList = append(sensitiveList, "灏肩應") + sensitiveList = append(sensitiveList, "灞佺溂") + sensitiveList = append(sensitiveList, "灞?灞勫眲") + sensitiveList = append(sensitiveList, "灞勫眲鐗瑰啓") + sensitiveList = append(sensitiveList, "灞勫眲鐗瑰") + sensitiveList = append(sensitiveList, "灞勬瘺") + sensitiveList = append(sensitiveList, "灞婁腑澶斂娌诲眬濮斿摗") + sensitiveList = append(sensitiveList, "灞?灞?") + sensitiveList = append(sensitiveList, "灞屼竷") + sensitiveList = append(sensitiveList, "灞屼汉涔?灞屼粬") + sensitiveList = append(sensitiveList, "灞屼綘") + sensitiveList = append(sensitiveList, "灞屽コ涔?灞屽ス") + sensitiveList = append(sensitiveList, "灞屽Τ") + sensitiveList = append(sensitiveList, "灞屾垜") + sensitiveList = append(sensitiveList, "灞屾瘺") + sensitiveList = append(sensitiveList, "灞岃タ") + sensitiveList = append(sensitiveList, "灞岄笭") + sensitiveList = append(sensitiveList, "灞犳潃") + sensitiveList = append(sensitiveList, "灞犳") + sensitiveList = append(sensitiveList, "灞?灞?灞卞彛绲?灞辨湰浜斿崄鍏?灞?宀?宀″窉") + sensitiveList = append(sensitiveList, "宀℃潙瀵ф") + sensitiveList = append(sensitiveList, "宀℃潙绉€妯?宄?宕楀摠澹叺") + sensitiveList = append(sensitiveList, "宥?宥?宸℃煡") + sensitiveList = append(sensitiveList, "宸ュ晢鏃舵姤") + sensitiveList = append(sensitiveList, "宸ヨ嚜鑱?宸ф帆濂告垙") + sensitiveList = append(sensitiveList, "宸ф帆濂告埐") + sensitiveList = append(sensitiveList, "宸ㄤ钩") + sensitiveList = append(sensitiveList, "宸ㄤ钩淇忓コ鍖?宸ㄤ钩淇忓コ閱?宸ㄤ钩绱犱汉") + sensitiveList = append(sensitiveList, "宸ㄥザ") + sensitiveList = append(sensitiveList, "宸ㄥ睂") + sensitiveList = append(sensitiveList, "宸ㄦ楱庡叺") + sensitiveList = append(sensitiveList, "宸ㄧ偖鍏靛洟") + sensitiveList = append(sensitiveList, "宸ㄧ偖鍏靛湗") + sensitiveList = append(sensitiveList, "宸ㄩ惖瑙掑搱鍏?宸ㄩǚ") + sensitiveList = append(sensitiveList, "宸ㄩ獨") + sensitiveList = append(sensitiveList, "宸村€緧寰?宸村€潶") + sensitiveList = append(sensitiveList, "宸?甯冧粈") + sensitiveList = append(sensitiveList, "甯冨笇") + sensitiveList = append(sensitiveList, "甯冭幈灏?甯冭悐鐖?甯冮浄灏?甯冮浄鐖?甯屾媺鍏?甯屾湜涔嬪0") + sensitiveList = append(sensitiveList, "甯屾湜涔嬪0鍥介檯骞挎挱鐢靛彴") + sensitiveList = append(sensitiveList, "甯岀壒鍕?甯濆浗涓讳箟") + sensitiveList = append(sensitiveList, "甯濆湅涓荤京") + sensitiveList = append(sensitiveList, "甯﹀鑲涗氦") + sensitiveList = append(sensitiveList, "甯宠櫉") + sensitiveList = append(sensitiveList, "甯跺鑲涗氦") + sensitiveList = append(sensitiveList, "甯稿媮") + sensitiveList = append(sensitiveList, "骞?骞?8") + sensitiveList = append(sensitiveList, "骞瞘m") + sensitiveList = append(sensitiveList, "骞瞘y") + sensitiveList = append(sensitiveList, "骞蹭竷鍏?骞蹭汉涔?骞蹭粬") + sensitiveList = append(sensitiveList, "骞蹭綘") + sensitiveList = append(sensitiveList, "骞蹭綘濡?骞蹭綘濡坆") + sensitiveList = append(sensitiveList, "骞蹭綘濡堥€?骞蹭綘濞?骞插コ涔?骞插ス") + sensitiveList = append(sensitiveList, "骞插Τ") + sensitiveList = append(sensitiveList, "骞插Τ濡?骞插Τ濞?骞插Τ鑰佹瘝") + sensitiveList = append(sensitiveList, "骞叉嫀濞?骞叉") + sensitiveList = append(sensitiveList, "骞叉浣?骞茬殑鐖?骞茬┐") + sensitiveList = append(sensitiveList, "骞诧紬锛?骞诧絿锝?骞?8") + sensitiveList = append(sensitiveList, "骞筨i") + sensitiveList = append(sensitiveList, "骞筭y") + sensitiveList = append(sensitiveList, "骞逛竴瀹?骞逛竷鍏?骞逛汉涔?骞逛粬") + sensitiveList = append(sensitiveList, "骞逛綘") + sensitiveList = append(sensitiveList, "骞逛綘濞?骞逛綘鑰佹瘝") + sensitiveList = append(sensitiveList, "骞逛綘鑰佹瘮") + sensitiveList = append(sensitiveList, "骞瑰叏瀹?骞瑰コ涔?骞瑰ス") + sensitiveList = append(sensitiveList, "骞瑰Τ") + sensitiveList = append(sensitiveList, "骞瑰构骞?骞规") + sensitiveList = append(sensitiveList, "骞规浣?骞规瘮") + sensitiveList = append(sensitiveList, "骞圭偖") + sensitiveList = append(sensitiveList, "骞圭殑浣?骞圭殑鐖?骞圭牪") + sensitiveList = append(sensitiveList, "骞圭┐") + sensitiveList = append(sensitiveList, "骞归€?骞癸紬锛?骞癸絿锝?骞间氦") + sensitiveList = append(sensitiveList, "骞煎湒") + sensitiveList = append(sensitiveList, "骞煎コ") + sensitiveList = append(sensitiveList, "骞煎") + sensitiveList = append(sensitiveList, "骞肩敺") + sensitiveList = append(sensitiveList, "骞奸€?骞奸闁?骞奸闃?骞奸綊") + sensitiveList = append(sensitiveList, "骞奸娇") + sensitiveList = append(sensitiveList, "骞奸緧") + sensitiveList = append(sensitiveList, "搴€") + sensitiveList = append(sensitiveList, "搴嗙孩") + sensitiveList = append(sensitiveList, "搴?搴斿彫") + sensitiveList = append(sensitiveList, "搴櫌璀﹁鍏?寤佸ゴ") + sensitiveList = append(sensitiveList, "寤佹墍鍋锋媿") + sensitiveList = append(sensitiveList, "寤佹墍鐩滄敐") + sensitiveList = append(sensitiveList, "寤夋斂澶ц珫澹?寤栭尗榫?寤?寤㈠瀹堣鑰?寤h仦") + sensitiveList = append(sensitiveList, "寤哄湅榛?寮€鎴?寮€鏀炬潅蹇?寮€鏀剧綉") + sensitiveList = append(sensitiveList, "寮€鑻?寮傚瀷鍙涜粛") + sensitiveList = append(sensitiveList, "寮傝浜哄+") + sensitiveList = append(sensitiveList, "寮傝浜哄+") + sensitiveList = append(sensitiveList, "寮撹櫧濂冲共") + sensitiveList = append(sensitiveList, "寮撻洊") + sensitiveList = append(sensitiveList, "寮曞皫") + sensitiveList = append(sensitiveList, "寮犲皬骞?寮犲痉姹?寮犳槬妗?寮犳槬璐?寮犳湞闃?寮犳疆闃?寮犵") + sensitiveList = append(sensitiveList, "寮犵珛鏄?寮犵闆?寮犻珮涓?寮典集绗?寮靛亯鍦?寮靛仴") + sensitiveList = append(sensitiveList, "寮靛崥闆?寮靛痉姹?寮靛織娓?寮垫槬姗?寮垫槶瀵?寮垫灄") + sensitiveList = append(sensitiveList, "寮垫竻鑺?寮垫韩榉?寮电闆?寮甸嫾") + sensitiveList = append(sensitiveList, "寮甸珮楹?寮峰埗娴h吀") + sensitiveList = append(sensitiveList, "寮峰Е") + sensitiveList = append(sensitiveList, "寮峰Е铏曞コ") + sensitiveList = append(sensitiveList, "寮锋毚") + sensitiveList = append(sensitiveList, "寮哄埗娴h偁") + sensitiveList = append(sensitiveList, "寮哄崼") + sensitiveList = append(sensitiveList, "寮哄ジ") + sensitiveList = append(sensitiveList, "寮哄ジ澶勫コ") + sensitiveList = append(sensitiveList, "寮哄ジ鐘?寮烘晥澶辨剰鑽?寮烘毚") + sensitiveList = append(sensitiveList, "寮虹‖鍙戣█") + sensitiveList = append(sensitiveList, "褰€") + sensitiveList = append(sensitiveList, "褰堝娋") + sensitiveList = append(sensitiveList, "褰?褰附濯?褰崥") + sensitiveList = append(sensitiveList, "褰崥鍟嗕笟鍛ㄥ垔") + sensitiveList = append(sensitiveList, "褰崥鏂伴椈绀?褰痉鎳?褰湡") + sensitiveList = append(sensitiveList, "褰簵濯?寰堥粍") + sensitiveList = append(sensitiveList, "寰屽涵") + sensitiveList = append(sensitiveList, "寰愬厜鏄?寰愬悜鍓?寰愬湅鑸?寰愭墠鍘?寰°伄浜屼唬鐩?寰峰浗涔嬪0涓枃缃?寰风董鐖?蹇冪伒娉曢棬鈥滅櫧璇濅經娉曗€濈郴鍒楄妭鐩?蹇箰av") + sensitiveList = append(sensitiveList, "蹇▊av") + sensitiveList = append(sensitiveList, "蹇界劧1鍛?鎬?鎬濈缇?鎬т箣绔?鎬т箰") + sensitiveList = append(sensitiveList, "鎬т氦") + sensitiveList = append(sensitiveList, "鎬т氦鍚炵簿") + sensitiveList = append(sensitiveList, "鎬т氦鍥?鎬т氦鍦?鎬т氦鏃犵爜") + sensitiveList = append(sensitiveList, "鎬т氦鏄?鎬т氦鐒$⒓") + sensitiveList = append(sensitiveList, "鎬т氦鑷媿") + sensitiveList = append(sensitiveList, "鎬т氦瑾?鎬т氦璇?鎬т剑") + sensitiveList = append(sensitiveList, "鎬у偔") + sensitiveList = append(sensitiveList, "鎬уゴ") + sensitiveList = append(sensitiveList, "鎬уゴ浼?鎬уゴ鏈?鎬ф伅") + sensitiveList = append(sensitiveList, "鎬ф剾") + sensitiveList = append(sensitiveList, "鎬ф剾鍦栧韩") + sensitiveList = append(sensitiveList, "鎬ф剾鍦栫墖") + sensitiveList = append(sensitiveList, "鎬ф剾鎻掔┐") + sensitiveList = append(sensitiveList, "鎬ф剾鎿傝嚭") + sensitiveList = append(sensitiveList, "鎬ф劅") + sensitiveList = append(sensitiveList, "鎬ф劅涔冲") + sensitiveList = append(sensitiveList, "鎬ф劅濡栧▎") + sensitiveList = append(sensitiveList, "鎬ф劅濡栧瑘") + sensitiveList = append(sensitiveList, "鎬ф劅鑲変笣") + sensitiveList = append(sensitiveList, "鎬ф劅鑲夌挡") + sensitiveList = append(sensitiveList, "鎬ф劅瑾樻儜") + sensitiveList = append(sensitiveList, "鎬ф劅璇辨儜") + sensitiveList = append(sensitiveList, "鎬ф垬鎿傚彴") + sensitiveList = append(sensitiveList, "鎬ф埌鎿傝嚭") + sensitiveList = append(sensitiveList, "鎬ф棤鑳?鎬ф") + sensitiveList = append(sensitiveList, "鎬х劇鑳?鎬х埍") + sensitiveList = append(sensitiveList, "鎬х埍鍥惧簱") + sensitiveList = append(sensitiveList, "鎬х埍鎿傚彴") + sensitiveList = append(sensitiveList, "鎬ц檸") + sensitiveList = append(sensitiveList, "鎬ц檸鑹茬恫") + sensitiveList = append(sensitiveList, "鎬ц檸鑹茬綉") + sensitiveList = append(sensitiveList, "鎬ц檺") + sensitiveList = append(sensitiveList, "鎬ц檺寰?鎬ч娓?鎬чゥ娓?鎬ч獨鎵?鎬诲眬") + sensitiveList = append(sensitiveList, "鎬荤悊") + sensitiveList = append(sensitiveList, "鎭嬪瓡") + sensitiveList = append(sensitiveList, "鎭愭€栦富涔?鎭愭€栦富缇?鎭愭€栦唤瀛?鎭?鎭ヨ颈杓ジ") + sensitiveList = append(sensitiveList, "鎭╂牸鏂?鎮?鎯呭│") + sensitiveList = append(sensitiveList, "鎯呯嵏") + sensitiveList = append(sensitiveList, "鎯呰壊") + sensitiveList = append(sensitiveList, "鎯呰壊浜旀湀") + sensitiveList = append(sensitiveList, "鎯呰壊澶╁礀") + sensitiveList = append(sensitiveList, "鎯呰壊瀵艰埅") + sensitiveList = append(sensitiveList, "鎯呰壊灏庤埅") + sensitiveList = append(sensitiveList, "鎯呰壊鏂囧") + sensitiveList = append(sensitiveList, "鎯呰壊鏂囧") + sensitiveList = append(sensitiveList, "鎯呰壊鑹烘湳澶╃┖") + sensitiveList = append(sensitiveList, "鎯呰壊钘濊澶╃┖") + sensitiveList = append(sensitiveList, "鎯充笂浣?鎯圭伀鑷媿") + sensitiveList = append(sensitiveList, "鎯圭伀韬潗") + sensitiveList = append(sensitiveList, "鎰?鎰忓織涓嶅爡鐨勫湒楱?鎰忔帆") + sensitiveList = append(sensitiveList, "鎰涘湒鍏湌") + sensitiveList = append(sensitiveList, "鎰涘コ浜?鎰涘娣┐") + sensitiveList = append(sensitiveList, "鎰涘辜闁?鎰涙恫") + sensitiveList = append(sensitiveList, "鎰涙恫姗祦") + sensitiveList = append(sensitiveList, "鎰涙粙") + sensitiveList = append(sensitiveList, "鎰涙粙鐥?鎰涜壊cc") + sensitiveList = append(sensitiveList, "鎰?鎱板畨濡?鎱板畨濠?鎱版槬鎯?鎱剁磪") + sensitiveList = append(sensitiveList, "鎲傞鐨勮壘鎷?鎳傛枃鍗?鎳夊彫") + sensitiveList = append(sensitiveList, "鎳掑彨") + sensitiveList = append(sensitiveList, "鎳掓暀") + sensitiveList = append(sensitiveList, "鎳跺彨") + sensitiveList = append(sensitiveList, "鎳舵暀") + sensitiveList = append(sensitiveList, "鎳风壒") + sensitiveList = append(sensitiveList, "鎴堟彋") + sensitiveList = append(sensitiveList, "鎴堢憺鐖惧痉") + sensitiveList = append(sensitiveList, "鎴愪汉a鐗?鎴愪汉bt") + sensitiveList = append(sensitiveList, "鎴愪汉鍗堝鍦?鎴愪汉鍗堝鍫?鎴愪汉鍥剧墖") + sensitiveList = append(sensitiveList, "鎴愪汉鍦栫墖") + sensitiveList = append(sensitiveList, "鎴愪汉瀵艰埅") + sensitiveList = append(sensitiveList, "鎴愪汉灏庤埅") + sensitiveList = append(sensitiveList, "鎴愪汉灏忚") + sensitiveList = append(sensitiveList, "鎴愪汉灏忚") + sensitiveList = append(sensitiveList, "鎴愪汉鏂囧") + sensitiveList = append(sensitiveList, "鎴愪汉鏂囧") + sensitiveList = append(sensitiveList, "鎴愪汉鐗?鎴愪汉鐢靛奖") + sensitiveList = append(sensitiveList, "鎴愪汉鐧惧挤") + sensitiveList = append(sensitiveList, "鎴愪汉鐧惧己") + sensitiveList = append(sensitiveList, "鎴愪汉绀惧尯") + sensitiveList = append(sensitiveList, "鎴愪汉绀惧崁") + sensitiveList = append(sensitiveList, "鎴愪汉缍茬珯") + sensitiveList = append(sensitiveList, "鎴愪汉缃戠珯") + sensitiveList = append(sensitiveList, "鎴愪汉鑷媿") + sensitiveList = append(sensitiveList, "鎴愪汉璜栧") + sensitiveList = append(sensitiveList, "鎴愪汉璁哄潧") + sensitiveList = append(sensitiveList, "鎴愪汉杞欢") + sensitiveList = append(sensitiveList, "鎴戝Τ鑰佺埜") + sensitiveList = append(sensitiveList, "鎴戝氨鍘昏壊") + sensitiveList = append(sensitiveList, "鎴戝氨鑹?鎴戝共") + sensitiveList = append(sensitiveList, "鎴戞搷") + sensitiveList = append(sensitiveList, "鎴戞搷浣?鎴戞棩") + sensitiveList = append(sensitiveList, "鎴戞棩浣?鎴戣鎬т氦") + sensitiveList = append(sensitiveList, "鎴?鎴?鎴樼墝") + sensitiveList = append(sensitiveList, "鎴充綘") + sensitiveList = append(sensitiveList, "鎴抽偅鍡巄") + sensitiveList = append(sensitiveList, "鎴抽偅鍡庢瘮") + sensitiveList = append(sensitiveList, "鎴抽偅鍡庨€?鎴存櫠") + sensitiveList = append(sensitiveList, "鎴存捣闈?鎴寸浉榫?鎴寸董鏁?鎴寸孩") + sensitiveList = append(sensitiveList, "鎴寸淮鏁?鎴?鎵変箣闃?鎵変箣闄?鎵嬫帆") + sensitiveList = append(sensitiveList, "鎵庡崱缍槸鑻遍泟") + sensitiveList = append(sensitiveList, "鎵庡崱缁?鎵庡崱缁存槸鑻遍泟") + sensitiveList = append(sensitiveList, "鎵掑眲") + sensitiveList = append(sensitiveList, "鎵掔┐") + sensitiveList = append(sensitiveList, "鎵撳€掑叡浜у厷") + sensitiveList = append(sensitiveList, "鎵撳€掑叡鐢㈤花") + sensitiveList = append(sensitiveList, "鎵撶偖") + sensitiveList = append(sensitiveList, "鎵撻噹鐐?鎵撻姗?鎵撻鏈?鎵惧皬濮?鎶戝埗鍓?鎶曟瘨鏉€浜?鎶犵┐") + sensitiveList = append(sensitiveList, "鎶㈠姭") + sensitiveList = append(sensitiveList, "鎶ゅ+璇辨儜") + sensitiveList = append(sensitiveList, "鎶煎ぇ") + sensitiveList = append(sensitiveList, "鎶煎皬") + sensitiveList = append(sensitiveList, "鎶戒綘涓殑") + sensitiveList = append(sensitiveList, "鎶藉ご") + sensitiveList = append(sensitiveList, "鎶芥彃") + sensitiveList = append(sensitiveList, "鎶介牠") + sensitiveList = append(sensitiveList, "鎷夊鏂彶灏斿痉") + sensitiveList = append(sensitiveList, "鎷夊鏂彶鐖惧痉") + sensitiveList = append(sensitiveList, "鎷夌櫥") + sensitiveList = append(sensitiveList, "鎷夌毊姊?鎷嶈偐绁炶嵂") + sensitiveList = append(sensitiveList, "鎷愬崠") + sensitiveList = append(sensitiveList, "鎷愯常") + sensitiveList = append(sensitiveList, "鎷斿嚭渚?鎷斿嚭鏉?鎷斿眲") + sensitiveList = append(sensitiveList, "鎷斿眲鑷媿") + sensitiveList = append(sensitiveList, "鎷涘") + sensitiveList = append(sensitiveList, "鎷涢稄") + sensitiveList = append(sensitiveList, "鎷充氦") + sensitiveList = append(sensitiveList, "鎷跨牬浠?鎷跨牬宕?鎸佷笉鍚屾斂瑕?鎸囬粸姹熷北璜栧") + sensitiveList = append(sensitiveList, "鎸夋懇妫?鎸ㄧ悆") + sensitiveList = append(sensitiveList, "鎹忎綘槎忓反") + sensitiveList = append(sensitiveList, "鎹忓紕") + sensitiveList = append(sensitiveList, "鎹㈠") + sensitiveList = append(sensitiveList, "鎹㈠澶т細") + sensitiveList = append(sensitiveList, "鎹㈠鏉備氦") + sensitiveList = append(sensitiveList, "鎺勫姛") + sensitiveList = append(sensitiveList, "鎺?鎺掓硠") + sensitiveList = append(sensitiveList, "鎺¤姳鍫?鎺ㄥコ閮?鎺ㄦ补") + sensitiveList = append(sensitiveList, "鎺?鎺扮┐") + sensitiveList = append(sensitiveList, "鎺扮┐鎵撴礊") + sensitiveList = append(sensitiveList, "鎺扮┐鐨崱涓?鎻抌") + sensitiveList = append(sensitiveList, "鎻抔m") + sensitiveList = append(sensitiveList, "鎻掍粬") + sensitiveList = append(sensitiveList, "鎻掍綘") + sensitiveList = append(sensitiveList, "鎻掍綘濡?鎻掍綘濯?鎻掍綘鐖虹埡") + sensitiveList = append(sensitiveList, "鎻掑叆鍏у皠") + sensitiveList = append(sensitiveList, "鎻掑叆鍐呭皠") + sensitiveList = append(sensitiveList, "鎻掑悗搴?鎻掑ス") + sensitiveList = append(sensitiveList, "鎻掑Τ") + sensitiveList = append(sensitiveList, "鎻掑緦搴?鎻掓垜") + sensitiveList = append(sensitiveList, "鎻掓毚") + sensitiveList = append(sensitiveList, "鎻掓瘮") + sensitiveList = append(sensitiveList, "鎻掓繁浜?鎻掔┐") + sensitiveList = append(sensitiveList, "鎻掔┐鎵嬫帆") + sensitiveList = append(sensitiveList, "鎻掔┐姝㈢棐") + sensitiveList = append(sensitiveList, "鎻掔┐姝㈢櫌") + sensitiveList = append(sensitiveList, "鎻掕繘") + sensitiveList = append(sensitiveList, "鎻掗€?鎻掗€?鎻掗偅鍡巄") + sensitiveList = append(sensitiveList, "鎻掗偅鍡庢瘮") + sensitiveList = append(sensitiveList, "鎻掗偅鍡庨€?鎻掗槾") + sensitiveList = append(sensitiveList, "鎻掗槾鑼?鎻掗櫚") + sensitiveList = append(sensitiveList, "鎻掗櫚鑾?鎻涘澶ф渻") + sensitiveList = append(sensitiveList, "鎻涘闆滀氦") + sensitiveList = append(sensitiveList, "鎻壒鏇?鎻翠氦") + sensitiveList = append(sensitiveList, "鎻翠氦濡?鎻翠氦鑷媿") + sensitiveList = append(sensitiveList, "鎻翠氦钖勭爜") + sensitiveList = append(sensitiveList, "鎻翠氦钖勭⒓") + sensitiveList = append(sensitiveList, "鎻村姪浜ゆ槗") + sensitiveList = append(sensitiveList, "鎼?鎼栭牠涓?鎼?鎼?鎼跺姭") + sensitiveList = append(sensitiveList, "鎽囧ご涓?鎽囧ご鐜?鎽╂礇瀹?鎽╅杸鏁?鎽╅棬鏁?鎽崇┐") + sensitiveList = append(sensitiveList, "鎽?鎽镐綘槎忓反") + sensitiveList = append(sensitiveList, "鎽搁槾钂?鎽搁櫚钂?鎾呰捣澶х櫧鑵?鎾掑垏灏?鎾掑翱") + sensitiveList = append(sensitiveList, "鎾?鎾?鎾╀汉") + sensitiveList = append(sensitiveList, "鎾?鎿囨皯") + sensitiveList = append(sensitiveList, "鎿婂€掑湒楱?鎿婂偡鐨勫湒楱?鎿?鎿?8") + sensitiveList = append(sensitiveList, "鎿峛") + sensitiveList = append(sensitiveList, "鎿峛鎸囧崡") + sensitiveList = append(sensitiveList, "鎿峠m") + sensitiveList = append(sensitiveList, "鎿峠y") + sensitiveList = append(sensitiveList, "鎿峹x") + sensitiveList = append(sensitiveList, "鎿嶄竷鍏?鎿嶄汉涔?鎿嶄粬") + sensitiveList = append(sensitiveList, "鎿嶄綘") + sensitiveList = append(sensitiveList, "鎿嶄綘鍏緢绁栧畻") + sensitiveList = append(sensitiveList, "鎿嶄綘濡?鎿嶄綘濡堝眲") + sensitiveList = append(sensitiveList, "鎿嶄綘濯?鎿嶄綘鐖虹埡") + sensitiveList = append(sensitiveList, "鎿嶅コ涔?鎿嶅ス") + sensitiveList = append(sensitiveList, "鎿嶅Τ") + sensitiveList = append(sensitiveList, "鎿嶅") + sensitiveList = append(sensitiveList, "鎿嶅皠") + sensitiveList = append(sensitiveList, "鎿嶅凹鐜?鎿嶅眲") + sensitiveList = append(sensitiveList, "鎿嶆垜") + sensitiveList = append(sensitiveList, "鎿嶆") + sensitiveList = append(sensitiveList, "鎿嶆瘝鐙?鎿嶆瘮") + sensitiveList = append(sensitiveList, "鎿嶇儌") + sensitiveList = append(sensitiveList, "鎿嶇垱") + sensitiveList = append(sensitiveList, "鎿嶇埥") + sensitiveList = append(sensitiveList, "鎿嶇┐") + sensitiveList = append(sensitiveList, "鎿嶇┐鍠锋按") + sensitiveList = append(sensitiveList, "鎿嶇┐鍣存按") + sensitiveList = append(sensitiveList, "鎿嶈偪") + sensitiveList = append(sensitiveList, "鎿嶈叓") + sensitiveList = append(sensitiveList, "鎿嶈泲") + sensitiveList = append(sensitiveList, "鎿嶉€?鎿嶉€兼瘺") + sensitiveList = append(sensitiveList, "鎿嶉偅鍡巄") + sensitiveList = append(sensitiveList, "鎿嶉偅鍡庢瘮") + sensitiveList = append(sensitiveList, "鎿嶉偅鍡庨€?鎿嶉稄") + sensitiveList = append(sensitiveList, "鎿嶉粦") + sensitiveList = append(sensitiveList, "鎿嶏紬锛?鎿嶏絿锝?鏀?鏀偅") + sensitiveList = append(sensitiveList, "鏀惧翱") + sensitiveList = append(sensitiveList, "鏀捐崱灏戝") + sensitiveList = append(sensitiveList, "鏀捐崱灏戝瀹鹃") + sensitiveList = append(sensitiveList, "鏀捐崱鐔熷コ") + sensitiveList = append(sensitiveList, "鏀捐暕") + sensitiveList = append(sensitiveList, "鏀捐暕灏戝│") + sensitiveList = append(sensitiveList, "鏀捐暕灏戝│璩撻え") + sensitiveList = append(sensitiveList, "鏀捐暕鐔熷コ") + sensitiveList = append(sensitiveList, "鏀垮彉") + sensitiveList = append(sensitiveList, "鏀垮簻") + sensitiveList = append(sensitiveList, "鏀挎瑠") + sensitiveList = append(sensitiveList, "鏀挎不") + sensitiveList = append(sensitiveList, "鏀挎不鍙嶅娲?鏀挎不鍙嶅皪娲?鏀挎不鐘?鏀胯畩") + sensitiveList = append(sensitiveList, "鏁欒偛閮?鏁欓闄?鏁浗绁炵ぞ") + sensitiveList = append(sensitiveList, "鏁告摎涓湅") + sensitiveList = append(sensitiveList, "鏂囧寲閮?鏂囨畩") + sensitiveList = append(sensitiveList, "鏂囩墿灞€") + sensitiveList = append(sensitiveList, "鏂уご闀板垁") + sensitiveList = append(sensitiveList, "鏂?鏂ぇ鏋?鏂毊鐖惧痉") + sensitiveList = append(sensitiveList, "鏂") + sensitiveList = append(sensitiveList, "鏂颁笁鎵?鏂板厷") + sensitiveList = append(sensitiveList, "鏂板彴婀炬柊闂?鏂板彶璁?鏂板攼浜虹數瑙嗗彴") + sensitiveList = append(sensitiveList, "鏂板攼浜虹數瑙嗗彴缃?鏂板ご澹?鏂版墜瑷撶反鐕?鏂版姤缃戠珯") + sensitiveList = append(sensitiveList, "鏂扮敓缍?鏂扮枂") + sensitiveList = append(sensitiveList, "鏂扮枂7.5浜嬩欢") + sensitiveList = append(sensitiveList, "鏂扮枂鍒嗚") + sensitiveList = append(sensitiveList, "鏂扮枂鍥?鏂扮枂鍦?鏂扮枂鐙珛") + sensitiveList = append(sensitiveList, "鏂扮枂鐛?鏂扮枂鐛ㄧ珛") + sensitiveList = append(sensitiveList, "鏂扮邯鍏?鏂扮邯鍏冨懆鍒?87鏈?鏂扮京瀹?鏂拌仦鍑虹増绺界讲") + sensitiveList = append(sensitiveList, "鏂拌仦灏侀帠") + sensitiveList = append(sensitiveList, "鏂拌仦绠″埗") + sensitiveList = append(sensitiveList, "鏂拌彲鍏ф儏") + sensitiveList = append(sensitiveList, "鏂拌彲鑸夊牨") + sensitiveList = append(sensitiveList, "鏂拌彲閫氳珫澹?鏂拌瀵熻珫澹?鏂拌獮绲?鏂伴椈绠″埗") + sensitiveList = append(sensitiveList, "鏂伴花") + sensitiveList = append(sensitiveList, "鏂瑰嫷涔?鏃呮父灞€") + sensitiveList = append(sensitiveList, "鏃呴え鑷媿") + sensitiveList = append(sensitiveList, "鏃呴鑷媿") + sensitiveList = append(sensitiveList, "鏃犱慨姝?鏃犲鍐呭皠") + sensitiveList = append(sensitiveList, "鏃犲鑷媿") + sensitiveList = append(sensitiveList, "鏃犲府鍥?鏃犲府杩?鏃犳瘺缇庡皯濂?鏃犵晫娴忚鍣?鏃犵爜") + sensitiveList = append(sensitiveList, "鏃犵爜涓濊") + sensitiveList = append(sensitiveList, "鏃犵爜浣撻獙") + sensitiveList = append(sensitiveList, "鏃犵爜鍋氱埍") + sensitiveList = append(sensitiveList, "鏃犵爜褰╁浘") + sensitiveList = append(sensitiveList, "鏃犵爜鏃犲") + sensitiveList = append(sensitiveList, "鏃犵爜娣コ") + sensitiveList = append(sensitiveList, "鏃犵爜娣极") + sensitiveList = append(sensitiveList, "鏃犵爜鐐浘") + sensitiveList = append(sensitiveList, "鏃犵爜绮鹃€?鏃犵爜闀跨墖") + sensitiveList = append(sensitiveList, "鏃犻偊鍥?鏃m") + sensitiveList = append(sensitiveList, "鏃濡?鏃ヤ粬濞?鏃ヤ綘") + sensitiveList = append(sensitiveList, "鏃ヤ綘濡?鏃ヤ綘濞?鏃ヤ綘濯?鏃ヤ綘鐖虹埡") + sensitiveList = append(sensitiveList, "鏃ュ収鐡﹂噾铻?鏃ュ眲") + sensitiveList = append(sensitiveList, "鏃ユ湰av濂充紭") + sensitiveList = append(sensitiveList, "鏃ユ湰av濂冲劒") + sensitiveList = append(sensitiveList, "鏃ユ湰ring") + sensitiveList = append(sensitiveList, "鏃ユ湰甯濆湅") + sensitiveList = append(sensitiveList, "鏃ユ湰鏈夌爜") + sensitiveList = append(sensitiveList, "鏃ユ湰鏈夌⒓") + sensitiveList = append(sensitiveList, "鏃ユ湰鐏岃偁") + sensitiveList = append(sensitiveList, "鏃ユ湰鐏岃吀") + sensitiveList = append(sensitiveList, "鏃ユ湰鐔熸瘝") + sensitiveList = append(sensitiveList, "鏃ユ湰绱犱汉") + sensitiveList = append(sensitiveList, "鏃ユ湰楱疯波") + sensitiveList = append(sensitiveList, "鏃ユ湰楠氳揣") + sensitiveList = append(sensitiveList, "鏃ユ湰楝煎瓙") + sensitiveList = append(sensitiveList, "鏃ユ湵椐?鏃ユ浣?鏃ヨ粛") + sensitiveList = append(sensitiveList, "鏃ラ€?鏃烘姤") + sensitiveList = append(sensitiveList, "鏄嗗湒") + sensitiveList = append(sensitiveList, "鏄庢収缍?鏄庢収缃?鏄庢姤") + sensitiveList = append(sensitiveList, "鏄庢姤鏈堝垔") + sensitiveList = append(sensitiveList, "鏄庢槦鏂拌仦缍?鏄庢槦娣浘") + sensitiveList = append(sensitiveList, "鏄庢槦娣湒") + sensitiveList = append(sensitiveList, "鏄庨暅鍛ㄥ垔") + sensitiveList = append(sensitiveList, "鏄庨暅鏂伴椈") + sensitiveList = append(sensitiveList, "鏄庨暅缃?鏄忚嵂") + sensitiveList = append(sensitiveList, "鏄忚糠鍦栭ò") + sensitiveList = append(sensitiveList, "鏄撲腹杌?鏄熷矝鏃ユ姤") + sensitiveList = append(sensitiveList, "鏄熷矝鏃ユ姤娑堟伅") + sensitiveList = append(sensitiveList, "鏄熷矝鐜悆缃?鏄ュ厜澶栨郴") + sensitiveList = append(sensitiveList, "鏄ュ厜澶栫€?鏄ュ鑷敱璜栧") + sensitiveList = append(sensitiveList, "鏄ヨ嵂") + sensitiveList = append(sensitiveList, "鏄ヨ棩") + sensitiveList = append(sensitiveList, "鏄浮") + sensitiveList = append(sensitiveList, "鏅備簨璜栧") + sensitiveList = append(sensitiveList, "鏅備唬璜栧") + sensitiveList = append(sensitiveList, "鏅氬勾鍛ㄦ仼渚?鏅氬勾鍛ㄦ仼鏉?鏅含") + sensitiveList = append(sensitiveList, "鏅尝") + sensitiveList = append(sensitiveList, "鏅搐") + sensitiveList = append(sensitiveList, "鏆楅粦娉曞斧") + sensitiveList = append(sensitiveList, "鏆翠贡") + sensitiveList = append(sensitiveList, "鏆翠钩") + sensitiveList = append(sensitiveList, "鏆翠簜") + sensitiveList = append(sensitiveList, "鏆村姏铏愬緟") + sensitiveList = append(sensitiveList, "鏆村姩") + sensitiveList = append(sensitiveList, "鏆村悰") + sensitiveList = append(sensitiveList, "鏆村ジ") + sensitiveList = append(sensitiveList, "鏆村共") + sensitiveList = append(sensitiveList, "鏆村构") + sensitiveList = append(sensitiveList, "鏆村緬") + sensitiveList = append(sensitiveList, "鏆存帆") + sensitiveList = append(sensitiveList, "鏆寸啽鐨勬埌澹?鏆磋簛鐨勫煄濉旈噹鐛?鏆磋簛鐨勮琛涘叺闈堥瓊") + sensitiveList = append(sensitiveList, "鏆磋簛鐨勯Μ鏉滃厠") + sensitiveList = append(sensitiveList, "鏆撮ⅷ浜¢潏") + sensitiveList = append(sensitiveList, "鏇癵m") + sensitiveList = append(sensitiveList, "鏇颁綘") + sensitiveList = append(sensitiveList, "鏇茬窔娑堥瓊") + sensitiveList = append(sensitiveList, "鏇茬嚎娑堥瓊") + sensitiveList = append(sensitiveList, "鏇寸敓鏃ユ姤") + sensitiveList = append(sensitiveList, "鏇?鏇瑰垰宸?鏇瑰墰宸?鏇规斂") + sensitiveList = append(sensitiveList, "鏇归暦闈?鏇煎痉鎷?鏇惧煿鐐?鏇惧簡绾?鏇炬叾绱?鏈堢稉") + sensitiveList = append(sensitiveList, "鏈堢粡") + sensitiveList = append(sensitiveList, "鏈堢粡涓嶈皟") + sensitiveList = append(sensitiveList, "鏈夊濂跺ぇ") + sensitiveList = append(sensitiveList, "鏈嶅姟鍣?鏈嶅姟澶╀娇") + sensitiveList = append(sensitiveList, "鏈嶅姟绠$悊") + sensitiveList = append(sensitiveList, "鏈嶅嫏澶╀娇") + sensitiveList = append(sensitiveList, "鏈嶅嫏绠$悊") + sensitiveList = append(sensitiveList, "鏈?鏈濆ぉ绌?鏈濋") + sensitiveList = append(sensitiveList, "鏈濋矞") + sensitiveList = append(sensitiveList, "鏈ㄥ瓙璜栧") + sensitiveList = append(sensitiveList, "鏈ㄨ€?鏈?鎷夌櫥") + sensitiveList = append(sensitiveList, "鏈湡鏃犵爜") + sensitiveList = append(sensitiveList, "鏈湡鐒$⒓") + sensitiveList = append(sensitiveList, "鏈媺鐧?鏈卞鍩?鏈卞楦?鏈卞痉") + sensitiveList = append(sensitiveList, "鏈辨€荤悊") + sensitiveList = append(sensitiveList, "鏈辨憾鍓?鏈辨憾鍔?鏈辩啍鍩?鏈辩嫧鍩?鏈辩珛鍊?鏈遍帞鍩?鏈遍晻鍩?鏈遍Э") + sensitiveList = append(sensitiveList, "鏈遍吵鑺?鏈?") + sensitiveList = append(sensitiveList, "鏈簓") + sensitiveList = append(sensitiveList, "鏈哄叓") + sensitiveList = append(sensitiveList, "鏈哄彮") + sensitiveList = append(sensitiveList, "鏈哄反") + sensitiveList = append(sensitiveList, "鏈烘幇") + sensitiveList = append(sensitiveList, "鏈鸿姯") + sensitiveList = append(sensitiveList, "鏈猴綑") + sensitiveList = append(sensitiveList, "鏉€浜?鏉€浜虹姱") + sensitiveList = append(sensitiveList, "鏉傜") + sensitiveList = append(sensitiveList, "鏉庡厛蹇?鏉庡厠寮?鏉庡厠寮?鏉庡叴骞?鏉庡ぇ甯?鏉庡ぇ甯?鏉庡ぇ閲?鏉庡畯蹇?鏉庡畯鏃?鏉庡皬榈?鏉庡北") + sensitiveList = append(sensitiveList, "鏉庡矚娓?鏉庡祼娓?鏉庡缓鍥?鏉庡紭鏃?鏉庡織缍?鏉庢叾瀹?鏉庢叾鑿?鏉庢椇闄?鏉庢湀鏈堥偿") + sensitiveList = append(sensitiveList, "鏉庢椽瀵?鏉庢椽蹇?鏉庢窇瀚?鏉庢簮娼?鏉庣憺鐜?鏉庣憺鐠?鏉庣櫥杓?鏉庣櫥杈?鏉庣澧?鏉庣鐗?鏉庣タ") + sensitiveList = append(sensitiveList, "鏉庣磪鐥?鏉庣附鐞?鏉庣附绲?鏉庣辜鑰?鏉庣孩蹇?鏉庤€佸斧") + sensitiveList = append(sensitiveList, "鏉庤垐骞?鏉庤槶鑿?鏉庨寗") + sensitiveList = append(sensitiveList, "鏉庨惖鏄?鏉庨搧鏄?鏉庨暦鏄?鏉庨暱鏄?鏉庨惮") + sensitiveList = append(sensitiveList, "鏉庨箯") + sensitiveList = append(sensitiveList, "鏉庨箯*") + sensitiveList = append(sensitiveList, "鏉滃喎涓?鏉滈闁€") + sensitiveList = append(sensitiveList, "鏉滈瞾闂?鏉ㄦ€濇晱") + sensitiveList = append(sensitiveList, "鏉变簽") + sensitiveList = append(sensitiveList, "鏉变簽鐥呭か") + sensitiveList = append(sensitiveList, "鏉变含鐔?鏉卞寳xx缍?鏉卞寳鐛ㄧ珛") + sensitiveList = append(sensitiveList, "鏉卞崌") + sensitiveList = append(sensitiveList, "鏉卞崡瑗垮寳璜栬珖") + sensitiveList = append(sensitiveList, "鏉卞湡鑰冲叾鏂潶") + sensitiveList = append(sensitiveList, "鏉辨柟鏅傜┖") + sensitiveList = append(sensitiveList, "鏉辨柟绱呮檪绌?鏉辨") + sensitiveList = append(sensitiveList, "鏉辨鑻辨") + sensitiveList = append(sensitiveList, "鏉辨鏁?鏉辨磱灞?鏉辩啽绌哄") + sensitiveList = append(sensitiveList, "鏉辩ぞ") + sensitiveList = append(sensitiveList, "鏉辩獊") + sensitiveList = append(sensitiveList, "鏉辩獊鏆村嫊") + sensitiveList = append(sensitiveList, "鏉辩獊鐛ㄧ珛") + sensitiveList = append(sensitiveList, "鏉辫タ鍗楀寳璜栧") + sensitiveList = append(sensitiveList, "鏉遍儴鍦颁笅姘磋矾") + sensitiveList = append(sensitiveList, "鏉遍櫌鐪嬪畧") + sensitiveList = append(sensitiveList, "鏋佸搧濂跺") + sensitiveList = append(sensitiveList, "鏋佸搧娉㈢") + sensitiveList = append(sensitiveList, "鏋佸搧娉㈤湼") + sensitiveList = append(sensitiveList, "鏋佸搧鐧借檸") + sensitiveList = append(sensitiveList, "鏋佸搧榛戜笣") + sensitiveList = append(sensitiveList, "鏋椾笟灞€") + sensitiveList = append(sensitiveList, "鏋椾匠榫?鏋椾繚鑿?鏋椾俊缇?鏋楀姜") + sensitiveList = append(sensitiveList, "鏋楁鍕?鏋楄偗") + sensitiveList = append(sensitiveList, "鏋楅噸璎?鏋楅暦鐩?鏋喅濂崇姱") + sensitiveList = append(sensitiveList, "鏋喅鐜板満") + sensitiveList = append(sensitiveList, "鏋敮寮硅嵂") + sensitiveList = append(sensitiveList, "鏌旈槾鏈?鏌旈櫚琛?鏌缓閵?鏌硿娴?鏌村鐖?鏌寸幉") + sensitiveList = append(sensitiveList, "鏍楁垬涔?鏍稿伐涓氬熀鍦?鏍稿伐妤熀鍦?鏍告鍣?鏍告經鑹?鏍告綔鑹?鏍规鑻楃磪") + sensitiveList = append(sensitiveList, "鏍规鑻楃孩") + sensitiveList = append(sensitiveList, "鏍奸浄(闂滃崱鎺掑悕绠$悊鑰?") + sensitiveList = append(sensitiveList, "鏍奸") + sensitiveList = append(sensitiveList, "鏍奸(鍩庨幃绉诲嫊)") + sensitiveList = append(sensitiveList, "妗冨洯铚滄礊") + sensitiveList = append(sensitiveList, "妗冨湌铚滄礊") + sensitiveList = append(sensitiveList, "妗?妗f灞€") + sensitiveList = append(sensitiveList, "姊呭痉闊嬪倯澶?姊呭痉闊︽澃澶?姊呮瘨") + sensitiveList = append(sensitiveList, "姊呰姳灞?姊呰姳缃?姊佃拏鍐堜簹娲叉柊闂婚€氳绀?姊佃拏鍐堝箍鎾數鍙?妫?妞?妤婂懆") + sensitiveList = append(sensitiveList, "妤婂穽") + sensitiveList = append(sensitiveList, "妤婂缓鍒?妤婃€濇晱") + sensitiveList = append(sensitiveList, "妤婃嚪瀹?妤婃湀娓?妤靛搧濂跺") + sensitiveList = append(sensitiveList, "妤靛搧娉㈢") + sensitiveList = append(sensitiveList, "妤靛搧娉㈤湼") + sensitiveList = append(sensitiveList, "妤靛搧鐧借檸") + sensitiveList = append(sensitiveList, "妤靛搧榛戠挡") + sensitiveList = append(sensitiveList, "姒?妲嶆敮褰堣棩") + sensitiveList = append(sensitiveList, "妲?姗嬩镜瑗插叺") + sensitiveList = append(sensitiveList, "姗?") + sensitiveList = append(sensitiveList, "姗焬") + sensitiveList = append(sensitiveList, "姗熷叓") + sensitiveList = append(sensitiveList, "姗熷彮") + sensitiveList = append(sensitiveList, "姗熷反") + sensitiveList = append(sensitiveList, "姗熸幇") + sensitiveList = append(sensitiveList, "姗熻姯") + sensitiveList = append(sensitiveList, "姗燂綑") + sensitiveList = append(sensitiveList, "妾?妾?娅?娆犲共") + sensitiveList = append(sensitiveList, "娆犲构") + sensitiveList = append(sensitiveList, "娆犳搷") + sensitiveList = append(sensitiveList, "娆犳棩") + sensitiveList = append(sensitiveList, "娆箰鎬т粖瀹?娆㈡濞变箰鏃剁┖") + sensitiveList = append(sensitiveList, "娆х編澶т钩") + sensitiveList = append(sensitiveList, "娆х編鏃犲") + sensitiveList = append(sensitiveList, "娆蹭粰娆叉") + sensitiveList = append(sensitiveList, "娆蹭粰娆叉氮") + sensitiveList = append(sensitiveList, "娆茬伀") + sensitiveList = append(sensitiveList, "娆茬伀鐒氳韩") + sensitiveList = append(sensitiveList, "娆芥湰绔?姝囬€?姝屽姛闋屽痉") + sensitiveList = append(sensitiveList, "姝愮編澶т钩") + sensitiveList = append(sensitiveList, "姝愮編鐒″") + sensitiveList = append(sensitiveList, "姝℃▊鎬т粖瀹?姝℃濞涙▊鏅傜┖") + sensitiveList = append(sensitiveList, "姝g京榛ㄨ珫澹?姝h缍?姝﹁棨") + sensitiveList = append(sensitiveList, "姝籫d") + sensitiveList = append(sensitiveList, "姝籫m") + sensitiveList = append(sensitiveList, "姝诲叏瀹?娈?娈?娈轰汉") + sensitiveList = append(sensitiveList, "娈轰汉鐘?娈轰綘涓€瀹?娈轰綘鍏ㄥ") + sensitiveList = append(sensitiveList, "姣€婊呭皠鎵?姣€婊呮鍏?姣€婊呴◣澹?姣?姣嶅コ鍙岄") + sensitiveList = append(sensitiveList, "姣嶅コ闆欓") + sensitiveList = append(sensitiveList, "姣嶅ジ") + sensitiveList = append(sensitiveList, "姣嶅瓙浜ゆ") + sensitiveList = append(sensitiveList, "姣嶅瓙浜ゆ") + sensitiveList = append(sensitiveList, "姣嶅瓙濂告儏") + sensitiveList = append(sensitiveList, "姣嶅瓙濮︽儏") + sensitiveList = append(sensitiveList, "姣掑搧") + sensitiveList = append(sensitiveList, "姣掗緧鑸旇叧") + sensitiveList = append(sensitiveList, "姣掗緳鑸旇剼") + sensitiveList = append(sensitiveList, "姣旀ǎ") + sensitiveList = append(sensitiveList, "姣旀瘺") + sensitiveList = append(sensitiveList, "姣泋x") + sensitiveList = append(sensitiveList, "姣涗竴椴?姣涗富甯?姣涗富搴?姣涘帟娲?姣涘帬娲?姣涙辰涓?姣涙兢鏉?姣涚墖") + sensitiveList = append(sensitiveList, "姣涜硦") + sensitiveList = append(sensitiveList, "姣涜硦鏉?姣涜醇涓?姣涢畱") + sensitiveList = append(sensitiveList, "姣涢矋") + sensitiveList = append(sensitiveList, "姣?姣?姘戜富") + sensitiveList = append(sensitiveList, "姘戜富涓浗") + sensitiveList = append(sensitiveList, "姘戜富鍏?姘戜富澧?姘戜富娼?姘戜富榛?姘戜紬鐢靛瓙鎶?姘戝浗") + sensitiveList = append(sensitiveList, "姘戝湅") + sensitiveList = append(sensitiveList, "姘戞剰璜栧") + sensitiveList = append(sensitiveList, "姘戞斂閮?姘戣伅") + sensitiveList = append(sensitiveList, "姘戣埅灞€") + sensitiveList = append(sensitiveList, "姘戣繍") + sensitiveList = append(sensitiveList, "姘戣繘鍏?姘戦€查花") + sensitiveList = append(sensitiveList, "姘戦亱") + sensitiveList = append(sensitiveList, "姘戦棿鐢靛彴") + sensitiveList = append(sensitiveList, "姘戦櫍") + sensitiveList = append(sensitiveList, "姘旇薄灞€") + sensitiveList = append(sensitiveList, "姘㈠脊") + sensitiveList = append(sensitiveList, "姘綀") + sensitiveList = append(sensitiveList, "姘村埄閮?姘村幓杞︿粦") + sensitiveList = append(sensitiveList, "姘靛幓") + sensitiveList = append(sensitiveList, "姘靛幓杌婁緰宸ュ姏") + sensitiveList = append(sensitiveList, "姘靛幓杌婁緰宸ュ姏?") + sensitiveList = append(sensitiveList, "姘?姹?姹?姹焎ore") + sensitiveList = append(sensitiveList, "姹熶富甯?姹熷叓") + sensitiveList = append(sensitiveList, "姹熷叓鐐?姹熷叓榛?姹熷墖姘?姹熸垙瀛?姹熸埐瀛?姹熸姌姘?姹熸嫨姘?姹熸牳蹇?姹熸辰姘?姹熸兢鎱?姹熸兢姘?姹熺嫭瑁?姹熺尓") + sensitiveList = append(sensitiveList, "姹熺尓濯?姹熺犊鎭?姹熺緟") + sensitiveList = append(sensitiveList, "姹熻爆濯?姹熻硦") + sensitiveList = append(sensitiveList, "姹熻硦姘?姹熻醇姘?姹熼潚") + sensitiveList = append(sensitiveList, "姹?姹笢椋?姹澅棰?姹磱") + sensitiveList = append(sensitiveList, "娌冨皵寮€瑗?娌冪埦闁嬭タ") + sensitiveList = append(sensitiveList, "娌夌潯鍦栭ò") + sensitiveList = append(sensitiveList, "娌虫畤") + sensitiveList = append(sensitiveList, "娌虫") + sensitiveList = append(sensitiveList, "娉?娉?杞?鍔?娉?杞?鍔?娉晘鍊?娉晘娣?娉晘缍?娉晘璜?娉晘杓?娉曚粦") + sensitiveList = append(sensitiveList, "娉曚緰") + sensitiveList = append(sensitiveList, "娉曞埗鍔?娉曞姛") + sensitiveList = append(sensitiveList, "娉曞崄杓崄鍔?娉曞浗鍥介檯骞挎挱鐢靛彴") + sensitiveList = append(sensitiveList, "娉曞箍涓枃缃?娉曞箍鏂伴椈缃?娉曞箍缃?娉曟劊") + sensitiveList = append(sensitiveList, "娉曟巹") + sensitiveList = append(sensitiveList, "娉曟巹鍔?娉曟藩") + sensitiveList = append(sensitiveList, "娉曠埦鍗?娉曠陡") + sensitiveList = append(sensitiveList, "娉曡タ鏂?娉曡") + sensitiveList = append(sensitiveList, "娉曡鍔?娉曡吉") + sensitiveList = append(sensitiveList, "娉曡吉鍔?娉曡吉澶ф硶") + sensitiveList = append(sensitiveList, "娉曡疆") + sensitiveList = append(sensitiveList, "娉曡疆鍔?娉曡疆澶ф硶") + sensitiveList = append(sensitiveList, "娉℃搏缍撴繜") + sensitiveList = append(sensitiveList, "娉㈤湼") + sensitiveList = append(sensitiveList, "娉板ゴ姗嬫帬濂€?娉板ゴ姗嬭琛涘叺") + sensitiveList = append(sensitiveList, "娲楄偁灏勫翱") + sensitiveList = append(sensitiveList, "娲楄剳鐝?娲楄叇") + sensitiveList = append(sensitiveList, "娲楄吀灏勫翱") + sensitiveList = append(sensitiveList, "娲涘厠鑿茬埦鐗?娲偝") + sensitiveList = append(sensitiveList, "娲叴") + sensitiveList = append(sensitiveList, "娲摬鍕?娲織") + sensitiveList = append(sensitiveList, "娲垐") + sensitiveList = append(sensitiveList, "娲诲姩绠$悊鍛?娲诲嫊") + sensitiveList = append(sensitiveList, "娲诲嫊绠$悊鍝?娲绘憳") + sensitiveList = append(sensitiveList, "娴佹皳") + sensitiveList = append(sensitiveList, "娴佹帆") + sensitiveList = append(sensitiveList, "娴佽湝姹?娴嬬粯灞€") + sensitiveList = append(sensitiveList, "娴嬭瘯") + sensitiveList = append(sensitiveList, "娴彨") + sensitiveList = append(sensitiveList, "娴コ") + sensitiveList = append(sensitiveList, "娴") + sensitiveList = append(sensitiveList, "娴│") + sensitiveList = append(sensitiveList, "娴┐") + sensitiveList = append(sensitiveList, "娴噣") + sensitiveList = append(sensitiveList, "娴村涔变鸡") + sensitiveList = append(sensitiveList, "娴村浜傚€?娴村鑷媿") + sensitiveList = append(sensitiveList, "娴村翱") + sensitiveList = append(sensitiveList, "娴锋磱灞€") + sensitiveList = append(sensitiveList, "娴锋礇鍥?娣嬬梾") + sensitiveList = append(sensitiveList, "娣樺疂") + sensitiveList = append(sensitiveList, "娣╄颈") + sensitiveList = append(sensitiveList, "娣╅嫆") + sensitiveList = append(sensitiveList, "娣?娣玝") + sensitiveList = append(sensitiveList, "娣伄鏂圭▼寮?娣笢鏂?娣笣鑽¤") + sensitiveList = append(sensitiveList, "娣功") + sensitiveList = append(sensitiveList, "习近平") +} + +func TestIsMatch(t *testing.T) { + sensitiveList := []string{"明月", "血", "毒", "金鳞岂是池中物", "岂是"} + input := "明天血" + + util := NewDFAUtil(sensitiveList) + if util.IsMatch(input) == false { + t.Errorf("Expected true, but got false") + } + + input = "血明" + if util.IsMatch(input) == false { + t.Errorf("Expected true, but got false") + } + + input = "明血" + if util.IsMatch(input) == false { + t.Errorf("Expected true, but got false") + } + + input = "金鳞岂是" + if util.IsMatch(input) == false { + t.Errorf("Expected true, but got false") + } +} + +func TestHandleWord(t *testing.T) { + sensitiveList := []string{"明月", "血", "毒", "金鳞岂是池中物", "池中物", "金鳞"} + util := NewDFAUtil(sensitiveList) + + input := "血明天血" + newInput := util.HandleWord(input, '*') + expected := "*明天*" + if newInput != expected { + t.Errorf("Expected %s, but got %s", expected, newInput) + } + + input = "明天血" + newInput = util.HandleWord(input, '*') + expected = "明天*" + if newInput != expected { + t.Errorf("Expected %s, but got %s", expected, newInput) + } + + input = "血明" + newInput = util.HandleWord(input, '*') + expected = "*明" + if newInput != expected { + t.Errorf("Expected %s, but got %s", expected, newInput) + } + + input = "明血" + newInput = util.HandleWord(input, '*') + expected = "明*" + if newInput != expected { + t.Errorf("Expected %s, but got %s", expected, newInput) + } + + input = "金鳞不是池中物" + newInput = util.HandleWord(input, '*') + expected = "**不是***" + if newInput != expected { + t.Errorf("Expected %s, but got %s", expected, newInput) + } +} + +func BenchmarkIsMatch(b *testing.B) { + input := "椰林摇曳,沙滩延绵。印度西海岸,阿拉伯海之畔。10月15日至16日,国家主席习近平出席在印度果阿举行的金砖国家领导人第八次会晤。从南非德班到巴西福塔莱萨,从俄罗斯乌法到印度果阿,这是习近平主席第4次出席金砖国家领导人会晤。今年恰逢金砖国家合作10周年。十年磨一剑。金砖国家合作正面临拓展深化的重要任务。习近平主席出席峰会,发表重要讲话,同各方深入交流,高瞻远瞩、切中肯綮,为金砖发展把脉开方。习近平" + + util := NewDFAUtil(sensitiveList) + b.ResetTimer() + for i := 0; i < b.N; i++ { + util.IsMatch(input) + } +} diff --git a/trunk/goutil/dfaUtil/trieNode.go b/trunk/goutil/dfaUtil/trieNode.go new file mode 100644 index 0000000..5151cc3 --- /dev/null +++ b/trunk/goutil/dfaUtil/trieNode.go @@ -0,0 +1,23 @@ +package dfaUtil + +const ( + INIT_TRIE_CHILDREN_NUM = 0 +) + +// trieNode data structure +// trieNode itself doesn't have any value. The value is represented on the path +type trieNode struct { + // if this node is the end of a word + isEndOfWord bool + + // the collection of children of this node + children map[rune]*trieNode +} + +// Create new trieNode +func newtrieNode() *trieNode { + return &trieNode{ + isEndOfWord: false, + children: make(map[rune]*trieNode, INIT_TRIE_CHILDREN_NUM), + } +} diff --git a/trunk/goutil/doc/Code Specification.txt b/trunk/goutil/doc/Code Specification.txt new file mode 100644 index 0000000..ad2af9e --- /dev/null +++ b/trunk/goutil/doc/Code Specification.txt @@ -0,0 +1,9 @@ +#go语言项目约定 +1、如果在函数内部出现错误,需记录该错误,然后抛出错误,直至回退到函数最外层进行处理。(对于项目初始化的函数,还需调用panic结束协程) +2、对于包内逻辑独立完整的类型,才需建立子包单独存放;对于其他情况,请在同一级包内进行处理。(注意:包与文件夹概念不同,轻易建立子包会造成数据与接口的暴露) +3、对于变量,一律以小写开头;如果该变量需要对包外提供,提供Get,Set接口进行数据访问。 +4、对于常量,如果该常量仅包内可见,前缀小写(con_);如果包外可见,前缀大写(Con_)。 +5、对于自定义类型,如果仅包内可见,小写开头;如果包外可见,大写开头。类型内部字段名默认小写开头,需要序列化的字段大写。 +6、对于通道(chan),如果仅包内可见,小写开头;如果包外可见,大写开头,此处不提供接口访问。 +7、在函数参数中出现的变量若与包内数据同名,统一以_开头加以区分。 +8、对于需要向包外提供的数据,如果不需要修改,统一返回副本,如果需要在包外修改,请返回指针。 \ No newline at end of file diff --git a/trunk/goutil/ensureSendUtil/.gitignore b/trunk/goutil/ensureSendUtil/.gitignore new file mode 100644 index 0000000..54b794f --- /dev/null +++ b/trunk/goutil/ensureSendUtil/.gitignore @@ -0,0 +1,3 @@ +DefaultLogPath/ + +/test_*/ diff --git a/trunk/goutil/ensureSendUtil/baseSender.go b/trunk/goutil/ensureSendUtil/baseSender.go new file mode 100644 index 0000000..09808dd --- /dev/null +++ b/trunk/goutil/ensureSendUtil/baseSender.go @@ -0,0 +1,54 @@ +package ensureSendUtil + +import ( + "fmt" +) + +/* +实现sender接口 +*/ + +type baseSender struct { + // 待发送的数据channel + waitingDataChan chan dataItem + + // 失败数据缓存 + cachedDataChan chan dataItem + + // 用于停止协程 + done chan struct{} +} + +func newBaseSender() *baseSender { + return &baseSender{ + waitingDataChan: make(chan dataItem, 1024), + cachedDataChan: make(chan dataItem, 1024000), + done: make(chan struct{}), + } +} + +// Sender接口 +// Send: +func (this *baseSender) Send() error { + // baseSender不实现发送 + // 由tcpSender和httpSender实现发送 + return fmt.Errorf("baseSender dose not have Send Method") +} + +// Sender接口 +// Data: 返回待发送的数据channel +func (this *baseSender) Data() <-chan dataItem { + return this.waitingDataChan +} + +// Sender接口 +// Cache:返回失败数据缓存channel +func (this *baseSender) Cache() chan dataItem { + return this.cachedDataChan +} + +// Sender接口 +// Done:返回channel用于判断是否关闭 +func (this *baseSender) Done() <-chan struct{} { + return this.done +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/baseSender.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/baseSender.go new file mode 100644 index 0000000..87c95ad --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/baseSender.go @@ -0,0 +1,54 @@ +package bytesSendUtil + +import ( + "fmt" +) + +/* +实现sender接口 +*/ + +type baseSender struct { + // 待发送的数据channel + waitingDataChan chan dataItem + + // 失败数据缓存 + cachedDataChan chan dataItem + + // 用于停止协程 + done chan struct{} +} + +func newBaseSender() *baseSender { + return &baseSender{ + waitingDataChan: make(chan dataItem, 1024), + cachedDataChan: make(chan dataItem, 1024000), + done: make(chan struct{}), + } +} + +// Sender接口 +// Send: +func (this *baseSender) Send() error { + // baseSender不实现发送 + // 由tcpSender和httpSender实现发送 + return fmt.Errorf("baseSender dose not have Send Method") +} + +// Sender接口 +// Data: 返回待发送的数据channel +func (this *baseSender) Data() <-chan dataItem { + return this.waitingDataChan +} + +// Sender接口 +// Cache:返回失败数据缓存channel +func (this *baseSender) Cache() chan dataItem { + return this.cachedDataChan +} + +// Sender接口 +// Done:返回channel用于判断是否关闭 +func (this *baseSender) Done() <-chan struct{} { + return this.done +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/dataItem.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/dataItem.go new file mode 100644 index 0000000..82ce3b9 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/dataItem.go @@ -0,0 +1,101 @@ +package bytesSendUtil + +import ( + "goutil/zlibUtil" +) + +type dataItem interface { + // 返回原始数据 + OriginData() []byte + + // 返回发送字节流 + Bytes() []byte + + // 设置发送次数 + SetCount(uint) + + // 返回发送次数 + Count() uint +} + +///////////////////////////////////////////////// +// httpDataItem + +type httpDataItem struct { + // 数据 + data []byte + + // 发送次数 + count uint +} + +func newHTTPData(_data []byte) dataItem { + return &httpDataItem{ + data: _data, + count: 0, + } +} + +// 返回原始数据 +func (this *httpDataItem) OriginData() []byte { + return this.data +} + +// 返回原始数据用于发送 +func (this *httpDataItem) Bytes() []byte { + return this.data +} + +func (this *httpDataItem) SetCount(cnt uint) { + this.count = cnt +} + +func (this *httpDataItem) Count() uint { + return this.count +} + +///////////////////////////////////////////////// +// tcpDataItem + +type tcpDataItem struct { + // 原始数据 + origin []byte + + // 压缩后数据 + data []byte + + // 重试次数 + count uint +} + +func newTCPDataItem(_data []byte) (dataItem, error) { + compressed, err := zlibUtil.Compress([]byte(_data), 5) + if err != nil { + return nil, err + } + + item := &tcpDataItem{ + origin: _data, + data: compressed, + count: 0, + } + return item, nil +} + +// 返回原始数据 +func (this *tcpDataItem) OriginData() []byte { + return this.origin +} + +// 返回压缩数据用于发送 +func (this *tcpDataItem) Bytes() []byte { + return this.data +} + +func (this *tcpDataItem) SetCount(cnt uint) { + this.count = cnt +} + +func (this *tcpDataItem) Count() uint { + return this.count +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSendUtil.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSendUtil.go new file mode 100644 index 0000000..8f2ecf4 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSendUtil.go @@ -0,0 +1,29 @@ +package bytesSendUtil + +/* +ensureSendUtil 用于推送数据 +支持TCP和HTTP两种形式,在发送失败时会缓存数据,并在一定时间间隔后重试 + +通过NewTCPSender和NewHTTPSender两个接口分别创建TCP和HTTP模式的EnsureSender + +type EnsureSender interface { + // 用于发送数据 + Write([]byte) error + + // 用于停止发送,此时会自动保存未发送数据 + Close() error +} + +// 创建一个tcp数据发送器 +// 参数: +// _dataFolder 数据存放目录 +// _address 连接地址 +func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) { + + +// 创建一个http数据发送器 +// 参数: +// _dataFolder 数据存放目录 +// _url 发送地址 +func NewHTTPSender(_dataFolder, _url string) (EnsureSender, error) { +*/ diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSender.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSender.go new file mode 100644 index 0000000..ee85c47 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/ensureSender.go @@ -0,0 +1,24 @@ +package bytesSendUtil + +type EnsureSender interface { + // use Write to send data + Write([]byte) error + + // stop sender + Close() error +} + +// resend和dataSaver通过此接口调用tcpSender与httpSender +type sender interface { + // 发送数据 + Send(dataItem) error + + // 返回待发送的数据channel + Data() <-chan dataItem + + // 返回失败数据缓存channel + Cache() chan dataItem + + // 用于判断是否关闭 + Done() <-chan struct{} +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender.go new file mode 100644 index 0000000..cab989b --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender.go @@ -0,0 +1,96 @@ +package bytesSendUtil + +import ( + "fmt" + + "goutil/webUtil" +) + +// 实现 EnsureSender和sender接口 +type httpSender struct { + // 需要实现的接口 + EnsureSender + + // 包含sender接口部分实现 + *baseSender + + // 数据目录 + dataFolder string + + // 发送地址 + url string + + // 用于sendLoop和resendLoop发送退出信号 + closeSignal chan struct{} +} + +// 创建一个http数据发送器 +// 参数: +// +// _dataFolder 数据存放目录 +// _url 发送地址 +func NewHTTPSender(_dataFolder, _url string) (EnsureSender, error) { + this := &httpSender{ + dataFolder: _dataFolder, + url: _url, + baseSender: newBaseSender(), + closeSignal: make(chan struct{}), + } + + // 新开协程发送数据 + go sendLoop(this, this.closeSignal) + + // 定时重发 + go resendLoop(this, _dataFolder, this.closeSignal) + + return this, nil +} + +// EnsureSender接口 +// Write:写入数据 +func (this *httpSender) Write(data []byte) error { + item := newHTTPData(data) + + this.waitingDataChan <- item + + return nil +} + +// EnsureSender接口 +// Close:关闭 +func (this *httpSender) Close() error { + close(this.done) + + // 等待sendLoop和resendLoop退出 + <-this.closeSignal + <-this.closeSignal + + // 保存数据 + _, e1 := saveData(this.Cache(), this.dataFolder) + _, e2 := saveData(this.Data(), this.dataFolder) + + if e2 != nil { + if e1 != nil { + return fmt.Errorf("%s %s", e1, e2) + } + return e2 + } else { + return e1 + } +} + +// sender接口 +// Send:发送数据 +func (this *httpSender) Send(item dataItem) error { + statusCode, _, err := webUtil.PostByteData2(this.url, item.Bytes(), nil, nil) + if err != nil || statusCode != 200 { + if err == nil { + err = fmt.Errorf("StatusCode is not 200") + } + + // 发送失败时发送次数+1 + item.SetCount(item.Count() + 1) + } + + return err +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender_test.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender_test.go new file mode 100644 index 0000000..8a257ea --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/httpSender_test.go @@ -0,0 +1,86 @@ +package bytesSendUtil + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "goutil/debugUtil" +) + +// 保存接收的数据用于校验 +var http_recv_msg = make([]byte, 0) + +func init() { + debugUtil.SetDebug(true) +} + +type httpHandler struct { + cnt int +} + +func (ctx *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + defer r.Body.Close() + result, _ := ioutil.ReadAll(r.Body) + + if string(result) == "http-msg-failed" { + http.NotFound(w, r) + } else { + ctx.cnt++ + // 模拟一次失败 + if ctx.cnt == 2 { + http.NotFound(w, r) + } else { + http_recv_msg = append(http_recv_msg, result...) + } + } + +} + +func Test_http(t *testing.T) { + http.Handle("/test", new(httpHandler)) + go http.ListenAndServe("127.0.0.1:9560", nil) + + httpSender, err := NewHTTPSender("./test_http", "http://127.0.0.1:9560/test") + if err != nil { + t.Error(err) + } + + time.Sleep(time.Millisecond * 50) + + // 第一次应该成功 + httpSender.Write([]byte("http-msg-1")) + + time.Sleep(time.Millisecond) + + // 发送消息,此数据会多次失败,被丢弃到giveup目录 + httpSender.Write([]byte("http-msg-failed")) + + time.Sleep(time.Second * 4) + + // 第二次应该失败 + httpSender.Write([]byte("http-msg-2")) + + time.Sleep(time.Millisecond) + + // 保存数据 + httpSender.Close() + + // 重启之后应该会重发数据 + httpSender, err = NewHTTPSender("./test_http", "http://127.0.0.1:9560/test") + if err != nil { + t.Error(err) + } + time.Sleep(time.Second * 2) + + httpSender.Close() + + if string(http_recv_msg) != "http-msg-1http-msg-2" { + t.Error("message error. got " + string(http_recv_msg)) + } else { + fmt.Println("HTTP OK") + } +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/saveData.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/saveData.go new file mode 100644 index 0000000..acb66cf --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/saveData.go @@ -0,0 +1,58 @@ +package bytesSendUtil + +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.ReadFileBytes(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.WriteFile4Byte(folder, filename, false, v.OriginData()); e != nil { + failed = append(failed, v) + log := fmt.Sprintf("ensureSendUtil.saveData: 写入错误\n目录:%s,文件:%s,错误信息为:%s, Data Len:%v", + folder, filename, err, len(v.OriginData())) + logUtil.NormalLog(log, logUtil.Error) + } + default: + return + } + } +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/send.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/send.go new file mode 100644 index 0000000..ee17417 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/send.go @@ -0,0 +1,111 @@ +package bytesSendUtil + +import ( + "fmt" + "time" + + "goutil/debugUtil" + "goutil/logUtil" +) + +// 负责发送数据的协程 +func sendLoop(s sender, closeSignal chan struct{}) { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + for { + select { + case <-s.Done(): + closeSignal <- struct{}{} + return + case v := <-s.Data(): + if err := s.Send(v); err != nil { + // 发送失败存入缓存 + s.Cache() <- v + } + } + } +} + +// 定时重发失败的数据 +func resendLoop(s sender, folder string, closeSignal chan struct{}) { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + // debug模式每秒重试1次 + var delay time.Duration + if debugUtil.IsDebug() { + delay = time.Second + } else { + delay = time.Minute * 5 + } + + // 定时重发失败数据 + for { + select { + case <-s.Done(): + closeSignal <- struct{}{} + return + case <-time.After(delay): + sendCacheData(s, folder) + loadData(s.(EnsureSender), folder) + } + } +} + +// 从sender获取失败数据重发 +func sendCacheData(s sender, folder string) { + failed := make([]dataItem, 0) + length := len(s.Cache()) + + defer func() { + // 用于记录多次失败后放弃发送的数据 + giveUpItems := make(chan dataItem, len(failed)) + + for _, v := range failed { + if v.Count() >= 3 { + // 失败次数太多的数据准备存放到磁盘中 + giveUpItems <- v + } else { + s.Cache() <- v + } + } + + giveUpLen := len(giveUpItems) + if giveUpLen > 0 { + // 将多次失败的数据保存到文件中 + if folder[len(folder)-1] == '/' { + folder = folder[:len(folder)-1] + } + saveData(giveUpItems, folder+"_giveup") + + if giveUpLen >= 5 { + log := fmt.Sprintf("ensureSendUtil: 有%d条数据多次发送失败", giveUpLen) + logUtil.NormalLog(log, logUtil.Error) + } + } + + // 输出信息 + log := fmt.Sprintf("ensureSendUtil: 重发%d条数据,失败%d条,存盘%d条\n", length, len(failed), giveUpLen) + logUtil.NormalLog(log, logUtil.Info) + }() + + for { + select { + case v := <-s.Cache(): + // 重发数据 + if e := s.Send(v); e != nil { + // 记录失败的数据 + failed = append(failed, v) + } + default: + return + } + } +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender.go new file mode 100644 index 0000000..fa17bbf --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender.go @@ -0,0 +1,208 @@ +package bytesSendUtil + +import ( + "encoding/binary" + "fmt" + "net" + "sync" + "time" + + "goutil/intAndBytesUtil" + "goutil/logUtil" +) + +var ( + errConnectEmpty = fmt.Errorf("scoket reconnecting...") + byterOrder = binary.LittleEndian +) + +// 实现 EnsureSender和sender接口 +type tcpSender struct { + // 需要实现的接口 + EnsureSender + + // 包含sender接口部分实现 + *baseSender + + // 数据目录 + dataFolder string + + // 服务器地址 + address string + + // 连接 + conn net.Conn + + // 用于重连时互斥 + mutex sync.Mutex + + // 用于sendLoop和resendLoop发送退出信号 + closeSignal chan struct{} +} + +// 创建一个tcp数据发送器 +// 参数: +// +// _dataFolder 数据存放目录 +// _address 连接地址 +func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) { + // 连接服务器 + conn, err := net.DialTimeout("tcp", _address, 5*time.Second) + if err != nil { + return nil, err + } + + this := &tcpSender{ + dataFolder: _dataFolder, + baseSender: newBaseSender(), + address: _address, + conn: conn, + closeSignal: make(chan struct{}), + } + + // 新开协程发送数据 + go sendLoop(this, this.closeSignal) + + // 定时重发 + go resendLoop(this, _dataFolder, this.closeSignal) + + // 发送心跳包 + go this.heartBeat() + + return this, nil +} + +// 每隔15秒发送心跳包 +func (this *tcpSender) heartBeat() { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + tick := time.Tick(time.Second * 15) + + for { + select { + case <-this.Done(): + return + case <-tick: + this.sendBytes([]byte{}) + } + } +} + +// EnsureSender接口 +// Write:写入数据 +func (this *tcpSender) Write(data []byte) error { + item, err := newTCPDataItem(data) + if err != nil { + return err + } + + this.waitingDataChan <- item + + return nil +} + +// EnsureSender接口 +// Close:关闭 +func (this *tcpSender) Close() error { + close(this.done) + + // 关闭socket连接 + conn := this.conn + if conn != nil { + conn.Close() + } + + // 等待sendLoop和resendLoop退出 + <-this.closeSignal + <-this.closeSignal + + // 保存数据 + _, e1 := saveData(this.Cache(), this.dataFolder) + _, e2 := saveData(this.Data(), this.dataFolder) + + if e2 != nil { + if e1 != nil { + return fmt.Errorf("%s %s", e1, e2) + } + return e2 + } else { + return e1 + } +} + +// Sender接口 +// Send:发送dataItem +func (this *tcpSender) Send(item dataItem) error { + err := this.sendBytes(item.Bytes()) + if err != nil && err != errConnectEmpty { + // 发送失败时发送次数+1 + item.SetCount(item.Count() + 1) + } + + return err +} + +// 发送字节数据 +// 发送格式:[lenght+data] +func (this *tcpSender) sendBytes(data []byte) error { + conn := this.conn + if conn == nil { + return errConnectEmpty + } + + // 将长度转化为字节数组 + header := intAndBytesUtil.Int32ToBytes(int32(len(data)), byterOrder) + + if len(data) > 0 { + data = append(header, data...) + } else { + data = header + } + + _, err := conn.Write(data) + if err != nil { + this.mutex.Lock() + // 发送失败 + // 检查失败的conn是否this.conn(避免多个线程失败后均调用reconnect) + // 是则关闭并重连 + if conn == this.conn { + this.conn.Close() + this.conn = nil + this.mutex.Unlock() + + // 重连 + go this.reconnect() + } else { + this.mutex.Unlock() + } + } + + return err +} + +// 重连服务器 +func (this *tcpSender) reconnect() error { + // lock-it + this.mutex.Lock() + defer this.mutex.Unlock() + + for { + // 检查是否已经重连 + if this.conn != nil { + return nil + } + + conn, err := net.DialTimeout("tcp", this.address, 5*time.Second) + if err != nil { + // 连接失败,5秒后重试 + <-time.After(time.Second * 5) + continue + } + + this.conn = conn + } +} diff --git a/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender_test.go b/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender_test.go new file mode 100644 index 0000000..67fd076 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/bytesSendUtil/tcpSender_test.go @@ -0,0 +1,95 @@ +package bytesSendUtil + +import ( + "fmt" + "net" + "testing" + "time" + + "goutil/debugUtil" + "goutil/zlibUtil" +) + +// 保存接收的数据用于校验 +var tcp_recv_msg = make([]byte, 0) + +func init() { + debugUtil.SetDebug(true) +} + +// 创建socket服务器,保存收到的数据 +func server(addr string) net.Listener { + listener, err := net.Listen("tcp", addr) + if err != nil { + panic(err) + } + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + return + } + + for { + buff := make([]byte, 512) + _, err := conn.Read(buff) + if err != nil { + break + } else { + decompressed, err := zlibUtil.Decompress(buff[4:]) + if err != nil { + panic(err) + } else { + tcp_recv_msg = append(tcp_recv_msg, decompressed...) + } + } + } + } + }() + + return listener +} + +func Test_tcp(t *testing.T) { + // 开启服务器 + l := server("127.0.0.1:9559") + + tcp, err := NewTCPSender("./test_tcp", "127.0.0.1:9559") + if err != nil { + t.Error(err) + } + + // 发送消息 + tcp.Write([]byte("tcp-msg-1")) + time.Sleep(time.Millisecond * 50) // 等待协程发送数据 + + // 关闭连接和服务器 + l.Close() + (tcp.(*tcpSender)).conn.Close() + + // 发送消息,此数据会失败 + tcp.Write([]byte("tcp-msg-2")) + // time.Sleep(time.Millisecond * 50) + + // 保存数据 + tcp.Close() + + // 重启,检查是否重发tcp-msg-2 + l = server("127.0.0.1:9559") + tcp, err = NewTCPSender("./test_tcp", "127.0.0.1:9559") + if err != nil { + t.Error(err) + } + + time.Sleep(time.Second * 2) + + if string(tcp_recv_msg) != "tcp-msg-1tcp-msg-2" { + t.Error("message error. got " + string(tcp_recv_msg)) + } else { + fmt.Println("TCP OK") + } + + tcp.Close() + l.Close() +} diff --git a/trunk/goutil/ensureSendUtil/dataItem.go b/trunk/goutil/ensureSendUtil/dataItem.go new file mode 100644 index 0000000..09e1119 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/dataItem.go @@ -0,0 +1,101 @@ +package ensureSendUtil + +import ( + "goutil/zlibUtil" +) + +type dataItem interface { + // 返回原始数据 + String() string + + // 返回发送字节流 + Bytes() []byte + + // 设置发送次数 + SetCount(uint) + + // 返回发送次数 + Count() uint +} + +///////////////////////////////////////////////// +// httpDataItem + +type httpDataItem struct { + // 数据 + data string + + // 发送次数 + count uint +} + +func newHTTPData(_data string) dataItem { + return &httpDataItem{ + data: _data, + count: 0, + } +} + +// 返回原始数据 +func (this *httpDataItem) String() string { + return this.data +} + +// 返回原始数据用于发送 +func (this *httpDataItem) Bytes() []byte { + return []byte(this.data) +} + +func (this *httpDataItem) SetCount(cnt uint) { + this.count = cnt +} + +func (this *httpDataItem) Count() uint { + return this.count +} + +///////////////////////////////////////////////// +// tcpDataItem + +type tcpDataItem struct { + // 原始数据 + origin string + + // 压缩后数据 + data []byte + + // 重试次数 + count uint +} + +func newTCPDataItem(_data string) (dataItem, error) { + compressed, err := zlibUtil.Compress([]byte(_data), 5) + if err != nil { + return nil, err + } + + item := &tcpDataItem{ + origin: _data, + data: compressed, + count: 0, + } + return item, nil +} + +// 返回原始数据 +func (this *tcpDataItem) String() string { + return this.origin +} + +// 返回压缩数据用于发送 +func (this *tcpDataItem) Bytes() []byte { + return this.data +} + +func (this *tcpDataItem) SetCount(cnt uint) { + this.count = cnt +} + +func (this *tcpDataItem) Count() uint { + return this.count +} diff --git a/trunk/goutil/ensureSendUtil/ensureSendUtil.go b/trunk/goutil/ensureSendUtil/ensureSendUtil.go new file mode 100644 index 0000000..b5425d1 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/ensureSendUtil.go @@ -0,0 +1,29 @@ +package ensureSendUtil + +/* +ensureSendUtil 用于推送数据 +支持TCP和HTTP两种形式,在发送失败时会缓存数据,并在一定时间间隔后重试 + +通过NewTCPSender和NewHTTPSender两个接口分别创建TCP和HTTP模式的EnsureSender + +type EnsureSender interface { + // 用于发送数据 + Write(string) error + + // 用于停止发送,此时会自动保存未发送数据 + Close() error +} + +// 创建一个tcp数据发送器 +// 参数: +// _dataFolder 数据存放目录 +// _address 连接地址 +func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) { + + +// 创建一个http数据发送器 +// 参数: +// _dataFolder 数据存放目录 +// _url 发送地址 +func NewHTTPSender(_dataFolder, _url string) (EnsureSender, error) { +*/ diff --git a/trunk/goutil/ensureSendUtil/ensureSender.go b/trunk/goutil/ensureSendUtil/ensureSender.go new file mode 100644 index 0000000..5fb3387 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/ensureSender.go @@ -0,0 +1,24 @@ +package ensureSendUtil + +type EnsureSender interface { + // use Write to send data + Write(string) error + + // stop sender + Close() error +} + +// resend和dataSaver通过此接口调用tcpSender与httpSender +type sender interface { + // 发送数据 + Send(dataItem) error + + // 返回待发送的数据channel + Data() <-chan dataItem + + // 返回失败数据缓存channel + Cache() chan dataItem + + // 用于判断是否关闭 + Done() <-chan struct{} +} diff --git a/trunk/goutil/ensureSendUtil/httpSender.go b/trunk/goutil/ensureSendUtil/httpSender.go new file mode 100644 index 0000000..6505cc5 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/httpSender.go @@ -0,0 +1,96 @@ +package ensureSendUtil + +import ( + "fmt" + + "goutil/webUtil" +) + +// 实现 EnsureSender和sender接口 +type httpSender struct { + // 需要实现的接口 + EnsureSender + + // 包含sender接口部分实现 + *baseSender + + // 数据目录 + dataFolder string + + // 发送地址 + url string + + // 用于sendLoop和resendLoop发送退出信号 + closeSignal chan struct{} +} + +// 创建一个http数据发送器 +// 参数: +// +// _dataFolder 数据存放目录 +// _url 发送地址 +func NewHTTPSender(_dataFolder, _url string) (EnsureSender, error) { + this := &httpSender{ + dataFolder: _dataFolder, + url: _url, + baseSender: newBaseSender(), + closeSignal: make(chan struct{}), + } + + // 新开协程发送数据 + go sendLoop(this, this.closeSignal) + + // 定时重发 + go resendLoop(this, _dataFolder, this.closeSignal) + + return this, nil +} + +// EnsureSender接口 +// Write:写入数据 +func (this *httpSender) Write(data string) error { + item := newHTTPData(data) + + this.waitingDataChan <- item + + return nil +} + +// EnsureSender接口 +// Close:关闭 +func (this *httpSender) Close() error { + close(this.done) + + // 等待sendLoop和resendLoop退出 + <-this.closeSignal + <-this.closeSignal + + // 保存数据 + _, e1 := saveData(this.Cache(), this.dataFolder) + _, e2 := saveData(this.Data(), this.dataFolder) + + if e2 != nil { + if e1 != nil { + return fmt.Errorf("%s %s", e1, e2) + } + return e2 + } else { + return e1 + } +} + +// sender接口 +// Send:发送数据 +func (this *httpSender) Send(item dataItem) error { + statusCode, _, err := webUtil.PostByteData2(this.url, item.Bytes(), nil, nil) + if err != nil || statusCode != 200 { + if err == nil { + err = fmt.Errorf("StatusCode is not 200") + } + + // 发送失败时发送次数+1 + item.SetCount(item.Count() + 1) + } + + return err +} diff --git a/trunk/goutil/ensureSendUtil/httpSender_test.go b/trunk/goutil/ensureSendUtil/httpSender_test.go new file mode 100644 index 0000000..a6b9172 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/httpSender_test.go @@ -0,0 +1,86 @@ +package ensureSendUtil + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "goutil/debugUtil" +) + +// 保存接收的数据用于校验 +var http_recv_msg = make([]byte, 0) + +func init() { + debugUtil.SetDebug(true) +} + +type httpHandler struct { + cnt int +} + +func (ctx *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + defer r.Body.Close() + result, _ := ioutil.ReadAll(r.Body) + + if string(result) == "http-msg-failed" { + http.NotFound(w, r) + } else { + ctx.cnt++ + // 模拟一次失败 + if ctx.cnt == 2 { + http.NotFound(w, r) + } else { + http_recv_msg = append(http_recv_msg, result...) + } + } + +} + +func Test_http(t *testing.T) { + http.Handle("/test", new(httpHandler)) + go http.ListenAndServe("127.0.0.1:9560", nil) + + httpSender, err := NewHTTPSender("./test_http", "http://127.0.0.1:9560/test") + if err != nil { + t.Error(err) + } + + time.Sleep(time.Millisecond * 50) + + // 第一次应该成功 + httpSender.Write("http-msg-1") + + time.Sleep(time.Millisecond) + + // 发送消息,此数据会多次失败,被丢弃到giveup目录 + httpSender.Write("http-msg-failed") + + time.Sleep(time.Second * 4) + + // 第二次应该失败 + httpSender.Write("http-msg-2") + + time.Sleep(time.Millisecond) + + // 保存数据 + httpSender.Close() + + // 重启之后应该会重发数据 + httpSender, err = NewHTTPSender("./test_http", "http://127.0.0.1:9560/test") + if err != nil { + t.Error(err) + } + time.Sleep(time.Second * 2) + + httpSender.Close() + + if string(http_recv_msg) != "http-msg-1http-msg-2" { + t.Error("message error. got " + string(http_recv_msg)) + } else { + fmt.Println("HTTP OK") + } +} diff --git a/trunk/goutil/ensureSendUtil/saveData.go b/trunk/goutil/ensureSendUtil/saveData.go new file mode 100644 index 0000000..d53356b --- /dev/null +++ b/trunk/goutil/ensureSendUtil/saveData.go @@ -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 + } + } +} diff --git a/trunk/goutil/ensureSendUtil/send.go b/trunk/goutil/ensureSendUtil/send.go new file mode 100644 index 0000000..3561842 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/send.go @@ -0,0 +1,111 @@ +package ensureSendUtil + +import ( + "fmt" + "time" + + "goutil/debugUtil" + "goutil/logUtil" +) + +// 负责发送数据的协程 +func sendLoop(s sender, closeSignal chan struct{}) { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + for { + select { + case <-s.Done(): + closeSignal <- struct{}{} + return + case v := <-s.Data(): + if err := s.Send(v); err != nil { + // 发送失败存入缓存 + s.Cache() <- v + } + } + } +} + +// 定时重发失败的数据 +func resendLoop(s sender, folder string, closeSignal chan struct{}) { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + // debug模式每秒重试1次 + var delay time.Duration + if debugUtil.IsDebug() { + delay = time.Second + } else { + delay = time.Minute * 5 + } + + // 定时重发失败数据 + for { + select { + case <-s.Done(): + closeSignal <- struct{}{} + return + case <-time.After(delay): + sendCacheData(s, folder) + loadData(s.(EnsureSender), folder) + } + } +} + +// 从sender获取失败数据重发 +func sendCacheData(s sender, folder string) { + failed := make([]dataItem, 0) + length := len(s.Cache()) + + defer func() { + // 用于记录多次失败后放弃发送的数据 + giveUpItems := make(chan dataItem, len(failed)) + + for _, v := range failed { + if v.Count() >= 3 { + // 失败次数太多的数据准备存放到磁盘中 + giveUpItems <- v + } else { + s.Cache() <- v + } + } + + giveUpLen := len(giveUpItems) + if giveUpLen > 0 { + // 将多次失败的数据保存到文件中 + if folder[len(folder)-1] == '/' { + folder = folder[:len(folder)-1] + } + saveData(giveUpItems, folder+"_giveup") + + if giveUpLen >= 5 { + log := fmt.Sprintf("ensureSendUtil: 有%d条数据多次发送失败", giveUpLen) + logUtil.NormalLog(log, logUtil.Error) + } + } + + // 输出信息 + log := fmt.Sprintf("ensureSendUtil: 重发%d条数据,失败%d条,存盘%d条\n", length, len(failed), giveUpLen) + logUtil.NormalLog(log, logUtil.Info) + }() + + for { + select { + case v := <-s.Cache(): + // 重发数据 + if e := s.Send(v); e != nil { + // 记录失败的数据 + failed = append(failed, v) + } + default: + return + } + } +} diff --git a/trunk/goutil/ensureSendUtil/tcpSender.go b/trunk/goutil/ensureSendUtil/tcpSender.go new file mode 100644 index 0000000..9b8a118 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/tcpSender.go @@ -0,0 +1,208 @@ +package ensureSendUtil + +import ( + "encoding/binary" + "fmt" + "net" + "sync" + "time" + + "goutil/intAndBytesUtil" + "goutil/logUtil" +) + +var ( + errConnectEmpty = fmt.Errorf("scoket reconnecting...") + byterOrder = binary.LittleEndian +) + +// 实现 EnsureSender和sender接口 +type tcpSender struct { + // 需要实现的接口 + EnsureSender + + // 包含sender接口部分实现 + *baseSender + + // 数据目录 + dataFolder string + + // 服务器地址 + address string + + // 连接 + conn net.Conn + + // 用于重连时互斥 + mutex sync.Mutex + + // 用于sendLoop和resendLoop发送退出信号 + closeSignal chan struct{} +} + +// 创建一个tcp数据发送器 +// 参数: +// +// _dataFolder 数据存放目录 +// _address 连接地址 +func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) { + // 连接服务器 + conn, err := net.DialTimeout("tcp", _address, 5*time.Second) + if err != nil { + return nil, err + } + + this := &tcpSender{ + dataFolder: _dataFolder, + baseSender: newBaseSender(), + address: _address, + conn: conn, + closeSignal: make(chan struct{}), + } + + // 新开协程发送数据 + go sendLoop(this, this.closeSignal) + + // 定时重发 + go resendLoop(this, _dataFolder, this.closeSignal) + + // 发送心跳包 + go this.heartBeat() + + return this, nil +} + +// 每隔15秒发送心跳包 +func (this *tcpSender) heartBeat() { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + } + }() + + tick := time.Tick(time.Second * 15) + + for { + select { + case <-this.Done(): + return + case <-tick: + this.sendBytes([]byte{}) + } + } +} + +// EnsureSender接口 +// Write:写入数据 +func (this *tcpSender) Write(data string) error { + item, err := newTCPDataItem(data) + if err != nil { + return err + } + + this.waitingDataChan <- item + + return nil +} + +// EnsureSender接口 +// Close:关闭 +func (this *tcpSender) Close() error { + close(this.done) + + // 关闭socket连接 + conn := this.conn + if conn != nil { + conn.Close() + } + + // 等待sendLoop和resendLoop退出 + <-this.closeSignal + <-this.closeSignal + + // 保存数据 + _, e1 := saveData(this.Cache(), this.dataFolder) + _, e2 := saveData(this.Data(), this.dataFolder) + + if e2 != nil { + if e1 != nil { + return fmt.Errorf("%s %s", e1, e2) + } + return e2 + } else { + return e1 + } +} + +// Sender接口 +// Send:发送dataItem +func (this *tcpSender) Send(item dataItem) error { + err := this.sendBytes(item.Bytes()) + if err != nil && err != errConnectEmpty { + // 发送失败时发送次数+1 + item.SetCount(item.Count() + 1) + } + + return err +} + +// 发送字节数据 +// 发送格式:[lenght+data] +func (this *tcpSender) sendBytes(data []byte) error { + conn := this.conn + if conn == nil { + return errConnectEmpty + } + + // 将长度转化为字节数组 + header := intAndBytesUtil.Int32ToBytes(int32(len(data)), byterOrder) + + if len(data) > 0 { + data = append(header, data...) + } else { + data = header + } + + _, err := conn.Write(data) + if err != nil { + this.mutex.Lock() + // 发送失败 + // 检查失败的conn是否this.conn(避免多个线程失败后均调用reconnect) + // 是则关闭并重连 + if conn == this.conn { + this.conn.Close() + this.conn = nil + this.mutex.Unlock() + + // 重连 + go this.reconnect() + } else { + this.mutex.Unlock() + } + } + + return err +} + +// 重连服务器 +func (this *tcpSender) reconnect() error { + // lock-it + this.mutex.Lock() + defer this.mutex.Unlock() + + for { + // 检查是否已经重连 + if this.conn != nil { + return nil + } + + conn, err := net.DialTimeout("tcp", this.address, 5*time.Second) + if err != nil { + // 连接失败,5秒后重试 + <-time.After(time.Second * 5) + continue + } + + this.conn = conn + } +} diff --git a/trunk/goutil/ensureSendUtil/tcpSender_test.go b/trunk/goutil/ensureSendUtil/tcpSender_test.go new file mode 100644 index 0000000..93600d2 --- /dev/null +++ b/trunk/goutil/ensureSendUtil/tcpSender_test.go @@ -0,0 +1,95 @@ +package ensureSendUtil + +import ( + "fmt" + "net" + "testing" + "time" + + "goutil/debugUtil" + "goutil/zlibUtil" +) + +// 保存接收的数据用于校验 +var tcp_recv_msg = make([]byte, 0) + +func init() { + debugUtil.SetDebug(true) +} + +// 创建socket服务器,保存收到的数据 +func server(addr string) net.Listener { + listener, err := net.Listen("tcp", addr) + if err != nil { + panic(err) + } + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + return + } + + for { + buff := make([]byte, 512) + _, err := conn.Read(buff) + if err != nil { + break + } else { + decompressed, err := zlibUtil.Decompress(buff[4:]) + if err != nil { + panic(err) + } else { + tcp_recv_msg = append(tcp_recv_msg, decompressed...) + } + } + } + } + }() + + return listener +} + +func Test_tcp(t *testing.T) { + // 开启服务器 + l := server("127.0.0.1:9559") + + tcp, err := NewTCPSender("./test_tcp", "127.0.0.1:9559") + if err != nil { + t.Error(err) + } + + // 发送消息 + tcp.Write("tcp-msg-1") + time.Sleep(time.Millisecond * 50) // 等待协程发送数据 + + // 关闭连接和服务器 + l.Close() + (tcp.(*tcpSender)).conn.Close() + + // 发送消息,此数据会失败 + tcp.Write("tcp-msg-2") + // time.Sleep(time.Millisecond * 50) + + // 保存数据 + tcp.Close() + + // 重启,检查是否重发tcp-msg-2 + l = server("127.0.0.1:9559") + tcp, err = NewTCPSender("./test_tcp", "127.0.0.1:9559") + if err != nil { + t.Error(err) + } + + time.Sleep(time.Second * 2) + + if string(tcp_recv_msg) != "tcp-msg-1tcp-msg-2" { + t.Error("message error. got " + string(tcp_recv_msg)) + } else { + fmt.Println("TCP OK") + } + + tcp.Close() + l.Close() +} diff --git a/trunk/goutil/esLogUtil/esIndexHandler.go b/trunk/goutil/esLogUtil/esIndexHandler.go new file mode 100644 index 0000000..6ff7d3a --- /dev/null +++ b/trunk/goutil/esLogUtil/esIndexHandler.go @@ -0,0 +1,113 @@ +package esLogUtil + +import ( + "bytes" + "encoding/json" + "sync" + "time" + + "github.com/elastic/go-elasticsearch/v8/esutil" + "golang.org/x/net/context" + "goutil/logUtil" +) + +var ( + indexMutex sync.Mutex + indexer esutil.BulkIndexer +) + +func timedReindex() { + go func() { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + + timedReindex() + time.Sleep(time.Second * 1) + } + }() + + currentIndexName := getIndexName() + for { + //设置休眠 + time.Sleep(time.Second * 1) + + newIndexName := getIndexName() + if currentIndexName != getIndexName() { + newIndexer(newIndexName) + currentIndexName = newIndexName + } + } + }() +} + +func newIndexer(newIndexName string) { + if indexer != nil { + closeIndex() + } + + //wait until get the lock + indexMutex.Lock() + defer indexMutex.Unlock() + + var err error + indexer, err = esutil.NewBulkIndexer(esutil.BulkIndexerConfig{ + Index: getIndexName(), // The default index name + Client: esClient, // The Elasticsearch client + FlushInterval: time.Second, // The periodic flush interval + }) + if err != nil { + logUtil.ErrorLog("[%s]: Creating the indexer err: %s", serverModuleName, err) + } +} + +func closeIndex() { + //wait until get the lock + indexMutex.Lock() + defer indexMutex.Unlock() + + if indexer == nil { + return + } + + err := indexer.Close(context.Background()) + if err != nil { + logUtil.ErrorLog("[%s]:Close err:%s", serverModuleName, err.Error()) + } + + indexer = nil +} + +// 批量保存到在线日志系统 +// 参数: +// +// 数量 +// +// 返回值: +// +// 日志列表对象 +func bulkSendHandler(logObj EsLog) { + if esClient == nil || indexer == nil { + return + } + + //try to get the lock in 10000 milliseconds,if cant obtain it,return false + indexMutex.Lock() + defer indexMutex.Unlock() + + message, err := json.Marshal(logObj) + if err != nil { + logUtil.ErrorLog("[%s]: Marshal failed. Err:%s", serverModuleName, err) + return + } + + err = indexer.Add( + context.Background(), + esutil.BulkIndexerItem{ + Action: "index", + Body: bytes.NewReader(message), + }) + if err != nil { + logUtil.ErrorLog("[%s]: Add data err:%s", serverModuleName, err.Error()) + } +} diff --git a/trunk/goutil/esLogUtil/esLogUtil.go b/trunk/goutil/esLogUtil/esLogUtil.go new file mode 100644 index 0000000..5de8b5a --- /dev/null +++ b/trunk/goutil/esLogUtil/esLogUtil.go @@ -0,0 +1,269 @@ +package esLogUtil + +import ( + "fmt" + "sync" + "time" + + "github.com/elastic/go-elasticsearch/v8" + "goutil/logUtil" + "goutil/stringUtil" +) + +var ( + serverModuleName = "esLog" + esClient *elasticsearch.Client + indexName string //Index名 + strServerGroupId string //区服 + isStop bool + logChan = make(chan EsLog, 2048) + warnCount = 2000 + closedChan = make(chan struct{}) + logPool = sync.Pool{ + New: func() interface{} { + return &EsLog{} + }, + } +) + +// 启动ES日志系统 +// 参数: +// +// esUrls:ES地址(多个地址使用,分割) +// name:IndexName +// serverGroupId:服务器组Id +// +// 返回值: +// +// 结果状态 +func Start(esUrls string, name string, serverGroupId int32) { + if stringUtil.IsEmpty(esUrls) { + return + } + + //构造Es客户端 + var err error + esClient, err = elasticsearch.NewClient(elasticsearch.Config{ + Addresses: stringUtil.Split(esUrls, []string{","}), + // Retry on 429 TooManyRequests statuses + // + RetryOnStatus: []int{502, 503, 504, 429}, + + // A simple incremental backoff function + // + RetryBackoff: func(i int) time.Duration { return time.Duration(i) * 100 * time.Millisecond }, + + // Retry up to 5 attempts + // + MaxRetries: 5, + }) + if err != nil { + panic(fmt.Sprintf("构造es对象出错,err:%s", err.Error())) + } + + indexName = name + strServerGroupId = fmt.Sprintf("%d", serverGroupId) + + //初始化ES + newIndexer(getIndexName()) + + timedReindex() + + startSendProcessor() + + guardProcessor() + return +} + +// 停止服务 +func Stop() { + //停止接受日志 + isStop = true + + if indexer == nil { + return + } + + if len(logChan) == 0 { + close(logChan) + } + + <-closedChan +} + +//#region 内部方法 + +func guardProcessor() { + go func() { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + + time.Sleep(1 * time.Second) + guardProcessor() + } + }() + + for { + time.Sleep(5 * time.Second) + + count := len(logChan) + if count < warnCount { + continue + } + + logUtil.NormalLog(fmt.Sprintf("ES日志通道中当前有%d条消息待消费。", count), logUtil.Warn) + } + }() +} + +func startSendProcessor() { + go func() { + defer func() { + if r := recover(); r != nil { + logUtil.LogUnknownError(r) + + time.Sleep(1 * time.Second) + startSendProcessor() + } + }() + + for { + select { + case logObj, ok := <-logChan: + if ok { + bulkSendHandler(logObj) // 执行刷新 + } + + if len(logChan) == 0 && isStop { + // is closed + closeIndex() + + closedChan <- struct{}{} + return + } + } + } + }() +} + +func getIndexName() string { + //获取当天日期 + return fmt.Sprintf("%s_%s", indexName, time.Now().Format("20060102")) +} + +// 写入在线日志 +// 参数: +// +// 日志信息对象 +// +// 返回值: +// +// 无 +func writeLog(logObj *EsLog) { + if isStop || indexer == nil { + return + } + + logChan <- *logObj +} + +// 组装ES日志对象 +// 参数: +// +// logType 日志类型 +// format 日志格式 +// args 参数列表 +// +// 返回值: +// +// 结果状态 +func buildLog(logType, format string, args ...interface{}) (newLogObj *EsLog) { + msg := format + if len(args) > 0 { + msg = fmt.Sprintf(format, args...) + } + + //构造新的日志对象 + newLogObj = new(logType, msg, strServerGroupId) + return +} + +//#endregion + +//#region 外部方法 + +// 日志记录 +// +// format:日志格式 +// logType:日志类型 +// args:参数列表 +// +// 返回值 +// +// 无 +func NormalLog(format string, logType logUtil.LogType, args ...interface{}) { + writeLog(buildLog(logType.String(), format, args...)) +} + +// 消息日志记录 +// +// format:日志格式 +// args:参数列表 +// +// 返回值 +// +// 无 +func InfoLog(format string, args ...interface{}) { + writeLog(buildLog("Info", format, args...)) +} + +// 警告日志记录 +// +// format:日志格式 +// args:参数列表 +// +// 返回值 +// +// 无 +func WarnLog(format string, args ...interface{}) { + writeLog(buildLog("Warn", format, args...)) +} + +// 调试日志记录 +// +// format:日志格式 +// args:参数列表 +// +// 返回值 +// +// 无 +func DebugLog(format string, args ...interface{}) { + writeLog(buildLog("Debug", format, args...)) +} + +// 错误日志记录 +// +// format:日志格式 +// args:参数列表 +// +// 返回值 +// +// 无 +func ErrorLog(format string, args ...interface{}) { + writeLog(buildLog("Error", format, args...)) +} + +// 致命错误日志记录 +// +// format:日志格式 +// args:参数列表 +// +// 返回值 +// +// 无 +func FatalLog(format string, args ...interface{}) { + writeLog(buildLog("Fatal", format, args...)) +} + +//#endregion diff --git a/trunk/goutil/esLogUtil/esLogUtil_test.go b/trunk/goutil/esLogUtil/esLogUtil_test.go new file mode 100644 index 0000000..c119522 --- /dev/null +++ b/trunk/goutil/esLogUtil/esLogUtil_test.go @@ -0,0 +1,35 @@ +package esLogUtil + +import ( + "testing" + "time" +) + +func TestWrite(t *testing.T) { + Start("http://10.1.0.86:9200", "dzg_gs_log_gmc2", 20008) + for i := 0; i < 10000; i++ { + InfoLog("ES在线日志测试") + WarnLog("ES在线日志测试") + DebugLog("ES在线日志测试") + ErrorLog("ES在线日志测试") + FatalLog("ES在线日志测试") + } + Stop() +} + +func BenchmarkWrite(b *testing.B) { + Start("http://106.52.100.147:14001", "20008_gs_log", 20008) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + InfoLog("ES在线日志测试%d", i) + WarnLog("ES在线日志测试%d", i) + DebugLog("ES在线日志测试%d", i) + ErrorLog("ES在线日志测试%d", i) + FatalLog("ES在线日志测试%d", i) + } + b.StopTimer() + + time.Sleep(30 * time.Second) + Stop() +} diff --git a/trunk/goutil/esLogUtil/model.go b/trunk/goutil/esLogUtil/model.go new file mode 100644 index 0000000..4aa5542 --- /dev/null +++ b/trunk/goutil/esLogUtil/model.go @@ -0,0 +1,39 @@ +package esLogUtil + +import ( + "time" +) + +// 日志对象 +type EsLog struct { + // 日志类型 + LogType string + + // 日志消息 + Message string + + // 程序标识 + InnerId string + + // 日志时间 + CrTime time.Time +} + +func new(logType string, msg string, innerId string) *EsLog { + logObj := logPool.Get().(*EsLog) + + logObj.CrTime = time.Now() + logObj.LogType = logType + logObj.Message = msg + logObj.InnerId = innerId + + return logObj + + // + //return EsLog{ + // LogType: logType, + // Message: msg, + // InnerId: innerId, + // CrTime: time.Now(), + //} +} diff --git a/trunk/goutil/fileUtil/bigFile.go b/trunk/goutil/fileUtil/bigFile.go new file mode 100644 index 0000000..0afc5f8 --- /dev/null +++ b/trunk/goutil/fileUtil/bigFile.go @@ -0,0 +1,200 @@ +package fileUtil + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "goutil/timeUtil" +) + +// 大文件对象,可用于连续写入内容而不关闭文件,直到达到指定的大小 +type BigFile struct { + // 文件夹名称 + path string + + // 当前文件名称 + fileName string + + // 文件名称前缀 + fileNamePrefix string + + // 当前文件大小(单位:Byte) + fileSize int + + // 最大的文件大小(单位:Byte) + maxFileSize int + + // 文件对象 + file *os.File + + // 获得新文件名称的方法 + newFileNameFunc func(string, string) string +} + +// 获取文件的完整路径 +func (this *BigFile) getFullPath() string { + return filepath.Join(this.path, this.fileName) +} + +// 初始化文件对象 +func (this *BigFile) initFile() error { + // 初始化文件名称 + this.fileName = this.newFileNameFunc(this.fileNamePrefix, this.fileName) + + // 初始化文件大小 + this.fileSize = 0 + + // 打开文件 + file, err := os.OpenFile(this.getFullPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm|os.ModeTemporary) + if err != nil { + return fmt.Errorf("打开文件%s错误,错误信息为:%s", this.getFullPath(), err) + } else { + this.file = file + } + + return nil +} + +// 返回当前文件名称 +// 返回值: +// 当前文件名称 +func (this *BigFile) FileName() string { + return this.fileName +} + +// 保存消息 +// message:消息内容 +// 返回值:错误对象 +func (this *BigFile) SaveMessage(message string) error { + if this.file == nil { + return fmt.Errorf("文件对象为空,path:%s", this.getFullPath()) + } + + // 增加文件大小 + this.fileSize += len([]byte(message)) + + // 写入消息(在结尾处增加一个换行符\n) + message = fmt.Sprintf("%s\n", message) + if _, err := this.file.WriteString(message); err != nil { + return fmt.Errorf("向文件%s写入信息错误,错误信息为:%s", this.getFullPath(), err) + } + + // 如果达到了文件的上限,则关闭文件并重新打开一个新文件 + if this.fileSize >= this.maxFileSize { + this.Close() + this.initFile() + } + + return nil +} + +// 写入字节消息 +// message:消息内容 +// 返回值:错误对象 +func (this *BigFile) WriteMessage(message []byte) error { + if this.file == nil { + return fmt.Errorf("文件对象为空,path:%s", this.getFullPath()) + } + + // 增加文件大小 + this.fileSize += len(message) + + // 写入消息 + if _, err := this.file.Write(message); err != nil { + return fmt.Errorf("向文件%s写入信息错误,错误信息为:%s", this.getFullPath(), err) + } + + // 如果达到了文件的上限,则关闭文件并重新打开一个新文件 + if this.fileSize >= this.maxFileSize { + this.Close() + this.initFile() + } + + return nil +} + +// 关闭对象 +// 返回值:无 +func (this *BigFile) Close() { + if this.file != nil { + this.file.Close() + this.file = nil + } +} + +// 创建新的大文件对象(obsolete) +// _path:文件夹路径 +// _maxFileSize:单个文件大小的最大值(单位:Byte) +// 返回值: +// 大文件对象 +// 错误对象 +func NewBigFile(_path string, _maxFileSize int) (*BigFile, error) { + return NewBigFileWithNewFileNameFunc(_path, "default", _maxFileSize, newFileName) +} + +// 创建新的大文件对象 +// _path:文件夹路径 +// _fileNamePrefix:文件名称前缀 +// _maxFileSize:单个文件大小的最大值(单位:Byte) +// 返回值: +// 大文件对象 +// 错误对象 +func NewBigFile2(_path, _fileNamePrefix string, _maxFileSize int) (*BigFile, error) { + return NewBigFileWithNewFileNameFunc(_path, _fileNamePrefix, _maxFileSize, newFileName) +} + +// 创建新的大文件对象 +// _path:文件夹路径 +// _fileNamePrefix:文件名称前缀 +// _maxFileSize:单个文件大小的最大值(单位:Byte) +// _newFileNameFunc:创建新文件名称的方法 +// 返回值: +// 大文件对象 +// 错误对象 +func NewBigFileWithNewFileNameFunc(_path, _fileNamePrefix string, _maxFileSize int, _newFileNameFunc func(string, string) string) (*BigFile, error) { + return NewBigFileWithNewFileNameFunc2(_path, _fileNamePrefix, "default", _maxFileSize, _newFileNameFunc) +} + +// 创建新的大文件对象 +// _path:文件夹路径 +// _fileNamePrefix:文件名称前缀 +// _fileName:文件名称 +// _maxFileSize:单个文件大小的最大值(单位:Byte) +// _newFileNameFunc:创建新文件名称的方法 +// 返回值: +// 大文件对象 +// 错误对象 +func NewBigFileWithNewFileNameFunc2(_path, _fileNamePrefix, _fileName string, _maxFileSize int, _newFileNameFunc func(string, string) string) (*BigFile, error) { + // 判断文件夹是否存在,如果不存在则创建 + if !IsDirExists(_path) { + os.MkdirAll(_path, os.ModePerm|os.ModeTemporary) + } + + // 初始化对象 + obj := &BigFile{ + path: _path, + fileNamePrefix: _fileNamePrefix, + fileName: _fileName, + maxFileSize: _maxFileSize, + newFileNameFunc: _newFileNameFunc, + } + + // 初始化文件对象 + if err := obj.initFile(); err != nil { + obj.Close() + return nil, err + } + + return obj, nil +} + +// 创建新的文件名称 +// prefix:前缀 +// currFileName:当前文件名称 +// 返回值: +// 新的文件名称 +func newFileName(prefix, currFileName string) string { + return fmt.Sprintf("%s_%s.data", prefix, timeUtil.Format(time.Now(), "yyyyMMddHHmmss")) +} diff --git a/trunk/goutil/fileUtil/bigFile_test.go b/trunk/goutil/fileUtil/bigFile_test.go new file mode 100644 index 0000000..806e514 --- /dev/null +++ b/trunk/goutil/fileUtil/bigFile_test.go @@ -0,0 +1,37 @@ +package fileUtil + +import ( + "fmt" + "testing" +) + +func BenchmarkSaveMessage(b *testing.B) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + bigFileObj, err := NewBigFile(path, 1024*1024*1024) + if err != nil { + b.Errorf("there should no err, but not there is:%s", err) + } + + for i := 0; i < b.N; i++ { + bigFileObj.SaveMessage(fmt.Sprintf("line %d", i)) + } +} + +func TestSaveMessage(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + bigFileObj, err := NewBigFile(path, 1024) + if err != nil { + t.Errorf("there should no err, but not there is:%s", err) + } + + for i := 0; i < 100000; i++ { + bigFileObj.SaveMessage(fmt.Sprintf("line %d", i)) + } + + fileList, err := GetFileList(path) + for _, item := range fileList { + fmt.Printf("file:%s\n", item) + } +} diff --git a/trunk/goutil/fileUtil/doc.go b/trunk/goutil/fileUtil/doc.go new file mode 100644 index 0000000..f539c3f --- /dev/null +++ b/trunk/goutil/fileUtil/doc.go @@ -0,0 +1,4 @@ +/* +文件助手类 +*/ +package fileUtil diff --git a/trunk/goutil/fileUtil/file.go b/trunk/goutil/fileUtil/file.go new file mode 100644 index 0000000..b371b52 --- /dev/null +++ b/trunk/goutil/fileUtil/file.go @@ -0,0 +1,298 @@ +package fileUtil + +import ( + "bufio" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" +) + +var ( + mutex sync.Mutex +) + +// 文件是否存在 +// 文件路径 +// 返回值: +// 是否存在 +// 错误对象 +func IsFileExists(path string) (bool, error) { + file, err := os.Stat(path) + if err == nil { + return file.IsDir() == false, nil + } else { + if os.IsNotExist(err) { + return false, nil + } + } + + return true, err +} + +// 文件夹是否存在 +// 文件夹路径 +// 返回值: +// 是否存在 +// 错误对象 +func IsDirectoryExists(path string) (bool, error) { + file, err := os.Stat(path) + if err == nil { + return file.IsDir(), nil + } else { + if os.IsNotExist(err) { + return false, nil + } + } + + return true, err +} + +// 文件夹是否存在(obsolete) +// 文件夹路径 +// 返回值: +// 是否存在 +func IsDirExists(path string) bool { + file, err := os.Stat(path) + if err != nil { + return false + } else { + return file.IsDir() + } +} + +// 获取当前路径 +// 返回值: +// 当前路径 +func GetCurrentPath() string { + file, _ := exec.LookPath(os.Args[0]) + fileAbsPath, _ := filepath.Abs(file) + + return filepath.Dir(fileAbsPath) +} + +// 获取目标文件列表(完整路径) +// path:文件夹路径 +// 返回值:文件列表(完整路径) +func GetFileList(path string) (fileList []string, err error) { + if exists, err1 := IsDirectoryExists(path); err1 != nil { + err = err1 + return + } else if !exists { + return + } + + // 遍历目录,获取所有文件列表 + err = filepath.Walk(path, func(fileName string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + // 忽略目录 + if fi.IsDir() { + return nil + } + + // 添加到列表 + fileList = append(fileList, fileName) + + return nil + }) + + return +} + +// 获取目标文件列表(完整路径) +// path:文件夹路径 +// prefix:文件前缀 +// suffix:文件后缀 +// 返回值:文件列表(完整路径) +func GetFileList2(path, prefix, suffix string) (fileList []string, err error) { + if exists, err1 := IsDirectoryExists(path); err1 != nil { + err = err1 + return + } else if !exists { + return + } + + // 遍历目录,获取所有文件列表 + err = filepath.Walk(path, func(fileName string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + // 忽略目录 + if fi.IsDir() { + return nil + } + + // 添加到列表 + baseName := filepath.Base(fileName) + if prefix != "" && strings.HasPrefix(baseName, prefix) == false { + return nil + } + + if suffix != "" && strings.HasSuffix(baseName, suffix) == false { + return nil + } + + fileList = append(fileList, fileName) + + return nil + }) + + return +} + +// 按行读取每一个文件的内容 +// fileName:文件的绝对路径 +// 返回值: +// 行内容列表 +// 错误信息 +func ReadFileLineByLine(fileName string) (lineList []string, err error) { + // 打开文件 + file, err1 := os.Open(fileName) + if err1 != nil { + err = err1 + return + } + defer file.Close() + + // 读取文件 + buf := bufio.NewReader(file) + for { + // 按行读取 + line, _, err2 := buf.ReadLine() + if err2 == io.EOF { + break + } + + //将byte[]转换为string,并添加到列表中 + lineList = append(lineList, string(line)) + } + + return +} + +// 读取文件内容(字符串) +// fileName:文件的绝对路径 +// 返回值: +// 文件内容 +// 错误信息 +func ReadFileContent(fileName string) (content string, err error) { + bytes, err1 := ioutil.ReadFile(fileName) + if err1 != nil { + err = err1 + return + } + + content = string(bytes) + return +} + +// 读取文件内容(字符数组) +// fileName:文件的绝对路径 +// 返回值: +// 文件内容 +// 错误信息 +func ReadFileBytes(fileName string) (content []byte, err error) { + content, err = ioutil.ReadFile(fileName) + return +} + +// 写入文件 +// filePath:文件夹路径 +// fileName:文件名称 +// ifAppend:是否追加内容 +// args:可变参数 +// 返回值: +// error:错误信息 +func WriteFile(filePath, fileName string, ifAppend bool, args ...string) error { + // 得到最终的fileName + fileName = filepath.Join(filePath, fileName) + + // 判断文件夹是否存在,如果不存在则创建 + mutex.Lock() + if !IsDirExists(filePath) { + os.MkdirAll(filePath, os.ModePerm|os.ModeTemporary) + } + mutex.Unlock() + + // 打开文件(如果文件存在就以写模式打开,并追加写入;如果文件不存在就创建,然后以写模式打开。) + var f *os.File + var err error + if ifAppend == false { + f, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm|os.ModeTemporary) + } else { + f, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm|os.ModeTemporary) + } + + if err != nil { + return err + } + defer f.Close() + + // 写入内容 + for _, arg := range args { + _, err = f.WriteString(arg) + if err != nil { + return err + } + } + + return nil +} + +// 写入文件 +// filePath:文件夹路径 +// fileName:文件名称 +// ifAppend:是否追加内容 +// args:可变参数 +// 返回值: +// error:错误信息 +func WriteFile4Byte(filePath, fileName string, ifAppend bool, args ...[]byte) error { + // 得到最终的fileName + fileName = filepath.Join(filePath, fileName) + + // 判断文件夹是否存在,如果不存在则创建 + mutex.Lock() + if !IsDirExists(filePath) { + os.MkdirAll(filePath, os.ModePerm|os.ModeTemporary) + } + mutex.Unlock() + + // 打开文件(如果文件存在就以写模式打开,并追加写入;如果文件不存在就创建,然后以写模式打开。) + var f *os.File + var err error + if ifAppend == false { + f, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm|os.ModeTemporary) + } else { + f, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm|os.ModeTemporary) + } + + if err != nil { + return err + } + defer f.Close() + + // 写入内容 + for _, arg := range args { + _, err = f.Write(arg) + if err != nil { + return err + } + } + + return nil +} + +// 删除文件 +// fileName:文件的绝对路径 +// 返回值: +// 错误对象 +func DeleteFile(fileName string) error { + return os.Remove(fileName) +} diff --git a/trunk/goutil/fileUtil/file_test.go b/trunk/goutil/fileUtil/file_test.go new file mode 100644 index 0000000..97f0d81 --- /dev/null +++ b/trunk/goutil/fileUtil/file_test.go @@ -0,0 +1,345 @@ +package fileUtil + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "testing" + "time" +) + +func BenchmarkWriteFile(b *testing.B) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + for i := 0; i < b.N; i++ { + WriteFile(path, "test.txt", true, fmt.Sprintf("line %d", i)) + } +} + +func TestIsFileExists(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + fmt.Printf("FileName:%s\n", fileName) + if exists, err := IsFileExists(fileName); err != nil || exists { + t.Errorf("the file %s should not be exists, but now it's exists", fileName) + } + + if err := WriteFile(path, "test.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + if exists, err := IsFileExists(fileName); err != nil || !exists { + t.Errorf("the file %s should be exists, but now it's not exists", fileName) + } + + if content, err := ReadFileContent(fileName); err != nil { + t.Errorf("there should be no error, but now err:%s", err) + } else { + fmt.Printf("Content:%s\n", content) + } + + DeleteFile(fileName) +} + +func TestIsDirectoryExists(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + filePath := filepath.Join(path, "Parent") + if exists, err := IsDirectoryExists(filePath); err != nil || exists { + t.Errorf("the file %s should not be exists, but now it's exists", filePath) + } + + fileName := fmt.Sprintf("%s/%s", filePath, "test.txt") + + if err := WriteFile(filePath, "test.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + if exists, err := IsDirectoryExists(filePath); err != nil || !exists { + t.Errorf("the file %s should be exists, but now it's not exists", filePath) + } + + if content, err := ReadFileContent(fileName); err != nil { + t.Errorf("there should be no error, but now err:%s", err) + } else { + fmt.Printf("Content:%s\n", content) + } + + DeleteFile(fileName) +} + +func TestIsDirExists(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + filePath := filepath.Join(path, "Parent2") + if IsDirExists(filePath) { + t.Errorf("the file %s should not be exists, but now it's exists", filePath) + } + + fileName := fmt.Sprintf("%s/%s", filePath, "test.txt") + if err := WriteFile(filePath, "test.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + if IsDirExists(filePath) == false { + t.Errorf("the file %s should be exists, but now it's not exists", filePath) + } + + if content, err := ReadFileContent(fmt.Sprintf("%s/%s", filePath, "test.txt")); err != nil { + t.Errorf("there should be no error, but now err:%s", err) + } else { + fmt.Printf("Content:%s\n", content) + } + + DeleteFile(fileName) +} + +func TestGetFileList(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName1 := "2017-09-12-12.txt" + fileName2 := "2017-09-12-13.txt" + fileName3 := "2017-09-12-14.txt" + fileName4 := "2017-09-12.tar.bz2" + + seperator := "\\" + if runtime.GOOS != "windows" { + seperator = "/" + } + + filePath1 := fmt.Sprintf("%s%s%s", path, seperator, fileName1) + filePath2 := fmt.Sprintf("%s%s%s", path, seperator, fileName2) + filePath3 := fmt.Sprintf("%s%s%s", path, seperator, fileName3) + filePath4 := fmt.Sprintf("%s%s%s", path, seperator, fileName4) + + if err := WriteFile(path, fileName1, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName2, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName3, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName4, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + fileList, err := GetFileList(path) + if err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if fileList[0] != filePath1 { + t.Errorf("Expected:%s, now got:%s", filePath1, fileList[0]) + } + if fileList[1] != filePath2 { + t.Errorf("Expected:%s, now got:%s", filePath2, fileList[1]) + } + if fileList[2] != filePath3 { + t.Errorf("Expected:%s, now got:%s", filePath3, fileList[2]) + } + if fileList[3] != filePath4 { + t.Errorf("Expected:%s, now got:%s", filePath4, fileList[3]) + } + + DeleteFile(filePath1) + DeleteFile(filePath2) + DeleteFile(filePath3) + DeleteFile(filePath4) +} + +func TestGetFileList2(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName1 := "2017-09-12-12.txt" + fileName2 := "2017-09-12-13.txt" + fileName3 := "2017-09-12-14.txt" + fileName4 := "2017-09-12.tar.bz2" + + seperator := "\\" + if runtime.GOOS != "windows" { + seperator = "/" + } + + filePath1 := fmt.Sprintf("%s%s%s", path, seperator, fileName1) + filePath2 := fmt.Sprintf("%s%s%s", path, seperator, fileName2) + filePath3 := fmt.Sprintf("%s%s%s", path, seperator, fileName3) + filePath4 := fmt.Sprintf("%s%s%s", path, seperator, fileName4) + + if err := WriteFile(path, fileName1, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName2, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName3, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, fileName4, true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + fileList, err := GetFileList2(path, "2017-09-12", "txt") + if err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + fmt.Printf("fileList:%v\n", fileList) + if fileList[0] != filePath1 { + t.Errorf("Expected:%s, now got:%s", filePath1, fileList[0]) + } + if fileList[1] != filePath2 { + t.Errorf("Expected:%s, now got:%s", filePath2, fileList[1]) + } + if fileList[2] != filePath3 { + t.Errorf("Expected:%s, now got:%s", filePath3, fileList[2]) + } + + fileList2, err := GetFileList2(path, "2017-09-12", "tar.bz2") + if err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + fmt.Printf("fileList2:%v\n", fileList2) + if fileList2[0] != filePath4 { + t.Errorf("Expected:%s, now got:%s", filePath4, fileList2[0]) + } + + DeleteFile(filePath1) + DeleteFile(filePath2) + DeleteFile(filePath3) + DeleteFile(filePath4) +} + +func TestReadFileLineByLine(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + if err := WriteFile(path, "test.txt", true, "first line\n"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, "test.txt", true, "second line\n"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + expectedFirstLine := "first line" + expectedSecondLine := "second line" + lineList, err := ReadFileLineByLine(fileName) + if err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if lineList[0] != expectedFirstLine { + t.Errorf("Expected:%s, but now got:%s", expectedFirstLine, lineList[0]) + } + if lineList[1] != expectedSecondLine { + t.Errorf("Expected:%s, but now got:%s", expectedSecondLine, lineList[1]) + } + + if err := DeleteFile(fileName); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } +} + +func TestReadFileContent(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + if err := WriteFile(path, "test.txt", true, "first line\n"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, "test.txt", true, "second line\n"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + expectedContent := "first line\nsecond line\n" + if content, err := ReadFileContent(fileName); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } else if content != expectedContent { + t.Errorf("Expected:%s, but now got:%s", expectedContent, content) + } + + if err := DeleteFile(fileName); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } +} + +func TestDeleteFile(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + if err := WriteFile(path, "test.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + if err := DeleteFile(fileName); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } +} + +func TestReadWriteSimultaneously(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + + file1, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm|os.ModeTemporary) + if err != nil { + t.Errorf("1:there should be no err, but now err:%s", err) + } + + // for i := 0; i < 10; i++ { + // file1.WriteString(fmt.Sprintf("line %d\n", i)) + // } + + go func() { + for i := 0; i < 10; i++ { + file1.WriteString(fmt.Sprintf("line %d\n", i)) + time.Sleep(time.Second) + } + }() + + file2, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm|os.ModeTemporary) + if err != nil { + t.Errorf("2:there should be no err, but now err:%s", err) + } + + go func() { + offset := 0 + + // 读取文件 + buf := bufio.NewReader(file2) + + for { + // 按行读取 + line, _, err2 := buf.ReadLine() + if err2 == io.EOF { + time.Sleep(500 * time.Millisecond) + continue + } + + if len(line) == 0 { + continue + } + + //将byte[]转换为string,并添加到列表中 + fmt.Printf("line %d:%s\n", offset, string(line)) + + offset += 1 + if offset >= 10 { + break + } + } + }() + + time.Sleep(30 * time.Second) + + fmt.Println("end") +} diff --git a/trunk/goutil/fileUtil/gzip.go b/trunk/goutil/fileUtil/gzip.go new file mode 100644 index 0000000..3cb94b4 --- /dev/null +++ b/trunk/goutil/fileUtil/gzip.go @@ -0,0 +1,78 @@ +package fileUtil + +import ( + "compress/gzip" + "fmt" + "io" + "os" + "path/filepath" +) + +// 对文件进行gzip压缩 +// source:源文件完整路径 +// target:目标文件文件夹(如果传空字符串,则为当前文件夹) +// 返回值 +// 错误对象 +func Gzip(source, target string) error { + reader, err := os.Open(source) + if err != nil { + return err + } + defer reader.Close() + + // 给目标文件夹赋值,如果传空,则默认为当前文件夹 + if target == "" { + target = filepath.Dir(source) + } + fileName := filepath.Base(source) + + targetFilePath := filepath.Join(target, fmt.Sprintf("%s.gz", fileName)) + writer, err := os.Create(targetFilePath) + if err != nil { + return err + } + defer writer.Close() + + archiver := gzip.NewWriter(writer) + archiver.Name = fileName + defer archiver.Close() + + _, err = io.Copy(archiver, reader) + + return err +} + +// 对文件进行gzip解压缩 +// source:源文件完整路径 +// target:目标文件文件夹(解压缩文件的名字是内部自动赋值) +// 返回值 +// 错误对象 +func UnGzip(source, target string) error { + reader, err := os.Open(source) + if err != nil { + return err + } + defer reader.Close() + + archive, err := gzip.NewReader(reader) + if err != nil { + return err + } + defer archive.Close() + + // 给目标文件夹赋值,如果传空,则默认为当前文件夹 + if target == "" { + target = filepath.Dir(source) + } + + targetFilePath := filepath.Join(target, archive.Name) + writer, err := os.Create(targetFilePath) + if err != nil { + return err + } + defer writer.Close() + + _, err = io.Copy(writer, archive) + + return err +} diff --git a/trunk/goutil/fileUtil/gzip_test.go b/trunk/goutil/fileUtil/gzip_test.go new file mode 100644 index 0000000..5b6a313 --- /dev/null +++ b/trunk/goutil/fileUtil/gzip_test.go @@ -0,0 +1,54 @@ +package fileUtil + +import ( + "fmt" + "testing" +) + +func TestGzip(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt") + if err := WriteFile(path, "test.txt", true, "first line\nHello world"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + if err := Gzip(fileName, ""); err != nil { + // if err := Gzip(fileName, path); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } + + if fileList, err := GetFileList(path); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } else { + for _, item := range fileList { + fmt.Printf("item:%s\n", item) + } + } + + DeleteFile(fileName) +} + +func TestUnGzip(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName := fmt.Sprintf("%s/%s", path, "test.txt.gz") + if err := UnGzip(fileName, ""); err != nil { + // if err := UnGzip(fileName, path); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } + + content, err := ReadFileContent(fmt.Sprintf("%s/%s", path, "test.txt")) + if err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } else { + fmt.Printf("content:%s\n", content) + } + + DeleteFile(fileName) + + fileName = fmt.Sprintf("%s/%s", path, "test.txt") + DeleteFile(fileName) +} diff --git a/trunk/goutil/fileUtil/netFile.go b/trunk/goutil/fileUtil/netFile.go new file mode 100644 index 0000000..270ad06 --- /dev/null +++ b/trunk/goutil/fileUtil/netFile.go @@ -0,0 +1,55 @@ +package fileUtil + +import ( + "io" + "net/http" + "os" + "path" +) + +// 下载网络文件 +// netUrl:网络文件地址 +// saveDir:存储位置 +// saveFileName:存储的文件名 +// ifTruncate:如果文件存在了,是否覆盖此文件 +// 返回值: +// err:错误对象 +func DownLoadNetFile(netUrl string, saveDir string, saveFileName string, ifTruncate bool) (err error) { + resp, err := http.Get(netUrl) + defer func() { + if resp != nil { + resp.Body.Close() + } + }() + if err != nil { + return + } + + // 创建文件夹 + if IsDirExists(saveDir) == false { + os.MkdirAll(saveDir, os.ModePerm|os.ModeTemporary) + } + + // 创建文件 + filePath := path.Join(saveDir, saveFileName) + var fileObj *os.File + if ifTruncate { + fileObj, err = os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm|os.ModeTemporary) + } else { + // 如果文件已经存在,则不能打开 + fileObj, err = os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, os.ModePerm|os.ModeTemporary) + } + defer func() { + if fileObj != nil { + fileObj.Close() + } + }() + if err != nil { + return + } + + // 写入文件数据 + _, err = io.Copy(fileObj, resp.Body) + + return +} diff --git a/trunk/goutil/fileUtil/netFile_test.go b/trunk/goutil/fileUtil/netFile_test.go new file mode 100644 index 0000000..1bfd4da --- /dev/null +++ b/trunk/goutil/fileUtil/netFile_test.go @@ -0,0 +1,13 @@ +package fileUtil + +import "testing" + +func TestDownLoadNetFile(t *testing.T) { + err := DownLoadNetFile("https://www.baidu.com/img/bd_logo1.png", "./", "baidu.png", false) + if err != nil { + t.Error(err) + return + } + + t.Log("成功了") +} diff --git a/trunk/goutil/fileUtil/tar.go b/trunk/goutil/fileUtil/tar.go new file mode 100644 index 0000000..e707517 --- /dev/null +++ b/trunk/goutil/fileUtil/tar.go @@ -0,0 +1,104 @@ +package fileUtil + +import ( + "archive/tar" + "io" + "os" + "path/filepath" +) + +// 对一组文件进行tar打包 +// sourceList:源文件完整路径列表 +// target:目标文件名称 +// 返回值 +// 错误对象 +func Tar(sourceList []string, target string) error { + tarFile, err := os.Create(target) + if err != nil { + return err + } + defer tarFile.Close() + + tarball := tar.NewWriter(tarFile) + defer tarball.Close() + + // 对源文件遍历处理 + for _, item := range sourceList { + info, err := os.Stat(item) + if err != nil || info.IsDir() { + continue + } + + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return err + } + + header.Name = filepath.Base(item) + + if err := tarball.WriteHeader(header); err != nil { + return err + } + + file, err := os.Open(item) + if err != nil { + return err + } + defer file.Close() + + if _, err = io.Copy(tarball, file); err != nil { + return err + } + } + + return nil +} + +// 对一组文件进行tar解包 +// source:源文件完整路径 +// target:目标文件名称 +// 返回值 +// 错误对象 +func Untar(source, target string) error { + reader, err := os.Open(source) + if err != nil { + return err + } + defer reader.Close() + tarReader := tar.NewReader(reader) + + // 给目标文件夹赋值,如果传空,则默认为当前文件夹 + if target == "" { + target = filepath.Dir(source) + } + + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } else if err != nil { + return err + } + + targetFilePath := filepath.Join(target, header.Name) + info := header.FileInfo() + if info.IsDir() { + if err = os.MkdirAll(targetFilePath, info.Mode()); err != nil { + return err + } + continue + } + + file, err := os.OpenFile(targetFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(file, tarReader) + if err != nil { + return err + } + } + + return nil +} diff --git a/trunk/goutil/fileUtil/tar_test.go b/trunk/goutil/fileUtil/tar_test.go new file mode 100644 index 0000000..aa66436 --- /dev/null +++ b/trunk/goutil/fileUtil/tar_test.go @@ -0,0 +1,73 @@ +package fileUtil + +import ( + "fmt" + "strings" + "testing" +) + +func TestTar(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + fileName1 := fmt.Sprintf("%s/%s", path, "test1.txt") + fileName2 := fmt.Sprintf("%s/%s", path, "test2.txt") + + if err := WriteFile(path, "test1.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + if err := WriteFile(path, "test2.txt", true, "first line"); err != nil { + t.Errorf("there should be no error, but now it is:%s", err) + } + + sourceList := make([]string, 0, 2) + sourceList = append(sourceList, fileName1) + sourceList = append(sourceList, fileName2) + target := fmt.Sprintf("%s/%s", path, "test.tar") + if err := Tar(sourceList, target); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } + + if fileList, err := GetFileList(path); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } else { + for _, item := range fileList { + fmt.Printf("item:%s\n", item) + } + } + + DeleteFile(fileName1) + DeleteFile(fileName2) +} + +func TestUntar(t *testing.T) { + path := GetCurrentPath() + fmt.Printf("CurrPath:%s\n", path) + + source := fmt.Sprintf("%s/%s", path, "test.tar") + // target := path + target := "" + if err := Untar(source, target); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } + + if fileList, err := GetFileList(path); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } else { + for _, item := range fileList { + fmt.Printf("item:%s\n", item) + + if strings.HasSuffix(item, "txt") { + if content, err := ReadFileContent(item); err != nil { + t.Errorf("There should be no error, but now it has:%s", err) + } else { + fmt.Printf("content:%s\n", content) + } + + DeleteFile(item) + } + } + + DeleteFile(source) + } +} diff --git a/trunk/goutil/go.mod b/trunk/goutil/go.mod new file mode 100644 index 0000000..881f141 --- /dev/null +++ b/trunk/goutil/go.mod @@ -0,0 +1,25 @@ +module goutil + +go 1.22.10 + +require ( + github.com/bkaradzic/go-lz4 v1.0.0 + github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4 + github.com/fatih/color v1.15.0 + github.com/go-sql-driver/mysql v1.5.0 + github.com/gomodule/redigo v1.8.9 + github.com/gorilla/websocket v1.4.2 + golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 + google.golang.org/grpc v1.45.0 + google.golang.org/protobuf v1.26.0 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c +) + +require ( + github.com/golang/protobuf v1.5.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect +) diff --git a/trunk/goutil/go.sum b/trunk/goutil/go.sum new file mode 100644 index 0000000..a633c1a --- /dev/null +++ b/trunk/goutil/go.sum @@ -0,0 +1,147 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= +github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4 h1:OoL469zqSNrTLSz5zeVF/I6VOO7fiw2bzSzQe4J557c= +github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/trunk/goutil/grpc-util/.gitignore b/trunk/goutil/grpc-util/.gitignore new file mode 100644 index 0000000..1a1566e --- /dev/null +++ b/trunk/goutil/grpc-util/.gitignore @@ -0,0 +1,2 @@ +Log/* +logs/* \ No newline at end of file diff --git a/trunk/goutil/grpc-util/client/client.go b/trunk/goutil/grpc-util/client/client.go new file mode 100644 index 0000000..ed64581 --- /dev/null +++ b/trunk/goutil/grpc-util/client/client.go @@ -0,0 +1,63 @@ +package client + +import ( + "context" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +var ( + // 缓存连接 key:rpc地址 value:连接指针对象 + rpcConMap sync.Map + + // 连接超时时间 + defaultTimeOut = time.Second * 1 +) + +// GetClientConn +// @description: 获取grpc连接对象 +// parameter: +// @rpcAddres:连接地址 +// return: +// @*grpc.ClientConn:连接对象 +// @error:错误对象 +func GetClientConn(rpcAddres string) (*grpc.ClientConn, error) { + // 从缓存获取 + if con1, isok := rpcConMap.Load(rpcAddres); isok { + return con1.(*grpc.ClientConn), nil + } + + // 构造新对象 + //ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*1)) + var ctx, _ = context.WithTimeout(context.Background(), defaultTimeOut) + con, err := grpc.DialContext(ctx, rpcAddres, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + + // 加入缓存 + rpcConMap.Store(rpcAddres, con) + + return con, nil +} + +// ClearClientConn +// @description: 根据连接地址,删除缓存的连接对象 +// parameter: +// @rpcAddres:连接地址 +// return: +func ClearClientConn(rpcAddres string) { + rpcConMap.Delete(rpcAddres) +} + +// SetDefaultTimeOut +// @description: 设置连接的默认超时 +// parameter: +// @outTime:超时时间 +// return: +func SetDefaultTimeOut(outTime time.Duration) { + defaultTimeOut = outTime +} diff --git a/trunk/goutil/grpc-util/client/doc.go b/trunk/goutil/grpc-util/client/doc.go new file mode 100644 index 0000000..84fe65e --- /dev/null +++ b/trunk/goutil/grpc-util/client/doc.go @@ -0,0 +1,8 @@ +// ************************************ +// @package: client +// @description: grpc客户端辅助包 +// @author: byron +// @revision history: +// @create date: 2022-01-19 16:50:24 +// ************************************ +package client diff --git a/trunk/goutil/grpc-util/client/readme.md b/trunk/goutil/grpc-util/client/readme.md new file mode 100644 index 0000000..cceb881 --- /dev/null +++ b/trunk/goutil/grpc-util/client/readme.md @@ -0,0 +1,29 @@ +client简单封装了grpc相关调用方法,缓存了connection连接 +整体文档入口请参考:[传送门](../readme.md) + +## 使用方式如下: + +### 1.导入包 +```go +import ( + "vast.com/goutil/grpc-util/client" +) +``` + +### 2.获取连接 +```go +con, err := client.GetClientConn(host) +``` + +### 3.清理某个连接 +```go +client.ClearClientConn("ip:port") +``` + +### 4.设置连接的默认超时时间 +```go +client.SetDefaultTimeOut(time.Second * 3) +``` +注意: +1.设置后对后续创建的连接生效,已经创建的不生效 +2.默认的超时 时间为1s \ No newline at end of file diff --git a/trunk/goutil/grpc-util/readme.md b/trunk/goutil/grpc-util/readme.md new file mode 100644 index 0000000..116144c --- /dev/null +++ b/trunk/goutil/grpc-util/readme.md @@ -0,0 +1,20 @@ +grpc-util做了如下工作: + +### server(废弃): + +1.实现服务的启动和相关方法的注册 +2.提供了公共对象和接口的实现 +3.提供公共req对象到res对象build辅助方法 +具体参考server文档 [传送门](server/readme.md) + +### client: + +1.缓存了grpc连接对象,使其可复用(可单独使用) +2.基于提供的公共对象和接口,提供了grpc的4种基本用法的调用(建议需要搭配server使用) +具体参考client文档 [传送门](client/readme.md) + +### util: + +1.提供pb相关辅助方法 +具体请直接看代码 + diff --git a/trunk/goutil/grpc-util/util/pbUtil.go b/trunk/goutil/grpc-util/util/pbUtil.go new file mode 100644 index 0000000..7de2f6d --- /dev/null +++ b/trunk/goutil/grpc-util/util/pbUtil.go @@ -0,0 +1,135 @@ +package util + +import ( + "context" + "fmt" + "net" + "strings" + + "google.golang.org/grpc/peer" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" +) + +var ( + moOpt protojson.MarshalOptions + uoOpt protojson.UnmarshalOptions +) + +func init() { + moOpt = protojson.MarshalOptions{ + // Multiline: true, + AllowPartial: true, + UseProtoNames: true, + UseEnumNumbers: true, + EmitUnpopulated: true, + } + uoOpt = protojson.UnmarshalOptions{ + DiscardUnknown: true, + } +} + +// PbCopy +// @description: 将from对象的内容copy给to对象 +// parameter: +// @from:proto.Message +// @to:proto.Message +// return: +// @error:如果失败,则返回错误,否则返回nil +func PbCopy(from, to proto.Message) error { + data, err := proto.Marshal(from) + if err != nil { + return err + } + + return proto.Unmarshal(data, to) +} + +// Pb2Json +// @description: 将pb对象转换为json字符串 +// parameter: +// @m: +// return: +// @string:pb对象的json标识形式 +// @error: +func Pb2Json(m proto.Message) (string, error) { + str, err := moOpt.Marshal(m) + if err != nil { + return "", err + } + + return string(str), nil +} + +// Json2Pb +// @description: 将json字符串转换成对应pb对象 +// parameter: +// @js: +// @m: +// return: +// @error: +func Json2Pb(js string, m proto.Message) error { + return uoOpt.Unmarshal([]byte(js), m) +} + +// Marshal +// @description: 序列化pb对象 +// parameter: +// @m: +// return: +// @[]byte: +// @error: +func Marshal(m proto.Message) ([]byte, error) { + data, err := proto.Marshal(m) + if err != nil { + return nil, err + } + + return data, nil +} + +// Marshal_Panic +// @description: 序列化pb对象 +// parameter: +// @m: +// return: +// @[]byte: +func Marshal_Panic(m proto.Message) []byte { + data, err := Marshal(m) + if err != nil { + panic(fmt.Sprintf("pbUtil Marshal pb错误,err:%s", err)) + } + + return data +} + +// Unmarshal +// @description: 将数据转换为pb对象 +// parameter: +// @data: +// @m: +// return: +func Unmarshal(data []byte, m proto.Message) error { + err := proto.Unmarshal(data, m) + return err +} + +// GetClientIP +// @description: 获取客户端Ip +// parameter: +// @ctx:grpc底层传递过来的上下文对象 +// return: +// @string:客户端ip +// @error:错误对象 +func GetClientIP(ctx context.Context) (string, error) { + pr, ok := peer.FromContext(ctx) + if !ok { + return "", fmt.Errorf("GetClietIP未获取到客户端ip") + } + if pr.Addr == net.Addr(nil) { + return "", fmt.Errorf("GetClientIP 获取到的peer.Addr=nil") + } + addSlice := strings.Split(pr.Addr.String(), ":") + + return addSlice[0], nil +} diff --git a/trunk/goutil/idUtil/idUtil_test.go b/trunk/goutil/idUtil/idUtil_test.go new file mode 100644 index 0000000..b00c481 --- /dev/null +++ b/trunk/goutil/idUtil/idUtil_test.go @@ -0,0 +1,136 @@ +package idUtil + +import ( + "testing" +) + +func TestNewTimeIdentifierSeedGenerator(t *testing.T) { + generator, err := NewTimeIdentifierSeedGenerator(40, 1, 7, 20) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + generator, err = NewTimeIdentifierSeedGenerator(10, int64(1), 7, 20) + if err != nil { + t.Errorf("there should be no err, but now there is.") + } + _, err = generator.GenerateNewId() + if err == nil { + t.Errorf("there should be err, but now not.") + } + + generator, err = NewTimeIdentifierSeedGenerator(40, int64(127), 3, 20) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + count := 1048575 + idMap := make(map[int64]struct{}, count) + for identifier := 0; identifier < 8; identifier++ { + generator, err = NewTimeIdentifierSeedGenerator(40, int64(identifier), 3, 20) + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + for i := 0; i < count; i++ { + id, err := generator.GenerateNewId() + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + if _, exist := idMap[id]; exist { + t.Errorf("Id:%d is duplicated.", id) + return + } + + idMap[id] = struct{}{} + } + } +} + +func TestNewIdentifierTimeSeedGenerator(t *testing.T) { + generator, err := NewIdentifierTimeSeedGenerator(int64(1), 7, 40, 20) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + generator, err = NewIdentifierTimeSeedGenerator(int64(1), 7, 10, 20) + if err != nil { + t.Errorf("there should be no err, but now there is.") + } + _, err = generator.GenerateNewId() + if err == nil { + t.Errorf("there should be err, but now not.") + } + + generator, err = NewIdentifierTimeSeedGenerator(int64(127), 3, 40, 20) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + count := 1048575 + idMap := make(map[int64]struct{}, count) + for identifier := 0; identifier < 8; identifier++ { + generator, err = NewIdentifierTimeSeedGenerator(int64(identifier), 3, 40, 20) + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + for i := 0; i < count; i++ { + id, err := generator.GenerateNewId() + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + if _, exist := idMap[id]; exist { + t.Errorf("Id:%d is duplicated.", id) + return + } + + idMap[id] = struct{}{} + } + } +} + +func TestNewIdentifierConstructCountSeedGenerator(t *testing.T) { + generator, err := NewIdentifierConstructCountSeedGenerator(int64(1), 27, int64(1), 20, 23) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + generator, err = NewIdentifierConstructCountSeedGenerator(int64(32768), 15, int64(1), 18, 30) + if err == nil { + t.Errorf("there should be err, but now not.") + } + + count := 1048575 + idMap := make(map[int64]struct{}, count) + for identifier := 0; identifier < 8; identifier++ { + for constructCount := 0; constructCount < 5; constructCount++ { + generator, err = NewIdentifierConstructCountSeedGenerator(int64(identifier), 15, int64(constructCount), 18, 30) + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + for i := 0; i < count; i++ { + id, err := generator.GenerateNewId() + if err != nil { + t.Errorf("There should be no error, but now there is:%s", err) + return + } + + if _, exist := idMap[id]; exist { + t.Errorf("Id:%d is duplicated.", id) + return + } + + idMap[id] = struct{}{} + } + } + } +} diff --git a/trunk/goutil/idUtil/identifierConstructorSeed.go b/trunk/goutil/idUtil/identifierConstructorSeed.go new file mode 100644 index 0000000..836e891 --- /dev/null +++ b/trunk/goutil/idUtil/identifierConstructorSeed.go @@ -0,0 +1,108 @@ +/* +用于生成唯一的、递增的Id。生成的规则如下: +1、生成的Id包含一个固定前缀值 +2、为了生成尽可能多的不重复数字,所以使用int64来表示一个数字,其中: +0 000000000000000 0000000000000000000000000000 00000000000000000000 +第一部分:1位,固定为0 +第二部分:共IdentifierBit位,表示固定唯一标识(机器号或者服务器Id等)。范围为[0, math.Pow(2, IdentifierBit)) +第三部分:共ConstructCountBit位,表示对象被构造的次数。范围为[0, math.Pow(2, ConstructCountBit)),以2020-1-1 00:00:00为基准 +第四部分:共SeedBit位,表示自增种子。范围为[0, math.Pow(2, SeedBit)) +3、总体而言,此规则支持总共创建math.Pow(2, ConstructCountBit)次对象,并且每次对象构造期间生成math.Pow(2, SeedBit)个不同的数字 +*/ + +/* +修改记录: +2020-03-04 14:30:00 调整了时间和唯一标识在Id中的位置,以便生成递增的Id +2020-04-20 21:10:00 同步了C#版本的逻辑 +*/ + +package idUtil + +import ( + "fmt" + "math" + "sync" +) + +type IdentifierConstructCountSeedGenerator struct { + identifier int64 // 唯一标识 + identifierBit int32 // 唯一标识(机器号或者服务器Id等)所占的位数 + identifierBitOffset int32 // 唯一标识的偏移位数 + + constructCount int64 // 对象构造的次数 + constructCountBit int32 // 对象构造的次数所占的位数 + constructCountBitOffset int32 // 时间戳对象构造的次数的偏移位数的偏移位数 + + seed int64 // 当前种子值 + seedBit int32 // 自增种子所占的位数 + seedBitOffset int32 // 自增种子的偏移位数 + maxSeed int64 // 最大的种子值 + + mutex sync.Mutex // 锁对象 +} + +func (this *IdentifierConstructCountSeedGenerator) getNewSeed() (int64, error) { + this.mutex.Lock() + defer this.mutex.Unlock() + + if this.seed >= this.maxSeed { + return 0, fmt.Errorf("Seed's value is out of scope") + } + + this.seed += 1 + + return this.seed, nil +} + +// 生成新的Id +// 返回值: +// 新的Id +// 错误对象 +func (this *IdentifierConstructCountSeedGenerator) GenerateNewId() (int64, error) { + seed, err := this.getNewSeed() + if err != nil { + return 0, err + } + id := (this.identifier << this.identifierBitOffset) | (this.constructCount << this.constructCountBitOffset) | (seed << this.seedBitOffset) + + return id, nil +} + +// 创建新的Id生成器对象(为了保证Id的唯一,需要保证生成的对象全局唯一) +// identifierBit + constructCountBit + seedBit <= 63 +// identifier:id唯一标识 +// identifierBit:id唯一标识(机器号或者服务器Id等)的位数 +// constructCount:对象构造次数 +// constructCountBit:对象构造次数的位数 +// seedBit:自增种子的位数 +// 返回值: +// 新的Id生成器对象 +// 错误对象 +func NewIdentifierConstructCountSeedGenerator(identifier int64, identifierBit int32, constructCount int64, constructCountBit, seedBit int32) (*IdentifierConstructCountSeedGenerator, error) { + // 之所以使用63位而不是64,是为了保证值为正数 + if identifierBit+constructCountBit+seedBit > 63 { + return nil, fmt.Errorf("总位数%d超过63位,请调整所有值的合理范围。", identifierBit+constructCountBit+seedBit) + } + if identifier < 0 || identifier > int64(math.Pow(2, float64(identifierBit)))-1 { + return nil, fmt.Errorf("唯一标识值溢出,有效范围为【0,%d】", int64(math.Pow(2, float64(identifierBit)))-1) + } + if constructCount < 0 || constructCount > int64(math.Pow(2, float64(constructCountBit)))-1 { + return nil, fmt.Errorf("对象构造次数的值溢出,有效范围为【0,%d】", int64(math.Pow(2, float64(constructCountBit)))-1) + } + + obj := &IdentifierConstructCountSeedGenerator{ + identifier: identifier, + identifierBit: identifierBit, + constructCount: constructCount, + constructCountBit: constructCountBit, + seed: 0, + seedBit: seedBit, + maxSeed: int64(math.Pow(2, float64(seedBit)) - 1), + } + + obj.seedBitOffset = 0 + obj.constructCountBitOffset = obj.seedBitOffset + obj.seedBit + obj.identifierBitOffset = obj.constructCountBitOffset + obj.constructCountBit + + return obj, nil +} diff --git a/trunk/goutil/idUtil/identifierTimeSeed.go b/trunk/goutil/idUtil/identifierTimeSeed.go new file mode 100644 index 0000000..6c8f571 --- /dev/null +++ b/trunk/goutil/idUtil/identifierTimeSeed.go @@ -0,0 +1,121 @@ +/* +用于生成唯一的、递增的Id。生成的规则如下: +1、生成的Id包含一个固定前缀值 +2、为了生成尽可能多的不重复数字,所以使用int64来表示一个数字,其中: +0 000000000000000 0000000000000000000000000000 00000000000000000000 +第一部分:1位,固定为0 +第二部分:共IdentifierBit位,表示固定唯一标识(机器号或者服务器Id等)。范围为[0, math.Pow(2, IdentifierBit)) +第三部分:共TimeBit位,表示当前时间距离基础时间的秒数。范围为[0, math.Pow(2, TimeBit)),以2020-1-1 00:00:00为基准 +第四部分:共SeedBit位,表示自增种子。范围为[0, math.Pow(2, SeedBit)) +3、总体而言,此规则支持每秒生成math.Pow(2, SeedBit)个不同的数字,并且在math.Pow(2, TimeBit)/60/60/24/365年的时间范围内有效 +*/ + +/* +修改记录: +2020-03-04 14:30:00 调整了时间和唯一标识在Id中的位置,以便生成递增的Id +2020-04-20 21:10:00 同步了C#版本的逻辑 +*/ + +package idUtil + +import ( + "fmt" + "math" + "sync" + "time" +) + +type IdentifierTimeSeedGenerator struct { + identifier int64 // 唯一标识 + identifierBit int32 // 唯一标识(机器号或者服务器Id等)所占的位数 + identifierBitOffset int32 // 唯一标识的偏移位数 + + timeBit int32 // 时间戳所占的位数 + timeBitOffset int32 // 时间戳的偏移位数 + startTimeStamp int64 // 起始时间戳 + endTimeStamp int64 // 结束时间戳 + + seedBit int32 // 自增种子所占的位数 + seedBitOffset int32 // 自增种子的偏移位数 + minSeed int64 // 最小的种子值 + maxSeed int64 // 最大的种子值 + currSeed int64 // 当前种子值 + + mutex sync.Mutex // 锁对象 +} + +func (this *IdentifierTimeSeedGenerator) getTimeStamp() (int64, error) { + if time.Now().Unix() > this.endTimeStamp { + return 0, fmt.Errorf("Time's value is out of scope") + } + + return time.Now().Unix() - this.startTimeStamp, nil +} + +func (this *IdentifierTimeSeedGenerator) getNewSeed() int64 { + this.mutex.Lock() + defer this.mutex.Unlock() + + if this.currSeed >= this.maxSeed { + this.currSeed = this.minSeed + } else { + this.currSeed = this.currSeed + 1 + } + + return this.currSeed +} + +// 生成新的Id +// identifier:Id的唯一标识值。取值范围必须可以用创建对象时指定的唯一标识值的位数来表示,否则会返回参数超出范围的错误 +// 返回值: +// 新的Id +// 错误对象 +func (this *IdentifierTimeSeedGenerator) GenerateNewId() (int64, error) { + timestamp, err := this.getTimeStamp() + if err != nil { + return 0, err + } + seed := this.getNewSeed() + id := (this.identifier << this.identifierBitOffset) | (timestamp << this.timeBitOffset) | (seed << this.seedBitOffset) + + return id, nil +} + +// 创建新的Id生成器对象(为了保证Id的唯一,需要保证生成的对象全局唯一) +// identifierBit + timeBit + seedBit <= 63 +// identifier:id唯一标识 +// identifierBit:id唯一标识(机器号或者服务器Id等)的位数 +// timeBit:时间的位数 +// seedBit:自增种子的位数 +// 返回值: +// 新的Id生成器对象 +// 错误对象 +func NewIdentifierTimeSeedGenerator(identifier int64, identifierBit, timeBit, seedBit int32) (*IdentifierTimeSeedGenerator, error) { + // 之所以使用63位而不是64,是为了保证值为正数 + if identifierBit+timeBit+seedBit > 63 { + return nil, fmt.Errorf("总位数%d超过63位,请调整所有值的合理范围。", identifierBit+timeBit+seedBit) + } + + if identifier < 0 || identifier > int64(math.Pow(2, float64(identifierBit)))-1 { + return nil, fmt.Errorf("唯一标识值溢出,有效范围为【0,%d】", int64(math.Pow(2, float64(identifierBit)))-1) + } + + startTimeStamp := time.Date(2019, time.January, 1, 0, 0, 0, 0, time.Local).Unix() + obj := &IdentifierTimeSeedGenerator{ + identifier: identifier, + identifierBit: identifierBit, + timeBit: timeBit, + startTimeStamp: startTimeStamp, + endTimeStamp: startTimeStamp + int64(math.Pow(2, float64(timeBit))) - 1, + seedBit: seedBit, + minSeed: 0, + maxSeed: int64(math.Pow(2, float64(seedBit))) - 1, + currSeed: 0, + } + + obj.seedBitOffset = 0 + obj.timeBitOffset = obj.seedBitOffset + obj.seedBit + obj.identifierBitOffset = obj.timeBitOffset + obj.timeBit + + return obj, nil +} diff --git a/trunk/goutil/idUtil/timeIdentifierSeed.go b/trunk/goutil/idUtil/timeIdentifierSeed.go new file mode 100644 index 0000000..885d145 --- /dev/null +++ b/trunk/goutil/idUtil/timeIdentifierSeed.go @@ -0,0 +1,121 @@ +/* +用于生成唯一的、递增的Id。生成的规则如下: +1、生成的Id包含一个固定前缀值 +2、为了生成尽可能多的不重复数字,所以使用int64来表示一个数字,其中: +0 000000000000000 0000000000000000000000000000 00000000000000000000 +第一部分:1位,固定为0 +第二部分:共TimeBit位,表示当前时间距离基础时间的秒数。范围为[0, math.Pow(2, TimeBit)),以2020-1-1 00:00:00为基准 +第三部分:共IdentifierBit位,表示固定唯一标识(机器号或者服务器Id等)。范围为[0, math.Pow(2, IdentifierBit)) +第四部分:共SeedBit位,表示自增种子。范围为[0, math.Pow(2, SeedBit)) +3、总体而言,此规则支持每秒生成math.Pow(2, SeedBit)个不同的数字,并且在math.Pow(2, TimeBit)/60/60/24/365年的时间范围内有效 +*/ + +/* +修改记录: +2020-03-04 14:30:00 调整了时间和唯一标识在Id中的位置,以便生成递增的Id +2020-04-20 21:10:00 同步了C#版本的逻辑 +*/ + +package idUtil + +import ( + "fmt" + "math" + "sync" + "time" +) + +type TimeIdentifierSeedGenerator struct { + timeBit int32 // 时间戳所占的位数 + timeBitOffset int32 // 时间戳的偏移位数 + startTimeStamp int64 // 起始时间戳 + endTimeStamp int64 // 结束时间戳 + + identifier int64 // 唯一标识 + identifierBit int32 // 唯一标识(机器号或者服务器Id等)所占的位数 + identifierBitOffset int32 // 唯一标识的偏移位数 + + seedBit int32 // 自增种子所占的位数 + seedBitOffset int32 // 自增种子的偏移位数 + minSeed int64 // 最小的种子值 + maxSeed int64 // 最大的种子值 + currSeed int64 // 当前种子值 + + mutex sync.Mutex // 锁对象 +} + +func (this *TimeIdentifierSeedGenerator) getTimeStamp() (int64, error) { + if time.Now().Unix() > this.endTimeStamp { + return 0, fmt.Errorf("Time's value is out of scope") + } + + return time.Now().Unix() - this.startTimeStamp, nil +} + +func (this *TimeIdentifierSeedGenerator) getNewSeed() int64 { + this.mutex.Lock() + defer this.mutex.Unlock() + + if this.currSeed >= this.maxSeed { + this.currSeed = this.minSeed + } else { + this.currSeed = this.currSeed + 1 + } + + return this.currSeed +} + +// 生成新的Id +// identifier:Id的唯一标识值。取值范围必须可以用创建对象时指定的唯一标识值的位数来表示,否则会返回参数超出范围的错误 +// 返回值: +// 新的Id +// 错误对象 +func (this *TimeIdentifierSeedGenerator) GenerateNewId() (int64, error) { + timestamp, err := this.getTimeStamp() + if err != nil { + return 0, err + } + seed := this.getNewSeed() + id := (timestamp << this.timeBitOffset) | (this.identifier << this.identifierBitOffset) | (seed << this.seedBitOffset) + + return id, nil +} + +// 创建新的Id生成器对象(为了保证Id的唯一,需要保证生成的对象全局唯一) +// timeBit + identifierBit + seedBit <= 63 +// timeBit:时间的位数 +// identifier:id唯一标识 +// identifierBit:id唯一标识(机器号或者服务器Id等)的位数 +// seedBit:自增种子的位数 +// 返回值: +// 新的Id生成器对象 +// 错误对象 +func NewTimeIdentifierSeedGenerator(timeBit int32, identifier int64, identifierBit, seedBit int32) (*TimeIdentifierSeedGenerator, error) { + // 之所以使用63位而不是64,是为了保证值为正数 + if timeBit+identifierBit+seedBit > 63 { + return nil, fmt.Errorf("总位数%d超过63位,请调整所有值的合理范围。", timeBit+identifierBit+seedBit) + } + + if identifier < 0 || identifier > int64(math.Pow(2, float64(identifierBit)))-1 { + return nil, fmt.Errorf("唯一标识值溢出,有效范围为【0,%d】", int64(math.Pow(2, float64(identifierBit)))-1) + } + + startTimeStamp := time.Date(2019, time.January, 1, 0, 0, 0, 0, time.Local).Unix() + obj := &TimeIdentifierSeedGenerator{ + timeBit: timeBit, + startTimeStamp: startTimeStamp, + endTimeStamp: startTimeStamp + int64(math.Pow(2, float64(timeBit))) - 1, + identifier: identifier, + identifierBit: identifierBit, + seedBit: seedBit, + minSeed: 0, + maxSeed: int64(math.Pow(2, float64(seedBit))) - 1, + currSeed: 0, + } + + obj.seedBitOffset = 0 + obj.identifierBitOffset = obj.seedBitOffset + obj.seedBit + obj.timeBitOffset = obj.identifierBitOffset + obj.identifierBit + + return obj, nil +} diff --git a/trunk/goutil/ini-config/ini.conf b/trunk/goutil/ini-config/ini.conf new file mode 100644 index 0000000..a947fe3 --- /dev/null +++ b/trunk/goutil/ini-config/ini.conf @@ -0,0 +1,16 @@ +#配置项说明 +log.es.enable=false #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录) +log.es.url= #(es服务地址) +log.es.indexName=1 #(es服务中Index名) +log.es.level=info #(debug|info|warn|error|fatal等级,等于或高于配置项则记录) + +log.file.enable=false #(默认false) +log.file.path=log #(运行目录下log目录,默认logs) +log.file.pre=log #(文件名前缀,默认log) +log.file.enableHour=true #(文件以小时划分,格式:yyyyMMddHH,默认true,false 一天一个文件,格式:yyyyMMdd) +log.file.level=info + +log.console.enable=false #(默认false) +log.console.level=info + +modelcenter.modelDBConnStr=root:moqikaka3306@tcp(10.255.0.10:3306)/xj_model_mr?charset=utf8&parseTime=true&loc=Local&timeout=60s||MaxOpenConns=10||MaxIdleConns=5 #(model数据库信息) \ No newline at end of file diff --git a/trunk/goutil/ini-config/ini.go b/trunk/goutil/ini-config/ini.go new file mode 100644 index 0000000..503ebf7 --- /dev/null +++ b/trunk/goutil/ini-config/ini.go @@ -0,0 +1,103 @@ +package ini_config + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "strings" +) + +// ParseFile +// @description: 从文件读取配置文件,并转化为map对象 +// parameter: +// @filePath:文件路径 +// return: +// @map[string]string: 配置信息 +// @error:错误信息 +func ParseFile(filePath string) (map[string]string, error) { + fn, err := os.Open(filePath) + if err != nil { + panic(err) + } + defer fn.Close() + + kvMap := make(map[string]string) + rd := bufio.NewReader(fn) + for { + data, _, err := rd.ReadLine() + if err != nil || io.EOF == err { + break + } + + line := strings.TrimSpace(string(data)) + if line == "" || line[0:1] == "#" { + continue + } + + var k string + var v string + k, v, err = Parse(line) + if err != nil { + return nil, err + } + + kvMap[k] = v + } + + return kvMap, nil +} + +// Parse +// @description: 转换单行字符串为配置对象 +// parameter: +// @line:单行内容 +// return: +// @string:key +// @string:value +// @error:错误信息 +func Parse(line string) (string, string, error) { + ls := strings.SplitN(line, "=", 2) + if len(ls) != 2 { + return "", "", errors.New(fmt.Sprintf("配置%s中未找到=分隔符", line)) + } + + var key = strings.TrimSpace(ls[0]) + var value string + + i := strings.LastIndex(ls[1], "#") + if i < 0 { + value = strings.TrimSpace(ls[1]) + } else { + s := ls[1][:i] + value = strings.TrimSpace(s) + } + + return key, value, nil +} + +// ParseMultipleLines +// @description: 转换多行字符串为配置对象 +// parameter: +// @content:多行内容(通过\n换行) +// return: +// @map[string]string:配置信息 +// @error:错误信息 +func ParseMultipleLines(content string) (map[string]string, error) { + lines := strings.Split(content, "\n") + kvMap := make(map[string]string) + for _, line := range lines { + if line == "" || line[0:1] == "#" { + continue + } + + k, v, err := Parse(line) + if err != nil { + return nil, err + } + kvMap[k] = v + } + + return kvMap, nil +} diff --git a/trunk/goutil/ini-config/ini_test.go b/trunk/goutil/ini-config/ini_test.go new file mode 100644 index 0000000..ca200f1 --- /dev/null +++ b/trunk/goutil/ini-config/ini_test.go @@ -0,0 +1,88 @@ +package ini_config + +import ( + "fmt" + "os" + "path" + "testing" +) + +func Test_File(t *testing.T) { + foder, _ := os.Getwd() + filePath := path.Join(foder, "ini.conf") + + kvmap, err := ParseFile(filePath) + if err != nil { + t.Error(err) + return + } + + if len(kvmap) != 12 { + t.Error("读取的内容数量不正确") + return + } + + if v, exists := kvmap["log.es.enable"]; exists == false || v != "false" { + t.Error("log.es.enable读取的值不正确") + } + + if v, exists := kvmap["log.es.url"]; exists == false || v != "" { + t.Error("log.es.url读取的值不正确") + } +} + +func Test_String(t *testing.T) { + k, v, err := Parse("log.es.enable") + if err == nil { + t.Error(fmt.Errorf("解析格式错误")) + return + } + + k, v, err = Parse("log.es.enable=false #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)") + if err != nil { + t.Error(err) + return + } + if k != "log.es.enable" || v != "false" { + t.Error("解析的值不正确") + } + + k, v, err = Parse("dbconnection=root:moqikaka3309!#@tcp(10.252.0.62:3309)/liangjian2_groupserver_auto_master?charset=utf8&parseTime=true&loc=Local&timeout=30s#数据库连接") + if err != nil { + t.Error(err) + return + } + if k != "dbconnection" || v != "root:moqikaka3309!#@tcp(10.252.0.62:3309)/liangjian2_groupserver_auto_master?charset=utf8&parseTime=true&loc=Local&timeout=30s" { + t.Error("解析的值不正确") + } + +} + +func Test_Content(t *testing.T) { + content := "#配置项说明\nlog.es.enable #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)\nlog.es.url= #(es服务地址)\nlog.es.indexName=1 #(es服务中Index名)\nlog.es.level=info #(debug|info|warn|error|fatal等级,等于或高于配置项则记录)\n\nlog.file.enable=false #(默认false)\nlog.file.path=log #(运行目录下log目录,默认logs)\nlog.file.pre=log #(文件名前缀,默认log)\nlog.file.enableHour=true #(文件以小时划分,格式:yyyyMMddHH,默认true,false 一天一个文件,格式:yyyyMMdd)\nlog.file.level=info\n\nlog.console.enable=false #(默认false)\nlog.console.level=info" + kvmap, err := ParseMultipleLines(content) + if err == nil { + t.Error(fmt.Errorf("配置格式不正确")) + return + } + + content = "#配置项说明\nlog.es.enable=false #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)\nlog.es.url= #(es服务地址)\nlog.es.indexName=1 #(es服务中Index名)\nlog.es.level=info #(debug|info|warn|error|fatal等级,等于或高于配置项则记录)\n\nlog.file.enable=false #(默认false)\nlog.file.path=log #(运行目录下log目录,默认logs)\nlog.file.pre=log #(文件名前缀,默认log)\nlog.file.enableHour=true #(文件以小时划分,格式:yyyyMMddHH,默认true,false 一天一个文件,格式:yyyyMMdd)\nlog.file.level=info\n\nlog.console.enable=false #(默认false)\nlog.console.level=info" + kvmap, err = ParseMultipleLines(content) + if err != nil { + t.Error(err) + return + } + + if len(kvmap) != 11 { + t.Error("读取的内容数量不正确") + return + } + + if v, exists := kvmap["log.es.enable"]; exists == false || v != "false" { + t.Error("log.es.enable读取的值不正确") + } + + if v, exists := kvmap["log.es.url"]; exists == false || v != "" { + t.Error("log.es.url读取的值不正确") + } +} diff --git a/trunk/goutil/intAndBytesUtil/bytes.go b/trunk/goutil/intAndBytesUtil/bytes.go new file mode 100644 index 0000000..37c012a --- /dev/null +++ b/trunk/goutil/intAndBytesUtil/bytes.go @@ -0,0 +1,58 @@ +package intAndBytesUtil + +import ( + "bytes" + "encoding/binary" +) + +// 字节数组转换成整形 +// b:字节数组 +// order:大、小端的枚举 +// 返回值:对应的int值 +func BytesToInt(b []byte, order binary.ByteOrder) int { + bytesBuffer := bytes.NewBuffer(b) + + var result int + binary.Read(bytesBuffer, order, &result) + + return result +} + +// 字节数组转换成整形 +// b:字节数组 +// order:大、小端的枚举 +// 返回值:对应的int16值 +func BytesToInt16(b []byte, order binary.ByteOrder) int16 { + bytesBuffer := bytes.NewBuffer(b) + + var result int16 + binary.Read(bytesBuffer, order, &result) + + return result +} + +// 字节数组转换成整形 +// b:字节数组 +// order:大、小端的枚举 +// 返回值:对应的int32值 +func BytesToInt32(b []byte, order binary.ByteOrder) int32 { + bytesBuffer := bytes.NewBuffer(b) + + var result int32 + binary.Read(bytesBuffer, order, &result) + + return result +} + +// 字节数组转换成整形 +// b:字节数组 +// order:大、小端的枚举 +// 返回值:对应的int64值 +func BytesToInt64(b []byte, order binary.ByteOrder) int64 { + bytesBuffer := bytes.NewBuffer(b) + + var result int64 + binary.Read(bytesBuffer, order, &result) + + return result +} diff --git a/trunk/goutil/intAndBytesUtil/bytes_test.go b/trunk/goutil/intAndBytesUtil/bytes_test.go new file mode 100644 index 0000000..33f6b30 --- /dev/null +++ b/trunk/goutil/intAndBytesUtil/bytes_test.go @@ -0,0 +1,70 @@ +package intAndBytesUtil + +import ( + "encoding/binary" + "testing" +) + +func TestBytesToInt(t *testing.T) { + var givenBigEndian []byte = []byte{0, 0, 1, 0} + var givenLittleEndian []byte = []byte{0, 1, 0, 0} + var expectedInt int32 = 256 + + result := BytesToInt32(givenBigEndian, binary.BigEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenBigEndian, result, expectedInt) + } + + result = BytesToInt32(givenLittleEndian, binary.LittleEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenLittleEndian, result, expectedInt) + } +} + +func TestBytesToInt16(t *testing.T) { + var givenBigEndian []byte = []byte{1, 0} + var givenLittleEndian []byte = []byte{0, 1} + var expectedInt int16 = 256 + + result := BytesToInt16(givenBigEndian, binary.BigEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenBigEndian, result, expectedInt) + } + + result = BytesToInt16(givenLittleEndian, binary.LittleEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenLittleEndian, result, expectedInt) + } +} + +func TestBytesToInt32(t *testing.T) { + var givenBigEndian []byte = []byte{0, 0, 1, 0} + var givenLittleEndian []byte = []byte{0, 1, 0, 0} + var expectedInt int32 = 256 + + result := BytesToInt32(givenBigEndian, binary.BigEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenBigEndian, result, expectedInt) + } + + result = BytesToInt32(givenLittleEndian, binary.LittleEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenLittleEndian, result, expectedInt) + } +} + +func TestBytesToInt64(t *testing.T) { + var givenBigEndian []byte = []byte{0, 0, 0, 0, 0, 0, 1, 0} + var givenLittleEndian []byte = []byte{0, 1, 0, 0, 0, 0, 0, 0} + var expectedInt int64 = 256 + + result := BytesToInt64(givenBigEndian, binary.BigEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenBigEndian, result, expectedInt) + } + + result = BytesToInt64(givenLittleEndian, binary.LittleEndian) + if result != expectedInt { + t.Errorf("BytesToInt(%v) failed.Got %v, expected %v", givenLittleEndian, result, expectedInt) + } +} diff --git a/trunk/goutil/intAndBytesUtil/doc.go b/trunk/goutil/intAndBytesUtil/doc.go new file mode 100644 index 0000000..1638da7 --- /dev/null +++ b/trunk/goutil/intAndBytesUtil/doc.go @@ -0,0 +1,4 @@ +/* +提供int和[]Bytes互相转化的助手方法,其中需要注意的是在不同的平台上大、小端是不同的 +*/ +package intAndBytesUtil diff --git a/trunk/goutil/intAndBytesUtil/int.go b/trunk/goutil/intAndBytesUtil/int.go new file mode 100644 index 0000000..bbef68a --- /dev/null +++ b/trunk/goutil/intAndBytesUtil/int.go @@ -0,0 +1,50 @@ +package intAndBytesUtil + +import ( + "bytes" + "encoding/binary" +) + +// 整形转换成字节(无效,因为系统无法判断读取的字节数) +// n:int型数字 +// order:大、小端的枚举 +// 返回值:对应的字节数组 +// func IntToBytes(n int, order binary.ByteOrder) []byte { +// bytesBuffer := bytes.NewBuffer([]byte{}) +// binary.Write(bytesBuffer, order, n) + +// return bytesBuffer.Bytes() +// } + +// 整形转换成字节 +// n:int16型数字 +// order:大、小端的枚举 +// 返回值:对应的字节数组 +func Int16ToBytes(n int16, order binary.ByteOrder) []byte { + bytesBuffer := bytes.NewBuffer([]byte{}) + binary.Write(bytesBuffer, order, n) + + return bytesBuffer.Bytes() +} + +// 整形转换成字节 +// n:int32型数字 +// order:大、小端的枚举 +// 返回值:对应的字节数组 +func Int32ToBytes(n int32, order binary.ByteOrder) []byte { + bytesBuffer := bytes.NewBuffer([]byte{}) + binary.Write(bytesBuffer, order, n) + + return bytesBuffer.Bytes() +} + +// 整形转换成字节 +// n:int64型数字 +// order:大、小端的枚举 +// 返回值:对应的字节数组 +func Int64ToBytes(n int64, order binary.ByteOrder) []byte { + bytesBuffer := bytes.NewBuffer([]byte{}) + binary.Write(bytesBuffer, order, n) + + return bytesBuffer.Bytes() +} diff --git a/trunk/goutil/intAndBytesUtil/int_test.go b/trunk/goutil/intAndBytesUtil/int_test.go new file mode 100644 index 0000000..4e5137f --- /dev/null +++ b/trunk/goutil/intAndBytesUtil/int_test.go @@ -0,0 +1,68 @@ +package intAndBytesUtil + +import ( + "encoding/binary" + "testing" +) + +func TestInt16ToBytes(t *testing.T) { + var expectedBigEndian []byte = []byte{1, 0} + var expectedLittleEndian []byte = []byte{0, 1} + var givenInt int16 = 256 + + result := Int16ToBytes(givenInt, binary.BigEndian) + if equal(result, expectedBigEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian) + } + + result = Int16ToBytes(givenInt, binary.LittleEndian) + if equal(result, expectedLittleEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian) + } +} + +func TestInt32ToBytes(t *testing.T) { + var expectedBigEndian []byte = []byte{0, 0, 1, 0} + var expectedLittleEndian []byte = []byte{0, 1, 0, 0} + var givenInt int32 = 256 + + result := Int32ToBytes(givenInt, binary.BigEndian) + if equal(result, expectedBigEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian) + } + + result = Int32ToBytes(givenInt, binary.LittleEndian) + if equal(result, expectedLittleEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian) + } +} + +func TestInt64ToBytes(t *testing.T) { + var expectedBigEndian []byte = []byte{0, 0, 0, 0, 0, 0, 1, 0} + var expectedLittleEndian []byte = []byte{0, 1, 0, 0, 0, 0, 0, 0} + var givenInt int64 = 256 + + result := Int64ToBytes(givenInt, binary.BigEndian) + if equal(result, expectedBigEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian) + } + + result = Int64ToBytes(givenInt, binary.LittleEndian) + if equal(result, expectedLittleEndian) == false { + t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian) + } +} + +func equal(b1, b2 []byte) bool { + if len(b1) != len(b2) { + return false + } + + for i := 0; i < len(b1); i++ { + if b1[i] != b2[i] { + return false + } + } + + return true +} diff --git a/trunk/goutil/intUtil/int.go b/trunk/goutil/intUtil/int.go new file mode 100644 index 0000000..f505d70 --- /dev/null +++ b/trunk/goutil/intUtil/int.go @@ -0,0 +1,44 @@ +package intUtil + +import ( + "fmt" + "math/rand" + "time" +) + +// ShuffleIntDigits 打乱整数的各个数字位置并返回新的整数 +func ShuffleIntDigits(num int64) (int64, error) { + if num < 0 { + return 0, fmt.Errorf("number must be non-negative") + } + + var digits []int64 + for num > 0 { + digits = append(digits, num%10) + num /= 10 + } + + // 如果原始数字是0,直接返回 + if len(digits) == 0 { + return 0, nil + } + + // 反转切片以保持原来的数字顺序 + for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 { + digits[i], digits[j] = digits[j], digits[i] + } + + // 打乱数字切片 + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(digits), func(i, j int) { + digits[i], digits[j] = digits[j], digits[i] + }) + + // 重新组合为新的整数 + result := int64(0) + for _, digit := range digits { + result = result*10 + digit + } + + return result, nil +} diff --git a/trunk/goutil/intUtil/int_test.go b/trunk/goutil/intUtil/int_test.go new file mode 100644 index 0000000..a50f921 --- /dev/null +++ b/trunk/goutil/intUtil/int_test.go @@ -0,0 +1,16 @@ +package intUtil + +import ( + "fmt" + "testing" +) + +func Test1(t *testing.T) { + var originalNum int64 = 1234567891011121314 + shuffledNum, err := ShuffleIntDigits(int64(originalNum)) + if err != nil { + fmt.Println("Error:", err) + return + } + fmt.Printf("Original number: %d\nShuffled number: %d\n", originalNum, shuffledNum) +} diff --git a/trunk/goutil/jsonUtil/json.go b/trunk/goutil/jsonUtil/json.go new file mode 100644 index 0000000..f03bf19 --- /dev/null +++ b/trunk/goutil/jsonUtil/json.go @@ -0,0 +1,78 @@ +package jsonUtil + +import ( + "encoding/json" + "strings" +) + +// 使用Number类型来反序列化字符串 +// 当被序列化为interface{}类型时,如果int型的长度大于7,则会被使用科学计数法进行表示 +// 当反序列化时,会无法转换为int类型,会导致错误 +// 所以需要使用Number类型 +// s:输入字符串 +// 返回值: +// 反序列化后的数据 +// 错误对象 +func UnMarshalWithNumberType(s string) (interface{}, error) { + // 构造decode对象 + var decode = json.NewDecoder(strings.NewReader(s)) + decode.UseNumber() + + // decode + var result interface{} + if err := decode.Decode(&result); err != nil { + return nil, err + } + + return result, nil +} + +// 深拷贝对象 +// src:源数据 +// 返回值: +// 新对象 +// 错误对象 +func DeepClone(src interface{}) (interface{}, error) { + var byteSlice []byte + var err error + + // 先序列化为[]byte + if byteSlice, err = json.Marshal(src); err != nil { + return nil, err + } + + // 再反序列化成对象 + var result interface{} + if err := json.Unmarshal(byteSlice, &result); err != nil { + return nil, err + } + + return result, nil +} + +// 使用Number类型来深拷贝对象 +// src:源数据 +// 返回值: +// 新对象 +// 错误对象 +func DeepCloneWithNumberType(src interface{}) (interface{}, error) { + var byteSlice []byte + var err error + + // 先序列化为[]byte + if byteSlice, err = json.Marshal(src); err != nil { + return nil, err + } + + // 构造decode对象 + var decode = json.NewDecoder(strings.NewReader(string(byteSlice))) + decode.UseNumber() + + // decode + var result interface{} + if err := decode.Decode(&result); err != nil { + return nil, err + } + + return result, nil +} diff --git a/trunk/goutil/jsonUtil/json_test.go b/trunk/goutil/jsonUtil/json_test.go new file mode 100644 index 0000000..99730be --- /dev/null +++ b/trunk/goutil/jsonUtil/json_test.go @@ -0,0 +1,131 @@ +package jsonUtil + +import ( + "encoding/json" + "fmt" + "testing" +) + +func TestUnMarshalWithNumberType(t *testing.T) { + src := make(map[string]int) + src["Name"] = 123 + src["Money"] = 100000000 + + var byteSlice []byte + var err error + if byteSlice, err = json.Marshal(src); err != nil { + t.Errorf("Marshal src failed\n") + } + + if target_interface, err := UnMarshalWithNumberType(string(byteSlice)); err != nil { + t.Errorf("Expected got nil, but got err:%s\n", err) + } else { + if target_map, ok := target_interface.(map[string]interface{}); !ok { + t.Errorf("Expected got nil, but got err:%s\n", err) + } else { + money, ok := target_map["Money"].(json.Number) + money_int, err := money.Int64() + if !ok || err != nil || money_int != 100000000 { + t.Errorf("Expected got 100000000, but got %v, ok:%v, err:%s\n", money_int, ok, err) + } + + fmt.Printf("target_map:%v\n", target_map) + } + } + + intSlice1 := []int{1, 2, 3, 5} + + if byteSlice, err = json.Marshal(intSlice1); err != nil { + t.Errorf("Marshal src failed\n") + } + + if target_interface, err := UnMarshalWithNumberType(string(byteSlice)); err != nil { + t.Errorf("Expected got nil, but got err:%s\n", err) + } else { + fmt.Printf("target_interface:%v\n", target_interface) + if target_slice, ok := target_interface.([]interface{}); !ok { + t.Errorf("Expected got []int, but failed.\n") + } else { + fmt.Printf("target_slice:%v\n", target_slice) + } + } +} + +func TestDeepClone(t *testing.T) { + p1 := NewPlayer(100000000, "Jordan") + if p1_map, err := DeepClone(p1); err != nil { + t.Errorf("Expected nil, but got err:%s", err) + } else { + fmt.Printf("p1:%s\n", p1) + before := fmt.Sprintf("%v", p1_map) + p1.Name = "Jordan Zuo" + fmt.Printf("p1:%s\n", p1) + after := fmt.Sprintf("%v", p1_map) + fmt.Printf("before:%s\n", before) + fmt.Printf("after:%s\n", after) + if before != after { + t.Errorf("Expected before and after same, but got different") + } + } + + intSlice1 := []int{1, 2, 3, 5} + if intSlice2_interface, err := DeepClone(intSlice1); err != nil { + t.Errorf("Expected nil, but got err:%s", err) + } else { + fmt.Printf("intSlice1:%v\n", intSlice1) + before := fmt.Sprintf("%v", intSlice2_interface) + intSlice1 = append(intSlice1, 10) + fmt.Printf("intSlice1:%v\n", intSlice1) + after := fmt.Sprintf("%v", intSlice2_interface) + fmt.Printf("before:%s\n", before) + fmt.Printf("after:%s\n", after) + if before != after { + t.Errorf("Expected before and after same, but got different") + } + } +} + +func TestDeepCloneWithNumberType(t *testing.T) { + p1 := NewPlayer(100000000, "Jordan") + if p1_interface, err := DeepCloneWithNumberType(p1); err != nil { + t.Errorf("Expected nil, but got err:%s", err) + } else { + if p1_map, ok := p1_interface.(map[string]interface{}); !ok { + t.Errorf("Expected nil, but got err:%s", err) + } else { + fmt.Printf("p1:%s\n", p1) + before := fmt.Sprintf("%v", p1_map) + p1.Name = "Jordan Zuo" + fmt.Printf("p1:%s\n", p1) + after := fmt.Sprintf("%v", p1_map) + fmt.Printf("before:%s\n", before) + fmt.Printf("after:%s\n", after) + if before != after { + t.Errorf("Expected before and after same, but got different") + } + + fmt.Printf("p1_interface_map:%v\n", p1_map) + id, ok := p1_map["Id"].(json.Number) + id_int, err := id.Int64() + if !ok || err != nil || id_int != 100000000 { + t.Errorf("Expected got 100000000, but got %v, ok:%v, err:%s\n", id_int, ok, err) + } + } + } +} + +type Player struct { + Id int + Name string +} + +func (player *Player) String() string { + return fmt.Sprintf("{Addr:%v, Id:%v, Name:%s}", &player, player.Id, player.Name) +} + +func NewPlayer(id int, name string) *Player { + return &Player{ + Id: id, + Name: name, + } +} diff --git a/trunk/goutil/logUtil/doc.go b/trunk/goutil/logUtil/doc.go new file mode 100644 index 0000000..702b395 --- /dev/null +++ b/trunk/goutil/logUtil/doc.go @@ -0,0 +1,2 @@ +// Package logUtil +package logUtil diff --git a/trunk/goutil/logUtil/impl-console/doc.go b/trunk/goutil/logUtil/impl-console/doc.go new file mode 100644 index 0000000..aaba376 --- /dev/null +++ b/trunk/goutil/logUtil/impl-console/doc.go @@ -0,0 +1 @@ +package impl_console diff --git a/trunk/goutil/logUtil/impl-console/logger.go b/trunk/goutil/logUtil/impl-console/logger.go new file mode 100644 index 0000000..82aa9d1 --- /dev/null +++ b/trunk/goutil/logUtil/impl-console/logger.go @@ -0,0 +1,84 @@ +package impl_console + +import ( + "fmt" + "github.com/fatih/color" +) + +type Logger struct { +} + +// NewLogger +// @description: 构造控制台日志 +// parameter: +// return: +// @*Logger: +func NewLogger() *Logger { + return &Logger{} +} + +// InfoLog +// @description: 信息日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (cl *Logger) InfoLog(format string, args ...interface{}) { + c := color.New(color.FgGreen) + s := c.Sprint("Info:") + fmt.Println(s, fmt.Sprintf(format, args...)) +} + +// DebugLog +// @description: 调试日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (cl *Logger) DebugLog(format string, args ...interface{}) { + c := color.New(color.FgGreen) + s := c.Sprint("Debug:") + fmt.Println(s, fmt.Sprintf(format, args...)) +} + +// WarnLog +// @description: 警告日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (cl *Logger) WarnLog(format string, args ...interface{}) { + c := color.New(color.FgYellow) + _, _ = c.Println("Warn:", fmt.Sprintf(format, args...)) +} + +// ErrorLog +// @description: 错误日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (cl *Logger) ErrorLog(format string, args ...interface{}) { + c := color.New(color.FgRed) + _, _ = c.Println("Error:", fmt.Sprintf(format, args...)) +} + +// FatalLog +// @description: 致命错误日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (cl *Logger) FatalLog(format string, args ...interface{}) { + c := color.New(color.FgRed) + _, _ = c.Println("Fatal:", fmt.Sprintf(format, args...)) +} + +// CloseLog +// @description: 关闭日志 +// parameter: +// @waitFinish:是否等待日志 +// return: +func (cl *Logger) CloseLog(waitFinish bool) { + +} diff --git a/trunk/goutil/logUtil/impl-console/logger_test.go b/trunk/goutil/logUtil/impl-console/logger_test.go new file mode 100644 index 0000000..41cbcc5 --- /dev/null +++ b/trunk/goutil/logUtil/impl-console/logger_test.go @@ -0,0 +1,14 @@ +package impl_console + +import ( + "testing" +) + +func TestInfoLog(t *testing.T) { + log := NewLogger() + log.DebugLog("Debug test") + log.InfoLog("Info test") + log.WarnLog("Warn test") + log.ErrorLog("Error test") + log.FatalLog("Fatal test") +} diff --git a/trunk/goutil/logUtil/impl-es/api.go b/trunk/goutil/logUtil/impl-es/api.go new file mode 100644 index 0000000..75277bc --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/api.go @@ -0,0 +1,63 @@ +package impl_es + +// InfoLog +// @description: 信息日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (l *Logger) InfoLog(format string, args ...interface{}) { + info := l.buildEsLog("Info", format, args...) + l.cache.addCacheLog(info) +} + +// DebugLog +// @description: 调试日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (l *Logger) DebugLog(format string, args ...interface{}) { + info := l.buildEsLog("Debug", format, args...) + l.cache.addCacheLog(info) +} + +// WarnLog +// @description: 警告日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (l *Logger) WarnLog(format string, args ...interface{}) { + info := l.buildEsLog("Warn", format, args...) + l.cache.addCacheLog(info) +} + +// ErrorLog +// @description: 错误日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (l *Logger) ErrorLog(format string, args ...interface{}) { + info := l.buildEsLog("Error", format, args...) + l.cache.addCacheLog(info) +} + +// FatalLog +// @description: 致命错误日志记录 +// parameter: +// @format:日志格式 +// @args:参数列表 +// return: +func (l *Logger) FatalLog(format string, args ...interface{}) { + info := l.buildEsLog("Fatal", format, args...) + l.cache.addCacheLog(info) +} + +// CloseLog +// @description: 关闭日志 +// parameter: +// @waitFinish:是否等待日志 +// return: +func (l *Logger) CloseLog(waitFinish bool) {} diff --git a/trunk/goutil/logUtil/impl-es/cache.go b/trunk/goutil/logUtil/impl-es/cache.go new file mode 100644 index 0000000..79550ad --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/cache.go @@ -0,0 +1,72 @@ +package impl_es + +import "sync" + +const ( + // 批量保存的消息数量 + con_MAX_NUMBER_OF_MESSAGE = 20 +) + +// logCache +// @description: 日志缓存 +type logCache struct { + logCacheList []*EsLogModel // logCacheList 日志缓存对象 + logCacheMutex sync.RWMutex // logCacheMutex 锁 +} + +// newlogCache +// @description: 构造日志缓存 +// parameter: +// return: +// @*logCache: +func newlogCache() *logCache { + m := &logCache{ + logCacheList: make([]*EsLogModel, 0, 256), + logCacheMutex: sync.RWMutex{}, + } + + return m +} + +// 写入在线日志 +// 参数: +// 日志信息对象 +// 返回值: +// 无 +func (c *logCache) addCacheLog(logObj *EsLogModel) { + c.logCacheMutex.Lock() + defer c.logCacheMutex.Unlock() + + c.logCacheList = append(c.logCacheList, logObj) +} + +// 获取日志数量 +// 参数: +// 无 +// 返回值: +// 缓存中的日志数量 +func (c *logCache) getCacheLogCount() int { + c.logCacheMutex.RLock() + defer c.logCacheMutex.RUnlock() + + return len(c.logCacheList) +} + +// 获取缓存中的日志 +// 参数: +// 数量 +// 返回值: +// 日志列表对象 +func (c *logCache) getCacheLog(logCount int) (logList []*EsLogModel) { + if logCount > con_MAX_NUMBER_OF_MESSAGE { + logCount = con_MAX_NUMBER_OF_MESSAGE + } + + c.logCacheMutex.Lock() + defer c.logCacheMutex.Unlock() + + logList = c.logCacheList[:logCount] + c.logCacheList = c.logCacheList[logCount:] + + return +} diff --git a/trunk/goutil/logUtil/impl-es/doc.go b/trunk/goutil/logUtil/impl-es/doc.go new file mode 100644 index 0000000..edeb851 --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/doc.go @@ -0,0 +1 @@ +package impl_es diff --git a/trunk/goutil/logUtil/impl-es/logger.go b/trunk/goutil/logUtil/impl-es/logger.go new file mode 100644 index 0000000..dd4223d --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/logger.go @@ -0,0 +1,236 @@ +package impl_es + +import ( + "bytes" + "encoding/json" + "fmt" + "time" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/elastic/go-elasticsearch/v8/esutil" + "golang.org/x/net/context" +) + +const ( + // 如果缓存中的日志数量超过阈值,则记录日志 + con_WRITE_LOG_THRESHOLD_NUMBER = 1000 +) + +// Logger +// @description: es日志处理对象 +type Logger struct { + esClient *elasticsearch.Client // esClient es客户端 + blukIndexer esutil.BulkIndexer // blukIndexer es批量索引 + indexNamePrefix string // indexNamePrefix 索引前缀 + curIndexName string // curIndexName 当前索引名字 + innerId string // innerId 系统唯一标识 + extendCb func() string // extendCb 扩展信息获取方法 + cache *logCache // cache 日志缓存 +} + +// NewLogger +// @description: 构造es日志对象 +// parameter: +// @urls: +// @username: +// @pwd: +// @esIndexName: +// @_innerId: +// @_extendCb: +// return: +// @*Logger: +// @error: +func NewLogger(urls []string, username, pwd string, esIndexName, _innerId string, _extendCb func() string) (*Logger, error) { + if _extendCb == nil { + _extendCb = func() string { + return "" + } + } + + esClient, err := elasticsearch.NewClient(elasticsearch.Config{ + Addresses: urls, + Username: username, + Password: pwd, + }) + if err != nil { + return nil, err + } + + l := &Logger{ + esClient: esClient, + blukIndexer: nil, + indexNamePrefix: esIndexName, + curIndexName: "", + innerId: _innerId, + extendCb: _extendCb, + cache: newlogCache(), + } + + l.curIndexName = l.getCurIndexName() + l.blukIndexer, err = l.newBlukIndexer() + if err != nil { + return nil, err + } + + // 启动日志处理 + l.start() + + return l, nil +} + +// start +// @description: 启动日志处理 +// parameter: +// @receiver l: +// return: +func (l *Logger) start() { + go l.logHandlerStart() + go l.indexNameCheck() +} + +// getCurIndexName +// @description: 获取索引名字 +// parameter: +// @receiver l: +// return: +// @string: +func (l *Logger) getCurIndexName() string { + //获取当天日期 + return fmt.Sprintf("%s_%s", l.indexNamePrefix, time.Now().Format("20060102")) +} + +// newBlukIndexer +// @description: 创建新的BlukIndexer +// parameter: +// @receiver l: +// return: +// @bulkIndexer: +// @err: +func (l *Logger) newBlukIndexer() (bulkIndexer esutil.BulkIndexer, err error) { + bulkIndexer, err = esutil.NewBulkIndexer(esutil.BulkIndexerConfig{ + Index: l.getCurIndexName(), // The default index name + Client: l.esClient, // The Elasticsearch client + FlushInterval: time.Second, // The periodic flush interval + }) + if err != nil { + fmt.Println("[es log]: Creating the indexer err: ", err) + } + + return +} + +// 日志处理 +func (l *Logger) logHandlerStart() { + defer func() { + if r := recover(); r != nil { + fmt.Println("[es log]: Creating the indexer err: ", r) + go l.logHandlerStart() + } + }() + + time.Sleep(1 * time.Second) + for { + // 达到指定的时间或者指定的日志数量,则保存到消息队列中去 + logCount := l.cache.getCacheLogCount() + if logCount == 0 || l.esClient == nil { + time.Sleep(time.Second * 1) + continue + } + + // 记录在线日志待发送数量 + if logCount > con_WRITE_LOG_THRESHOLD_NUMBER { + fmt.Printf("[es log]: 当前缓存中共有%d条未发送到在线日志系统的日志", logCount) + } + + // 执行刷新 + l.bulkPushToOnlineLogSystem(l.cache.getCacheLog(logCount)) + } +} + +// indexNameCheck +// @description: index名字校验 +// parameter: +// @receiver l: +// return: +func (l *Logger) indexNameCheck() { + defer func() { + if r := recover(); r != nil { + fmt.Println("[es log]: indexNameCheck err: ", r) + go l.indexNameCheck() + } + }() + + time.Sleep(1 * time.Second) + for { + time.Sleep(time.Second * 1) + + tempIndexName := l.getCurIndexName() + if tempIndexName == l.curIndexName { + continue + } + + // 关闭老的blukIndexer+建立新的blukIndexer + newBlukIndexer, err := l.newBlukIndexer() + if err != nil { + continue + } + + tempBlukIndexer := l.blukIndexer + l.blukIndexer = newBlukIndexer + l.curIndexName = tempIndexName + + // 暂停3s,等待正在写入的数据写入完成 + time.Sleep(3 * time.Second) + err = tempBlukIndexer.Close(context.Background()) + if err != nil { + fmt.Println("[es log]:BlukIndexer close err:", err.Error()) + } + } +} + +// bulkPushToOnlineLogSystem +// @description: 批量保存到在线日志系统 +// parameter: +// @receiver l: +// @logList: +// return: +func (l *Logger) bulkPushToOnlineLogSystem(logList []*EsLogModel) { + for _, logObj := range logList { + message, err := json.Marshal(logObj) + if err != nil { + fmt.Println("[es log]: Marshal failed. Err:", err) + continue + } + + err = l.blukIndexer.Add( + context.Background(), + esutil.BulkIndexerItem{ + Action: "index", + Body: bytes.NewReader(message), + }) + if err != nil { + fmt.Println("[es log]: Add data err:", err.Error()) + } + } +} + +// buildOnlineLog +// @description: 组装es日志对象 +// parameter: +// @receiver l: +// @logType: +// @format: +// @args: +// return: +// @newLogObj: +func (l *Logger) buildEsLog(logType, format string, args ...interface{}) (newLogObj *EsLogModel) { + msg := format + if len(args) > 0 { + msg = fmt.Sprintf(format, args...) + } + + // 构造新的日志对象 + newLogObj = newEsLogModel(logType, msg, l.innerId, l.extendCb) + + return +} diff --git a/trunk/goutil/logUtil/impl-es/logger_test.go b/trunk/goutil/logUtil/impl-es/logger_test.go new file mode 100644 index 0000000..6b8fd2c --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/logger_test.go @@ -0,0 +1,25 @@ +package impl_es + +import ( + "testing" + "time" +) + +func TestLog(t *testing.T) { + urls := []string{"http://10.1.0.71:9101/"} + log, err := NewLogger(urls, "", "", "es_log_test", "les_log_test", nil) + if err != nil { + t.Error("esLog 创建失败") + } + + for i := 1; i <= 1000; i++ { + log.DebugLog("Debug test %v", i) + log.InfoLog("Info test %v", i) + log.WarnLog("Warn test %v", i) + log.ErrorLog("Error test %v", i) + log.FatalLog("Fatal test %v", i) + time.Sleep(time.Millisecond * 100) + } + + time.Sleep(time.Second * 5) +} diff --git a/trunk/goutil/logUtil/impl-es/model.go b/trunk/goutil/logUtil/impl-es/model.go new file mode 100644 index 0000000..3b7c48a --- /dev/null +++ b/trunk/goutil/logUtil/impl-es/model.go @@ -0,0 +1,53 @@ +package impl_es + +import ( + "sync" + "time" +) + +var ( + logPool = sync.Pool{ + New: func() interface{} { + return &EsLogModel{} + }, + } +) + +// 日志对象 +type EsLogModel struct { + // 日志类型 + LogType string + + // 日志消息 + Message string + + // 程序标识 + InnerId string + + // Extendstr 扩展信息 + Extendstr string + + // 日志时间 + CrTime time.Time +} + +// newEsLogModel +// @description: 构造esLog对象 +// parameter: +// @logType:日志类型 +// @msg:日志内容 +// @innerId:内部唯一id +// @extendCb:扩展信息回调方法 +// return: +// @*EsLogModel: +func newEsLogModel(logType string, msg string, innerId string, extendCb func() string) *EsLogModel { + logObj := logPool.Get().(*EsLogModel) + + logObj.CrTime = time.Now() + logObj.LogType = logType + logObj.Message = msg + logObj.InnerId = innerId + logObj.Extendstr = extendCb() + + return logObj +} diff --git a/trunk/goutil/logUtil/impl-localfile/api.go b/trunk/goutil/logUtil/impl-localfile/api.go new file mode 100644 index 0000000..fc28df4 --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/api.go @@ -0,0 +1,35 @@ +package impl_localfile + +import "sync" + +func (l *Logger) InfoLog(format string, args ...interface{}) { + l.log(getLog(format, args...), info, true) +} + +func (l *Logger) WarnLog(format string, args ...interface{}) { + l.log(getLog(format, args...), warn, true) +} + +func (l *Logger) DebugLog(format string, args ...interface{}) { + l.log(getLog(format, args...), debug, true) +} + +func (l *Logger) ErrorLog(format string, args ...interface{}) { + l.log(getLog(format, args...), _error, true) +} + +func (l *Logger) FatalLog(format string, args ...interface{}) { + l.log(getLog(format, args...), fatal, true) +} + +func (l *Logger) CloseLog(waitFinish bool) { + wg := sync.WaitGroup{} + + // 关闭所有的file + for _, logger := range l.loggerMap { + wg.Add(1) + go logger.Close(&wg, waitFinish) + } + + wg.Wait() +} diff --git a/trunk/goutil/logUtil/impl-localfile/doc.go b/trunk/goutil/logUtil/impl-localfile/doc.go new file mode 100644 index 0000000..908b48b --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/doc.go @@ -0,0 +1,3 @@ +package impl_localfile + +// 包名不能采用中分线,而且前面的包已经说明了是log包,这个子文件夹不需要在加上log diff --git a/trunk/goutil/logUtil/impl-localfile/log_file.go b/trunk/goutil/logUtil/impl-localfile/log_file.go new file mode 100644 index 0000000..a3da298 --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/log_file.go @@ -0,0 +1,203 @@ +package impl_localfile + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strconv" + "sync" + "time" +) + +const ( + con_FILE_SUFFIX = "txt" + + defaultPanicNum = 10 // 默认支持的连续panic次数 + defaultPanicIntervals = 5 // 秒 + + fileMaxSize = 1 << 27 // 128M +) + +type fileLog struct { + logPath string + + // fileDir + fileName 用于判断是否需要切换日志文件 + fileDir string + fileName string + lv levelType + + msgChan chan logObject + + f *os.File + + // 如果出现未知panic并一直发生,那么应该要真正的panic + panicNum int + panicTime time.Time + + // 当前已经写入的大小 + curWriteSize int64 +} + +func newLog(logPath string, lv levelType) *fileLog { + f := &fileLog{ + logPath: logPath, + fileDir: "", + fileName: "", + msgChan: make(chan logObject, 10240), + lv: lv, + f: nil, + } + + go f.loop() + + return f +} + +func (f *fileLog) loop() { + defer func() { + if r := recover(); r != nil { + // 将错误输出,而不是记录到文件,是因为可能导致死循环 + fmt.Println("log file lv:", f.lv, " err:", r) + + // 超过间隔时间重置 + if time.Since(f.panicTime)/time.Second > defaultPanicIntervals { + f.panicNum = 0 + f.panicTime = time.Now() + } + + // 这里处理连续panic 也是防止循环调用形成死循环 + f.panicNum++ + if f.panicNum >= defaultPanicNum { + panic(r) + } + } + go f.loop() + }() + + for logItem := range f.msgChan { + f.writeLog(logItem) + } +} + +func (f *fileLog) writeLog(logObj logObject) { + // 由于不使用直接 logUtil 调用,logUtilPlus 存在终端打印的控制,所以 logUtil 只作为纯文件日志组件 + // if PrintImportantLog && (logObj.level == warn || logObj.level == _error || logObj.level == fatal) { + // fmt.Println(logObj.logInfo) + // } + + // 检查是否需要去切换文件或者创建文件 + f.checkFileAndLoop(logObj.level, logObj.ifIncludeHour) + + f.curWriteSize += int64(len(logObj.logInfo)) + _, _ = f.f.WriteString(logObj.logInfo) +} + +// 检查文件是否需要切换 +func (f *fileLog) checkFileAndLoop(level levelType, ifIncludeHour bool) { + // 获取当前时间 + now := time.Now() + fileAbsoluteDirectory := filepath.Join(f.logPath, strconv.Itoa(now.Year()), strconv.Itoa(int(now.Month()))) + fileName := "" + + if ifIncludeHour { + fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02-15"), level, con_FILE_SUFFIX) + } else { + fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02"), level, con_FILE_SUFFIX) + } + + // 说明已经存在, 检查文件大小,并切换 + if f.f != nil && f.fileName == fileName && f.fileDir == fileAbsoluteDirectory { + if f.curWriteSize < fileMaxSize { + return + } + + // 大小超额切换文件 + f.switchFile() + return + } + + // 创建文件夹 + if f.fileDir != fileAbsoluteDirectory { + if err := os.MkdirAll(fileAbsoluteDirectory, os.ModePerm|os.ModeTemporary); err != nil { + log.Println("make dir all is err :", err) + } + f.fileDir = fileAbsoluteDirectory + } + + // 创建文件 + if f.fileName != fileName { + f.fileName = fileName + } + + // 到这里说明要切换了,关闭之前的file + if f.f != nil { + _ = f.f.Close() + } + + // 正常切换文件 + f.createFile() +} + +func (f *fileLog) switchFile() { + // 关闭文件 + _ = f.f.Close() + + // 重命名 + curFileName := filepath.Join(f.fileDir, f.fileName) + newFileName := filepath.Join(f.fileDir, fmt.Sprintf("%s.%s.%s", f.fileName, time.Now().Format("15-04-05.999"), con_FILE_SUFFIX)) + err := os.Rename(curFileName, newFileName) + if err != nil { + log.Println(err) + } + + // 再次创建文件 + f.createFile() +} + +func (f *fileLog) createFile() { + // 得到最终的文件绝对路径 + fileAbsolutePath := filepath.Join(f.fileDir, f.fileName) + // 打开文件(如果文件存在就以读写模式打开,并追加写入;如果文件不存在就创建,然后以写模式打开。) + file, err := os.OpenFile(fileAbsolutePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm|os.ModeTemporary) + if err != nil { + log.Println("open file is err :", err) + return + } + f.f = file + stat, _ := file.Stat() + f.curWriteSize = stat.Size() +} + +func (f *fileLog) SetLogPath(logPath string) { + f.logPath = logPath +} + +// Close 最后等待 3s 钟 +func (f *fileLog) Close(wg *sync.WaitGroup, waitFinish bool) { + ticker := time.NewTicker(3 * time.Second) + defer func() { + ticker.Stop() + + if f.f != nil { + _ = f.f.Close() + } + + wg.Done() + }() + + for { + if !waitFinish || len(f.msgChan) == 0 { + break + } + + select { + case <-ticker.C: + fmt.Println("wait close log file timeout:3s") + return + default: + // 1ms 写入文件500条,最多等待 15ms 检查一次就行 + time.Sleep(15 * time.Millisecond) + } + } +} diff --git a/trunk/goutil/logUtil/impl-localfile/logger.go b/trunk/goutil/logUtil/impl-localfile/logger.go new file mode 100644 index 0000000..a0bca06 --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/logger.go @@ -0,0 +1,223 @@ +package impl_localfile + +import ( + "fmt" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "goutil/fileUtil" + "goutil/timeUtil" +) + +const ( + con_SEPERATOR = "\n------------------------------------------------------\n" +) + +// Logger +// @description: 日志对象 +type Logger struct { + logPath string + loggerMap map[levelType]*fileLog + bufPool *sync.Pool + + // 压缩属性 + // 上一次日志压缩的日期 时间戳 + preCompressDate int64 + // 压缩锁对象 + compressLock sync.Mutex +} + +// NewLogger +// @description: 构造日志对象 +// parameter: +// +// @*Logger: +func NewLogger() *Logger { + log := &Logger{ + logPath: "Log", + loggerMap: make(map[levelType]*fileLog, 4), + bufPool: &sync.Pool{ + New: func() interface{} { + return &strings.Builder{} + }, + }, + } + + // 初始化基础日志 + for lv := range levels { + log.loggerMap[levelType(lv)] = newLog(log.logPath, levelType(lv)) + } + return log +} + +// log +// @description: 记录日志 +// parameter: +// +// @receiver l: +// @logInfo: +// @lv: +// @ifIncludeHour: +// +// return: +func (l *Logger) log(logInfo string, lv levelType, ifIncludeHour bool) { + buf := l.bufPool.Get().(*strings.Builder) + defer l.bufPool.Put(buf) + buf.Reset() + + // 组装所有需要写入的内容 + // 获取当前时间 + buf.WriteString(time.Now().Format("2006-01-02 15:04:05")) + buf.WriteString("---->\n") + buf.WriteString(logInfo) + // 加上最后的分隔符 + buf.WriteString(con_SEPERATOR) + + // 写入之前检查 + l.checkCompress() + + // 构造对象并添加到队列中 + l.loggerMap[lv].msgChan <- newLogObject(buf.String(), lv, ifIncludeHour) +} + +// SetLogPath +// @description: 设置文件路径 +// parameter: +// +// @receiver l: +// @_logPath: +// +// return: +func (l *Logger) SetLogPath(_logPath string) { + l.logPath = _logPath + for _, lf := range l.loggerMap { + lf.SetLogPath(_logPath) + } +} + +// checkCompress +// @description: 压缩 +// parameter: +// +// @receiver l: +// +// return: +func (l *Logger) checkCompress() { + // 不用压缩,直接返回 + if !l.preCompress() { + return + } + + // 日志压缩 + go l.doCompress() +} + +// closeLogFile +// @description: 关闭日志文件 +// parameter: +// +// @receiver l: +// +// return: +func (l *Logger) closeLogFile() { + // 需要压缩文件,关闭已经打开的文件 + for _, lf := range l.loggerMap { + _ = lf.f.Close() + } +} + +// preCompress 确定是否要压缩 +func (l *Logger) preCompress() bool { + // 检查是否需要进行数据压缩 + nowDate := timeUtil.GetDate(time.Now()).Unix() + if nowDate == l.preCompressDate { + return false + } + + l.compressLock.Lock() + defer l.compressLock.Unlock() + + // 上一次压缩的时间 + if nowDate == l.preCompressDate { + return false + } + + l.closeLogFile() + + l.preCompressDate = nowDate + + return true +} + +// 日志压缩 +func (l *Logger) doCompress() { + defer func() { + if r := recover(); r != nil { + // 将错误输出,而不是记录到文件,是因为可能导致死循环 + fmt.Println(r) + } + }() + + // 获取昨天的日期,并获取昨天对应的文件夹 + yesterday := time.Now().AddDate(0, 0, -1) + dateString := timeUtil.Format(yesterday, "yyyy-MM-dd") + fileAbsoluteDirectory := filepath.Join(l.logPath, strconv.Itoa(yesterday.Year()), strconv.Itoa(int(yesterday.Month()))) + + // 判断是否已经存在压缩文件 + compressFileName := fmt.Sprintf("%s.tar.gz", dateString) + compressAbsolutePath := filepath.Join(fileAbsoluteDirectory, compressFileName) + if exists, err := fileUtil.IsFileExists(compressAbsolutePath); err == nil && exists { + return + } + + // 获取昨天的文件列表 + fileList, err := fileUtil.GetFileList2(fileAbsoluteDirectory, dateString, con_FILE_SUFFIX) + if err != nil { + fmt.Printf("logUtil.compress.fileUtil.GetFileList2 err:%s\n", err) + return + } + if len(fileList) == 0 { + return + } + + // 进行tar操作,得到yyyy-MM-dd.tar + tarFileName := fmt.Sprintf("%s.tar", dateString) + tarAbsolutePath := filepath.Join(fileAbsoluteDirectory, tarFileName) + if err := fileUtil.Tar(fileList, tarAbsolutePath); err != nil { + fmt.Printf("logUtil.compress.fileUtil.Tar err:%s\n", err) + } + + // 进行gzip操作,得到yyyy-MM-dd.tar.gz + if err := fileUtil.Gzip(tarAbsolutePath, ""); err != nil { + fmt.Printf("logUtil.compress.fileUtil.Gzip err:%s\n", err) + } + + // 删除原始文件 + for _, item := range fileList { + _ = fileUtil.DeleteFile(item) + } + + // 删除tar文件 + _ = fileUtil.DeleteFile(tarAbsolutePath) +} + +// getLog +// @description: 组装日志 +// parameter: +// +// @format: +// @args: +// +// return: +// +// @string: +func getLog(format string, args ...interface{}) string { + if len(args) == 0 { + return format + } else { + return fmt.Sprintf(format, args...) + } +} diff --git a/trunk/goutil/logUtil/impl-localfile/logtype.go b/trunk/goutil/logUtil/impl-localfile/logtype.go new file mode 100644 index 0000000..0002341 --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/logtype.go @@ -0,0 +1,33 @@ +package impl_localfile + +type levelType int + +// 日志等级 +const ( + // info 信息 + info levelType = iota + + // warn 警告 + warn + + // debug 调试 + debug + + // _error 错误 + _error + + // fatal 致命 + fatal +) + +var levels = [...]string{ + "info", + "warn", + "debug", + "error", + "fatal", +} + +func (t levelType) String() string { + return levels[t] +} diff --git a/trunk/goutil/logUtil/impl-localfile/model.go b/trunk/goutil/logUtil/impl-localfile/model.go new file mode 100644 index 0000000..db70bec --- /dev/null +++ b/trunk/goutil/logUtil/impl-localfile/model.go @@ -0,0 +1,20 @@ +package impl_localfile + +type logObject struct { + // 日志信息 + logInfo string + + // 日志等级 + level levelType + + // 日志文件名称是否包含小时 + ifIncludeHour bool +} + +func newLogObject(logInfo string, level levelType, ifIncludeHour bool) logObject { + return logObject{ + logInfo: logInfo, + level: level, + ifIncludeHour: ifIncludeHour, + } +} diff --git a/trunk/goutil/logUtil/interface.go b/trunk/goutil/logUtil/interface.go new file mode 100644 index 0000000..2ace17a --- /dev/null +++ b/trunk/goutil/logUtil/interface.go @@ -0,0 +1,52 @@ +package logUtil + +// ILog +// @description: 日志接口 +type ILog interface { + // InfoLog + // @description: 信息日志记录 + // parameter: + // @format:日志格式 + // @args:参数列表 + // return: + InfoLog(format string, args ...interface{}) + + // DebugLog + // @description: 调试日志记录 + // parameter: + // @format:日志格式 + // @args:参数列表 + // return: + DebugLog(format string, args ...interface{}) + + // WarnLog + // @description: 警告日志记录 + // parameter: + // @format:日志格式 + // @args:参数列表 + // return: + WarnLog(format string, args ...interface{}) + + // ErrorLog + // @description: 错误日志记录 + // parameter: + // @format:日志格式 + // @args:参数列表 + // return: + ErrorLog(format string, args ...interface{}) + + // FatalLog + // @description: 致命错误日志记录 + // parameter: + // @format:日志格式 + // @args:参数列表 + // return: + FatalLog(format string, args ...interface{}) + + // CloseLog + // @description: 关闭日志 + // parameter: + // @waitFinish:是否等待日志 + // return: + CloseLog(waitFinish bool) +} diff --git a/trunk/goutil/logUtil/log.go b/trunk/goutil/logUtil/log.go new file mode 100644 index 0000000..8410351 --- /dev/null +++ b/trunk/goutil/logUtil/log.go @@ -0,0 +1,57 @@ +package logUtil + +import ( + "goutil/logUtil/impl-localfile" +) + +// 定义一个全局的日志对象 +var ( + // 日志列表 + logs []ILog + + // 文件log + fileLog *impl_localfile.Logger +) + +func init() { + // 提供默认的日志对象 + fileLog = impl_localfile.NewLogger() + logs = append(logs, fileLog) +} + +// AddLogger +// @description: 添加日志对象 +// parameter: +// +// @l:日志实现 +// +// return: +func SettingLogs(_logs []ILog) { + if _logs == nil || len(_logs) == 0 { + panic("_logs不能为nil或者len(_logs)==0") + } + + logs = _logs +} + +// GetLocalFileLog +// @description: 获取文件日志对象 +// parameter: +// return: +// +// @*log_localfile.Logger: +func GetLocalFileLog() *impl_localfile.Logger { + return fileLog +} + +// SetLogPath +// @description: 设置文件日志路径 +// parameter: +// +// @_logPath:路径 +// +// return: +// Deprecated: use GetLocalFileLog().SetLogPath(_logPath) api instead +func SetLogPath(_logPath string) { + fileLog.SetLogPath(_logPath) +} diff --git a/trunk/goutil/logUtil/log_api.go b/trunk/goutil/logUtil/log_api.go new file mode 100644 index 0000000..92f898c --- /dev/null +++ b/trunk/goutil/logUtil/log_api.go @@ -0,0 +1,127 @@ +package logUtil + +import ( + "fmt" + "runtime" + "strings" +) + +const ( + con_MIN_SKIP = 1 + con_MAX_SKIP = 10 +) + +// InfoLog 信息日志记录 +// format:日志格式 +// args:参数列表 +func InfoLog(format string, args ...interface{}) { + for _, log := range logs { + log.InfoLog(format, args...) + } +} + +// WarnLog 警告日志记录 +// format:日志格式 +// args:参数列表 +func WarnLog(format string, args ...interface{}) { + for _, log := range logs { + log.WarnLog(format, args...) + } +} + +// DebugLog 调试日志记录 +// format:日志格式 +// args:参数列表 +func DebugLog(format string, args ...interface{}) { + for _, log := range logs { + log.DebugLog(format, args...) + } +} + +// ErrorLog 错误日志记录 +// format:日志格式 +// args:参数列表 +func ErrorLog(format string, args ...interface{}) { + for _, log := range logs { + log.ErrorLog(format, args...) + } +} + +// FatalLog 致命错误日志记录 +// format:日志格式 +// args:参数列表 +func FatalLog(format string, args ...interface{}) { + for _, log := range logs { + log.FatalLog(format, args...) + } +} + +// Close +// @description: 关闭日志 +// parameter: +// @waitFinish:是否等待关闭完成 +// return: +func Close(waitFinish bool) { + for _, log := range logs { + log.CloseLog(waitFinish) + } +} + +//--------------------------Deprecated methods start---------------------------- + +// Log 日志记录 +// Deprecated: use XXXLog api instead +func Log(logInfo string, lv LogType, ifIncludeHour bool) { + switch lv { + case Info: + InfoLog(logInfo) + case Warn: + WarnLog(logInfo) + case Debug: + DebugLog(logInfo) + case Error: + ErrorLog(logInfo) + case Fatal: + FatalLog(logInfo) + } +} + +// NormalLog 日志记录 +// Deprecated: use XXXLog api instead +func NormalLog(logInfo string, level LogType) { + Log(logInfo, level, true) +} + +// LogAndPrint 日志记录 +// Deprecated: use XXXLog api instead +func LogAndPrint(logInfo string, level LogType) { + NormalLog(logInfo, level) + fmt.Println(logInfo) +} + +// LogUnknownError 日志记录 +func LogUnknownError(r interface{}, args ...string) { + buf := strings.Builder{} + buf.WriteString(fmt.Sprintf("通过recover捕捉到的未处理异常:%v \n", r)) + + // 获取附加信息 + if len(args) > 0 { + buf.WriteString("附加信息:") + buf.WriteString(strings.Join(args, "-")) + buf.WriteString("\n") + } + + // 获取堆栈信息 + for skip := con_MIN_SKIP; skip <= con_MAX_SKIP; skip++ { + _, file, line, ok := runtime.Caller(skip) + if !ok { + break + } + buf.WriteString(fmt.Sprintf("skip = %d, file = %s, line = %d \n", skip, file, line)) + buf.WriteString("\n") + } + + ErrorLog(buf.String()) +} + +//--------------------------Deprecated methods end---------------------------- diff --git a/trunk/goutil/logUtil/log_test.go b/trunk/goutil/logUtil/log_test.go new file mode 100644 index 0000000..c35cf5e --- /dev/null +++ b/trunk/goutil/logUtil/log_test.go @@ -0,0 +1,67 @@ +package logUtil + +import ( + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + impl_console "goutil/logUtil/impl-console" + impl_es "goutil/logUtil/impl-es" + impl_localfile "goutil/logUtil/impl-localfile" +) + +func TestAllLog(t *testing.T) { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + logPath := filepath.Dir(path) + + GetLocalFileLog().SetLogPath(logPath) + + //添加控制台日志 + consoleLog := impl_console.NewLogger() + + //添加es日志 + urls := []string{"http://10.1.0.71:9101/"} + eslog, err := impl_es.NewLogger(urls, "", "", "es_log_test", "les_log_test_innerid", nil) + if err != nil { + t.Error("esLog 创建失败") + } + SettingLogs([]ILog{consoleLog, eslog, impl_localfile.NewLogger()}) + + for i := 1; i < 10; i++ { + InfoLog("Info记录") + InfoLog("Info记录2:%v %v", i, time.Now()) + + DebugLog("Debug记录") + DebugLog("Debug记录2:%v %v", i, time.Now()) + + WarnLog("Warn记录") + WarnLog("Warn记录2:%v %v", i, time.Now()) + + ErrorLog("Error记录") + ErrorLog("ErrorLog记录2:%v %v", i, time.Now()) + + FatalLog("Fatal记录") + FatalLog("Fatal记录2:%v %v", i, time.Now()) + } + + time.Sleep(time.Second * 5) + + Close(true) +} + +func BenchmarkInfoLog(b *testing.B) { + file, _ := exec.LookPath(os.Args[0]) + path, _ := filepath.Abs(file) + logPath := filepath.Dir(path) + + GetLocalFileLog().SetLogPath(logPath) + + for i := 0; i < b.N; i++ { + DebugLog("Debug 记录") + InfoLog("info记录 :%v", time.Now()) + } + Close(true) +} diff --git a/trunk/goutil/logUtil/log_type.go b/trunk/goutil/logUtil/log_type.go new file mode 100644 index 0000000..5d618f3 --- /dev/null +++ b/trunk/goutil/logUtil/log_type.go @@ -0,0 +1,44 @@ +package logUtil + +type LogType int + +// 日志等级 +const ( + // Info 信息 + Info LogType = iota + + // Warn 警告 + Warn + + // Debug 调试 + Debug + + // Error 错误 + Error + + // Fatal 致命 + Fatal +) + +// String +// @description: 类型转化为字符串 +// parameter: +// @receiver t: +// return: +// @string: +func (t LogType) String() string { + switch t { + case Info: + return "Info" + case Warn: + return "Warn" + case Debug: + return "Debug" + case Error: + return "Error" + case Fatal: + return "Fatal" + } + + return "LogTypeDefault" +} diff --git a/trunk/goutil/logUtil/readme.md b/trunk/goutil/logUtil/readme.md new file mode 100644 index 0000000..034133b --- /dev/null +++ b/trunk/goutil/logUtil/readme.md @@ -0,0 +1,50 @@ +### 日志帮助类,默认实现 文本文件,控制台,elasticsearch(简写es) 三种记录方式。 + + 1.在调用设置参数方法(SettingLogs)之前,会开启文本文件记录方式. + 2.在调用设置参数方法(SettingLogs)之后,默认记录方式关闭,根据参数配置决定开启哪些日志记录方式. + 3.注意:由于日志采用了缓存,关闭程序可能会导致未写入的日志丢失. + 4.日志帮助类内部错误日志默认路径为程序执行目录下Log子目录下,可以帮助排除日志帮助类内部异常. + +#### =======================>注意事项<========================= + + 1.生产环境禁止开始控制台输出,频繁的控制台输出造成性能瓶颈。 + +#### =======================>使用方法说明<========================= + +1.引入包 +2.配置日志输出,如果采用默认输出,可以跳过这一步 +3.调用方法输出日志 + +```go +import ( + "goutil/logUtil" + impl_console "goutil/logUtil/impl-console" + impl_es "goutil/logUtil/impl-es" + impl_localfile "goutil/logUtil/impl-localfile" +) + +func init(){ + //添加控制台日志 + consoleLog := impl_console.NewLogger() + + //添加es日志 + urls := []string{"http://10.1.0.71:9101/"} + eslog, err := impl_es.NewLogger(urls, "", "", "es_log_test", "les_log_test_innerid", nil) + if err != nil { + panic("esLog 创建失败") + } + + // 设置程序日志 + logUtil.SettingLogs([]ILog{consoleLog, eslog, impl_localfile.NewLogger()}) +} +``` + +3.调用输出方法输出 + +```go +logUtil.DebugLog("加载游戏配置start"); +logUtil.InfoLog("加载游戏配置start"); +logUtil.WarnLog("加载游戏配置start"); +logUtil.ErrorLog("加载游戏配置start"); +logUtil.FatalLog("加载游戏配置start"); +``` \ No newline at end of file diff --git a/trunk/goutil/logUtilPlus/logUtilPlus.go b/trunk/goutil/logUtilPlus/logUtilPlus.go new file mode 100644 index 0000000..afbcfec --- /dev/null +++ b/trunk/goutil/logUtilPlus/logUtilPlus.go @@ -0,0 +1,146 @@ +package logUtilPlus + +import ( + "fmt" + "goutil/debugUtil" + "goutil/esLogUtil" + "goutil/logUtil" + "log" + "strings" +) + +var ( + enableDebugLog = true + enableInfoLog = true + enableWarnLog = true +) + +// 设置日志级别 +// debug / all 时全部开启 +// info 记录 info/warn/error/fatal +// warn 记录 warn/error/fatal +// error 记录 error/fatal +func SetLevel(level string) { + // 大写不敏感 + level = strings.ToLower(level) + + //全部开启 + if level == "debug" || level == "all" { + // debug / all 时全部开启 + enableInfoLog = true + enableDebugLog = true + enableWarnLog = true + } else if level == "info" { + // info 记录 info/warn/error/fatal + enableDebugLog = false + enableInfoLog = true + enableWarnLog = true + } else if level == "warn" { + // warn 记录 warn/error/fatal + enableDebugLog = false + enableInfoLog = false + enableWarnLog = true + } else if level == "error" { + // error 记录 error/fatal + enableInfoLog = false + enableDebugLog = false + enableWarnLog = false + } +} + +// 启动ES日志系统(不调用则不启动ES日志系统,默认记录本地) +// 参数: +// +// esUrls:ES地址(多个地址使用,分割) +// name:IndexName +// serverGroupId:服务器组Id +// +// 返回值: +// +// 结果状态 +func Start(esUrls string, name string, serverGroupId int32) { + esLogUtil.Start(esUrls, name, serverGroupId) +} + +// 停止服务 +func Stop() { + esLogUtil.Stop() +} + +// 调试日志 +func InfoLog(format string, args ...interface{}) { + if !enableInfoLog { + return + } + PrintAndWriteLog(logUtil.Info, format, args...) +} + +// 警告日志 +func WarnLog(format string, args ...interface{}) { + if !enableWarnLog { + return + } + PrintAndWriteLog(logUtil.Warn, format, args...) +} + +// 调试日志 +func DebugLog(format string, args ...interface{}) { + if !enableDebugLog { + return + } + PrintAndWriteLog(logUtil.Debug, format, args...) +} + +// 错误日志 +func ErrorLog(format string, args ...interface{}) { + PrintAndWriteLog(logUtil.Error, format, args...) +} + +// 致命错误日志 +func FatalLog(format string, args ...interface{}) { + PrintAndWriteLog(logUtil.Fatal, format, args...) +} + +// 打印到控制台并写日志 +func PrintAndWriteLog(logType logUtil.LogType, format string, args ...interface{}) { + //控制台打印一行 + PrintLog(format, args...) + + if len(args) <= 0 { + logUtil.NormalLog(format, logType) + esLogUtil.NormalLog(format, logType) + } else { + logUtil.NormalLog(fmt.Sprintf(format, args...), logType) + esLogUtil.NormalLog(fmt.Sprintf(format, args...), logType) + } + + if debugUtil.IsDebug() && (logType == logUtil.Warn || logType == logUtil.Error || logType == logUtil.Fatal) { + StdLog(format, args...) + } +} + +// 控制台打印 +func PrintLog(format string, args ...interface{}) { + //避免非DEBUG模式下组装字符串,提前判断一次 + if debugUtil.IsDebug() { + debugUtil.Println(fmt.Sprintf(format, args...)) + } +} + +// 标准库控制台输出打印 +func StdLog(format string, args ...interface{}) { + if len(args) <= 0 { + log.Print(format) + } else { + log.Print(fmt.Sprintf(format, args...)) + } +} + +// 记录未知错误日志 +// 参数: +// +// err:recover获取到的错误对象 +// args:附加参数 +func LogUnknownError(err interface{}, args ...string) { + logUtil.LogUnknownError(err, args...) +} diff --git a/trunk/goutil/logUtilPlus/logUtilPlus_test.go b/trunk/goutil/logUtilPlus/logUtilPlus_test.go new file mode 100644 index 0000000..5586a8f --- /dev/null +++ b/trunk/goutil/logUtilPlus/logUtilPlus_test.go @@ -0,0 +1,35 @@ +package logUtilPlus + +import ( + "testing" + "time" +) + +func TestWrite(t *testing.T) { + Start("http://10.254.0.242:9200", "20008_gs_log", 20008) + + InfoLog("日志测试") + WarnLog("日志测试") + DebugLog("日志测试") + ErrorLog("日志测试") + FatalLog("日志测试") + + time.Sleep(1 * time.Second) + Stop() +} + +func BenchmarkWrite(b *testing.B) { + Start("http://10.254.0.242:9200", "20008_gs_log", 20008) + b.ResetTimer() + for i := 0; i < b.N; i++ { + InfoLog("日志测试%d", i) + WarnLog("日志测试%d", i) + DebugLog("日志测试%d", i) + ErrorLog("日志测试%d", i) + FatalLog("日志测试%d", i) + } + b.StopTimer() + + time.Sleep(1 * time.Second) + Stop() +} diff --git a/trunk/goutil/lz4Util/doc.go b/trunk/goutil/lz4Util/doc.go new file mode 100644 index 0000000..0a3c4f8 --- /dev/null +++ b/trunk/goutil/lz4Util/doc.go @@ -0,0 +1,4 @@ +/* +lz4压缩、解压缩相关的助手包 +*/ +package lz4Util diff --git a/trunk/goutil/lz4Util/lz4.go b/trunk/goutil/lz4Util/lz4.go new file mode 100644 index 0000000..f466ecc --- /dev/null +++ b/trunk/goutil/lz4Util/lz4.go @@ -0,0 +1,36 @@ +package lz4Util + +import ( + "fmt" + "github.com/bkaradzic/go-lz4" +) + +// 压缩 +// data:待压缩数据 +// 返回值: +// 压缩后数据 +// 对应的错误 +func Compress(data []byte)([]byte, error) { + compressed, err := lz4.Encode(nil, data) + if err != nil { + fmt.Println("Failed to encode:", err) + return nil,err + } + + return compressed,nil +} + +// 解压缩 +// data:待解压缩数据 +// 返回值: +// 解压缩后数据 +// 对应的错误 +func Decompress(data []byte) ([]byte, error){ + decompressed, err := lz4.Decode(nil, data) + if err != nil { + fmt.Println("Failed to decode:", err) + return nil,err + } + + return decompressed,nil +} \ No newline at end of file diff --git a/trunk/goutil/lz4Util/lz4_test.go b/trunk/goutil/lz4Util/lz4_test.go new file mode 100644 index 0000000..39b08e6 --- /dev/null +++ b/trunk/goutil/lz4Util/lz4_test.go @@ -0,0 +1,65 @@ +package lz4Util + +import ( + "fmt" + "strings" + "testing" +) + +var ( + InitString = `{"Code":4,"Message":"IPNotAuthorized","Data":null}` + CompressBytes []byte +) + +//正确性测试 +func TestCompress(t *testing.T){ + s := "hello world" + repeatCount:= 1000 + toCompress := []byte(strings.Repeat(s, repeatCount)) + compressed,err := Compress(toCompress) + if err!=nil{ + fmt.Println(err) + } + fmt.Println("compressed Data:", string(compressed)) + + //decompress + decompressed,err := Decompress(compressed) + if err!=nil{ + fmt.Println(err) + } + + if strings.Repeat(s, repeatCount)!=string(decompressed){ + fmt.Println("有问题") + }else{ + fmt.Println("没问题") + } + + fmt.Println("\ndecompressed Data:", string(decompressed)) +} + +//BenchmarkCompress-12 28647 37948 ns/op +func BenchmarkCompress(b *testing.B) { + toCompress := []byte(strings.Repeat(InitString, 100)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _,err := Compress(toCompress) + if err!=nil{ + b.Log(err) + } + } + b.StopTimer() +} + +//BenchmarkDeCompress-12 267382 4127 ns/op +func BenchmarkDeCompress(b *testing.B) { + toCompress := []byte(strings.Repeat(InitString, 100)) + compressedData,_ := Compress(toCompress) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _,err := Decompress(compressedData) + if err!=nil{ + b.Log(err) + } + } + b.StopTimer() +} \ No newline at end of file diff --git a/trunk/goutil/mailUtil/doc.go b/trunk/goutil/mailUtil/doc.go new file mode 100644 index 0000000..9df80d3 --- /dev/null +++ b/trunk/goutil/mailUtil/doc.go @@ -0,0 +1,5 @@ +package mailUtil + +/* +邮件助手(用于发送邮件) +*/ diff --git a/trunk/goutil/mailUtil/simpleClient.go b/trunk/goutil/mailUtil/simpleClient.go new file mode 100644 index 0000000..bd36802 --- /dev/null +++ b/trunk/goutil/mailUtil/simpleClient.go @@ -0,0 +1,193 @@ +package mailUtil + +import ( + "bytes" + "crypto/tls" + "encoding/base64" + "fmt" + "io/ioutil" + "net" + "net/mail" + "net/smtp" + "path/filepath" + "strings" +) + +// SMTPClient实现 +type simpleClient struct { + host string + port int + isSSL bool + + senderName string + senderAddr string + senderPwd string +} + +// 返回一个simpleClient作为SMTPClient接口 +func SimpleSMTPClient(_host string, _port int, _isSSL bool, + name, address, password string) SMTPClient { + + return &simpleClient{ + host: _host, + port: _port, + isSSL: _isSSL, + + senderName: name, + senderAddr: address, + senderPwd: password, + } +} + +func (this *simpleClient) SetServer(_host string, _port int, _isSSL bool) { + this.host = _host + this.port = _port + this.isSSL = _isSSL +} + +func (this *simpleClient) SetSender(name, address, password string) { + this.senderName = name + this.senderAddr = address + this.senderPwd = password +} + +//发送邮件: +// mailTo 接收方列表 +// subject 主题 +// body 正文 +// isHtmlBody 正文是否html格式 +// attachFiles 附件 +func (this *simpleClient) SendMail( + mailTo []string, + subject, body string, isHtmlBody bool, + attachFiles []string) (err error) { + + defer func() { + if r := recover(); r != nil { + err = r.(error) + } + }() + + // 创建连接 + var conn net.Conn + + if this.isSSL { + // TLS config + tlsconfig := &tls.Config{ + InsecureSkipVerify: true, + ServerName: this.host, + } + conn, err = tls.Dial("tcp", fmt.Sprintf("%s:%d", this.host, this.port), tlsconfig) + } else { + conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", this.host, this.port)) + } + if err != nil { + return err + } + defer conn.Close() + + // 创建smtp.Client + c, err := smtp.NewClient(conn, this.host) + if err != nil { + return err + } + + // 验证信息 + auth := smtp.PlainAuth("", this.senderAddr, this.senderPwd, this.host) + if err = c.Auth(auth); err != nil { + return err + } + + // 发送方 + from := mail.Address{this.senderName, this.senderAddr} + // 接收方 + to := make([]string, 0, len(mailTo)) + for _, v := range mailTo { + to = append(to, "<"+v+">") + } + + // To && From + if err = c.Mail(from.Address); err != nil { + return err + } + + for _, v := range mailTo { + if err = c.Rcpt(v); err != nil { + return err + } + } + + // 边界 + boundary := "a40acf3c8b7200fc6b04c2f1b3da" + + buff := bytes.NewBuffer(nil) + + // 写入基本信息 + buff.WriteString(fmt.Sprintf("From: %s\r\n", from.String())) + buff.WriteString(fmt.Sprintf("To: %s\r\n", strings.Join(to, ", "))) + buff.WriteString(fmt.Sprintf("Subject: %s\r\n", subject)) + + // 写入邮件头部信息 + if len(attachFiles) > 0 { + buff.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", boundary)) + + // 写入正文的边界信息 + buff.WriteString(fmt.Sprintf("\r\n--%s\r\n", boundary)) + } + + // 写入正文头部 + if isHtmlBody { + buff.WriteString(fmt.Sprintf("Content-Type: text/html; charset=\"utf-8\"\r\n")) + } else { + buff.WriteString(fmt.Sprintf("Content-Type: text/plain; charset=\"utf-8\"\r\n")) + } + buff.WriteString("\r\n") + // 写入正文内容 + buff.WriteString(body) + + if len(attachFiles) > 0 { + for _, file := range attachFiles { + fileBytes, err := ioutil.ReadFile(file) + if err != nil { + return err + } + + _, fileName := filepath.Split(file) + + // 写入文件信息 + buff.WriteString(fmt.Sprintf("\r\n\r\n--%s\r\n", boundary)) + buff.WriteString("Content-Type: application/octet-stream\r\n") + buff.WriteString("Content-Transfer-Encoding: base64\r\n") + buff.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", fileName)) + + // 写入文件数据 + b := make([]byte, base64.StdEncoding.EncodedLen(len(fileBytes))) + base64.StdEncoding.Encode(b, fileBytes) + buff.Write(b) + } + + // 写入结束标识 + buff.WriteString(fmt.Sprintf("\r\n--%s--", boundary)) + } + + // Data + w, err := c.Data() + if err != nil { + return err + } + + // 写入邮件数据 + _, err = w.Write(buff.Bytes()) + if err != nil { + return err + } + + err = w.Close() + if err != nil { + return err + } + + c.Quit() + + return nil +} diff --git a/trunk/goutil/mailUtil/simpleClient_test.go b/trunk/goutil/mailUtil/simpleClient_test.go new file mode 100644 index 0000000..567adf9 --- /dev/null +++ b/trunk/goutil/mailUtil/simpleClient_test.go @@ -0,0 +1,20 @@ +package mailUtil + +import ( + "testing" +) + +func TestSendMail(t *testing.T) { + svr := SimpleSMTPClient("smtp.exmail.qq.com", 465, true, "name", "service@public.com", "Sv123456") + err := svr.SendMail([]string{"164760769@qq.com"}, + "邮件发送测试", + "+ Hello,This is an example for gxpath. +
+ + + + */ + doc := createNode("", xpath.RootNode) + xhtml := doc.createChildNode("html", xpath.ElementNode) + xhtml.addAttribute("lang", "en") + + // The HTML head section. + head := xhtml.createChildNode("head", xpath.ElementNode) + n := head.createChildNode("title", xpath.ElementNode) + n = n.createChildNode("Hello", xpath.TextNode) + n = head.createChildNode("meta", xpath.ElementNode) + n.addAttribute("name", "language") + n.addAttribute("content", "en") + // The HTML body section. + body := xhtml.createChildNode("body", xpath.ElementNode) + n = body.createChildNode("h1", xpath.ElementNode) + n = n.createChildNode(" This is a H1 ", xpath.TextNode) + ul := body.createChildNode("ul", xpath.ElementNode) + n = ul.createChildNode("li", xpath.ElementNode) + n = n.createChildNode("a", xpath.ElementNode) + n.addAttribute("id", "1") + n.addAttribute("href", "/") + n = n.createChildNode("Home", xpath.TextNode) + n = ul.createChildNode("li", xpath.ElementNode) + n = n.createChildNode("a", xpath.ElementNode) + n.addAttribute("id", "2") + n.addAttribute("href", "/about") + n = n.createChildNode("about", xpath.TextNode) + n = ul.createChildNode("li", xpath.ElementNode) + n = n.createChildNode("a", xpath.ElementNode) + n.addAttribute("id", "3") + n.addAttribute("href", "/account") + n = n.createChildNode("login", xpath.TextNode) + n = ul.createChildNode("li", xpath.ElementNode) + + n = body.createChildNode("p", xpath.ElementNode) + n = n.createChildNode("Hello,This is an example for gxpath.", xpath.TextNode) + + n = body.createChildNode("footer", xpath.ElementNode) + n = n.createChildNode("footer script", xpath.TextNode) + + return xhtml +} diff --git a/trunk/goutil/xmlUtil/gxpath/xpath/node.go b/trunk/goutil/xmlUtil/gxpath/xpath/node.go new file mode 100644 index 0000000..d8a5245 --- /dev/null +++ b/trunk/goutil/xmlUtil/gxpath/xpath/node.go @@ -0,0 +1,25 @@ +package xpath + +// A Node is an element node that can navigating to +// an node attribute and another node. +//type Node interface{} + +// A type of XPath node. +type NodeType int + +const ( + // A root node of the XML document or node tree. + RootNode NodeType = iota + + // An element, such as+ Hello,This is an example for gxpath. +
+ + + +` + root, errMsg := LoadFromString(xml) + if errMsg != nil { + t.Error("文件加载失败:", errMsg) + t.Fail() + return + } + + node := root.SelectElement("html/head/title") + if node == nil { + t.Error("读取节点失败:", "html/head/title") + } + + fmt.Println("节点值:", strings.TrimSpace(node.InnerText())) +} diff --git a/trunk/goutil/xmlUtil/node.go b/trunk/goutil/xmlUtil/node.go new file mode 100644 index 0000000..50e10d2 --- /dev/null +++ b/trunk/goutil/xmlUtil/node.go @@ -0,0 +1,345 @@ +package xmlUtil + +import ( + "bytes" + "container/list" + "encoding/xml" + "errors" + "fmt" + "io" + "strings" +) + +// A NodeType is the type of a Node. +type NodeType uint + +const ( + // 文档对象节点(根节点) + DocumentNode NodeType = iota + + // 头不声明节点 + DeclarationNode + + // 元素节点 + ElementNode + + // 节点文本 + TextNode + + // 注释 + CommentNode +) + +// xml节点对象 +type Node struct { + Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node + + Type NodeType + NodeName string + Namespace string + Attr []xml.Attr + + level int // node level in the tree +} + +// InnerText returns the text between the start and end tags of the object. +func (n *Node) InnerText() string { + if n.Type == TextNode || n.Type == CommentNode { + return n.NodeName + } + + var buf bytes.Buffer + for child := n.FirstChild; child != nil; child = child.NextSibling { + // filt commentnode + if child.Type == CommentNode { + continue + } + + buf.WriteString(child.InnerText()) + } + + return buf.String() +} + +func outputXML(buf *bytes.Buffer, n *Node) { + if n.Type == TextNode || n.Type == CommentNode { + buf.WriteString(strings.TrimSpace(n.NodeName)) + return + } + buf.WriteString("<" + n.NodeName) + for _, attr := range n.Attr { + if attr.Name.Space != "" { + buf.WriteString(fmt.Sprintf(` %s:%s="%s"`, attr.Name.Space, attr.Name.Local, attr.Value)) + } else { + buf.WriteString(fmt.Sprintf(` %s="%s"`, attr.Name.Local, attr.Value)) + } + } + buf.WriteString(">") + for child := n.FirstChild; child != nil; child = child.NextSibling { + outputXML(buf, child) + } + buf.WriteString(fmt.Sprintf("%s>", n.NodeName)) +} + +// OutputXML returns the text that including tags name. +func (n *Node) OutputXML() string { + var buf bytes.Buffer + outputXML(&buf, n) + return buf.String() +} + +// get all children +func (n *Node) Children() []*Node { + childrenList := make([]*Node, 0) + nowChild := n.FirstChild + for { + if nowChild == nil { + break + } + + childrenList = append(childrenList, nowChild) + if nowChild == n.LastChild { + break + } + + nowChild = nowChild.NextSibling + } + + return childrenList +} + +// get children len +func (n *Node) ChildrenLen() int { + var count int = 0 + nowChild := n.FirstChild + for { + if nowChild == nil { + break + } + + count += 1 + if nowChild == n.LastChild { + break + } + + nowChild = nowChild.NextSibling + } + + return count +} + +// get all attribute +func (n *Node) ALLAttribute() []xml.Attr { + if n.Attr == nil { + return nil + } + + return n.Attr[:] +} + +// 获取属性个数 +func (n *Node) AttributeLen() int { + if n.Attr == nil { + return 0 + } + + return len(n.Attr) +} + +// 输出所有(主要用于测试) +func (this *Node) OutALL() { + stack := list.New() + tmpItem := this + for { + if tmpItem != nil { + stack.PushBack(tmpItem) + tmpItem = tmpItem.NextSibling + } + + break + } + + for { + if stack.Len() <= 0 { + break + } + + nowNode := stack.Front().Value.(*Node) + stack.Remove(stack.Front()) + for _, item := range nowNode.Children() { + stack.PushFront(item) + } + + fmt.Println("name:", nowNode.NodeName, " level: ", nowNode.level, " attr:", nowNode.Attr) + } +} + +// SelectElements finds child elements with the specified name. +func (n *Node) SelectElements(name string) []*Node { + return Find(n, name) +} + +// SelectElements finds child elements with the specified name. +func (n *Node) SelectElement(name string) *Node { + return FindOne(n, name) +} + +// SelectAttr returns the attribute value with the specified name. +func (n *Node) SelectAttr(name string) (string, bool) { + var local, space string + local = name + if i := strings.Index(name, ":"); i > 0 { + space = name[:i] + local = name[i+1:] + } + for _, attr := range n.Attr { + if attr.Name.Local == local && attr.Name.Space == space { + return attr.Value, true + } + } + return "", false +} + +// 给节点添加属性值 +func addAttr(n *Node, key, val string) { + var attr xml.Attr + if i := strings.Index(key, ":"); i > 0 { + attr = xml.Attr{ + Name: xml.Name{Space: key[:i], Local: key[i+1:]}, + Value: val, + } + } else { + attr = xml.Attr{ + Name: xml.Name{Local: key}, + Value: val, + } + } + + n.Attr = append(n.Attr, attr) +} + +// 给节点添加子节点 +func addChild(parent, n *Node) { + n.Parent = parent + if parent.FirstChild == nil { + parent.FirstChild = n + } else { + parent.LastChild.NextSibling = n + n.PrevSibling = parent.LastChild + } + + parent.LastChild = n +} + +// 给节点添加兄弟节点 +func addSibling(sibling, n *Node) { + n.Parent = sibling.Parent + sibling.NextSibling = n + n.PrevSibling = sibling + if sibling.Parent != nil { + sibling.Parent.LastChild = n + } +} + +// 从reader里面加载xml文档 +func LoadFromReader(r io.Reader) (*Node, error) { + var ( + decoder = xml.NewDecoder(r) //// xml解码对象 + doc = &Node{Type: DocumentNode} + level = 0 + declared = false + ) + var prev *Node = doc + for { + tok, err := decoder.Token() + switch { + case err == io.EOF: + goto quit + case err != nil: + return nil, err + } + + switch tok := tok.(type) { + case xml.StartElement: + //if !declared { // if have no xml node,we also need add children + // return nil, errors.New("xml: document is invalid") + //} + // if there is no xml node.then create it + if declared == false { + level++ + + tmpNode := &Node{Type: DeclarationNode, level: level} + addAttr(tmpNode, "version", "1.0") + addAttr(tmpNode, "encoding", "UTF-8") + declared = true + if level == prev.level { + addSibling(prev, tmpNode) + } else if level > prev.level { + addChild(prev, tmpNode) + } + + prev = tmpNode + } + node := &Node{ + Type: ElementNode, + NodeName: tok.Name.Local, + Namespace: tok.Name.Space, + Attr: tok.Attr, + level: level, + } + //fmt.Println(fmt.Sprintf("start > %s : %d", node.Data, level)) + if level == prev.level { + addSibling(prev, node) + } else if level > prev.level { + addChild(prev, node) + } else if level < prev.level { + for i := prev.level - level; i > 1; i-- { + prev = prev.Parent + } + addSibling(prev.Parent, node) + } + prev = node + level++ + case xml.EndElement: + level-- + case xml.CharData: + node := &Node{Type: TextNode, NodeName: string(tok), level: level} + if level == prev.level { + addSibling(prev, node) + } else if level > prev.level { + addChild(prev, node) + } + case xml.Comment: + node := &Node{Type: CommentNode, NodeName: string(tok), level: level} + if level == prev.level { + addSibling(prev, node) + } else if level > prev.level { + addChild(prev, node) + } + case xml.ProcInst: // Processing Instruction + if declared || (!declared && tok.Target != "xml") { + return nil, errors.New("xml: document is invalid") + } + level++ + node := &Node{Type: DeclarationNode, level: level} + pairs := strings.Split(string(tok.Inst), " ") + for _, pair := range pairs { + pair = strings.TrimSpace(pair) + if i := strings.Index(pair, "="); i > 0 { + addAttr(node, pair[:i], strings.Trim(pair[i+1:], `"`)) + } + } + declared = true + if level == prev.level { + addSibling(prev, node) + } else if level > prev.level { + addChild(prev, node) + } + prev = node + case xml.Directive: + } + + } +quit: + return doc, nil +} diff --git a/trunk/goutil/xmlUtil/node_test.go b/trunk/goutil/xmlUtil/node_test.go new file mode 100644 index 0000000..1de6919 --- /dev/null +++ b/trunk/goutil/xmlUtil/node_test.go @@ -0,0 +1,186 @@ +package xmlUtil + +import ( + "strings" + "testing" +) + +func findNode(root *Node, name string) *Node { + node := root.FirstChild + for { + if node == nil || node.NodeName == name { + break + } + node = node.NextSibling + } + return node +} + +func childNodes(root *Node, name string) []*Node { + var list []*Node + node := root.FirstChild + for { + if node == nil { + break + } + if node.NodeName == name { + list = append(list, node) + } + node = node.NextSibling + } + return list +} + +func testNode(t *testing.T, n *Node, expected string) { + if n.NodeName != expected { + t.Fatalf("expected node name is %s,but got %s", expected, n.NodeName) + } +} + +func testAttr(t *testing.T, n *Node, name, expected string) { + for _, attr := range n.Attr { + if attr.Name.Local == name && attr.Value == expected { + return + } + } + t.Fatalf("not found attribute %s in the node %s", name, n.NodeName) +} + +func testValue(t *testing.T, val, expected string) { + if val != expected { + t.Fatalf("expected value is %s,but got %s", expected, val) + } +} + +func TestParse(t *testing.T) { + s := ` ++ Hello,This is an example for gxpath. +
+ + + \ No newline at end of file diff --git a/trunk/goutil/yamlUtil/config.yaml b/trunk/goutil/yamlUtil/config.yaml new file mode 100644 index 0000000..100c1b0 --- /dev/null +++ b/trunk/goutil/yamlUtil/config.yaml @@ -0,0 +1,54 @@ +# 配置根节点 +root: + # 是否是调试模式 + debug: true + + # Web服务监听地址和端口 + web_server_address: "192.168.50.85:10051" + + # Elasticsearch 地址 + es_urls: "http://10.252.0.70:18099" + + # 数据库配置 + db_config: + admin_db: + # 最大处于开启状态的连接数 + max_open_conns: 0 + + # 最大处于空闲状态的连接数 + max_idle_conns: 0 + + # 数据库连接字符串 + connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/admin?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true" + + user_db: + # 最大处于开启状态的连接数 + max_open_conns: 0 + + # 最大处于空闲状态的连接数 + max_idle_conns: 0 + + # 数据库连接字符串 + connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/user?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true" + + redis_config: + # 数据库连接字符串 + connection_string: "192.168.50.110:6379" + + # 密码, 如果要设置用户Id,则密码设置为:"UserId:Password" + password: "" + + # 数据库序号 + database: 5 + + # 最大活跃连接数 + max_active: 500 + + # 最大空闲的连接数 + max_idle: 200 + + # 连接空闲超时时间,单位:秒 + idle_timeout: 300 + + # 连接超时时间, 单位:秒 + dial_connect_timeout: 10 \ No newline at end of file diff --git a/trunk/goutil/yamlUtil/load.go b/trunk/goutil/yamlUtil/load.go new file mode 100644 index 0000000..f9104f4 --- /dev/null +++ b/trunk/goutil/yamlUtil/load.go @@ -0,0 +1,18 @@ +package yamlUtil + +import ( + "io/ioutil" + "log" +) + +// LoadFromFile 加载配置文件(这里不反序列化) +func LoadFromFile(filePath string) ([]byte, error) { + // 读取 YAML 文件 + yamlFile, err := ioutil.ReadFile(filePath) + if err != nil { + log.Fatalf("Error reading config.yaml file: %v", err) + return nil, err + } + + return yamlFile, nil +} diff --git a/trunk/goutil/yamlUtil/load_test.go b/trunk/goutil/yamlUtil/load_test.go new file mode 100644 index 0000000..c07a12b --- /dev/null +++ b/trunk/goutil/yamlUtil/load_test.go @@ -0,0 +1,14 @@ +package yamlUtil + +import ( + "fmt" + "goutil/jsonUtil" + "testing" +) + +func TestLoadYaml(t *testing.T) { + config, _ := LoadFromFile("config.yaml") + configStr, _ := jsonUtil.DeepClone(config) + + fmt.Print(configStr) +} diff --git a/trunk/goutil/zlibUtil/doc.go b/trunk/goutil/zlibUtil/doc.go new file mode 100644 index 0000000..164e080 --- /dev/null +++ b/trunk/goutil/zlibUtil/doc.go @@ -0,0 +1,4 @@ +/* +zlib压缩、解压缩相关的助手包 +*/ +package zlibUtil diff --git a/trunk/goutil/zlibUtil/zlib.go b/trunk/goutil/zlibUtil/zlib.go new file mode 100644 index 0000000..1822149 --- /dev/null +++ b/trunk/goutil/zlibUtil/zlib.go @@ -0,0 +1,48 @@ +package zlibUtil + +import ( + "bytes" + "compress/zlib" + "io" +) + +// 压缩 +// in:待压缩数据 +// level:压缩等级 +// 返回值: +// 压缩后数据 +// 对应的错误 +func Compress(data []byte, level int) ([]byte, error) { + var buffer bytes.Buffer + compressor, err := zlib.NewWriterLevelDict(&buffer, level, nil) + if err != nil { + return nil, err + } + + compressor.Write(data) + compressor.Close() + + return buffer.Bytes(), nil +} + +// 解压缩 +// in:待解压缩数据 +// 返回值: +// 解压缩后数据 +// 对应的错误 +func Decompress(data []byte) ([]byte, error) { + dataReader := bytes.NewReader(data) + zlibReader, err := zlib.NewReader(dataReader) + if err != nil { + return nil, err + } + defer zlibReader.Close() + + var buffer bytes.Buffer + _, err = io.Copy(&buffer, zlibReader) + if err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} diff --git a/trunk/goutil/zlibUtil/zlib_test.go b/trunk/goutil/zlibUtil/zlib_test.go new file mode 100644 index 0000000..b44d8af --- /dev/null +++ b/trunk/goutil/zlibUtil/zlib_test.go @@ -0,0 +1,77 @@ +package zlibUtil + +import ( + "compress/zlib" + "strings" + "testing" +) + +var ( + InitString = `{"Code":4,"Message":"IPNotAuthorized","Data":null}` + CompressBytes []byte +) + +func TestCompress(t *testing.T) { + data := ([]byte)(InitString) + result, _ := Compress(data, zlib.DefaultCompression) + // if isEqual(result, CompressBytes) == false { + // t.Errorf("压缩失败,期待%v,实际%v", InitBytes, result) + // } + + CompressBytes = result +} + +func TestDecompress(t *testing.T) { + data, _ := Decompress(CompressBytes) + result := string(data) + if result != InitString { + t.Errorf("解压缩失败,期待%s,实际%s", InitString, result) + } +} + +func isEqual(a, b []byte) bool { + if a == nil && b == nil { + return true + } else if a == nil || b == nil { + return false + } + + if len(a) != len(b) { + return false + } + + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + + return true +} + +//BenchmarkCompress-12 10989 107509 ns/op +func BenchmarkCompress(b *testing.B) { + toCompress := []byte(strings.Repeat(InitString, 100)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _,err :=Compress(toCompress, zlib.DefaultCompression) + if err!=nil{ + b.Log(err) + } + } + b.StopTimer() +} + +//BenchmarkDeCompress-12 99153 10802 ns/op +func BenchmarkDeCompress(b *testing.B) { + toCompress := []byte(strings.Repeat(InitString, 100)) + compressedData,_ := Compress(toCompress, zlib.DefaultCompression) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _,err := Decompress(compressedData) + if err!=nil{ + b.Log(err) + } + } + b.StopTimer() +} \ No newline at end of file