├── .gitattributes
├── .github
└── workflows
│ └── go.yml
├── .gitignore
├── .idea
├── .gitignore
├── LollipopGo2.8x.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── LICENSE
├── Proxy_Server
└── Proto
│ ├── Proto_Proxy.go
│ └── gen.bat
├── README.md
├── global_Interface
└── Interface_impl.go
├── go.mod
├── go.sum
├── leaf
├── processor.go
└── utils.go
├── log
├── LogService.go
├── example_test.go
└── log.go
├── lollipopGo.go
├── network
├── http.go
├── initNet.go
├── kcp.go
├── rpc.go
├── tcp.go
├── websocket.go
└── websocketPB.go
├── timer
├── exanple_test.go
└── timer.go
├── tools
├── DFA
│ └── dfa.go
├── collection
│ ├── array.go
│ ├── array_test.go
│ ├── map.go
│ └── set.go
├── database
│ ├── gorm.go
│ ├── mgo.go
│ └── redigo.go
├── deepcopy
│ ├── json.go
│ └── reflect.go
├── fs
│ ├── config.go
│ └── file.go
├── jsonutils
│ ├── array.go
│ └── object.go
├── mem
│ ├── lru_cache.go
│ └── ttl_cache.go
├── misc.go
├── num
│ ├── num.go
│ └── random.go
├── sample
│ ├── alias.go
│ └── rand.go
└── tz
│ ├── tz.go
│ └── tz_test.go
├── util
├── map.go
├── rand.go
├── slice.go
├── sort.go
├── strconv.go
└── util.go
└── version.go
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 | on: [push]
3 | jobs:
4 |
5 | build:
6 | name: Build
7 | runs-on: ubuntu-latest
8 | steps:
9 |
10 | - name: Set up Go 1.13
11 | uses: actions/setup-go@v1
12 | with:
13 | go-version: 1.13
14 | id: go
15 |
16 | - name: Check out code into the Go module directory
17 | uses: actions/checkout@v2
18 |
19 | - name: Get dependencies
20 | run: |
21 | go get -v -t -d ./...
22 | if [ -f Gopkg.toml ]; then
23 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
24 | dep ensure
25 | fi
26 |
27 | - name: Build
28 | run: go build -v .
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/LollipopGo2.8x.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Golangltd
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/Proxy_Server/Proto/Proto_Proxy.go:
--------------------------------------------------------------------------------
1 | package Proto_Proxy
2 |
3 | // 代理协议
4 | // 主协议 1
5 | const (
6 | INIYPROXY = iota // ==0
7 | C2Proxy_SendDataProto // C2Proxy_SendDataProto == 1 客户端发送协议
8 | Proxy2C_SendDataProto // Proxy2C_SendDataProto == 2
9 | G2Proxy_ConnDataProto // G2Proxy_ConnDataProto == 3 服务器链接协议
10 | Proxy2G_ConnDataProto // Proxy2G_ConnDataProto == 4
11 | G2Proxy_SendDataProto // G2Proxy_SendDataProto == 5 服务器发送代理
12 | Proxy2G_SendDataProto // Proxy2G_SendDataProto == 6
13 | C2Proxy_ConnDataProto // C2Proxy_ConnDataProto == 7 客户端连接协议
14 | Proxy2C_ConnDataProto // Proxy2C_ConnDataProto == 8
15 | )
16 |
17 | //------------------------------------------------------------------------------
18 | // C2Proxy_ConnDataProto 客户端连接协议
19 | type C2Proxy_ConnData struct {
20 | Protocol int
21 | Protocol2 int
22 | OpenID string // 客户端第一次发空
23 | }
24 |
25 | // Proxy2C_ConnDataProto
26 | type Proxy2C_ConnData struct {
27 | Protocol int
28 | Protocol2 int
29 | OpenID string
30 | }
31 |
32 | //------------------------------------------------------------------------------
33 | // G2Proxy_SendDataProto 服务器发送代理
34 | type G2Proxy_SendData struct {
35 | Protocol int
36 | Protocol2 int
37 | OpenID string
38 | Data interface{}
39 | }
40 |
41 | // Proxy2G_SendDataProto
42 | type Proxy2G_SendData struct {
43 | Protocol int
44 | Protocol2 int
45 | OpenID string
46 | Data interface{}
47 | }
48 |
49 | //------------------------------------------------------------------------------
50 | // G2Proxy_ConnDataProto 服务器链接协议
51 | type G2Proxy_ConnData struct {
52 | Protocol int
53 | Protocol2 int
54 | ServerID string
55 | }
56 |
57 | // Proxy2G_ConnDataProto
58 | type Proxy2G_ConnData struct {
59 | Protocol int
60 | Protocol2 int
61 | }
62 |
63 | //------------------------------------------------------------------------------
64 | // C2Proxy_SendDataProto 客户端发送协议
65 | type C2Proxy_SendData struct {
66 | Protocol int
67 | Protocol2 int
68 | ServerID string
69 | Data interface{} //
70 | }
71 |
72 | //type Proxy2GS_InitHall struct {
73 | // Protocol int
74 | // Protocol2 int
75 | // Token string
76 | // OpenID string
77 | // LoginType string
78 | // //IMEI string
79 | //}
80 |
81 | // Proxy2C_SendDataProto
82 | type Proxy2C_SendData struct {
83 | Protocol int
84 | Protocol2 int
85 | ServerID string
86 | Data interface{}
87 | }
88 |
89 | //------------------------------------------------------------------------------
90 |
--------------------------------------------------------------------------------
/Proxy_Server/Proto/gen.bat:
--------------------------------------------------------------------------------
1 | protoc --go_out=. *.proto
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LollipopGo
2 | Golang语言情怀 全球服游戏服务器框架,目前协议支持websocket、http及RPC,采用状态同步,愿景:打造竞技实时【比赛】对战游戏平台框架! 功能持续更新中... ...
3 | >微信订阅号:Golang语言情怀
4 | >微信服务号:Golang语言游戏服务器
5 | >商业定制版:联系彬哥(微信:cserli)
6 | >以下是本框架的实时对战3v3游戏项目:《荒野坦克大战》
7 | >[苹果商店【坦克对决】下载地址:https://apps.apple.com/us/app/versus-war/id6444600708](https://apps.apple.com/us/app/versus-war/id6444600708)
8 | >[谷歌play海外【荒野坦克大战】下载地址:https://play.google.com/store/apps/details?id=com.byteedu.tankBattle](https://play.google.com/store/apps/details?id=com.byteedu.tankBattle)
9 | >[taptap国内【荒野坦克大战】下载地址:https://www.taptap.cn/app/243380](https://www.taptap.cn/app/243380)
10 | >[4399游戏盒子国内【荒野坦克大战】下载地址:http://a.4399.cn/game-id-252256.html](http://a.4399.cn/game-id-252256.html)
11 | >微信小程序国内版本,小程序搜索:荒野坦克
12 |
13 |
14 | 论坛
15 | --------------
16 | WwW.Golang.Ltd
17 |
18 | LollipopGo框架交流群
19 | -----------
20 | 955259501
21 |
22 | Go语言交流群
23 | ----------
24 | 221273219
25 |
26 |
27 | 腾讯云+社区专栏
28 | -----------
29 | [腾讯专栏](https://cloud.tencent.com/developer/column/2170)
30 |
31 |
32 | Golang语言情怀
33 | -----------
34 |
35 |
36 | - 希望更多喜欢Go语言的同学及想从事Go语言开发游戏服务器的同学一个方向的指引
37 | - 课程多维度教学,lollipopGo游戏框架实战课程等等
38 | - LollipopGo架构 最新版本: v2.8.X
39 | - LollipopGo架构 直接下载就可以使用(彬哥维护),无需依赖管理,否则导致部分官方接口无法使用
40 | - LollipopGo架构 手机对战游戏视频:点击访问
41 | - LollipopGo架构 PC端游对战游戏视频:点击访问
42 | - 同时我们的免费课程也在持续更新中; 点击访问:网易云课堂
43 | - 同时我们的免费课程也在持续更新中; 点击访问:B站(bilibili.com)
44 | - 同时我们的免费课程也在持续更新中; 点击访问:LollipopGo框架文档地址,关注公众服务号:Golang语言游戏服务器
45 |
46 |
47 |
48 |
49 |
50 |
51 | 架构目录说明
52 | -----------
53 | ```go
54 | ├── global_Interface # 网络接口定义,分布式服务器需要单独实现接口
55 | ├── leaf # leaf的一些扩展函数,包括自定义的protobuf消息解析器
56 | ├── network # 网络处理封装,目前支持:http、rpc、websocket
57 | ├── Proxy_Server
58 | │ └── Proto # 反向代理消息公用模块,框架标准
59 | ├── timer # 通用定时器
60 | ├── tools
61 | │ ├── collection # 集合类的扩展方法
62 | │ ├── database # 快速初始化数据库连接
63 | │ ├── deepcopy # 通用深拷贝(使用反射)
64 | │ ├── DFA # 过滤敏感字
65 | │ ├── fs # 文件系统/配置解析
66 | │ ├── jsonutils # json工具库
67 | │ ├── mem # 常用的内存缓存类
68 | │ ├── num # 基础数字类型工具函数
69 | │ ├── sample # 随机抽样函数
70 | │ └── tz # 时间函数
71 | └── util # 随机数,并发安全map、排序等相关公用接口
72 | ```
73 |
76 |
77 |
--------------------------------------------------------------------------------
/global_Interface/Interface_impl.go:
--------------------------------------------------------------------------------
1 | package MsgHandleClt
2 |
3 | /*
4 | ps: v 2.8.X 版本以前
5 | type Msg_data interface {
6 | HandleCltProtocol(protocol interface{}, protocol2 interface{}, ProtocolData map[string]interface{}, Connection *websocket.Conn) interface{}
7 | HandleCltProtocol2(protocol2 interface{}, ProtocolData map[string]interface{}, Connection *websocket.Conn) interface{}
8 | PlayerSendMessage(senddata interface{}) int
9 | CloseEOF(closeEvent interface{}) int
10 | }
11 | */
12 |
13 | // v 2.9.X 以后版本
14 | type Msg_data interface {
15 | HandleCltProtocol(protocol interface{}, protocol2 interface{}, ProtocolData map[string]interface{}, Connection interface{}) interface{}
16 | HandleCltProtocol2(protocol2 interface{}, ProtocolData map[string]interface{}, Connection interface{}) interface{}
17 | PlayerSendMessage(senddata interface{}) int
18 | CloseEOF(closeEvent interface{}) int
19 | }
20 |
21 | type Msg_dataPB interface {
22 | HandleCltProtocolPB(ProtocolData []byte, Connection interface{}) interface{}
23 | HandleCltProtocolPB2(protocol int32, protocol2 int32, ProtocolData []byte, Connection interface{}) interface{}
24 | PlayerSendMessagePB(senddata []byte) int
25 | CloseEOFPB(closeEvent interface{}) int
26 | }
27 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module LollipopGo
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
7 | github.com/go-sql-driver/mysql v1.5.0
8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
9 | github.com/golang/protobuf v1.5.0
10 | github.com/gomodule/redigo v1.8.2
11 | github.com/gorilla/websocket v1.4.2 // indirect
12 | github.com/jinzhu/gorm v1.9.12
13 | github.com/json-iterator/go v1.1.10
14 | github.com/klauspost/reedsolomon v1.9.9 // indirect
15 | github.com/kr/pretty v0.1.0 // indirect
16 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
17 | github.com/name5566/leaf v0.0.0-20200516012428-8592b1abbbbe
18 | github.com/pkg/errors v0.9.1
19 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
20 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
21 | github.com/tjfoc/gmsm v1.3.2 // indirect
22 | github.com/xtaci/kcp-go v5.4.20+incompatible
23 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect
24 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
25 | google.golang.org/protobuf v1.30.0
26 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
27 | )
28 |
29 | replace golang.org/x/net/websocket => github.com/Golangltd/websocket_old v0.0.0-20200610144333-40b6804bddb4
30 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
5 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
6 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
7 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
8 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
9 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
10 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
11 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
12 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
13 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
14 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
15 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
16 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
18 | github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
19 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
20 | github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
21 | github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
22 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
23 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
24 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
25 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
26 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
27 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
28 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
29 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
30 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
31 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
32 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
33 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
34 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
35 | github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
36 | github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
37 | github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
38 | github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
39 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
40 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
41 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
42 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
44 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
45 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
46 | github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
47 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
48 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
49 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
50 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
51 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
52 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
53 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
54 | github.com/name5566/leaf v0.0.0-20200516012428-8592b1abbbbe h1:JoFGzGJqV8VUSmN1Q8nJUMnWOvdQtHDS5grYRMv8hCo=
55 | github.com/name5566/leaf v0.0.0-20200516012428-8592b1abbbbe/go.mod h1:JrOIxq3vDxvtuEI7Kmm2yqkuBfuT9DMLFMnCyYHLaKM=
56 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
57 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
58 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
59 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
60 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
61 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
62 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
63 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
64 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
65 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
66 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
67 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
68 | github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
69 | github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
70 | github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
71 | github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
72 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
73 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
74 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
75 | golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
76 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
77 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
78 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
79 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
80 | golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk=
81 | golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
82 | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
83 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
84 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
85 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
86 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
87 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
88 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
89 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
90 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
91 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
92 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
93 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
94 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
95 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
96 | golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
97 | golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
98 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
99 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
100 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
101 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
102 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
103 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
104 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
105 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
106 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
107 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
108 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
109 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
110 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
111 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
112 |
--------------------------------------------------------------------------------
/leaf/processor.go:
--------------------------------------------------------------------------------
1 | package leaf
2 |
3 | import (
4 | "encoding/binary"
5 | "errors"
6 | "fmt"
7 | "github.com/golang/protobuf/proto"
8 | "github.com/name5566/leaf/chanrpc"
9 | "log"
10 | "math"
11 | "reflect"
12 | )
13 |
14 | // 对leaf中已有的Protobuf协议格式进行扩展,允许自定义的id
15 | // -------------------------
16 | // | id | protobuf message |
17 | // -------------------------
18 | type processor struct {
19 | littleEndian bool
20 | msgInfo map[uint16]*msgInfoST
21 | msgID map[reflect.Type]uint16
22 | }
23 |
24 | type msgInfoST struct {
25 | msgType reflect.Type
26 | msgRouter *chanrpc.Server
27 | msgHandler msgHandlerST
28 | msgRawHandler msgHandlerST
29 | }
30 |
31 | type msgHandlerST func([]interface{})
32 |
33 | type msgRawST struct {
34 | msgID uint16
35 | msgRawData []byte
36 | }
37 |
38 | func newGameProcessor() *processor {
39 | p := new(processor)
40 | p.littleEndian = false
41 | p.msgID = make(map[reflect.Type]uint16)
42 | p.msgInfo = make(map[uint16]*msgInfoST)
43 | return p
44 | }
45 |
46 | // It's dangerous to call the method on routing or marshaling (unmarshaling)
47 | func (p *processor) SetByteOrder(littleEndian bool) {
48 | p.littleEndian = littleEndian
49 | }
50 |
51 | // It's dangerous to call the method on routing or marshaling (unmarshaling)
52 | func (p *processor) Register(msg proto.Message, eventType uint16) {
53 | msgType := reflect.TypeOf(msg)
54 | if msgType == nil || msgType.Kind() != reflect.Ptr {
55 | log.Fatal("protobuf message pointer required")
56 | }
57 | if _, ok := p.msgID[msgType]; ok {
58 | log.Fatal("message %s is already registered", msgType)
59 | }
60 | if len(p.msgInfo) >= math.MaxUint16 {
61 | log.Fatal("too many protobuf messages (max = %v)", math.MaxUint16)
62 | }
63 |
64 | i := new(msgInfoST)
65 | i.msgType = msgType
66 | p.msgInfo[eventType] = i
67 | p.msgID[msgType] = eventType
68 | }
69 |
70 | // It's dangerous to call the method on routing or marshaling (unmarshaling)
71 | func (p *processor) SetRouter(msg proto.Message, msgRouter *chanrpc.Server) {
72 | msgType := reflect.TypeOf(msg)
73 | id, ok := p.msgID[msgType]
74 | if !ok {
75 | log.Fatal("message %s not registered", msgType)
76 | }
77 |
78 | p.msgInfo[id].msgRouter = msgRouter
79 | }
80 |
81 | // It's dangerous to call the method on routing or marshaling (unmarshaling)
82 | func (p *processor) SetHandler(msg proto.Message, msgHandler msgHandlerST) {
83 | msgType := reflect.TypeOf(msg)
84 | id, ok := p.msgID[msgType]
85 | if !ok {
86 | log.Fatal("message %s not registered", msgType)
87 | }
88 |
89 | p.msgInfo[id].msgHandler = msgHandler
90 | }
91 |
92 | // It's dangerous to call the method on routing or marshaling (unmarshaling)
93 | func (p *processor) SetRawHandler(id uint16, msgRawHandler msgHandlerST) {
94 | if id >= uint16(len(p.msgInfo)) {
95 | log.Fatal("message id %v not registered", id)
96 | }
97 |
98 | p.msgInfo[id].msgRawHandler = msgRawHandler
99 | }
100 |
101 | // goroutine safe
102 | func (p *processor) Route(msg interface{}, userData interface{}) error {
103 | // raw
104 | if msgRaw, ok := msg.(msgRawST); ok {
105 | if msgRaw.msgID >= uint16(len(p.msgInfo)) {
106 | return fmt.Errorf("message id %v not registered", msgRaw.msgID)
107 | }
108 | i := p.msgInfo[msgRaw.msgID]
109 | if i.msgRawHandler != nil {
110 | i.msgRawHandler([]interface{}{msgRaw.msgID, msgRaw.msgRawData, userData})
111 | }
112 | return nil
113 | }
114 |
115 | // protobuf
116 | msgType := reflect.TypeOf(msg)
117 | id, ok := p.msgID[msgType]
118 | if !ok {
119 | return fmt.Errorf("message %s not registered", msgType)
120 | }
121 | i := p.msgInfo[id]
122 | if i.msgHandler != nil {
123 | i.msgHandler([]interface{}{msg, userData})
124 | }
125 | if i.msgRouter != nil {
126 | i.msgRouter.Go(msgType, msg, userData)
127 | }
128 | return nil
129 | }
130 |
131 | // goroutine safe
132 | func (p *processor) Unmarshal(data []byte) (interface{}, error) {
133 | if len(data) < 2 {
134 | return nil, errors.New("protobuf data too short")
135 | }
136 |
137 | // id
138 | var id uint16
139 | if p.littleEndian {
140 | id = binary.LittleEndian.Uint16(data)
141 | } else {
142 | id = binary.BigEndian.Uint16(data)
143 | }
144 | i, ok := p.msgInfo[id]
145 | if !ok {
146 | return nil, fmt.Errorf("message id %v not registered", id)
147 | }
148 | // msg
149 | if i.msgRawHandler != nil {
150 | return msgRawST{id, data[2:]}, nil
151 | } else {
152 | msg := reflect.New(i.msgType.Elem()).Interface()
153 | return msg, proto.UnmarshalMerge(data[2:], msg.(proto.Message))
154 | }
155 | }
156 |
157 | // goroutine safe
158 | func (p *processor) Marshal(msg interface{}) ([][]byte, error) {
159 | msgType := reflect.TypeOf(msg)
160 |
161 | // id
162 | _id, ok := p.msgID[msgType]
163 | if !ok {
164 | err := fmt.Errorf("message %s not registered", msgType)
165 | return nil, err
166 | }
167 |
168 | id := make([]byte, 2)
169 | if p.littleEndian {
170 | binary.LittleEndian.PutUint16(id, _id)
171 | } else {
172 | binary.BigEndian.PutUint16(id, _id)
173 | }
174 |
175 | // data
176 | data, err := proto.Marshal(msg.(proto.Message))
177 | return [][]byte{id, data}, err
178 | }
179 |
180 | // goroutine safe
181 | func (p *processor) Range(f func(id uint16, t reflect.Type)) {
182 | for id, i := range p.msgInfo {
183 | f(uint16(id), i.msgType)
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/leaf/utils.go:
--------------------------------------------------------------------------------
1 | package leaf
2 |
3 | import (
4 | _ "LollipopGo/Proxy_Server/Proto"
5 | "github.com/name5566/leaf/chanrpc"
6 | "github.com/name5566/leaf/gate"
7 | "github.com/name5566/leaf/module"
8 | "time"
9 | )
10 |
11 | const (
12 | // server conf
13 | PendingWriteNum = 2000
14 | MaxMsgLen = 1 * 1024 * 1024 // 最大长度为1M
15 | HTTPTimeout = 5 * time.Second
16 | LenMsgLen = 4
17 | MaxConnNum = 20000
18 |
19 | // skeleton conf
20 | GoLen = 10000
21 | TimerDispatcherLen = 10000
22 | AsynCallLen = 10000
23 | ChanRPCLen = 10000
24 | )
25 |
26 | //proto文件序列化/反序列化工具,作为一个全局单例
27 | var MsgProcessor = newGameProcessor()
28 |
29 | func NewSkeleton() *module.Skeleton {
30 | skeleton := &module.Skeleton{
31 | GoLen: GoLen,
32 | TimerDispatcherLen: TimerDispatcherLen,
33 | AsynCallLen: AsynCallLen,
34 | ChanRPCServer: chanrpc.NewServer(ChanRPCLen),
35 | }
36 | skeleton.Init()
37 | return skeleton
38 | }
39 |
40 | func NewGate(wsAddr string, chanRPC *chanrpc.Server) *gate.Gate {
41 | return &gate.Gate{
42 | MaxConnNum: MaxConnNum,
43 | PendingWriteNum: PendingWriteNum,
44 | MaxMsgLen: MaxMsgLen,
45 | WSAddr: wsAddr,
46 | HTTPTimeout: HTTPTimeout,
47 | LenMsgLen: LenMsgLen,
48 | LittleEndian: false,
49 | Processor: MsgProcessor,
50 | AgentChanRPC: chanRPC,
51 | }
52 | }
53 |
54 | func CheckAuth(ag gate.Agent) bool {
55 | if ag == nil {
56 | return false
57 | }
58 | if ag.UserData() == nil {
59 | ag.Close()
60 | return false
61 | }
62 | return true
63 | }
64 |
--------------------------------------------------------------------------------
/log/LogService.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "log"
6 | "net/rpc"
7 | "net/rpc/jsonrpc"
8 | )
9 |
10 | /*
11 | 1. 日志收集,设计建议是存储mongoDB数据库
12 | 2. 通信方式采用异步RPC发送到日志服
13 | */
14 |
15 | const (
16 | LevelINIT = iota
17 | DebugLevel // Debug
18 | ReleaseLevel // Releases
19 | ErrorLevel // Error
20 | FatalLevel // Fatal 严重的错误
21 | )
22 |
23 | type LogService struct {
24 | ServerID string
25 | TypeLog int
26 | ServiceUrl string
27 | ConnRPC *rpc.Client
28 | }
29 |
30 | type LogSt struct {
31 | Level int
32 | Data interface{}
33 | }
34 |
35 | func NewLogService(served string,serviceurl string) *LogService {
36 | client, err := jsonrpc.Dial("tcp", serviceurl)
37 | if err != nil {
38 | glog.Info("dial error:", err)
39 | return nil
40 | }
41 |
42 | return &LogService{
43 | ServerID: served,
44 | TypeLog: LevelINIT,
45 | ServiceUrl:serviceurl,
46 | ConnRPC:client,
47 | }
48 | }
49 |
50 | func (this *LogService)RecordLog(data LogSt) {
51 |
52 | switch data.Level {
53 | case DebugLevel:
54 | case ReleaseLevel:
55 | case ErrorLevel:
56 | case FatalLevel:
57 | log.Fatalln(data) // 严重错误
58 | default:
59 | }
60 | if this !=nil {
61 | this.sendlogServer(data)
62 | }
63 | }
64 |
65 | /*
66 | 1. 注册结构
67 | func rpcRegister() {
68 | _ = rpc.Register(new(LogSt))
69 | }
70 | 2. 函数逻辑
71 | func (r *LogSt) SaveMongoDB(data log.LogSt, reply *bool) error {
72 | // 保存数据库逻辑
73 | }
74 | 3. 基础逻辑
75 |
76 | func main() {
77 | conf.InitConfig()
78 | MainListener(conf.GetConfig().Server.WSAddr)
79 | }
80 |
81 | func MainListener(strport string) {
82 | rpcRegister()
83 | tcpAddr, err := net.ResolveTCPAddr("tcp", ":"+strport)
84 | checkError(err)
85 | Listener, err := net.ListenTCP("tcp", tcpAddr)
86 | checkError(err)
87 |
88 | for {
89 | defer func() {
90 | if err := recover(); err != nil {
91 | strerr := fmt.Sprintf("%s", err)
92 | fmt.Println("异常捕获:", strerr)
93 | }
94 | }()
95 | conn, err := Listener.Accept()
96 | if err != nil {
97 | fmt.Fprint(os.Stderr, "accept err: %s", err.Error())
98 | continue
99 | }
100 | go jsonrpc.ServeConn(conn)
101 | }
102 | }
103 |
104 | func checkError(err error) {
105 | if err != nil {
106 | fmt.Fprint(os.Stderr, "Usage: %s", err.Error())
107 | }
108 | }
109 | 注:日志服务器需要注册 LogSt结构
110 | */
111 | func (this *LogService)sendlogServer(data LogSt){
112 | if this.ConnRPC == nil{
113 | log.Fatalln("初始化错误!") // 严重错误
114 | return
115 | }
116 | args := data
117 | var reply bool
118 | divCall := this.ConnRPC.Go("LogSt.SaveMongoDB", args, &reply, nil)
119 | replyCall := <-divCall.Done
120 | glog.Info(replyCall.Reply)
121 | glog.Info("the LogData return is :", reply)
122 | }
--------------------------------------------------------------------------------
/log/example_test.go:
--------------------------------------------------------------------------------
1 | package log_test
2 |
3 | import (
4 | l "log"
5 |
6 | "LollipopGo/log"
7 | )
8 |
9 | func Example() {
10 | name := "LollipopGo"
11 |
12 | log.Debug("My name is %v", name)
13 | log.Release("My name is %v", name)
14 | log.Error("My name is %v", name)
15 | // log.Fatal("My name is %v", name)
16 |
17 | logger, err := log.New("release", "", l.LstdFlags)
18 | if err != nil {
19 | return
20 | }
21 | defer logger.Close()
22 |
23 | logger.Debug("will not print")
24 | logger.Release("My name is %v", name)
25 |
26 | log.Export(logger)
27 |
28 | log.Debug("will not print")
29 | log.Release("My name is %v", name)
30 | }
31 |
--------------------------------------------------------------------------------
/log/log.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "log"
7 | "os"
8 | "path"
9 | "strings"
10 | "time"
11 | )
12 |
13 | // levels
14 | const (
15 | debugLevel = 0
16 | releaseLevel = 1
17 | errorLevel = 2
18 | fatalLevel = 3
19 | )
20 |
21 | const (
22 | printDebugLevel = "[debug ] "
23 | printReleaseLevel = "[release] "
24 | printErrorLevel = "[error ] "
25 | printFatalLevel = "[fatal ] "
26 | )
27 |
28 | type Logger struct {
29 | level int
30 | baseLogger *log.Logger
31 | baseFile *os.File
32 | }
33 |
34 | func New(strLevel string, pathname string, flag int) (*Logger, error) {
35 | // level
36 | var level int
37 | switch strings.ToLower(strLevel) {
38 | case "debug":
39 | level = debugLevel
40 | case "release":
41 | level = releaseLevel
42 | case "error":
43 | level = errorLevel
44 | case "fatal":
45 | level = fatalLevel
46 | default:
47 | return nil, errors.New("unknown level: " + strLevel)
48 | }
49 |
50 | // logger
51 | var baseLogger *log.Logger
52 | var baseFile *os.File
53 | if pathname != "" {
54 | now := time.Now()
55 |
56 | filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log",
57 | now.Year(),
58 | now.Month(),
59 | now.Day(),
60 | now.Hour(),
61 | now.Minute(),
62 | now.Second())
63 |
64 | file, err := os.Create(path.Join(pathname, filename))
65 | if err != nil {
66 | return nil, err
67 | }
68 |
69 | baseLogger = log.New(file, "", flag)
70 | baseFile = file
71 | } else {
72 | baseLogger = log.New(os.Stdout, "", flag)
73 | }
74 |
75 | // new
76 | logger := new(Logger)
77 | logger.level = level
78 | logger.baseLogger = baseLogger
79 | logger.baseFile = baseFile
80 |
81 | return logger, nil
82 | }
83 |
84 | // It's dangerous to call the method on logging
85 | func (logger *Logger) Close() {
86 | if logger.baseFile != nil {
87 | logger.baseFile.Close()
88 | }
89 |
90 | logger.baseLogger = nil
91 | logger.baseFile = nil
92 | }
93 |
94 | func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) {
95 | if level < logger.level {
96 | return
97 | }
98 | if logger.baseLogger == nil {
99 | panic("logger closed")
100 | }
101 |
102 | format = printLevel + format
103 | logger.baseLogger.Output(3, fmt.Sprintf(format, a...))
104 |
105 | if level == fatalLevel {
106 | os.Exit(1)
107 | }
108 | }
109 |
110 | func (logger *Logger) Debug(format string, a ...interface{}) {
111 | logger.doPrintf(debugLevel, printDebugLevel, format, a...)
112 | }
113 |
114 | func (logger *Logger) Release(format string, a ...interface{}) {
115 | logger.doPrintf(releaseLevel, printReleaseLevel, format, a...)
116 | }
117 |
118 | func (logger *Logger) Error(format string, a ...interface{}) {
119 | logger.doPrintf(errorLevel, printErrorLevel, format, a...)
120 | }
121 |
122 | func (logger *Logger) Fatal(format string, a ...interface{}) {
123 | logger.doPrintf(fatalLevel, printFatalLevel, format, a...)
124 | }
125 |
126 | var gLogger, _ = New("debug", "", log.LstdFlags)
127 |
128 | // It's dangerous to call the method on logging
129 | func Export(logger *Logger) {
130 | if logger != nil {
131 | gLogger = logger
132 | }
133 | }
134 |
135 | func Debug(format string, a ...interface{}) {
136 | gLogger.doPrintf(debugLevel, printDebugLevel, format, a...)
137 | }
138 |
139 | func Release(format string, a ...interface{}) {
140 | gLogger.doPrintf(releaseLevel, printReleaseLevel, format, a...)
141 | }
142 |
143 | func Error(format string, a ...interface{}) {
144 | gLogger.doPrintf(errorLevel, printErrorLevel, format, a...)
145 | }
146 |
147 | func Fatal(format string, a ...interface{}) {
148 | gLogger.doPrintf(fatalLevel, printFatalLevel, format, a...)
149 | }
150 |
151 | func Close() {
152 | gLogger.Close()
153 | }
154 |
--------------------------------------------------------------------------------
/lollipopGo.go:
--------------------------------------------------------------------------------
1 | package LollipopGo
2 |
3 | import (
4 | "LollipopGo/log"
5 | "flag"
6 | )
7 |
8 | func init() {
9 | log.Release("Golang语言社区 LollipopGo %v starting up", Version)
10 | }
11 |
12 | func Run() {
13 | flag.Set("alsologtostderr", "true")
14 | flag.Set("log_dir", "./log")
15 | flag.Set("v", "3")
16 | flag.Parse()
17 | }
18 |
--------------------------------------------------------------------------------
/network/http.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | "io/ioutil"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | // 发送GET请求
14 | // url:请求地址
15 | // response:请求返回的内容
16 | func Get(url string) (response string) {
17 | client := http.Client{Timeout: 30 * time.Second}
18 | resp, error := client.Get(url)
19 | if error != nil {
20 | panic(error)
21 | }
22 | resp.Header.Add("content-type", "application/json")
23 | resp.Header.Add("content-type", "charset=UTF-8")
24 | defer resp.Body.Close()
25 | var buffer [512]byte
26 | result := bytes.NewBuffer(nil)
27 | for {
28 | n, err := resp.Body.Read(buffer[0:])
29 | result.Write(buffer[0:n])
30 | if err != nil && err == io.EOF {
31 | // fmt.Println("关闭conn", err)
32 | break
33 | } else if err != nil {
34 | panic(err)
35 | }
36 | }
37 |
38 | response = result.String()
39 | return
40 | }
41 |
42 | // 发送POST请求
43 | // url:请求地址,data:POST请求提交的数据,contentType:请求体格式,如:application/json
44 | // content:请求放回的内容
45 | func Post(url string, data interface{}, contentType string) (content string) {
46 | jsonStr, err := json.Marshal(data)
47 | if err != nil {
48 | fmt.Println(err)
49 | }
50 | fmt.Println(string(jsonStr))
51 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
52 | req.Header.Add("content-type", contentType)
53 | req.Header.Add("content-type", "charset=UTF-8")
54 | if err != nil {
55 | panic(err)
56 | }
57 | defer req.Body.Close()
58 |
59 | client := &http.Client{Timeout: 15 * time.Second}
60 | resp, error := client.Do(req)
61 | if error != nil {
62 | panic(error)
63 | }
64 | defer resp.Body.Close()
65 |
66 | result, _ := ioutil.ReadAll(resp.Body)
67 | content = string(result)
68 | return
69 | }
70 |
71 | //post
72 | func HttpPost(uri string, data string) ([]byte, error) {
73 | response, err := http.Post(uri, "application/x-www-form-urlencoded;charset=utf-8", bytes.NewBuffer([]byte(data)))
74 | if err != nil {
75 | //log.Println(err)
76 | return nil, err
77 | }
78 | defer response.Body.Close()
79 |
80 | if response.StatusCode != http.StatusOK {
81 | return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
82 | }
83 | return ioutil.ReadAll(response.Body)
84 | }
85 |
86 | // //HTTPGet get 请求
87 | // func HTTPGet(uri string) ([]byte, error) {
88 | // response, err := http.Get(uri)
89 | // if err != nil {
90 | // return nil, err
91 | // }
92 |
93 | // defer response.Body.Close()
94 | // if response.StatusCode != http.StatusOK {
95 | // return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
96 | // }
97 | // return ioutil.ReadAll(response.Body)
98 | // }
99 |
100 | // //post
101 | // func HttpPost(uri string, data string) ([]byte, error) {
102 | // response, err := http.Post(uri, "application/x-www-form-urlencoded;charset=utf-8", bytes.NewBuffer([]byte(data)))
103 | // if err != nil {
104 | // log.Println(err)
105 | // return nil, err
106 | // }
107 | // defer response.Body.Close()
108 |
109 | // if response.StatusCode != http.StatusOK {
110 | // return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
111 | // }
112 | // return ioutil.ReadAll(response.Body)
113 | // }
114 |
115 | // //PostJSON post json 数据请求
116 | // func PostJSON(uri string, obj interface{}) ([]byte, error) {
117 | // jsonData, err := json.Marshal(obj)
118 | // if err != nil {
119 | // return nil, err
120 | // }
121 | // body := bytes.NewBuffer(jsonData)
122 | // response, err := http.Post(uri, "application/json;charset=utf-8", body)
123 | // if err != nil {
124 | // return nil, err
125 | // }
126 | // defer response.Body.Close()
127 |
128 | // if response.StatusCode != http.StatusOK {
129 | // return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
130 | // }
131 | // return ioutil.ReadAll(response.Body)
132 | // }
133 |
134 | // //PostFile 上传文件
135 | // func PostFile(fieldname, filename, uri string) ([]byte, error) {
136 | // fields := []MultipartFormField{
137 | // {
138 | // IsFile: true,
139 | // Fieldname: fieldname,
140 | // Filename: filename,
141 | // },
142 | // }
143 | // return PostMultipartForm(fields, uri)
144 | // }
145 |
146 | // //MultipartFormField 保存文件或其他字段信息
147 | // type MultipartFormField struct {
148 | // IsFile bool
149 | // Fieldname string
150 | // Value []byte
151 | // Filename string
152 | // }
153 |
154 | // //PostMultipartForm 上传文件或其他多个字段
155 | // func PostMultipartForm(fields []MultipartFormField, uri string) (respBody []byte, err error) {
156 | // bodyBuf := &bytes.Buffer{}
157 | // bodyWriter := multipart.NewWriter(bodyBuf)
158 |
159 | // for _, field := range fields {
160 | // if field.IsFile {
161 | // fileWriter, e := bodyWriter.CreateFormFile(field.Fieldname, field.Filename)
162 | // if e != nil {
163 | // err = fmt.Errorf("error writing to buffer , err=%v", e)
164 | // return
165 | // }
166 |
167 | // fh, e := os.Open(field.Filename)
168 | // if e != nil {
169 | // err = fmt.Errorf("error opening file , err=%v", e)
170 | // return
171 | // }
172 | // defer fh.Close()
173 |
174 | // if _, err = io.Copy(fileWriter, fh); err != nil {
175 | // return
176 | // }
177 | // } else {
178 | // partWriter, e := bodyWriter.CreateFormField(field.Fieldname)
179 | // if e != nil {
180 | // err = e
181 | // return
182 | // }
183 | // valueReader := bytes.NewReader(field.Value)
184 | // if _, err = io.Copy(partWriter, valueReader); err != nil {
185 | // return
186 | // }
187 | // }
188 | // }
189 |
190 | // contentType := bodyWriter.FormDataContentType()
191 | // bodyWriter.Close()
192 |
193 | // resp, e := http.Post(uri, contentType, bodyBuf)
194 | // if e != nil {
195 | // err = e
196 | // return
197 | // }
198 | // defer resp.Body.Close()
199 | // if resp.StatusCode != http.StatusOK {
200 | // return nil, err
201 | // }
202 | // respBody, err = ioutil.ReadAll(resp.Body)
203 | // return
204 | // }
205 |
--------------------------------------------------------------------------------
/network/initNet.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | "github.com/golang/glog"
5 | "github.com/xtaci/kcp-go"
6 | "golang.org/x/net/websocket"
7 | "net"
8 | )
9 |
10 | // LollipopGo 支持的网络类型
11 | const (
12 | WebSocket = "websocket"
13 | RPC = "rpc"
14 | TCP = "tcp"
15 | KCP = "kcp"
16 | )
17 |
18 | // 连接服务器类型
19 | const (
20 | CONNINIT = iota
21 | ConnProxy // ConnProxy == 1 主动连接 Proxy服务器,Proxy作为全球服或者区域服
22 | StartProxy // StartProxy == 2 Proxy服务器使用
23 | )
24 |
25 | // 初始化网络
26 | func InitNet(netty string, netdata ...interface{}) interface{} {
27 | switch netty {
28 | case WebSocket:
29 | InitConnectionPB(netdata[0].(*websocket.Conn))
30 | return IMsgPB
31 | case RPC:
32 | InitConnectionRPC(netty) // rpc 不需要返回
33 | case KCP:
34 | InitConnectionKCP(netdata[0].(*kcp.UDPSession), netdata[1].(*kcp.Listener))
35 | return IMsgPB
36 | case TCP:
37 | InitConnectionTCP(netdata[0].(*net.Conn), netdata[1].(*net.Listener))
38 | return IMsgPB
39 | default:
40 | glog.Info("InitNet is failed,net type is not exist!")
41 | }
42 | return nil
43 | }
44 |
45 | // 启动网络监听
46 | func Start(url string, route string, conntype int, netty string) {
47 | switch netty {
48 | case WebSocket:
49 |
50 | case RPC:
51 | case KCP:
52 | case TCP:
53 | default:
54 | glog.Info("InitNet is failed,net type is not exist!")
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/network/kcp.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | MsgHandleClt "LollipopGo/global_Interface"
5 | "fmt"
6 | "github.com/golang/glog"
7 | "github.com/xtaci/kcp-go"
8 | "io"
9 | "net"
10 | "strings"
11 | )
12 |
13 | // KCP 格式
14 | type OnlineKCP struct {
15 | Listener *kcp.Listener
16 | Connection *kcp.UDPSession
17 | inChan chan string
18 | outChan chan interface{}
19 | closeChan chan int
20 | goExit chan int
21 | isClosed bool
22 | HandleClt MsgHandleClt.Msg_data
23 | }
24 | // 初始化网络
25 | func InitConnectionKCP(kcpConn *kcp.UDPSession,Listener*kcp.Listener) (*OnlineKCP, error) {
26 |
27 | conn := &OnlineKCP{
28 | Listener:Listener,
29 | Connection: kcpConn,
30 | inChan: make(chan string, BytebufLen),
31 | }
32 |
33 | go conn.handleLoop()
34 | conn.readLoop()
35 |
36 | return conn, nil
37 | }
38 |
39 | func (this *OnlineKCP) readLoop() {
40 |
41 | for {
42 | go func(conn net.Conn) {
43 | var buffer = make([]byte, 1024, 1024)
44 | for {
45 | n, e := conn.Read(buffer)
46 | if e != nil {
47 | if e == io.EOF {
48 | IMsg.CloseEOF(this.Connection)
49 | break
50 | }
51 | break
52 | }
53 | fmt.Println("receive from client:", buffer[:n])
54 | }
55 | select {
56 | case this.inChan <- string(buffer):
57 | }
58 | }(this.Connection)
59 | }
60 | }
61 |
62 | func (this *OnlineKCP) handleLoop() {
63 |
64 | defer func() {
65 | if err := recover(); err != nil {
66 | strerr := fmt.Sprintf("%s", err)
67 | glog.Info("异常捕获:", strerr)
68 | }
69 | }()
70 |
71 | for {
72 | var r Requestbody
73 | select {
74 | case r.req = <-this.inChan:
75 | }
76 |
77 | if len(r.req) <= 0 {
78 | continue
79 | }
80 |
81 | if ProtocolData, err := r.Json2map(); err == nil {
82 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
83 | } else {
84 | content := r.req
85 | content = strings.Replace(content, "\"", "", -1)
86 | contentstr, errr := base64Decode([]byte(content))
87 | if errr != nil {
88 | fmt.Println(errr)
89 | continue
90 | }
91 | r.req = string(contentstr)
92 | if ProtocolData, err := r.Json2map(); err == nil {
93 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/network/rpc.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | "fmt"
5 | "github.com/golang/glog"
6 | "net"
7 | "net/rpc"
8 | "net/rpc/jsonrpc"
9 | "os"
10 | )
11 |
12 | // RPC数据结构
13 | type RPCSt struct {
14 | ServiceUrl string
15 | SendData interface{} // 发送的数据
16 | ReplyData interface{} // 接受的数据
17 | ConnRPC *rpc.Client
18 | }
19 |
20 | func InitConnectionRPC(Addr string) *RPCSt {
21 | return &RPCSt{
22 | ServiceUrl:Addr,
23 | SendData:nil,
24 | ReplyData:nil,
25 | ConnRPC:createClientConn(Addr),
26 | }
27 | }
28 |
29 | func createClientConn(Addr string) *rpc.Client {
30 | client, err := jsonrpc.Dial("tcp", Addr)
31 | if err != nil {
32 | glog.Info("dial error:", err)
33 | return nil
34 | }
35 | return client
36 | }
37 |
38 | func (this *RPCSt)GetClientConnRPC() *rpc.Client {
39 | if this.ConnRPC != nil{
40 | return this.ConnRPC
41 | }else {
42 | client, err := jsonrpc.Dial("tcp", this.ServiceUrl)
43 | if err != nil {
44 | glog.Info("dial error:", err)
45 | return nil
46 | }
47 | return client
48 | }
49 | }
50 |
51 | // 实际操作信息,
52 | func (this *RPCSt)Send_LollipopGoRPC(data RPCSt) interface{} {
53 | if this.ConnRPC == nil{
54 | return nil
55 | }
56 | args := data
57 | var reply RPCSt
58 | divCall := this.ConnRPC.Go("RPCSt.LollipopGoRPC", args, &reply, nil)
59 | replyCall := <-divCall.Done
60 | glog.Info(replyCall.Reply)
61 | glog.Info("the arith.LollipopGoRPC is :", reply)
62 | return reply
63 | }
64 |
65 | //----------------------------------------------------------------------------------------------------------------------
66 | // RPC 服务器调用
67 |
68 | func MainListener(strport string) {
69 | rpcRegister()
70 | tcpAddr, err := net.ResolveTCPAddr("tcp", ":"+strport)
71 | checkError(err)
72 | Listener, err := net.ListenTCP("tcp", tcpAddr)
73 | checkError(err)
74 | for {
75 | defer func() {
76 | if err := recover(); err != nil {
77 | strerr := fmt.Sprintf("%s", err)
78 | fmt.Println("异常捕获:", strerr)
79 | }
80 | }()
81 | conn, err := Listener.Accept()
82 | if err != nil {
83 | fmt.Fprint(os.Stderr, "accept err: %s", err.Error())
84 | continue
85 | }
86 | go jsonrpc.ServeConn(conn)
87 | }
88 | }
89 |
90 | func rpcRegister() {
91 | _ = rpc.Register(new(RPCSt))
92 | }
93 |
94 | func checkError(err error) {
95 | if err != nil {
96 | fmt.Fprint(os.Stderr, "Usage: %s", err.Error())
97 | }
98 | }
--------------------------------------------------------------------------------
/network/tcp.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | MsgHandleClt "LollipopGo/global_Interface"
5 | "fmt"
6 | "github.com/golang/glog"
7 | "io"
8 | "net"
9 | "strings"
10 | )
11 |
12 | // TCP 格式
13 | type OnlineTCP struct {
14 | Listener *net.Listener
15 | Connection *net.Conn
16 | inChan chan string
17 | outChan chan interface{}
18 | closeChan chan int
19 | goExit chan int
20 | isClosed bool
21 | HandleClt MsgHandleClt.Msg_data
22 | }
23 |
24 | func Bin() {
25 | ln, err := net.Listen("tcp", ":10010")
26 |
27 | if err != nil {
28 | fmt.Println("listen failed, err:", err)
29 | return
30 | }
31 |
32 | for {
33 | conn, err := ln.Accept()
34 | if err != nil {
35 | fmt.Println("accept failed, err:", err)
36 | continue
37 | }
38 | _=conn
39 | }
40 | }
41 |
42 | // 初始化网络
43 | func InitConnectionTCP(tcpConn *net.Conn,Listener *net.Listener) (*OnlineTCP, error) {
44 |
45 | conn := &OnlineTCP{
46 | Listener:Listener,
47 | Connection: tcpConn,
48 | inChan: make(chan string, BytebufLen),
49 | }
50 |
51 | go conn.handleLoop()
52 | conn.readLoop()
53 |
54 | return conn, nil
55 | }
56 |
57 | func (this *OnlineTCP) readLoop() {
58 |
59 | for {
60 | go func(conn *net.Conn) {
61 | var buffer = make([]byte, 1024, 1024)
62 | for {
63 | n, e := (*conn).Read(buffer)
64 | if e != nil {
65 | if e == io.EOF {
66 | IMsg.CloseEOF(this.Connection)
67 | break
68 | }
69 | break
70 | }
71 | fmt.Println("receive from client:", buffer[:n])
72 | }
73 | select {
74 | case this.inChan <- string(buffer):
75 | }
76 | }(this.Connection)
77 | }
78 | }
79 |
80 | func (this *OnlineTCP) handleLoop() {
81 |
82 | defer func() {
83 | if err := recover(); err != nil {
84 | strerr := fmt.Sprintf("%s", err)
85 | glog.Info("异常捕获:", strerr)
86 | }
87 | }()
88 |
89 | for {
90 | var r Requestbody
91 | select {
92 | case r.req = <-this.inChan:
93 | }
94 |
95 | if len(r.req) <= 0 {
96 | continue
97 | }
98 |
99 | if ProtocolData, err := r.Json2map(); err == nil {
100 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
101 | } else {
102 | content := r.req
103 | content = strings.Replace(content, "\"", "", -1)
104 | contentstr, errr := base64Decode([]byte(content))
105 | if errr != nil {
106 | fmt.Println(errr)
107 | continue
108 | }
109 | r.req = string(contentstr)
110 | if ProtocolData, err := r.Json2map(); err == nil {
111 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/network/websocket.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | "LollipopGo/Proxy_Server/Proto"
5 | "LollipopGo/global_Interface"
6 | "LollipopGo/util"
7 | "encoding/base64"
8 | "fmt"
9 | "github.com/golang/glog"
10 | "github.com/json-iterator/go"
11 | "golang.org/x/net/websocket"
12 | "io"
13 | "net/http"
14 | "runtime"
15 | "strconv"
16 | "strings"
17 | "time"
18 | )
19 |
20 | var BytebufLen int64 = 10000
21 | var IMsg MsgHandleClt.Msg_data
22 |
23 | type OnlineUser struct {
24 | Connection *websocket.Conn
25 | inChan chan string
26 | outChan chan interface{}
27 | closeChan chan int
28 | goExit chan int
29 | isClosed bool
30 | HandleClt MsgHandleClt.Msg_data
31 | }
32 |
33 | func InitConnection(wsConn *websocket.Conn) (*OnlineUser, error) {
34 | conn := &OnlineUser{
35 | Connection: wsConn,
36 | inChan: make(chan string, BytebufLen),
37 | }
38 |
39 | defer conn.Connection.Close()
40 | go conn.handleLoop()
41 | conn.readLoop()
42 | //go conn.readLoop()
43 | //select {}
44 |
45 | return conn, nil
46 | }
47 |
48 | // 20240710
49 | func (this *OnlineUser) readLoop() {
50 |
51 | for {
52 | var content string
53 | err := websocket.Message.Receive(this.Connection, &content)
54 | if err != nil {
55 | //if err == io.EOF || err == io.ErrClosedPipe || content == "" || err == io.ErrNoProgress {
56 | if err == io.EOF {
57 | IMsg.CloseEOF(this.Connection)
58 | glog.Info("协程的数量 :", runtime.NumGoroutine())
59 | //this.Connection.Close()
60 | //runtime.Goexit()
61 | //return
62 | }
63 | //break
64 | continue
65 | }
66 | select {
67 | case this.inChan <- content:
68 | case <-time.After(60 * time.Second):
69 | glog.Info("readLoop:超时----")
70 | //glog.Info("协程的数量 :", runtime.NumGoroutine())
71 | //this.Connection.Close()
72 | //runtime.Goexit()
73 | //return
74 | //default:
75 | // fmt.Println("Channel is empty, unable to read data")
76 | }
77 | }
78 | }
79 |
80 | func (this *OnlineUser) handleLoop() {
81 |
82 | defer func() {
83 | if err := recover(); err != nil {
84 | strerr := fmt.Sprintf("%s", err)
85 | glog.Info("异常捕获:", strerr)
86 | }
87 | }()
88 |
89 | for {
90 | if this.inChan == nil {
91 | continue
92 | }
93 | var r Requestbody
94 | select {
95 | case r.req = <-this.inChan:
96 | case <-time.After(200 * time.Second):
97 | glog.Info("handleLoop:超时----")
98 | //glog.Info("协程的数量 :", runtime.NumGoroutine())
99 | //this.Connection.Close()
100 | //runtime.Goexit()
101 | //return
102 | }
103 | if len(r.req) <= 0 {
104 | continue
105 | }
106 |
107 | if ProtocolData, err := r.Json2map(); err == nil {
108 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
109 | } else {
110 | content := r.req
111 | content = strings.Replace(content, "\"", "", -1)
112 | contentstr, errr := base64Decode([]byte(content))
113 | if errr != nil {
114 | fmt.Println(errr)
115 | this.Connection.Write([]byte("数据格式错误"))
116 | continue
117 | }
118 | r.req = string(contentstr)
119 | if ProtocolData, err := r.Json2map(); err == nil {
120 | IMsg.HandleCltProtocol(ProtocolData["Protocol"], ProtocolData["Protocol2"], ProtocolData, this.Connection)
121 | }
122 | }
123 | }
124 | }
125 |
126 | func base64Decode(src []byte) ([]byte, error) {
127 | return base64.StdEncoding.DecodeString(string(src))
128 | }
129 |
130 | //func (this *OnlineUser) writeLoop() {
131 | // defer func() {
132 | // if err := recover(); err != nil {
133 | // strerr := fmt.Sprintf("%s", err)
134 | // glog.Info("异常捕获:", strerr)
135 | // }
136 | // }()
137 | //
138 | // //this.PlayerSendMessage(this.outChan)
139 | //
140 | // for {
141 | // select {
142 | // case data := <-this.outChan:
143 | // if iret := this.PlayerSendMessage(data); iret == 2 {
144 | // this.Connection.Close()
145 | // runtime.Goexit() //new24
146 | // goto ERR
147 | // }
148 | // case <-this.goExit:
149 | // this.Connection.Close()
150 | // runtime.Goexit() //new24
151 | // }
152 | // }
153 | //ERR:
154 | // this.Connection.Close()
155 | // runtime.Goexit()
156 | //}
157 |
158 | func (this *OnlineUser) PlayerSendMessage(senddata interface{}) int {
159 |
160 | glog.Info("协程的数量 :", runtime.NumGoroutine())
161 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
162 | b, err1 := jsoniter.Marshal(senddata)
163 | if err1 != nil {
164 | glog.Error("PlayerSendMessage json.Marshal data fail ! err:", err1.Error())
165 | glog.Flush()
166 | return 1
167 | }
168 | err := websocket.JSON.Send(this.Connection, b)
169 | if err != nil {
170 | glog.Error("PlayerSendMessage send data fail ! err:", err.Error())
171 | glog.Flush()
172 | return 2
173 | }
174 | return 0
175 | }
176 |
177 | type Requestbody struct {
178 | req string
179 | }
180 |
181 | func (r *Requestbody) Json2map() (s map[string]interface{}, err error) {
182 | var result map[string]interface{}
183 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
184 | if err := jsoniter.Unmarshal([]byte(r.req), &result); err != nil {
185 | return nil, err
186 | }
187 | return result, nil
188 | }
189 |
190 | func PlayerSendToServer(conn *websocket.Conn, data interface{}) {
191 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
192 | jsons, err := jsoniter.Marshal(data)
193 | if err != nil {
194 | glog.Info("err:", err.Error())
195 | return
196 | }
197 | errq := websocket.Message.Send(conn, jsons)
198 | if errq != nil {
199 | glog.Info(errq)
200 | }
201 | return
202 | }
203 |
204 | // ------------------------------------------------------------------------------
205 | func PlayerSendToProxyServer(conn *websocket.Conn, senddata interface{}, strOpenID string) {
206 | if len(strOpenID) > 50 {
207 | return
208 | }
209 | data := Proto_Proxy.G2Proxy_SendData{
210 | Protocol: 1,
211 | Protocol2: Proto_Proxy.G2Proxy_SendDataProto,
212 | OpenID: strOpenID,
213 | Data: senddata,
214 | }
215 | var sssend interface{}
216 | sssend = data
217 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
218 | jsons, err := jsoniter.Marshal(sssend)
219 | if err != nil {
220 | glog.Info("err:", err.Error())
221 | return
222 | }
223 | errq := websocket.Message.Send(conn, jsons)
224 | if errq != nil {
225 | glog.Info(errq)
226 | }
227 | return
228 | }
229 |
230 | func PlayerSendMessageOfProxy(conn *websocket.Conn, senddata interface{}, strServerID string) int {
231 |
232 | datasend := Proto_Proxy.C2Proxy_SendData{
233 | Protocol: 1,
234 | Protocol2: 1,
235 | ServerID: strServerID,
236 | Data: senddata,
237 | }
238 | var sssend interface{}
239 | sssend = datasend
240 | glog.Info("协程的数量 :", runtime.NumGoroutine())
241 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
242 | b, err1 := jsoniter.Marshal(sssend)
243 | if err1 != nil {
244 | glog.Error("PlayerSendMessage json.Marshal data fail ! err:", err1.Error())
245 | glog.Flush()
246 | return 1
247 | }
248 | glog.Flush()
249 | encoding := base64.StdEncoding.EncodeToString(b)
250 | err := websocket.JSON.Send(conn, encoding)
251 | if err != nil {
252 | glog.Error("PlayerSendMessage send data fail ! err:", err.Error())
253 | glog.Flush()
254 | return 2
255 | }
256 | return 0
257 | }
258 |
259 | func PlayerSendMessageOfExit(conn *websocket.Conn, senddata interface{}, strServerID string) int {
260 |
261 | var sssend interface{}
262 | sssend = senddata
263 | glog.Info("协程的数量 :", runtime.NumGoroutine())
264 | var jsoniter = jsoniter.ConfigCompatibleWithStandardLibrary
265 | b, err1 := jsoniter.Marshal(sssend)
266 | if err1 != nil {
267 | glog.Error("PlayerSendMessage json.Marshal data fail ! err:", err1.Error())
268 | glog.Flush()
269 | return 1
270 | }
271 | glog.Flush()
272 | encoding := base64.StdEncoding.EncodeToString(b)
273 | err := websocket.JSON.Send(conn, encoding)
274 | if err != nil {
275 | glog.Error("PlayerSendMessage send data fail ! err:", err.Error())
276 | glog.Flush()
277 | return 2
278 | }
279 | return 0
280 | }
281 |
282 | // WebSocketStart websocket启动
283 | func WebSocketStart(url string,
284 | route string,
285 | BuildConnection func(ws *websocket.Conn),
286 | conntype int,
287 | serverId int,
288 | proxyUrl []string, //[0] = ProxyHost;[1]=ProxyPort,[2]=ProxyPath
289 | GameServerReceive func(ws *websocket.Conn),
290 | ConnXZ *websocket.Conn) {
291 | var StartDesc = ""
292 | if conntype == ConnProxy { //作为内网的服务器连接代理服务器
293 | proxyURL := AddParamsToGetReq("ws", proxyUrl, map[string]string{"data": "{ID:1}"})
294 | glog.Infof("connect to proxy addr:%s\n", proxyURL)
295 | conn, err := websocket.Dial(proxyURL, "", "test://golang/")
296 | if err != nil {
297 | glog.Errorln("err:", err.Error())
298 | return
299 | }
300 | ConnXZ = conn
301 | data := Proto_Proxy.G2Proxy_ConnData{
302 | Protocol: 1,
303 | Protocol2: Proto_Proxy.G2Proxy_ConnDataProto,
304 | ServerID: util.MD5_LollipopGO(strconv.Itoa(serverId)),
305 | }
306 | PlayerSendToServer(conn, data)
307 | go GameServerReceive(conn)
308 | } else if conntype == StartProxy {
309 | StartDesc = "proxy server"
310 | }
311 | http.Handle("/"+route, websocket.Handler(BuildConnection))
312 | glog.Infof("game listen to:[%s]\n", route)
313 | glog.Info("game start ok ", StartDesc)
314 | if err := http.ListenAndServe(url, nil); err != nil {
315 | glog.Info("Entry nil", err.Error())
316 | glog.Flush()
317 | return
318 | }
319 | }
320 |
321 | // 添加参数到get请求
322 | func AddParamsToGetReq(tpType string, strArr []string, paramsMap map[string]string) string {
323 | urlPath := getUrlPath(tpType, strArr)
324 | if len(paramsMap) <= 0 || paramsMap == nil { //如果没有参数需要添加直接返回当前路径
325 | return urlPath
326 | }
327 | urlPath = urlPath + "?" //如果参数个数大于等于0,路径后缀加上?
328 | paramList := make([]string, 0)
329 | for k, v := range paramsMap {
330 | paramList = append(paramList, fmt.Sprintf("%s=%s", k, v))
331 | }
332 | tempStr := strings.Join(paramList, "&")
333 | return fmt.Sprintf("%s%s", urlPath, tempStr)
334 | }
335 |
336 | // 获取url路径
337 | func getUrlPath(tpType string, strArr []string) string {
338 | urlPath := strings.Join(strArr, "")
339 | return fmt.Sprintf("%s://%s", tpType, urlPath)
340 | }
341 |
--------------------------------------------------------------------------------
/network/websocketPB.go:
--------------------------------------------------------------------------------
1 | package impl
2 |
3 | import (
4 | Proto_Proxy "LollipopGo/Proxy_Server/Proto"
5 | MsgHandleClt "LollipopGo/global_Interface"
6 | "LollipopGo/util"
7 | "fmt"
8 | "github.com/golang/glog"
9 | "golang.org/x/net/websocket"
10 | "io"
11 | "net/http"
12 | "strconv"
13 | )
14 |
15 | var BytebufLenPB int = 10000
16 | var IMsgPB MsgHandleClt.Msg_dataPB
17 |
18 | type OnlineUserPB struct {
19 | Connection *websocket.Conn
20 | inChan chan []byte
21 | outChan chan interface{}
22 | closeChan chan int
23 | goExit chan int
24 | isClosed bool
25 | HandleClt MsgHandleClt.Msg_dataPB
26 | }
27 |
28 | func InitConnectionPB(wsConn *websocket.Conn) (*OnlineUserPB, error) {
29 | conn := &OnlineUserPB{
30 | Connection: wsConn,
31 | inChan: make(chan []byte, BytebufLenPB),
32 | }
33 | go conn.handleLoopPB()
34 | conn.readLoopPB()
35 |
36 | return conn, nil
37 | }
38 |
39 | func (this *OnlineUserPB) readLoopPB() {
40 |
41 | for {
42 | var content []byte
43 | err := websocket.Message.Receive(this.Connection, &content)
44 | if err != nil {
45 | if err == io.EOF || err == io.ErrClosedPipe || len(content) == 0 || err == io.ErrNoProgress {
46 | IMsgPB.CloseEOFPB(this.Connection)
47 | return
48 | }
49 | break
50 | }
51 | select {
52 | case this.inChan <- content:
53 | }
54 | }
55 | }
56 |
57 | func (this *OnlineUserPB) handleLoopPB() {
58 |
59 | defer func() {
60 | if err := recover(); err != nil {
61 | strerr := fmt.Sprintf("%s", err)
62 | glog.Info("异常捕获:", strerr)
63 | }
64 | }()
65 |
66 | for {
67 | var r RequestbodyPB
68 | select {
69 | case r.req = <-this.inChan:
70 | }
71 | if len(r.req) <= 0 {
72 | continue
73 | }
74 | //if ProtocolData, err := r.Json2mapPB(); err == nil {
75 | IMsgPB.HandleCltProtocolPB(r.req, this.Connection)
76 | //if ProtocolData.PackageData == nil {
77 | // glog.Info("-----------------ProtocolData", ProtocolData)
78 | // IMsgPB.HandleCltProtocolPB(Proto_Proxy.Proxy_CMD(ProtocolData.Protocol), Proto_Proxy.Proxy_CMD(ProtocolData.Protocol2), ProtocolData.PackageData, this.Connection)
79 | //} else {
80 | // if ProtocolDataServer, err := r.Json2mapPBServer(); err == nil {
81 | // glog.Info("-----------------ProtocolDataServer", ProtocolDataServer)
82 | // IMsgPB.HandleCltProtocolPB(Proto_Proxy.Proxy_CMD(ProtocolDataServer.Protocol), Proto_Proxy.Proxy_CMD(ProtocolDataServer.Protocol2), ProtocolDataServer.PackageData, this.Connection)
83 | // }
84 | //}
85 | //}
86 | //if ProtocolData, err := r.Json2mapPBServer(); err == nil {
87 | // IMsgPB.HandleCltProtocolPB(Proto_Proxy.Proxy_CMD(ProtocolData.Protocol), Proto_Proxy.Proxy_CMD(ProtocolData.Protocol2), r.req, this.Connection)
88 | //}
89 | }
90 | }
91 |
92 | type RequestbodyPB struct {
93 | req []byte
94 | }
95 |
96 | // WebSocketStart websocket启动
97 | func WebSocketStartPB(url string,
98 | route string,
99 | BuildConnection func(ws *websocket.Conn),
100 | conntype int,
101 | serverId int,
102 | proxyUrl []string, //[0] = ProxyHost;[1]=ProxyPort,[2]=ProxyPath
103 | GameServerReceive func(ws *websocket.Conn),
104 | ConnXZ *websocket.Conn) {
105 | var StartDesc = ""
106 | if conntype == ConnProxy { //作为内网的服务器连接代理服务器
107 | proxyURL := AddParamsToGetReq("ws", proxyUrl, map[string]string{"data": "{ID:1}"})
108 | glog.Infof("connect to proxy addr:%s\n", proxyURL)
109 | conn, err := websocket.Dial(proxyURL, "", "test://golang/")
110 | if err != nil {
111 | glog.Errorln("err:", err.Error())
112 | return
113 | }
114 | ConnXZ = conn
115 | data := Proto_Proxy.G2Proxy_ConnData{
116 | Protocol: 1,
117 | Protocol2: Proto_Proxy.G2Proxy_ConnDataProto,
118 | ServerID: util.MD5_LollipopGO(strconv.Itoa(serverId)),
119 | }
120 | PlayerSendToServer(conn, data)
121 | go GameServerReceive(conn)
122 | } else if conntype == StartProxy {
123 | StartDesc = "proxy server"
124 | }
125 | http.Handle("/"+route, websocket.Handler(BuildConnection))
126 | glog.Infof("game listen to:[%s]\n", route)
127 | glog.Info("game start ok ", StartDesc)
128 | if err := http.ListenAndServe(url, nil); err != nil {
129 | glog.Info("Entry nil", err.Error())
130 | glog.Flush()
131 | return
132 | }
133 | }
134 |
135 | func PlayerSendToServerPB(conn *websocket.Conn, data []byte) {
136 | errq := websocket.Message.Send(conn, data)
137 | if errq != nil {
138 | glog.Info(errq)
139 | }
140 | return
141 | }
142 |
143 | ////------------------------------------------------------------------------------
144 | //func PlayerSendToProxyServerPBC(conn *websocket.Conn, main_cmd int32, sub_cmd int32, senddata []byte, strOpenID string) {
145 | //
146 | // // 组装GamePackage
147 | // GamePackage := &Proto_Proxy.GamePackage{
148 | // MainCmd: main_cmd,
149 | // SubCmd: sub_cmd,
150 | // OpenId: strOpenID,
151 | // PackageData: senddata,
152 | // }
153 | //
154 | // MarshalGamePackage, err := proto.Marshal(GamePackage)
155 | // if err != nil {
156 | // glog.Info("序列化失败:", err)
157 | // return
158 | // }
159 | //
160 | // // 组装ProxyC2S_SendData
161 | // ProxyC2S_SendData := &Proto_Proxy.ProxyC2S_SendData{
162 | // Protocol: 1,
163 | // Protocol2: int32(Proto_Proxy.Proxy_S2P_SendData),
164 | // OpenId: strOpenID,
165 | // PackageData: MarshalGamePackage,
166 | // }
167 | // MarshalProxyC2S_SendData, err := proto.Marshal(ProxyC2S_SendData)
168 | // if err != nil {
169 | // glog.Info("序列化失败:", err)
170 | // return
171 | // }
172 | //
173 | // // 发往代理服
174 | // errq := websocket.Message.Send(conn, MarshalProxyC2S_SendData)
175 | // if errq != nil {
176 | // glog.Info(errq)
177 | // }
178 | // return
179 | //}
180 |
181 | //
182 | ////------------------------------------------------------------------------------
183 | //func PlayerSendToProxyServerPB(conn *websocket.Conn, senddata []byte, strOpenID string) {
184 | // if len(strOpenID) > 50 {
185 | // return
186 | // }
187 | //
188 | // proxydata1 := &Proto_Proxy.ProxyS2C_SendData{
189 | // Protocol: 1,
190 | // Protocol2: int32(Proto_Proxy.Proxy_P2C_SendData),
191 | // OpenId: strOpenID,
192 | // PackageData: senddata,
193 | // }
194 | // PackageDatan, err := proto.Marshal(proxydata1)
195 | //
196 | // data := &Proto_Proxy.ProxyC2S_SendData{
197 | // Protocol: 10,
198 | // Protocol2: int32(Proto_Proxy.Proxy_S2P_SendData),
199 | // OpenId: strOpenID,
200 | // PackageData: PackageDatan,
201 | // }
202 | // PackageData, err := proto.Marshal(data)
203 | // if err != nil {
204 | // glog.Info("序列化失败:", err)
205 | // return
206 | // }
207 | //
208 | // errq := websocket.Message.Send(conn, PackageData)
209 | // if errq != nil {
210 | // glog.Info(errq)
211 | // }
212 | // return
213 | //}
214 | //
215 | //func PlayerSendToProxyServerPBConnet(conn *websocket.Conn, senddata []byte, strOpenID string) {
216 | // if len(strOpenID) > 50 {
217 | // return
218 | // }
219 | //
220 | // proxydata1 := &Proto_Proxy.ProxyS2C_SendData{
221 | // Protocol: 1,
222 | // Protocol2: int32(Proto_Proxy.Proxy_P2C_Connect),
223 | // OpenId: strOpenID,
224 | // PackageData: senddata,
225 | // }
226 | // PackageDatan, err := proto.Marshal(proxydata1)
227 | //
228 | // data := &Proto_Proxy.ProxyC2S_SendData{
229 | // Protocol: 1,
230 | // Protocol2: int32(Proto_Proxy.Proxy_S2P_SendData),
231 | // OpenId: strOpenID,
232 | // PackageData: PackageDatan,
233 | // }
234 | // PackageData, err := proto.Marshal(data)
235 | // if err != nil {
236 | // glog.Info("序列化失败:", err)
237 | // return
238 | // }
239 | //
240 | // errq := websocket.Message.Send(conn, PackageData)
241 | // if errq != nil {
242 | // glog.Info(errq)
243 | // }
244 | // return
245 | //}
246 |
--------------------------------------------------------------------------------
/timer/exanple_test.go:
--------------------------------------------------------------------------------
1 | package LollipopGo_timer
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func ExampleTimer() {
8 | d := NewDispatcher(10)
9 | // timer 1
10 | d.AfterFunc(1, func() {
11 | fmt.Println("My name is LollipopGo")
12 | })
13 | // timer 2
14 | t := d.AfterFunc(1, func() {
15 | fmt.Println("will not print")
16 | })
17 | t.Stop()
18 | (<-d.ChanTimer).Cb()
19 | }
--------------------------------------------------------------------------------
/timer/timer.go:
--------------------------------------------------------------------------------
1 | package LollipopGo_timer
2 |
3 | import (
4 | "LollipopGo/log"
5 | "runtime"
6 | "time"
7 | )
8 |
9 | var LenStackBuf int = 4096
10 |
11 | type Dispatcher struct {
12 | ChanTimer chan *Timer
13 | }
14 | // Timer
15 | type Timer struct {
16 | t *time.Timer
17 | cb func()
18 | }
19 |
20 | func (t *Timer) Stop() {
21 | t.t.Stop()
22 | t.cb = nil
23 | }
24 |
25 | func NewDispatcher(l int) *Dispatcher {
26 | disp := new(Dispatcher)
27 | disp.ChanTimer = make(chan *Timer, l)
28 | return disp
29 | }
30 |
31 | func (t *Timer) Cb() {
32 | defer func() {
33 | t.cb = nil
34 | if r := recover(); r != nil {
35 | if LenStackBuf > 0 {
36 | buf := make([]byte, LenStackBuf)
37 | l := runtime.Stack(buf, false)
38 | log.Error("%v: %s", r, buf[:l])
39 | } else {
40 | log.Error("%v", r)
41 | }
42 | }
43 | }()
44 |
45 | if t.cb != nil {
46 | t.cb()
47 | }
48 | }
49 |
50 | func (disp *Dispatcher) AfterFunc(d time.Duration, cb func()) *Timer {
51 | t := new(Timer)
52 | t.cb = cb
53 | t.t = time.AfterFunc(d, func() {
54 | disp.ChanTimer <- t
55 | })
56 | return t
57 | }
--------------------------------------------------------------------------------
/tools/DFA/dfa.go:
--------------------------------------------------------------------------------
1 | package DFA
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "path"
7 | "strings"
8 | )
9 |
10 | const (
11 | FILE_FILTER = "filter.txt"
12 | )
13 |
14 | var (
15 | ConfExample *ConfigFilter
16 | )
17 |
18 | type ConfigFilter struct {
19 | FilterList map[rune]*FilterModel //屏蔽字树
20 | }
21 |
22 | //加载词库
23 | func InitConfigFilter(configpath string) *ConfigFilter {
24 | result := new(ConfigFilter)
25 | {
26 | li := make(map[rune]*FilterModel)
27 | //我这里用的是一个文本文件,一行表示一个屏蔽词
28 | file, err := os.Open(path.Join(configpath, FILE_FILTER))
29 | if err != nil {
30 | panic(err)
31 | }
32 | barr, _ := ioutil.ReadAll(file)
33 | bstr := string(barr)
34 | bstr = strings.ReplaceAll(bstr, "\r", "")
35 | rows := strings.Split(bstr, "\n")
36 | for _, row := range rows {
37 | rowr := []rune(row)
38 | fmd, ok := li[rowr[0]]
39 | if !ok {
40 | fmd = new(FilterModel)
41 | fmd.NodeStr = rowr[0]
42 | fmd.Subli = make(map[rune]*FilterModel)
43 | li[rowr[0]] = fmd
44 | }
45 | fmd.IsEnd = filterFor(fmd.Subli, rowr, 1)
46 | }
47 | result.FilterList = li
48 | }
49 | return result
50 | }
51 |
52 | func filterFor(li map[rune]*FilterModel, rowr []rune, index int) bool {
53 | if len(rowr) <= index {
54 | return true
55 | }
56 | fmd, ok := li[rowr[index]]
57 | if !ok {
58 | fmd = new(FilterModel)
59 | fmd.NodeStr = rowr[index]
60 | fmd.Subli = make(map[rune]*FilterModel)
61 | li[rowr[index]] = fmd
62 | }
63 | index++
64 | fmd.IsEnd = filterFor(fmd.Subli, rowr, index)
65 | return false
66 | }
67 |
68 | //屏蔽字结构
69 | type FilterModel struct {
70 | NodeStr rune //内容
71 | Subli map[rune]*FilterModel //屏蔽子集合
72 | IsEnd bool //是否为结束
73 | }
74 |
75 | //屏蔽字操作,这个方法就是外部调用的入口方法
76 | func LollipopGoFilterCheck(data string) (result string) {
77 | filterli := ConfExample.FilterList
78 | arr := []rune(data)
79 | for i := 0; i < len(arr); i++ {
80 | fmd, ok := filterli[arr[i]]
81 | if !ok {
82 | continue
83 | }
84 | if ok, index := filterChackFor(arr, i+1, fmd.Subli); ok {
85 | arr[i] = rune('*')
86 | i = index
87 | }
88 | }
89 | return string(arr)
90 | }
91 |
92 | //递归调用检查屏蔽字
93 | func filterChackFor(arr []rune, index int, filterli map[rune]*FilterModel) (bool, int) {
94 | if len(arr) <= index {
95 | return false, index
96 | }
97 | if arr[index] == rune(' ') {
98 | if ok, i := filterChackFor(arr, index+1, filterli); ok {
99 | arr[index] = rune('*')
100 | return true, i
101 | }
102 | }
103 | fmd, ok := filterli[arr[index]]
104 | if !ok {
105 | return false, index
106 | }
107 | if fmd.IsEnd {
108 | arr[index] = rune('*')
109 | ok, i := filterChackFor(arr, index+1, fmd.Subli)
110 | if ok {
111 | return true, i
112 | }
113 | return true, index
114 | } else if ok, i := filterChackFor(arr, index+1, fmd.Subli); ok {
115 | arr[index] = rune('*')
116 | return true, i
117 | }
118 | return false, index
119 | }
120 |
--------------------------------------------------------------------------------
/tools/collection/array.go:
--------------------------------------------------------------------------------
1 | package collection
2 |
3 | func SumInt32s(array []int32) int64 {
4 | var sum int64
5 | for _, v := range array {
6 | sum += int64(v)
7 | }
8 | return sum
9 | }
10 |
11 | func SumInt(array []int) int64 {
12 | var sum int64
13 | for _, v := range array {
14 | sum += int64(v)
15 | }
16 | return sum
17 | }
18 |
19 | //会删去所有相等的元素
20 | func DeleteInt32s(array []int32, elem ...int32) []int32 {
21 | toDelete := NewInt32Set(elem...)
22 | result := make([]int32, 0, len(array))
23 | for _, v := range array {
24 | if !toDelete.Contains(v) {
25 | result = append(result, v)
26 | }
27 | }
28 | return result
29 | }
30 |
31 | //只会删除第一个相等的元素
32 | func DeleteInt32(array []int32, elem int32) []int32 {
33 | index := -1
34 | for i, v := range array {
35 | if v == elem {
36 | index = i
37 | break
38 | }
39 | }
40 | if index == -1 {
41 | return array
42 | }
43 | return DeleteInt32ByIndex(array, index)
44 | }
45 |
46 | func DeleteInt32ByIndex(array []int32, index int) []int32 {
47 | return append(array[:index], array[index+1:]...)
48 | }
49 |
50 | func GetElementIndexInt32(array []int32, elem int32) int {
51 | for i, d := range array {
52 | if d == elem {
53 | return i
54 | }
55 | }
56 | return -1
57 | }
58 |
59 | //是否完全包含elem,如果elem中有重复的元素,按两次计算
60 | func ContainInt32s(array []int32, elem ...int32) bool {
61 | if len(elem) == 0 {
62 | return false
63 | }
64 | if len(elem) == 1 {
65 | return GetElementIndexInt32(array, elem[0]) >= 0
66 | }
67 | counter := make(map[int32]int, len(array))
68 | var (
69 | ok bool
70 | count int
71 | )
72 | for _, item := range array {
73 | if _, ok = counter[item]; ok {
74 | counter[item]++
75 | } else {
76 | counter[item] = 1
77 | }
78 | }
79 | for _, e := range elem {
80 | if count, ok = counter[e]; !ok || count <= 0 {
81 | return false
82 | } else {
83 | counter[e]--
84 | }
85 | }
86 | return true
87 | }
88 |
--------------------------------------------------------------------------------
/tools/collection/array_test.go:
--------------------------------------------------------------------------------
1 | package collection
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestDeleteInt32s(t *testing.T) {
9 | type args struct {
10 | array []int32
11 | elem []int32
12 | }
13 | tests := []struct {
14 | name string
15 | args args
16 | want []int32
17 | }{
18 | {"", args{[]int32{1, 2, 2, 3, 3, 5, 7}, []int32{2, 3, 3}}, []int32{1, 5, 7}},
19 | {"", args{[]int32{1, 2, 3, 4, 5, 6}, []int32{1, 2, 3}}, []int32{4, 5, 6}},
20 | }
21 | for _, tt := range tests {
22 | t.Run(tt.name, func(t *testing.T) {
23 | if got := DeleteInt32s(tt.args.array, tt.args.elem...); !reflect.DeepEqual(got, tt.want) {
24 | t.Errorf("DeleteInt32s() = %v, want %v", got, tt.want)
25 | }
26 | })
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tools/collection/map.go:
--------------------------------------------------------------------------------
1 | package collection
2 |
3 | import "sync"
4 |
5 | func CountSyncMap(m *sync.Map) int {
6 | size := 0
7 | m.Range(func(key, value interface{}) bool {
8 | size++
9 | return true
10 | })
11 | return size
12 | }
13 |
--------------------------------------------------------------------------------
/tools/collection/set.go:
--------------------------------------------------------------------------------
1 | package collection
2 |
3 | //由于golang缺乏泛型,这里只写了最常用的int32 set,可以根据需要自行实现其他
4 | //如果需要通用泛型,也可以使用github.com/deckarep/golang-set这个包
5 | //非goroutine安全
6 | type Int32Set struct {
7 | set map[int32]struct{}
8 | }
9 |
10 | func NewInt32Set(items ...int32) *Int32Set {
11 | d := &Int32Set{
12 | set: make(map[int32]struct{}, len(items)),
13 | }
14 | for _, item := range items {
15 | d.set[item] = struct{}{}
16 | }
17 | return d
18 | }
19 |
20 | func (d *Int32Set) Add(items ...int32) *Int32Set {
21 | for _, item := range items {
22 | d.set[item] = struct{}{}
23 | }
24 | return d
25 | }
26 |
27 | func (d *Int32Set) Remove(items ...int32) *Int32Set {
28 | for _, item := range items {
29 | delete(d.set, item)
30 | }
31 | return d
32 | }
33 |
34 | func (d *Int32Set) Contains(items ...int32) bool {
35 | var ok bool
36 | for _, item := range items {
37 | if _, ok = d.set[item]; !ok {
38 | return false
39 | }
40 | }
41 | return true
42 | }
43 |
44 | func (d *Int32Set) Size() int {
45 | return len(d.set)
46 | }
47 |
48 | //交集
49 | func (d *Int32Set) Intersect(other *Int32Set) *Int32Set {
50 | result := NewInt32Set()
51 | //遍历较小的那个
52 | toRange, another := d.set, other
53 | if d.Size() > other.Size() {
54 | toRange, another = other.set, d
55 | }
56 | for k := range toRange {
57 | if another.Contains(k) {
58 | result.Add(k)
59 | }
60 | }
61 | return result
62 | }
63 |
64 | //并集
65 | func (d *Int32Set) Union(other *Int32Set) *Int32Set {
66 | result := NewInt32Set()
67 | for k, v := range d.set {
68 | result.set[k] = v
69 | }
70 | for k, v := range other.set {
71 | result.set[k] = v
72 | }
73 | return result
74 | }
75 |
76 | //差集
77 | func (d *Int32Set) Difference(other *Int32Set) *Int32Set {
78 | result := NewInt32Set()
79 | for k := range d.set {
80 | if !other.Contains(k) {
81 | result.Add(k)
82 | }
83 | }
84 | return result
85 | }
86 |
87 | func (d *Int32Set) ToArray() []int32 {
88 | result := make([]int32, 0, d.Size())
89 | for k := range d.set {
90 | result = append(result, k)
91 | }
92 | return result
93 | }
94 |
--------------------------------------------------------------------------------
/tools/database/gorm.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/go-sql-driver/mysql"
6 | "github.com/jinzhu/gorm"
7 | "log"
8 | "os"
9 | )
10 |
11 | type logger struct {
12 | Stdout *log.Logger
13 | StdErr *log.Logger
14 | }
15 |
16 | func (lg *logger) Print(values ...interface{}) {
17 | if values[0] == "sql" {
18 | return
19 | }
20 | msg := fmt.Sprintf("[error ] db error, msg:%v", values[1:])
21 | lg.Stdout.Output(3, msg)
22 | lg.StdErr.Output(3, msg)
23 | }
24 |
25 | func newLogger() *logger {
26 | info := log.New(os.Stdout, "", log.LstdFlags)
27 | err := log.New(os.Stderr, "", log.LstdFlags)
28 | return &logger{
29 | Stdout: info,
30 | StdErr: err,
31 | }
32 | }
33 |
34 | func NewMysqlConn(host string, isDebug bool) *gorm.DB {
35 | orm, err := gorm.Open("mysql", host)
36 | if err != nil {
37 | log.Fatal("can't init db: ", err)
38 | }
39 | orm.DB().SetMaxIdleConns(10)
40 | orm.DB().SetMaxOpenConns(100)
41 | orm.SingularTable(true)
42 | if isDebug {
43 | orm.LogMode(true)
44 | } else {
45 | orm.SetLogger(newLogger())
46 | }
47 | return orm
48 | }
49 |
--------------------------------------------------------------------------------
/tools/database/mgo.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/globalsign/mgo"
5 | "time"
6 | )
7 |
8 | func NewMongoSession(host string) *mgo.Session {
9 | session, err := mgo.DialWithTimeout(host, 3*time.Second)
10 | if err != nil {
11 | }
12 | session.SetPoolLimit(300)
13 | return session
14 | }
15 |
--------------------------------------------------------------------------------
/tools/database/redigo.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "fmt"
5 | "github.com/gomodule/redigo/redis"
6 | "time"
7 | )
8 |
9 | func NewRedisPool(host string, pwd string, db int) *redis.Pool {
10 | return &redis.Pool{
11 | MaxIdle: 3,
12 | IdleTimeout: 5 * time.Minute,
13 | Dial: func() (redis.Conn, error) {
14 | c, err := redis.Dial("tcp",
15 | host,
16 | redis.DialPassword(pwd),
17 | redis.DialDatabase(db),
18 | //redis.DialConnectTimeout(5*time.Second),
19 | //redis.DialReadTimeout(3*time.Second),
20 | //redis.DialWriteTimeout(3*time.Second),
21 | )
22 | if err != nil {
23 | return nil, fmt.Errorf("can't conn to redis: %s", err)
24 | }
25 | return c, nil
26 | },
27 | TestOnBorrow: func(c redis.Conn, t time.Time) error {
28 | _, err := c.Do("PING")
29 | return err
30 | },
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tools/deepcopy/json.go:
--------------------------------------------------------------------------------
1 | package deepcopy
2 |
3 | import (
4 | "LollipopGo/tools/jsonutils"
5 | "encoding/json"
6 | )
7 |
8 | //用json序列化的方法深拷贝,比反射更慢;有时候需要用json的tag去除某些字段充当RO
9 | //这时候可以用该函数
10 | func CopyJsonObject(obj interface{}) jsonutils.JsonObject {
11 | bytes, err := json.Marshal(obj)
12 | if err != nil {
13 | return nil
14 | }
15 | var cp jsonutils.JsonObject
16 | err = json.Unmarshal(bytes, &cp)
17 | return cp
18 | }
19 |
--------------------------------------------------------------------------------
/tools/deepcopy/reflect.go:
--------------------------------------------------------------------------------
1 | //from: https://github.com/mohae/deepcopy
2 | package deepcopy
3 |
4 | import (
5 | "reflect"
6 | "time"
7 | )
8 |
9 | /*
10 | The following struct tags is supported:
11 |
12 | type A struct {
13 | Field1 SomeType `deepcopy:"-"` // skip, will have zero-value in copy
14 | Field2 *SomeType `deepcopy:"="` // treat like with "=" assignment operator
15 | }
16 | Specifically the = tag is usable when you want to copy a struct containing something
17 | like *sync.Mutex or *os.File and don't want it to be deeply copied but simply assigned.
18 | */
19 |
20 | // Interface for delegating copy process to type
21 | type Interface interface {
22 | DeepCopy() interface{}
23 | }
24 |
25 | // Copy creates a deep copy of whatever is passed to it and returns the copy
26 | // in an interface{}. The returned value will need to be asserted to the
27 | // correct type.
28 | func Copy(src interface{}) interface{} {
29 | if src == nil {
30 | return nil
31 | }
32 |
33 | // Make the interface a reflect.Value
34 | original := reflect.ValueOf(src)
35 |
36 | // Make a copy of the same type as the original.
37 | cpy := reflect.New(original.Type()).Elem()
38 |
39 | // Recursively copy the original.
40 | copyRecursive(original, cpy)
41 |
42 | // Return the copy as an interface.
43 | return cpy.Interface()
44 | }
45 |
46 | // copyRecursive does the actual copying of the interface. It currently has
47 | // limited support for what it can handle. Add as needed.
48 | func copyRecursive(original, cpy reflect.Value) {
49 | // check for implement deepcopy.Interface
50 | if original.CanInterface() {
51 | if copier, ok := original.Interface().(Interface); ok {
52 | cpy.Set(reflect.ValueOf(copier.DeepCopy()))
53 | return
54 | }
55 | }
56 |
57 | // handle according to original's Kind
58 | switch original.Kind() {
59 | case reflect.Ptr:
60 | // Get the actual value being pointed to.
61 | originalValue := original.Elem()
62 |
63 | // if it isn't valid, return.
64 | if !originalValue.IsValid() {
65 | return
66 | }
67 | cpy.Set(reflect.New(originalValue.Type()))
68 | copyRecursive(originalValue, cpy.Elem())
69 |
70 | case reflect.Interface:
71 | // If this is a nil, don't do anything
72 | if original.IsNil() {
73 | return
74 | }
75 | // Get the value for the interface, not the pointer.
76 | originalValue := original.Elem()
77 |
78 | // Get the value by calling Elem().
79 | copyValue := reflect.New(originalValue.Type()).Elem()
80 | copyRecursive(originalValue, copyValue)
81 | cpy.Set(copyValue)
82 |
83 | case reflect.Struct:
84 | t, ok := original.Interface().(time.Time)
85 | if ok {
86 | cpy.Set(reflect.ValueOf(t))
87 | return
88 | }
89 | // Go through each field of the struct and copy it.
90 | for i := 0; i < original.NumField(); i++ {
91 | field := original.Type().Field(i)
92 | // The Type's structField for a given field is checked to see if StructField.PkgPath
93 | // is set to determine if the field is exported or not because CanSet() returns false
94 | // for settable fields. I'm not sure why. -mohae
95 | if original.Type().Field(i).PkgPath != "" {
96 | continue
97 | }
98 | switch field.Tag.Get("deepcopy") {
99 | case "-":
100 | continue
101 | case "=":
102 | cpy.Field(i).Set(original.Field(i))
103 | continue
104 | }
105 | copyRecursive(original.Field(i), cpy.Field(i))
106 | }
107 |
108 | case reflect.Slice:
109 | if original.IsNil() {
110 | return
111 | }
112 | // Make a new slice and copy each element.
113 | cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
114 | for i := 0; i < original.Len(); i++ {
115 | copyRecursive(original.Index(i), cpy.Index(i))
116 | }
117 |
118 | case reflect.Map:
119 | if original.IsNil() {
120 | return
121 | }
122 | cpy.Set(reflect.MakeMap(original.Type()))
123 | for _, key := range original.MapKeys() {
124 | originalValue := original.MapIndex(key)
125 | copyValue := reflect.New(originalValue.Type()).Elem()
126 | copyRecursive(originalValue, copyValue)
127 | copyKey := Copy(key.Interface())
128 | cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
129 | }
130 |
131 | default:
132 | cpy.Set(original)
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/tools/fs/config.go:
--------------------------------------------------------------------------------
1 | package fs
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | //解析器需要实现的接口
8 | type IConfigParser interface {
9 | ReloadConfig(path string, init bool) bool //重载配置
10 | GetConfig() interface{} //获取配置
11 | }
12 |
13 | //解析器的默认实现,用于嵌套
14 | type ParserMixIn struct {
15 | sync.RWMutex
16 | lastModified map[string]int64
17 | }
18 |
19 | //SetLastModifyTime update lastModifyTime
20 | func (pmi *ParserMixIn) SetLastModifyTime(path string, ts int64) {
21 | if pmi.lastModified == nil {
22 | pmi.lastModified = make(map[string]int64)
23 | }
24 | pmi.lastModified[path] = ts
25 | }
26 |
27 | //GetPathLastModifyTime
28 | func (pmi *ParserMixIn) GetPathModifyTime(path string) int64 {
29 | if pmi.lastModified == nil {
30 | return 0
31 | }
32 | return pmi.lastModified[path]
33 | }
34 |
35 | //CheckModify return if modified and last modify time
36 | func (pmi *ParserMixIn) CheckModify(path string) (bool, int64) {
37 | ts, err := GetLastModifyTime(path)
38 | if err != nil {
39 | return false, 0
40 | }
41 | if pmi.lastModified == nil {
42 | return true, ts
43 | }
44 | return ts != pmi.lastModified[path], ts
45 | }
46 |
47 | //周期性监测文件变化,调用Parser的回调
48 | func WatchConfigFiles(parsers map[string]IConfigParser) {
49 | for path, parser := range parsers {
50 | if parser.ReloadConfig(path, false) {
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tools/fs/file.go:
--------------------------------------------------------------------------------
1 | package fs
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | func GetLastModifyTime(path string) (ts int64, err error) {
8 | var (
9 | f *os.File
10 | fi os.FileInfo
11 | )
12 | if f, err = os.Open(path); err == nil {
13 | if fi, err = f.Stat(); err == nil {
14 | ts = fi.ModTime().Unix()
15 | _ = f.Close()
16 | }
17 | }
18 | return
19 | }
20 |
21 | // 判断所给路径文件/文件夹是否存在
22 | func Exists(path string) bool {
23 | _, err := os.Stat(path) //os.Stat获取文件信息
24 | if err != nil {
25 | if os.IsExist(err) {
26 | return true
27 | }
28 | return false
29 | }
30 | return true
31 | }
32 |
33 | // 判断所给路径是否为文件夹
34 | func IsDir(path string) bool {
35 | s, err := os.Stat(path)
36 | if err != nil {
37 | return false
38 | }
39 | return s.IsDir()
40 | }
41 |
42 | // 判断所给路径是否为文件
43 | func IsFile(path string) bool {
44 | return !IsDir(path)
45 | }
46 |
47 | //将给定文本保存为文件
48 | func SaveFile(fileName string, fileContent string) bool {
49 | f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
50 | if err != nil {
51 | return false
52 | }
53 | _, err = f.WriteString(fileContent)
54 | if err != nil {
55 | return false
56 | }
57 | _ = f.Close()
58 | return true
59 | }
60 |
--------------------------------------------------------------------------------
/tools/jsonutils/array.go:
--------------------------------------------------------------------------------
1 | package jsonutils
2 |
3 | type JsonArray []interface{}
4 |
5 | func (ja JsonArray) ToNumberArray() ([]float64, error) {
6 | if ja == nil {
7 | return nil, TypeError
8 | }
9 | resp := make([]float64, len(ja))
10 | var (
11 | v float64
12 | err error
13 | )
14 | for i := 0; i < len(ja); i++ {
15 | v, err = ja.GetFloat64ByIndex(i)
16 | if err != nil {
17 | return nil, err
18 | }
19 | resp = append(resp, v)
20 | }
21 | return resp, nil
22 | }
23 |
24 | func (ja JsonArray) ToStringArray() ([]string, error) {
25 | if ja == nil {
26 | return nil, TypeError
27 | }
28 | resp := make([]string, len(ja))
29 | var (
30 | v string
31 | err error
32 | )
33 | for i := 0; i < len(ja); i++ {
34 | v, err = ja.GetStringByIndex(i)
35 | if err != nil {
36 | return nil, err
37 | }
38 | resp = append(resp, v)
39 | }
40 | return resp, nil
41 | }
42 |
43 | func (ja JsonArray) ToBoolArray() ([]bool, error) {
44 | if ja == nil {
45 | return nil, TypeError
46 | }
47 | resp := make([]bool, len(ja))
48 | var (
49 | v bool
50 | err error
51 | )
52 | for i := 0; i < len(ja); i++ {
53 | v, err = ja.GetBoolByIndex(i)
54 | if err != nil {
55 | return nil, err
56 | }
57 | resp = append(resp, v)
58 | }
59 | return resp, nil
60 | }
61 |
62 | func (ja JsonArray) ToObjectArray() ([]JsonObject, error) {
63 | if ja == nil {
64 | return nil, TypeError
65 | }
66 | resp := make([]JsonObject, len(ja))
67 | var (
68 | v JsonObject
69 | err error
70 | )
71 | for i := 0; i < len(ja); i++ {
72 | v, err = ja.GetObjectByIndex(i)
73 | if err != nil {
74 | return nil, err
75 | }
76 | resp = append(resp, v)
77 | }
78 | return resp, nil
79 | }
80 |
81 | func (ja JsonArray) ToArrayOfArray() ([]JsonArray, error) {
82 | if ja == nil {
83 | return nil, TypeError
84 | }
85 | resp := make([]JsonArray, len(ja))
86 | var (
87 | v JsonArray
88 | err error
89 | )
90 | for i := 0; i < len(ja); i++ {
91 | v, err = ja.GetArrayByIndex(i)
92 | if err != nil {
93 | return nil, err
94 | }
95 | resp = append(resp, v)
96 | }
97 | return resp, nil
98 | }
99 |
100 | func (ja JsonArray) GetFloat64ByIndex(index int) (float64, error) {
101 | var (
102 | tmp interface{}
103 | resp float64
104 | ok bool
105 | )
106 | if index < 0 || ja == nil || index >= len(ja) {
107 | return 0, IndexError
108 | }
109 | tmp = ja[index]
110 | if resp, ok = tmp.(float64); ok {
111 | return resp, nil
112 | }
113 | return 0, TypeError
114 | }
115 |
116 | func (ja JsonArray) GetStringByIndex(index int) (string, error) {
117 | var (
118 | tmp interface{}
119 | resp string
120 | ok bool
121 | )
122 | if index < 0 || ja == nil || index >= len(ja) {
123 | return "", IndexError
124 | }
125 | tmp = ja[index]
126 | if resp, ok = tmp.(string); ok {
127 | return resp, nil
128 | }
129 | return "", TypeError
130 | }
131 |
132 | func (ja JsonArray) GetBoolByIndex(index int) (bool, error) {
133 | var (
134 | tmp interface{}
135 | resp bool
136 | ok bool
137 | )
138 | if index < 0 || ja == nil || index >= len(ja) {
139 | return false, IndexError
140 | }
141 | tmp = ja[index]
142 | if resp, ok = tmp.(bool); ok {
143 | return resp, nil
144 | }
145 | return false, TypeError
146 | }
147 |
148 | func (ja JsonArray) GetObjectByIndex(index int) (JsonObject, error) {
149 | var (
150 | tmp interface{}
151 | resp map[string]interface{}
152 | ok bool
153 | )
154 | if index < 0 || ja == nil || index >= len(ja) {
155 | return nil, IndexError
156 | }
157 | tmp = ja[index]
158 | if resp, ok = tmp.(map[string]interface{}); ok {
159 | return resp, nil
160 | }
161 | return nil, TypeError
162 | }
163 |
164 | func (ja JsonArray) GetArrayByIndex(index int) (JsonArray, error) {
165 | var (
166 | tmp interface{}
167 | resp []interface{}
168 | ok bool
169 | )
170 | if index < 0 || ja == nil || index >= len(ja) {
171 | return nil, IndexError
172 | }
173 | tmp = ja[index]
174 | if resp, ok = tmp.([]interface{}); ok {
175 | return resp, nil
176 | }
177 | return nil, TypeError
178 | }
179 |
--------------------------------------------------------------------------------
/tools/jsonutils/object.go:
--------------------------------------------------------------------------------
1 | package jsonutils
2 |
3 | import "github.com/pkg/errors"
4 | import "github.com/globalsign/mgo/bson"
5 |
6 | //一个动态的json对象
7 | //注意:json在Unmarshal到interface{}时,会把JsonNumber转成float64,除非使用UseNumber
8 | //因此这里仅提供float64接口,其他数据类型外部转换
9 | //如果json的是{type: 1, data: {}}这种格式,需要通过type解析具体的data,则推荐使用json.RawMessage来解析
10 |
11 | type JsonObject map[string]interface{}
12 |
13 | var (
14 | TypeError = errors.New("type convert error")
15 | KeyError = errors.New("key not exist")
16 | IndexError = errors.New("index not exist")
17 | )
18 |
19 | func (jm JsonObject) HasKey(key string) bool {
20 | if _, ok := jm[key]; ok {
21 | return true
22 | }
23 | return false
24 | }
25 |
26 | func (jm JsonObject) HasNotNilKey(key string) bool {
27 | if tmp, ok := jm[key]; ok {
28 | if tmp != nil {
29 | return true
30 | }
31 | }
32 | return false
33 | }
34 |
35 | func (jm JsonObject) GetObjectId() (bson.ObjectId, error) {
36 | if tmp, ok := jm["_id"]; ok {
37 | if id, ok := tmp.(bson.ObjectId); ok {
38 | return id, nil
39 | }
40 | }
41 | return "", TypeError
42 | }
43 |
44 | func (jm JsonObject) GetFloat64(key string) (float64, error) {
45 | var (
46 | tmp interface{}
47 | resp float64
48 | ok bool
49 | )
50 | if tmp, ok = jm[key]; ok {
51 | if resp, ok = tmp.(float64); ok {
52 | return resp, nil
53 | }
54 | return 0, TypeError
55 | }
56 | return 0, KeyError
57 | }
58 |
59 | func (jm JsonObject) GetFloat64Default(key string, defaultValue float64) float64 {
60 | var (
61 | tmp interface{}
62 | resp float64
63 | ok bool
64 | )
65 | if tmp, ok = jm[key]; ok {
66 | if resp, ok = tmp.(float64); ok {
67 | return resp
68 | }
69 | }
70 | return defaultValue
71 | }
72 |
73 | func (jm JsonObject) GetString(key string) (string, error) {
74 | var (
75 | tmp interface{}
76 | resp string
77 | ok bool
78 | )
79 | if tmp, ok = jm[key]; ok {
80 | if resp, ok = tmp.(string); ok {
81 | return resp, nil
82 | }
83 | return "", TypeError
84 | }
85 | return "", KeyError
86 | }
87 |
88 | func (jm JsonObject) GetStringDefault(key string, defaultValue string) string {
89 | var (
90 | tmp interface{}
91 | resp string
92 | ok bool
93 | )
94 | if tmp, ok = jm[key]; ok {
95 | if resp, ok = tmp.(string); ok {
96 | return resp
97 | }
98 | }
99 | return defaultValue
100 | }
101 |
102 | func (jm JsonObject) GetBool(key string) (bool, error) {
103 | var (
104 | tmp interface{}
105 | resp bool
106 | ok bool
107 | )
108 | if tmp, ok = jm[key]; ok {
109 | if resp, ok = tmp.(bool); ok {
110 | return resp, nil
111 | }
112 | return false, TypeError
113 | }
114 | return false, KeyError
115 | }
116 |
117 | func (jm JsonObject) GetBoolDefault(key string, defaultValue bool) bool {
118 | var (
119 | tmp interface{}
120 | resp bool
121 | ok bool
122 | )
123 | if tmp, ok = jm[key]; ok {
124 | if resp, ok = tmp.(bool); ok {
125 | return resp
126 | }
127 | }
128 | return defaultValue
129 | }
130 |
131 | func (jm JsonObject) GetJsonArray(key string) (JsonArray, error) {
132 | var (
133 | tmp interface{}
134 | resp []interface{}
135 | ok bool
136 | )
137 | if tmp, ok = jm[key]; ok {
138 | if resp, ok = tmp.([]interface{}); ok {
139 | return resp, nil
140 | }
141 | return nil, TypeError
142 | }
143 | return nil, KeyError
144 | }
145 |
146 | func (jm JsonObject) GetJsonObject(key string) (JsonObject, error) {
147 | var (
148 | tmp interface{}
149 | resp map[string]interface{}
150 | ok bool
151 | )
152 | if tmp, ok = jm[key]; ok {
153 | if resp, ok = tmp.(map[string]interface{}); ok {
154 | return resp, nil
155 | }
156 | return nil, TypeError
157 | }
158 | return nil, KeyError
159 | }
160 |
--------------------------------------------------------------------------------
/tools/mem/lru_cache.go:
--------------------------------------------------------------------------------
1 | package mem
2 |
3 | import (
4 | "github.com/pkg/errors"
5 | "sync"
6 | )
7 |
8 | var (
9 | KeyError = errors.New("Key not exist")
10 | )
11 | //缓存在内存里的lru cache
12 | type node struct {
13 | Prev *node
14 | Next *node
15 | Key string
16 | Val interface{}
17 | }
18 |
19 | func (nd *node) Detach() {
20 | if nd.Prev != nil {
21 | nd.Prev.Next = nd.Next
22 | }
23 | if nd.Next != nil {
24 | nd.Next.Prev = nd.Prev
25 | }
26 | }
27 |
28 | type LRU struct {
29 | head *node //头部节点
30 | tail *node //尾部节点
31 | keyNodes map[string]*node //key统一使用字符串
32 | capacity int //缓存大小
33 | sync.Mutex
34 | }
35 |
36 | //maxSize: 缓存的最大数量
37 | func NewLRUCache(capacity int) *LRU {
38 | if capacity <= 0 {
39 | capacity = 40000
40 | }
41 | result := &LRU{
42 | head: &node{},
43 | tail: &node{},
44 | keyNodes: make(map[string]*node, capacity),
45 | capacity: capacity,
46 | }
47 | result.head.Prev = nil
48 | result.head.Next = result.tail
49 | result.tail.Prev = result.head
50 | result.tail.Next = nil
51 | return result
52 | }
53 |
54 | //将节点移动到链表最前面,在外围加锁
55 | func (lru *LRU) mvHead(nd *node) {
56 | if lru.head.Next == nd {
57 | return
58 | }
59 | nd.Detach()
60 | nd.Next = lru.head.Next
61 | nd.Prev = lru.head
62 | lru.head.Next = nd
63 | nd.Next.Prev = nd
64 | }
65 |
66 | func (lru *LRU) Get(key string) (interface{}, error) {
67 | lru.Lock()
68 | defer lru.Unlock()
69 | if node, ok := lru.keyNodes[key]; ok {
70 | lru.mvHead(node)
71 | return node.Val, nil
72 | } else {
73 | return nil, KeyError
74 | }
75 | }
76 |
77 | //删除尾部节点,外围加锁
78 | func (lru *LRU) rmTail() {
79 | oldTail := lru.tail.Prev
80 | delete(lru.keyNodes, oldTail.Key)
81 | oldTail.Detach()
82 | }
83 |
84 | func (lru *LRU) Set(key string, value interface{}) {
85 | lru.Lock()
86 | defer lru.Unlock()
87 | if nd, ok := lru.keyNodes[key]; ok {
88 | lru.mvHead(nd)
89 | nd.Val = value
90 | } else {
91 | nd = &node{
92 | Key: key,
93 | Val: value,
94 | }
95 | if len(lru.keyNodes) == lru.capacity {
96 | lru.rmTail()
97 | }
98 | lru.keyNodes[key] = nd
99 | lru.mvHead(nd)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/tools/mem/ttl_cache.go:
--------------------------------------------------------------------------------
1 | package mem
2 |
3 | import (
4 | "sync"
5 | "time"
6 | )
7 |
8 | type ttlNode struct {
9 | Val interface{}
10 | LastUpdate int64
11 | }
12 | type TTL struct {
13 | alive int64
14 | capacity int
15 | data map[string]*ttlNode
16 | sync.Mutex
17 | }
18 |
19 | //aliveSeconds: 缓存有效期,capacity: 缓存容量,注意合理估计capacity为未过期元素的最大数量
20 | func NewTTLCache(aliveSeconds int64, capacity int) *TTL {
21 | return &TTL{
22 | alive: aliveSeconds,
23 | capacity: capacity,
24 | data: make(map[string]*ttlNode, capacity),
25 | }
26 | }
27 | //当缓存容量超出指定值时,会主动扫描一遍过期键
28 | //但是这并不能保证缓存容量减少到指定范围之内
29 | func (ttl *TTL) Set(key string, value interface{}) {
30 | ttl.Lock()
31 | defer ttl.Unlock()
32 | now := time.Now().Unix()
33 | if nd, ok := ttl.data[key]; ok {
34 | nd.Val = value
35 | nd.LastUpdate = now
36 | } else {
37 | ttl.data[key] = &ttlNode{
38 | Val: value,
39 | LastUpdate: now,
40 | }
41 | if len(ttl.data) >= ttl.capacity {
42 | for k, v := range ttl.data {
43 | if v.LastUpdate-now > ttl.alive {
44 | delete(ttl.data, k)
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
51 | func (ttl *TTL) Get(key string) (interface{}, error) {
52 | ttl.Lock()
53 | defer ttl.Unlock()
54 | if nd, ok := ttl.data[key]; ok {
55 | if time.Now().Unix()-nd.LastUpdate >= ttl.alive {
56 | delete(ttl.data, key)
57 | return nil, KeyError
58 | } else {
59 | return nd.Val, nil
60 | }
61 | }
62 | return nil, KeyError
63 | }
64 |
--------------------------------------------------------------------------------
/tools/misc.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "LollipopGo/tools/fs"
5 | "os"
6 | "path/filepath"
7 | "time"
8 | )
9 |
10 | const (
11 | configDir = "_config"
12 | )
13 |
14 | //约定程序二进制文件和_config文件夹在同一层
15 | //否则逐层往上找直到找到(方便进行单元测试)
16 | func GetConfigDir() string {
17 | wd, _ := os.Getwd()
18 |
19 | x := filepath.Join(wd, configDir)
20 | for !fs.Exists(x) {
21 | if wd == "/" {
22 | }
23 | wd = filepath.Dir(wd)
24 | x = filepath.Join(wd, configDir)
25 | }
26 | return x
27 | }
28 |
29 | type TextDuration struct {
30 | time.Duration
31 | }
32 |
33 | func (d *TextDuration) UnmarshalText(text []byte) error {
34 | var err error
35 | d.Duration, err = time.ParseDuration(string(text))
36 | return err
37 | }
38 |
39 | //通用recover函数,在单独协程的最开始使用defer调用
40 | func RecoverFromPanic(cb func()) {
41 | if r := recover(); r != nil {
42 | if cb != nil {
43 | cb()
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tools/num/num.go:
--------------------------------------------------------------------------------
1 | package num
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "sort"
7 | )
8 |
9 | //ConvertToInt guess Num format and convert to Int
10 | func ConvertToInt(temp interface{}) (int, error) {
11 | switch t := temp.(type) {
12 | case int:
13 | return int(t), nil
14 | case float64, float32:
15 | return int(reflect.ValueOf(t).Float()), nil
16 | case int64, int32:
17 | return int(reflect.ValueOf(t).Int()), nil
18 | default:
19 | return 0, fmt.Errorf("can't convert to int:%v", temp)
20 | }
21 | }
22 |
23 | var floatType = reflect.TypeOf(float64(0))
24 |
25 | //ConvertToFloat64 guess Num format and convert to Float64
26 | func ConvertToFloat64(unk interface{}) (float64, error) {
27 | v := reflect.ValueOf(unk)
28 | v = reflect.Indirect(v)
29 | if !v.Type().ConvertibleTo(floatType) {
30 | return 0, fmt.Errorf("cannot convert %v to float64", v.Type())
31 | }
32 | fv := v.Convert(floatType)
33 | return fv.Float(), nil
34 | }
35 |
36 | func MinInt(x, y int) int {
37 | if x < y {
38 | return x
39 | }
40 | return y
41 | }
42 |
43 | func MinInt32(x, y int32) int32 {
44 | if x < y {
45 | return x
46 | }
47 | return y
48 | }
49 |
50 | func MinInt64(x, y int64) int64 {
51 | if x < y {
52 | return x
53 | }
54 | return y
55 | }
56 |
57 | func MaxInt(x, y int) int {
58 | if x < y {
59 | return y
60 | }
61 | return x
62 | }
63 |
64 | func MaxInt32(x, y int32) int32 {
65 | if x < y {
66 | return y
67 | }
68 | return x
69 | }
70 |
71 | func MaxInt64(x, y int64) int64 {
72 | if x < y {
73 | return y
74 | }
75 | return x
76 | }
77 |
78 | func gcd(a, b int) int {
79 | if b == 0 {
80 | return a
81 | }
82 | return gcd(b, a%b)
83 | }
84 |
85 | // div : divide by gcd
86 | func div(a, b int) (a0, b0 int) {
87 | gcd := gcd(a, b)
88 | a /= gcd
89 | b /= gcd
90 | return a, b
91 | }
92 |
93 | // 计算组合结果
94 | func C(n, k int) int {
95 | i := k + 1
96 | r := n - k
97 | if r > k {
98 | i = r + 1
99 | r = k
100 | }
101 | f1, f2 := 1, 1
102 | j := 1
103 | for ; i <= n; i++ {
104 | f1 *= i
105 | for ; j <= r; j++ {
106 | f2 *= j
107 | if f2 > f1 {
108 | j++
109 | break
110 | }
111 | if gcd := gcd(f1, f2); gcd > 1 {
112 | f1, f2 = div(f1, f2)
113 | }
114 | }
115 | }
116 | return f1 / f2
117 | }
118 |
119 | //全排列
120 | func Permutations(arr []int) [][]int {
121 | var helper func([]int, int)
122 | var res [][]int
123 |
124 | helper = func(arr []int, n int) {
125 | if n == 1 {
126 | tmp := make([]int, len(arr))
127 | copy(tmp, arr)
128 | res = append(res, tmp)
129 | } else {
130 | for i := 0; i < n; i++ {
131 | helper(arr, n-1)
132 | if n%2 == 1 {
133 | tmp := arr[i]
134 | arr[i] = arr[n-1]
135 | arr[n-1] = tmp
136 | } else {
137 | tmp := arr[0]
138 | arr[0] = arr[n-1]
139 | arr[n-1] = tmp
140 | }
141 | }
142 | }
143 | }
144 | helper(arr, len(arr))
145 | return res
146 | }
147 |
148 | //从数组中选出m个任意组合
149 | //算法:先固定某一位的数字,再遍历其他位的可能性,递归此过程
150 | func Combinations(arr []int, m int) [][]int {
151 | if arr == nil || m > len(arr) || m <= 0 {
152 | return nil
153 | }
154 | result := make([][]int, 0, C(len(arr), m))
155 | data := make([]int, m)
156 | var helper func(int, int, int)
157 |
158 | helper = func(start int, end int, index int) {
159 | if index == m {
160 | d := make([]int, m)
161 | copy(d, data)
162 | result = append(result, d)
163 | return
164 | }
165 | for i := start; i < end && end-i+1 >= m-index; i++ {
166 | data[index] = arr[i]
167 | helper(i+1, end, index+1)
168 | //去重
169 | for i+1 < end && arr[i] == arr[i+1] {
170 | i++
171 | }
172 | }
173 | }
174 | sort.Slice(arr, func(i, j int) bool {
175 | return arr[i] < arr[j]
176 | })
177 | helper(0, len(arr), 0)
178 | return result
179 | }
180 |
181 | //从数组中选出m个任意组合,Int32版
182 | //算法:先固定某一位的数字,再遍历其他位的可能性,递归此过程
183 | func CombinationsInt32(arr []int32, m int) [][]int32 {
184 | if arr == nil || m > len(arr) || m <= 0 {
185 | return nil
186 | }
187 | result := make([][]int32, 0, C(len(arr), m))
188 | data := make([]int32, m)
189 | var helper func(int, int, int)
190 |
191 | helper = func(start int, end int, index int) {
192 | if index == m {
193 | d := make([]int32, m)
194 | copy(d, data)
195 | result = append(result, d)
196 | return
197 | }
198 | for i := start; i < end && end-i+1 >= m-index; i++ {
199 | data[index] = arr[i]
200 | helper(i+1, end, index+1)
201 | //去重
202 | for i+1 < end && arr[i] == arr[i+1] {
203 | i++
204 | }
205 | }
206 | }
207 | sort.Slice(arr, func(i, j int) bool {
208 | return arr[i] < arr[j]
209 | })
210 | helper(0, len(arr), 0)
211 | return result
212 | }
213 |
214 | //任意多个集合的笛卡尔积(直积)
215 | //回溯法遍历所有可能性
216 | func DirectProduct(items ...[]int) [][]int {
217 | if len(items) == 0 {
218 | return nil
219 | }
220 | size := 1
221 | for _, item := range items {
222 | size *= len(item)
223 | }
224 | result := make([][]int, 0, size)
225 | data := make([]int, len(items))
226 | var backtrack func(int)
227 | backtrack = func(index int) {
228 | if len(items) == index {
229 | d := make([]int, len(items))
230 | copy(d, data)
231 | result = append(result, d)
232 | return
233 | }
234 | for i := 0; i < len(items[index]); i++ {
235 | data[index] = items[index][i]
236 | backtrack(index + 1)
237 | }
238 | }
239 | backtrack(0)
240 | return result
241 | }
242 |
243 | //任意多个集合的笛卡尔积(直积),Int32版
244 | //回溯法遍历所有可能性
245 | func DirectProductInt32(items ...[]int32) [][]int32 {
246 | if len(items) == 0 {
247 | return nil
248 | }
249 | size := 1
250 | for _, item := range items {
251 | size *= len(item)
252 | }
253 | result := make([][]int32, 0, size)
254 | data := make([]int32, len(items))
255 | var backtrack func(int)
256 | backtrack = func(index int) {
257 | if len(items) == index {
258 | d := make([]int32, len(items))
259 | copy(d, data)
260 | result = append(result, d)
261 | return
262 | }
263 | for i := 0; i < len(items[index]); i++ {
264 | data[index] = items[index][i]
265 | backtrack(index + 1)
266 | }
267 | }
268 | backtrack(0)
269 | return result
270 | }
271 |
272 | //生成一个从start到end-1的数组
273 | func Range(start, end int) []int {
274 | if start >= end {
275 | return nil
276 | }
277 | result := make([]int, 0, end-start)
278 | for start < end {
279 | result = append(result, start)
280 | start++
281 | }
282 | return result
283 | }
284 |
285 | //生成一个从start到end-1的数组
286 | func RangeInt32(start, end int32) []int32 {
287 | if start >= end {
288 | return nil
289 | }
290 | result := make([]int32, 0, end-start)
291 | for start < end {
292 | result = append(result, start)
293 | start++
294 | }
295 | return result
296 | }
297 |
--------------------------------------------------------------------------------
/tools/num/random.go:
--------------------------------------------------------------------------------
1 | package num
2 |
3 | import "math/rand"
4 |
5 | //是否命中百分比
6 | func HitRate100(rate int32) bool {
7 | return rand.Int31n(100) < rate
8 | }
9 |
10 | //是否命中千分比
11 | func HitRate1000(rate int32) bool {
12 | return rand.Int31n(1000) < rate
13 | }
14 |
15 | //是否命中万分比
16 | func HitRate10000(rate int32) bool {
17 | return rand.Int31n(10000) < rate
18 | }
--------------------------------------------------------------------------------
/tools/sample/alias.go:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | //alias method,O(1)随机抽样算法; 当需要对同一个对象多次大量采样时,使用该算法,否则使用WeightedChoice即可
4 |
5 | import (
6 | "LollipopGo/tools/collection"
7 | "math/rand"
8 | )
9 |
10 | // AliasTable is a discrete distribution
11 | type AliasTable struct {
12 | rnd *rand.Rand
13 | alias []int
14 | prob []float64
15 | }
16 |
17 | // array-based stack
18 | type workList []int
19 |
20 | func (w *workList) push(i int) {
21 | *w = append(*w, i)
22 | }
23 |
24 | func (w *workList) pop() int {
25 | l := len(*w) - 1
26 | n := (*w)[l]
27 | *w = (*w)[:l]
28 | return n
29 | }
30 |
31 | // 新建一个抽样器,weightList是权重列表,src是随机数种子
32 | func NewAlias(weightList []int32, src rand.Source) AliasTable {
33 |
34 | n := len(weightList)
35 | total := collection.SumInt32s(weightList)
36 | v := AliasTable{
37 | alias: make([]int, n),
38 | prob: make([]float64, n),
39 | rnd: rand.New(src),
40 | }
41 |
42 | p := make([]float64, n)
43 | for i, w := range weightList {
44 | p[i] = float64(int(w)*i) / float64(total)
45 | }
46 |
47 | var small, large workList
48 |
49 | for i, pi := range p {
50 | if pi < 1 {
51 | small = append(small, i)
52 | } else {
53 | large = append(large, i)
54 | }
55 | }
56 |
57 | for len(large) > 0 && len(small) > 0 {
58 | l := small.pop()
59 | g := large.pop()
60 | v.prob[l] = p[l]
61 | v.alias[l] = g
62 |
63 | p[g] = (p[g] + p[l]) - 1
64 | if p[g] < 1 {
65 | small.push(g)
66 | } else {
67 | large.push(g)
68 | }
69 | }
70 |
71 | for len(large) > 0 {
72 | g := large.pop()
73 | v.prob[g] = 1
74 | }
75 |
76 | for len(small) > 0 {
77 | l := small.pop()
78 | v.prob[l] = 1
79 | }
80 |
81 | return v
82 | }
83 |
84 | // Next returns the next random value from the discrete distribution
85 | func (v *AliasTable) Next() int {
86 |
87 | n := len(v.alias)
88 |
89 | i := v.rnd.Intn(n)
90 |
91 | if v.rnd.Float64() < v.prob[i] {
92 | return i
93 | }
94 |
95 | return v.alias[i]
96 | }
97 |
--------------------------------------------------------------------------------
/tools/sample/rand.go:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import (
4 | "LollipopGo/tools/collection"
5 | "LollipopGo/tools/tz"
6 | "math/rand"
7 | )
8 |
9 | //初始化random
10 | func InitRand(){
11 | rand.Seed(tz.GetNowTsMs())
12 | }
13 |
14 | //随机字符串,包含大小写字母和数字
15 | func RandomString(l int) string {
16 | bytes := make([]byte, l)
17 | for i := 0; i < l; i++ {
18 | x := rand.Intn(3)
19 | switch x {
20 | case 0:
21 | bytes[i] = byte(RandInt(65, 90)) //大写字母
22 | case 1:
23 | bytes[i] = byte(RandInt(97, 122))
24 | case 2:
25 | bytes[i] = byte(rand.Intn(10))
26 | }
27 | }
28 | return string(bytes)
29 | }
30 |
31 | //闭区间
32 | func RandInt(min, max int) int {
33 | return min + rand.Intn(max-min+1)
34 | }
35 |
36 | func RandInt32(min, max int32) int32 {
37 | return min + rand.Int31n(max-min+1)
38 | }
39 |
40 | func RandInt64(min, max int64) int64 {
41 | return min + rand.Int63n(max-min+1)
42 | }
43 |
44 | func Shuffle(array []int) {
45 | for i := range array {
46 | j := rand.Intn(i + 1)
47 | array[i], array[j] = array[j], array[i]
48 | }
49 | }
50 |
51 | func ShuffleInt32(array []int32) {
52 | for i := range array {
53 | j := rand.Intn(i + 1)
54 | array[i], array[j] = array[j], array[i]
55 | }
56 | }
57 |
58 | func ShuffleInt64(array []int64) {
59 | for i := range array {
60 | j := rand.Intn(i + 1)
61 | array[i], array[j] = array[j], array[i]
62 | }
63 | }
64 |
65 | func ShuffleUint64(array []uint64) {
66 | for i := range array {
67 | j := rand.Intn(i + 1)
68 | array[i], array[j] = array[j], array[i]
69 | }
70 | }
71 |
72 | func RandChoiceInt32(array []int32, n int) []int32 {
73 | if n <= 0 {
74 | return nil
75 | }
76 | if n == 1 {
77 | return []int32{array[rand.Intn(len(array))]}
78 | }
79 | tmp := make([]int32, len(array))
80 | copy(tmp, array)
81 | if len(tmp) <= n {
82 | return tmp
83 | }
84 | ShuffleInt32(tmp)
85 | return tmp[:n]
86 | }
87 |
88 | func RandChoice(array []int, n int) []int {
89 | if n <= 0 {
90 | return nil
91 | }
92 | if n == 1 {
93 | return []int{array[rand.Intn(len(array))]}
94 | }
95 | tmp := make([]int, len(array))
96 | copy(tmp, array)
97 | if len(tmp) <= n {
98 | return tmp
99 | }
100 | Shuffle(tmp)
101 | return tmp[:n]
102 | }
103 |
104 | //根据权重随机,返回对应选项的索引,O(n)
105 | func WeightedChoice(weightArray []int) int {
106 | if weightArray == nil {
107 | return -1
108 | }
109 | total := collection.SumInt(weightArray)
110 | rv := rand.Int63n(total)
111 | for i, v := range weightArray {
112 | if rv < int64(v) {
113 | return i
114 | }
115 | rv -= int64(v)
116 | }
117 | return len(weightArray) - 1
118 | }
119 |
120 | //是否命中百分比
121 | func HitRate100(rate int) bool {
122 | return rand.Intn(100) < rate
123 | }
124 |
125 | //是否命中千分比
126 | func HitRate1000(rate int) bool {
127 | return rand.Intn(1000) < rate
128 | }
129 |
130 | //是否命中万分比
131 | func HitRate10000(rate int) bool {
132 | return rand.Intn(10000) < rate
133 | }
134 |
--------------------------------------------------------------------------------
/tools/tz/tz.go:
--------------------------------------------------------------------------------
1 | package tz
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | const (
8 | FullFormat = "2006-01-02 15:04:05" //最常用的格式
9 | DayFormat = "2006-01-02"
10 | )
11 |
12 | func TsToDateStr(ts int64) string {
13 | return GetLocalStr(time.Unix(ts, 0).UTC(), "")
14 | }
15 | func TsToDateTimeStr(ts int64) string {
16 | return GetLocalStr(time.Unix(ts, 0).UTC(), FullFormat)
17 | }
18 |
19 | func GetTodayStr() string {
20 | return GetLocalStr(time.Now().UTC(), "")
21 | }
22 |
23 | func IndiaTimezone() *time.Location {
24 | loc, _ := time.LoadLocation("Asia/Kolkata")
25 | return loc
26 | }
27 |
28 | // GetLocalStr change utc time to local date str
29 | func GetLocalStr(base time.Time, format string) string {
30 | if format == "" {
31 | format = DayFormat
32 | }
33 | return base.In(IndiaTimezone()).Format(format)
34 | }
35 |
36 | //如果想要将本地时间转换成UTC,直接用UTC()方法即可
37 | //如果解析字符串,对应的是本地时间且字符串中没有时区,使用time.ParseInLocation(ChinaTimeZone())
38 | func UTCToLocal(base time.Time) time.Time {
39 | return base.In(IndiaTimezone())
40 | }
41 |
42 | // IsSameDay check if two time is same day locally
43 | func IsSameDay(l time.Time, r time.Time) bool {
44 | return GetLocalStr(l, "") == GetLocalStr(r, "")
45 | }
46 |
47 | func GetNowTsMs() int64 {
48 | return time.Now().UnixNano() / int64(time.Millisecond)
49 | }
50 |
51 | func GetNowTs() int64 {
52 | return time.Now().Unix()
53 | }
54 |
55 | func Schedule(what func(), delay time.Duration, stop chan bool) {
56 | DynamicSchedule(what, &delay, stop)
57 | }
58 |
59 | func LocalNow() time.Time {
60 | return UTCToLocal(time.Now().UTC())
61 | }
62 |
63 | //可以动态修改延迟时间、可关闭的定时器
64 | func DynamicSchedule(what func(), delayAddr *time.Duration, stop chan bool) {
65 | go func() {
66 | for {
67 | select {
68 | case <-time.After(*delayAddr):
69 | what()
70 | case <-stop:
71 | return
72 | }
73 | }
74 | }()
75 | }
76 |
--------------------------------------------------------------------------------
/tools/tz/tz_test.go:
--------------------------------------------------------------------------------
1 | package tz
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestGetLocalStr(t *testing.T) {
9 | var ts = TsToDateStr(1574960492)
10 | fmt.Println(ts)
11 | }
12 |
13 | func TestTsToDateTimeStr(t *testing.T) {
14 | var ts = TsToDateTimeStr(1575003617)
15 | fmt.Println(ts)
16 | }
17 |
--------------------------------------------------------------------------------
/util/map.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type Map struct {
8 | sync.RWMutex
9 | m map[interface{}]interface{}
10 | }
11 |
12 | func (m *Map) init() {
13 | if m.m == nil {
14 | m.m = make(map[interface{}]interface{})
15 | }
16 | }
17 |
18 | func (m *Map) UnsafeGet(key interface{}) interface{} {
19 | if m.m == nil {
20 | return nil
21 | } else {
22 | return m.m[key]
23 | }
24 | }
25 |
26 | func (m *Map) Get(key interface{}) interface{} {
27 | m.RLock()
28 | defer m.RUnlock()
29 | return m.UnsafeGet(key)
30 | }
31 |
32 | func (m *Map) UnsafeSet(key interface{}, value interface{}) {
33 | m.init()
34 | m.m[key] = value
35 | }
36 |
37 | func (m *Map) Set(key interface{}, value interface{}) {
38 | m.Lock()
39 | defer m.Unlock()
40 | m.UnsafeSet(key, value)
41 | }
42 |
43 | func (m *Map) TestAndSet(key interface{}, value interface{}) interface{} {
44 | m.Lock()
45 | defer m.Unlock()
46 |
47 | m.init()
48 |
49 | if v, ok := m.m[key]; ok {
50 | return v
51 | } else {
52 | m.m[key] = value
53 | return nil
54 | }
55 | }
56 |
57 | func (m *Map) UnsafeDel(key interface{}) {
58 | m.init()
59 | delete(m.m, key)
60 | }
61 |
62 | func (m *Map) Del(key interface{}) {
63 | m.Lock()
64 | defer m.Unlock()
65 | m.UnsafeDel(key)
66 | }
67 |
68 | func (m *Map) UnsafeLen() int {
69 | if m.m == nil {
70 | return 0
71 | } else {
72 | return len(m.m)
73 | }
74 | }
75 |
76 | func (m *Map) Len() int {
77 | m.RLock()
78 | defer m.RUnlock()
79 | return m.UnsafeLen()
80 | }
81 |
82 | func (m *Map) UnsafeRange(f func(interface{}, interface{})) {
83 | if m.m == nil {
84 | return
85 | }
86 | for k, v := range m.m {
87 | f(k, v)
88 | }
89 | }
90 |
91 | // 并发安全读取
92 | func (m *Map) LollipopGo_RLockRange(data map[string]interface{}) map[string]interface{} {
93 | m.RLock()
94 | defer m.RUnlock()
95 | // 枚举处理
96 | if m.m == nil {
97 | return nil
98 | }
99 | for k, v := range m.m {
100 | if k == nil {
101 | continue
102 | }
103 | data[k.(string)] = v
104 | }
105 |
106 | return data
107 | }
108 |
109 | // 枚举数据
110 | func (m *Map) RLockRange(f func(interface{}, interface{})) {
111 | m.RLock()
112 | defer m.RUnlock()
113 | m.UnsafeRange(f)
114 | }
115 |
116 | func (m *Map) LockRange(f func(interface{}, interface{})) {
117 | m.Lock()
118 | defer m.Unlock()
119 | m.UnsafeRange(f)
120 | }
121 |
122 | // 累加数据
123 | func (m *Map) AddCount(key interface{}, value interface{}) {
124 | // Get
125 | m.Get(key)
126 | // Set
127 | // m.Set()
128 | return
129 | }
130 |
--------------------------------------------------------------------------------
/util/rand.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 | func init() {
9 | rand.Seed(time.Now().UnixNano())
10 | }
11 |
12 | func RandGroup_LollipopGo(p ...uint32) int {
13 | rand.Seed(time.Now().UnixNano())
14 | if p == nil {
15 | panic("args not found")
16 | }
17 |
18 | r := make([]uint32, len(p))
19 | for i := 0; i < len(p); i++ {
20 | if i == 0 {
21 | r[0] = p[0]
22 | } else {
23 | r[i] = r[i-1] + p[i]
24 | }
25 | }
26 |
27 | rl := r[len(r)-1]
28 | if rl == 0 {
29 | return 0
30 | }
31 |
32 | rn := uint32(rand.Int63n(int64(rl)))
33 | for i := 0; i < len(r); i++ {
34 | if rn < r[i] {
35 | return i
36 | }
37 | }
38 |
39 | panic("bug")
40 | }
41 |
42 | func RandInterval_LollipopGo(b1, b2 int32) int32 {
43 | rand.Seed(time.Now().UnixNano())
44 | if b1 == b2 {
45 | return b1
46 | }
47 |
48 | min, max := int64(b1), int64(b2)
49 | if min > max {
50 | min, max = max, min
51 | }
52 | return int32(rand.Int63n(max-min+1) + min)
53 | }
54 |
55 | func RandIntervalN_LollipopGo(b1, b2 int32, n uint32) []int32 {
56 | rand.Seed(time.Now().UnixNano())
57 | if b1 == b2 {
58 | return []int32{b1}
59 | }
60 |
61 | min, max := int64(b1), int64(b2)
62 | if min > max {
63 | min, max = max, min
64 | }
65 | l := max - min + 1
66 | if int64(n) > l {
67 | n = uint32(l)
68 | }
69 |
70 | r := make([]int32, n)
71 | m := make(map[int32]int32)
72 | for i := uint32(0); i < n; i++ {
73 | v := int32(rand.Int63n(l) + min)
74 |
75 | if mv, ok := m[v]; ok {
76 | r[i] = mv
77 | } else {
78 | r[i] = v
79 | }
80 |
81 | lv := int32(l - 1 + min)
82 | if v != lv {
83 | if mv, ok := m[lv]; ok {
84 | m[v] = mv
85 | } else {
86 | m[v] = lv
87 | }
88 | }
89 |
90 | l--
91 | }
92 |
93 | return r
94 | }
95 |
--------------------------------------------------------------------------------
/util/slice.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "reflect"
5 | )
6 |
7 | func RemoveSlice(s []int, i int) []int {
8 | return append(s[:i], s[i+1:]...)
9 | }
10 |
11 | func Duplicate(a interface{}) (ret []interface{}) {
12 | va := reflect.ValueOf(a)
13 | for i := 0; i < va.Len(); i++ {
14 |
15 | if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {
16 | continue
17 | }
18 | ret = append(ret, va.Index(i).Interface())
19 | }
20 | return ret
21 | }
22 |
--------------------------------------------------------------------------------
/util/sort.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 |
4 |
5 | /*//------------------------------------------------------------------------------
6 | // 例子已经写在简书:https://www.jianshu.com/p/e30a9db07da0
7 | // 详见《彬哥Go语言笔记》
8 | func Sort_LollipopGo(data map[string]*conf.DSQ_Exp, iExp int) int {
9 |
10 | if iExp == 0 {
11 | return 0
12 | }
13 | var length = len(data)
14 | var ssort []int
15 |
16 | for _, v := range data {
17 | ssort = append(ssort, Str2intLollipopgo(v.Exp))
18 | }
19 |
20 | for i := 1; i < length; i++ {
21 | for j := i; j > 0 && ssort[j] < ssort[j-1]; j-- {
22 | ssort[j], ssort[j-1] = ssort[j-1], ssort[j]
23 | }
24 | }
25 | for index, val := range ssort {
26 | if iExp == val {
27 | return index
28 | }
29 | }
30 | return 0
31 | }
32 | */
33 |
34 | // i := Minimum(1, 3, 5, 7, 9, 10, -1, 1).(int)
35 | func Minimum(first interface{}, rest ...interface{}) interface{} {
36 | minimum := first
37 |
38 | for _, v := range rest {
39 | switch v.(type) {
40 | case int:
41 | if v := v.(int); v < minimum.(int) {
42 | minimum = v
43 | }
44 | case float64:
45 | if v := v.(float64); v < minimum.(float64) {
46 | minimum = v
47 | }
48 | case string:
49 | if v := v.(string); v < minimum.(string) {
50 | minimum = v
51 | }
52 | }
53 | }
54 | return minimum
55 | }
56 |
--------------------------------------------------------------------------------
/util/strconv.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | )
7 |
8 | func Str2intLollipopgo(data string) int {
9 | v, err := strconv.Atoi(data)
10 | if err != nil {
11 | return -1
12 | }
13 | return v
14 | }
15 |
16 | func Int2str_LollipopGo(data int) string {
17 | return strconv.Itoa(data)
18 | }
19 |
20 | // data to string all
21 | func ToString(arg interface{}) string {
22 | switch arg.(type) {
23 | case bool:
24 | return boolToString(arg.(bool))
25 | case float32:
26 | return floatToString(float64(arg.(float32)))
27 | case float64:
28 | return floatToString(arg.(float64))
29 | case int:
30 | return intToString(int64(arg.(int)))
31 | case int8:
32 | return intToString(int64(arg.(int8)))
33 | case int16:
34 | return intToString(int64(arg.(int16)))
35 | case int32:
36 | return intToString(int64(arg.(int32)))
37 | case int64:
38 | return intToString(int64(arg.(int64)))
39 | default:
40 | return fmt.Sprint(arg)
41 | }
42 | }
43 |
44 | func floatToString(f float64) string {
45 | return strconv.FormatFloat(f, 'E', -1, 64)
46 | }
47 | func intToString(i int64) string {
48 | return strconv.FormatInt(i, 10)
49 | }
50 | func boolToString(b bool) string {
51 | if b {
52 | return "true"
53 | } else {
54 | return "false"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "fmt"
7 | "math/rand"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | //------------------------------------------------------------------------------
13 |
14 | // package main
15 |
16 | // import (
17 | // "fmt"
18 | // "strconv"
19 | // "time"
20 | // )
21 |
22 | // func main() {
23 | // t := time.Now()
24 | // fmt.Println(t)
25 |
26 | // fmt.Println(t.UTC().Format(time.UnixDate))
27 |
28 | // fmt.Println(t.Unix())
29 |
30 | // timestamp := strconv.FormatInt(t.UTC().UnixNano(), 10)
31 | // fmt.Println(timestamp)
32 | // timestamp = timestamp[:10]
33 | // fmt.Println(timestamp)
34 | // }
35 |
36 | // 输出:
37 | // 2017-06-21 11:52:29.0826692 + 0800 CST
38 | // Wed Jun 21 03:52:29 UTC 2017
39 | // 1498017149
40 | // 1498017149082669200
41 | // 1498017149
42 |
43 | // 生成时间戳的函数
44 | func UTCTime_LollipopGO() string {
45 | t := time.Now()
46 | return strconv.FormatInt(t.UTC().UnixNano(), 10)
47 | }
48 |
49 | // MD5 实现 :主要是针对 字符串的加密
50 | func MD5_LollipopGO(data string) string {
51 | h := md5.New()
52 | h.Write([]byte(data))
53 | return hex.EncodeToString(h.Sum(nil))
54 | }
55 |
56 | //------------------------------------------------------------------------------
57 | //返回[0,max)的随机整数
58 | func Randnum_LollipopGO(max int) int {
59 |
60 | if max == 0 {
61 | panic("随机函数,传递参数错误!")
62 | return -1
63 | }
64 | // 随机种子:系统时间
65 | rand.Seed(time.Now().Unix())
66 | return rand.Intn(max)
67 | }
68 |
69 | //------------------------------------------------------------------------------
70 |
71 | func CheckErr_LollipopGO(err error) {
72 | if err != nil {
73 | // panic(err)
74 | fmt.Println("err:", err)
75 | }
76 | }
77 |
78 | func GetTime_LollipopGO() string {
79 | const shortForm = "2006-01-02 15:04:05"
80 | t := time.Now()
81 | temp := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.Local)
82 | str := temp.Format(shortForm)
83 | return str
84 | }
85 |
86 | func GetNowtimeMD5_LollipopGO() string {
87 | t := time.Now()
88 | timestamp := strconv.FormatInt(t.UTC().UnixNano(), 10)
89 | return MD5_LollipopGO(timestamp)
90 | }
91 |
92 | //单位s ,打印结果:1491888244
93 | func GetNowUnix_LollipopGo() int64 {
94 | return time.Now().Unix()
95 | }
96 |
97 | //单位纳秒,打印结果:1491888244752784461
98 | func GetNowUnixNano_LollipopGo() int64 {
99 | return time.Now().UnixNano()
100 | }
101 |
102 | ////測試函數
103 | //func Try(fn func()) (err error) {
104 | // defer func{
105 | // err = recover()
106 | // }()
107 |
108 | // fn()
109 | // return
110 | //}
111 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | package LollipopGo
2 |
3 | const Version = "v2.9.20201102"
--------------------------------------------------------------------------------