├── .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 | 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 |
  1. 希望更多喜欢Go语言的同学及想从事Go语言开发游戏服务器的同学一个方向的指引
  2. 37 |
  3. 课程多维度教学,lollipopGo游戏框架实战课程等等
  4. 38 |
  5. LollipopGo架构 最新版本: v2.8.X
  6. 39 |
  7. LollipopGo架构 直接下载就可以使用(彬哥维护),无需依赖管理,否则导致部分官方接口无法使用
  8. 40 |
  9. LollipopGo架构 手机对战游戏视频:点击访问
  10. 41 |
  11. LollipopGo架构 PC端游对战游戏视频:点击访问
  12. 42 |
  13. 同时我们的免费课程也在持续更新中; 点击访问:网易云课堂
  14. 43 |
  15. 同时我们的免费课程也在持续更新中; 点击访问:B站(bilibili.com)
  16. 44 |
  17. 同时我们的免费课程也在持续更新中; 点击访问:LollipopGo框架文档地址,关注公众服务号:Golang语言游戏服务器
  18. 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" --------------------------------------------------------------------------------