├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── discovery ├── desc.go ├── discovery.go ├── kvconfig │ └── kvconfig.go ├── memsd │ ├── api │ │ ├── api.go │ │ ├── api_test.go │ │ ├── config.go │ │ ├── conn.go │ │ ├── const.go │ │ ├── kv.go │ │ ├── log.go │ │ ├── packet.go │ │ ├── rpc.go │ │ ├── setup.go │ │ ├── svc.go │ │ ├── transmitter.go │ │ └── util.go │ ├── cmd.go │ ├── main.go │ ├── model │ │ ├── kv.go │ │ └── svcmodel.go │ ├── persist.go │ ├── proto │ │ ├── MakeProto.sh │ │ ├── msgbind_gen.go │ │ ├── msgsvc_gen.go │ │ ├── proto_svc.txt │ │ ├── protolist.sh │ │ └── sd.proto │ ├── redundant.go │ ├── svc.go │ └── svc_msg.go ├── safevalue.go ├── safevalue_test.go └── util.go ├── dummy.go ├── go.mod ├── go.sum ├── service ├── discovery.go ├── flag.go ├── hooker.go ├── init.go ├── log.go ├── matchrule.go ├── model.go ├── msg.go ├── multipeer.go ├── query.go ├── reg.go ├── remotesvc.go ├── safevalue_test.go ├── svcid.go └── svcid_test.go ├── tool └── protogen │ ├── gengo │ ├── func.go │ ├── gen.go │ └── text.go │ └── main.go └── util ├── flagfile.go ├── log.go ├── log_test.go ├── uuid64.go ├── uuid64_test.go ├── wilecard.go └── wilecard_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | demo/bin 2 | *.stackdump 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | - go get -u -v github.com/davyxu/cellmesh 5 | 6 | go: 7 | - 1.11.x 8 | 9 | script: 10 | - cd $GOPATH && sh src/github.com/davyxu/cellmesh/shell/DownloadThirdParty.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Davy xu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status][3]][4] [![Go Report Card][5]][6] [![MIT licensed][11]][12] [![GoDoc][1]][2] 2 | 3 | [1]: https://godoc.org/github.com/davyxu/cellmesh?status.svg 4 | [2]: https://godoc.org/github.com/davyxu/cellmesh 5 | [3]: https://travis-ci.org/davyxu/cellmesh.svg?branch=master 6 | [4]: https://travis-ci.org/davyxu/cellmesh 7 | [5]: https://goreportcard.com/badge/github.com/davyxu/cellmesh 8 | [6]: https://goreportcard.com/report/github.com/davyxu/cellmesh 9 | [11]: https://img.shields.io/badge/license-MIT-blue.svg 10 | [12]: LICENSE 11 | 12 | # cellmesh 13 | 基于cellnet的游戏服务框架 14 | 15 | # 特点 16 | 17 | ## Based On Service Discovery(基于服务发现) 18 | 19 | 通过服务发现自动实现服务互联,探测,挂接.无需配置服务器间的端口. 20 | 21 | ## Cloud Config File(云配置文件) 22 | 23 | 任何服务器配置均通过服务发现的KV存取,本地不保存配置,更新配置更为方便 24 | 25 | ## Code Generation(代码生成) 26 | 27 | 基于github.com/davyxu/protoplus的代码生成技术,迅速粘合逻辑与底层,代码好看易懂且高效. 28 | 29 | 使用更简单更强大的schema编写协议, 并自动生成protobuf schema. 30 | 31 | ## Transport based on cellnet(基于cellnet的网络传输) 32 | 33 | 提供强大的扩展及适配能力. 34 | 35 | # 使用cellmesh 36 | 37 | cellmesh 使用go module管理源码依赖, 所以确保go版本在1.12以上 38 | 39 | ## 下载cellmesh源码 40 | 41 | ``` 42 | go get github.com/davyxu/cellmesh 43 | ``` 44 | 45 | # cellmesh demo 46 | demo工程请参见 47 | 48 | https://github.com/davyxu/cellmesh_demo 49 | 50 | Demo包含服务进程及工具配套等 51 | 52 | # 服务发现(memsd) 53 | 54 | ## 服务启动方法 55 | 56 | ``` 57 | go run github.com/davyxu/cellmesh/discovery/memsd 58 | ``` 59 | 60 | ## 服务启动参数 61 | 62 | - datafile 63 | 开启持久化,默认每隔1分钟将内存数据落地到指定的datafile中,格式为JSON 64 | 65 | - addr 66 | 按给定的地址侦听,例如memsd -addr=localhost:9099 67 | 68 | 69 | ## memsd客户端功能 70 | 71 | 客户端通用参数 72 | - addr 73 | 连接指定地址的服务发现, 例如:-addr=localhost:9099 74 | 75 | ### 查看注册的服务 76 | 77 | ``` 78 | go run github.com/davyxu/cellmesh/discovery/memsd -viewsvc 79 | ``` 80 | 81 | ### 清空注册的服务 82 | 83 | ``` 84 | go run github.com/davyxu/cellmesh/discovery/memsd -clearsvc 85 | ``` 86 | 87 | ### 查看配置的key 88 | 89 | ``` 90 | go run github.com/davyxu/cellmesh/discovery/memsd -viewkey 91 | ``` 92 | 93 | ### 获取配置值 94 | 95 | ``` 96 | go run github.com/davyxu/cellmesh/discovery/memsd -getvalue 97 | ``` 98 | 99 | ### 设置配置值 100 | 101 | ``` 102 | go run github.com/davyxu/cellmesh/discovery/memsd -setvalue 103 | ``` 104 | 105 | ### 删除配置值 106 | 107 | ``` 108 | go run github.com/davyxu/cellmesh/discovery/memsd -deletevalue 109 | ``` 110 | 111 | 112 | ### 清空配置 113 | 114 | ``` 115 | go run github.com/davyxu/cellmesh/discovery/memsd -clearvalue 116 | ``` 117 | 118 | # 概念 119 | 120 | ## Service(服务) 121 | 122 | 一个Service为一套连接器或侦听器,挂接消息处理的派发器Dispatcher 123 | 124 | - 侦听端口自动分配 125 | 126 | Service默认启动时以地址:0启动,网络底层自动分配端口,由cellmesh将服务信息报告到服务发现 127 | 128 | 其他Service发现新的服务进入网络时,根据需要自动连接服务 129 | 130 | ## Connection Management(连接维护) 131 | 132 | 从服务发现的服务信息,创建到不同服务间的长连接。同时在这些连接断开时维护连接 133 | 134 | 逻辑中根据策略从已有连接及信息选择出连接与目标通信,例如:选择负载最低的一台游戏服务器 135 | 136 | # 目录结构 137 | ``` 138 | discovery 139 | kvconfig 140 | 配置的快速获取接口。 141 | memsd 142 | 面向游戏优化的服务发现实现。 143 | service 144 | 服务通信基础,以及服务发现封装。 145 | tool 146 | protogen 147 | 协议生成器,生成Go的消息绑定以及消息响应入口。 148 | util 149 | 所有框架通用的工具代码。 150 | 151 | ``` 152 | 153 | # 服务参数 154 | service包为服务进程提供命令行参数支持。服务进程的命令行参数同时也可以使用FlagFile像使用配置文件一样批量设置进程配置(参考demo/cfg/LocalFlag.cfg) 155 | 156 | 详细参数说明如下: 157 | 158 | - sdaddr 159 | 160 | 指定服务发现服务器(memsd)地址, 通过服务发现,服务器可以快速获取配置以及其他可连接服务器地址,实现服务互联. 161 | 162 | - svcgroup 163 | 164 | 指定服务器分组. 一般情况下,认为一台物理机归属于一个svcgroup. 当然,也可以在一台物理机上放置多个分组,比如开发阶段. 165 | 166 | 服务器分组也能方便服务器打包以及定位服务器位置. 167 | 168 | - svcindex 169 | 170 | 指定服务器索引, 标识同类服务器的多个不同进程,同类中的svcindex必须唯一,逻辑上,svcindex还会与uuid关联. 171 | 172 | - wanip 173 | 174 | 指定服务器所在物理机的外网IP,方便通知客户端要连接的IP,例如:login通知客户端game的外网IP. 175 | 176 | - logcolor 177 | 178 | 对日志着色, 规则参见github/davyxu/golog中的color.go, 写入文件时,请关闭此选项,避免日志中出现着色字符. 179 | 180 | - logfile 181 | 182 | 将日志写入文件,并不再输出到控制台. 183 | 184 | - logfilesize 185 | 186 | 指定输出日志文件单个大小,可使用B/M/G标识, 注意: golog默认不是rolling方式,日志会写入到尾数最大的日志文件. 187 | 188 | - loglevel 189 | 190 | 指定日志输出级别, 格式 日志名|级别, 日志名支持正则表达式, 级别可以为error, info等 191 | 192 | - mutemsglog 193 | 194 | 屏蔽指定消息的日志,多个消息使用'|'分割 195 | 196 | - flagfile 197 | 198 | 使用FlagFile格式(参考 [LocalFlag](https://github.com/davyxu/cellmesh_demo/blob/master/cfg/LocalFlag.cfg)),作为进程的命令行参数 199 | 200 | 201 | # Tips 202 | ## 为什么使用memsd的服务发现替换consul? 203 | 早期版本的cellmesh使用consul作为服务发现,cellmesh使用主动汇报服务信息的方式保证consul中能及时更新服务信息。 204 | 但实际使用中发现有如下问题: 205 | 1. 偶尔出现高CPU占用,Windows休眠恢复后也会造成严重的高CPU现象。 206 | 2. consul的API并没有本地cache,需要高速查询时,并没有很好的性能。 207 | 3. 多服更新时没有原子更新,容易形成严重的不同步现象。 208 | 4. 依赖重,代码量巨大,使用vendor而不是go module方式管理代码,编译慢。 209 | 基于以上考虑,决定兼容服务发现接口,同时编写对游戏服务友好的发现系统:memsd。 210 | 211 | # 备注 212 | 213 | 感觉不错请star, 谢谢! 214 | 215 | 知乎: http://www.zhihu.com/people/sunicdavy 216 | 217 | 提交bug及特性: https://github.com/davyxu/cellmesh/issues 218 | -------------------------------------------------------------------------------- /discovery/desc.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sort" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // 注册到服务发现的服务描述 12 | type ServiceDesc struct { 13 | Name string 14 | ID string // 所有service中唯一的id 15 | Host string 16 | Port int 17 | Tags []string // 分类标签 18 | Meta map[string]string // 细节配置 19 | } 20 | 21 | func (self *ServiceDesc) Equals(sd *ServiceDesc) bool { 22 | 23 | if sd.ID != self.ID { 24 | return false 25 | } 26 | 27 | if sd.Port != self.Port { 28 | return false 29 | } 30 | 31 | if sd.Name != self.Name { 32 | return false 33 | } 34 | 35 | if sd.Host != self.Host { 36 | return false 37 | } 38 | 39 | if !reflect.DeepEqual(self.Tags, sd.Tags) { 40 | return false 41 | } 42 | 43 | if !reflect.DeepEqual(self.Meta, sd.Meta) { 44 | return false 45 | } 46 | 47 | return true 48 | } 49 | 50 | func (self *ServiceDesc) ContainTags(tag string) bool { 51 | for _, libtag := range self.Tags { 52 | if libtag == tag { 53 | return true 54 | } 55 | } 56 | 57 | return false 58 | } 59 | 60 | func (self *ServiceDesc) SetMeta(key, value string) { 61 | if self.Meta == nil { 62 | self.Meta = make(map[string]string) 63 | } 64 | 65 | self.Meta[key] = value 66 | } 67 | 68 | func (self *ServiceDesc) GetMeta(name string) string { 69 | if self.Meta == nil { 70 | return "" 71 | } 72 | 73 | return self.Meta[name] 74 | } 75 | 76 | func (self *ServiceDesc) GetMetaAsInt(name string) int { 77 | v, err := strconv.ParseInt(self.GetMeta(name), 10, 64) 78 | if err != nil { 79 | return 0 80 | } 81 | 82 | return int(v) 83 | } 84 | 85 | func (self *ServiceDesc) Address() string { 86 | return fmt.Sprintf("%s:%d", self.Host, self.Port) 87 | } 88 | 89 | func (self *ServiceDesc) String() string { 90 | var sb strings.Builder 91 | if len(self.Meta) > 0 { 92 | 93 | sb.WriteString("meta: [ ") 94 | for key, value := range self.Meta { 95 | sb.WriteString(key) 96 | sb.WriteString("=") 97 | sb.WriteString(value) 98 | sb.WriteString(" ") 99 | } 100 | sb.WriteString("]") 101 | } 102 | 103 | return fmt.Sprintf("%s host: %s port: %d %s", self.ID, self.Host, self.Port, sb.String()) 104 | } 105 | 106 | func (self *ServiceDesc) FormatString() string { 107 | 108 | var sb strings.Builder 109 | if len(self.Meta) > 0 { 110 | 111 | type pair struct { 112 | key string 113 | value string 114 | } 115 | 116 | var pairs []pair 117 | 118 | for key, value := range self.Meta { 119 | pairs = append(pairs, pair{key, value}) 120 | } 121 | 122 | sort.Slice(pairs, func(i, j int) bool { 123 | 124 | return pairs[i].key < pairs[j].key 125 | }) 126 | 127 | sb.WriteString("meta: [ ") 128 | for _, kv := range pairs { 129 | sb.WriteString(kv.key) 130 | sb.WriteString("=") 131 | sb.WriteString(kv.value) 132 | sb.WriteString(" ") 133 | } 134 | sb.WriteString("]") 135 | } 136 | 137 | return fmt.Sprintf("%25s host: %15s port: %5d %s", self.ID, self.Host, self.Port, sb.String()) 138 | } 139 | -------------------------------------------------------------------------------- /discovery/discovery.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | type ValueMeta struct { 4 | Key string 5 | Value []byte 6 | } 7 | 8 | type CheckerFunc func() (output, status string) 9 | 10 | type Discovery interface { 11 | 12 | // 注册服务 13 | Register(*ServiceDesc) error 14 | 15 | // 解注册服务 16 | Deregister(svcid string) error 17 | 18 | // 根据服务名查到可用的服务 19 | Query(name string) (ret []*ServiceDesc) 20 | 21 | // 注册服务变化通知 22 | RegisterNotify(mode string) (ret chan struct{}) 23 | 24 | // 解除服务变化通知 25 | DeregisterNotify(mode string, c chan struct{}) 26 | 27 | // 设置值 28 | SetValue(key string, value interface{}, optList ...interface{}) error 29 | 30 | // 取值,并赋值到变量 31 | GetValue(key string, valuePtr interface{}) error 32 | 33 | // 删除值 34 | DeleteValue(key string) error 35 | } 36 | 37 | var ( 38 | Default Discovery 39 | ) 40 | -------------------------------------------------------------------------------- /discovery/kvconfig/kvconfig.go: -------------------------------------------------------------------------------- 1 | package kvconfig 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "reflect" 6 | ) 7 | 8 | func doRaw(d discovery.Discovery, key string, defaultValue, ret interface{}) { 9 | if d == nil { 10 | return 11 | } 12 | 13 | err := d.GetValue(key, ret) 14 | 15 | if err != nil && err.Error() == "value not exists" { 16 | 17 | reflect.Indirect(reflect.ValueOf(ret)).Set(reflect.ValueOf(defaultValue)) 18 | // 默认值初始化 19 | d.SetValue(key, defaultValue) 20 | } 21 | 22 | return 23 | } 24 | 25 | // 根据key从Consul中取配置,若不存在,使用默认值且自动写入KV 26 | func String(d discovery.Discovery, key string, defaultValue string) (ret string) { 27 | doRaw(d, key, defaultValue, &ret) 28 | return 29 | } 30 | 31 | func Int32(d discovery.Discovery, key string, defaultValue int32) (ret int32) { 32 | doRaw(d, key, defaultValue, &ret) 33 | return 34 | } 35 | 36 | func Int64(d discovery.Discovery, key string, defaultValue int64) (ret int64) { 37 | doRaw(d, key, defaultValue, &ret) 38 | return 39 | } 40 | 41 | func Bool(d discovery.Discovery, key string, defaultValue bool) (ret bool) { 42 | doRaw(d, key, defaultValue, &ret) 43 | return 44 | } 45 | -------------------------------------------------------------------------------- /discovery/memsd/api/api.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellmesh/discovery/memsd/model" 6 | "github.com/davyxu/cellnet" 7 | "sync" 8 | ) 9 | 10 | type notifyContext struct { 11 | stack string 12 | mode string 13 | } 14 | 15 | type memDiscovery struct { 16 | config *Config 17 | 18 | ses cellnet.Session 19 | sesGuard sync.RWMutex 20 | 21 | kvCache map[string][]byte 22 | kvCacheGuard sync.RWMutex 23 | 24 | svcCache map[string][]*discovery.ServiceDesc 25 | svcCacheGuard sync.RWMutex 26 | 27 | notifyMap sync.Map // key=mode+c value=string 28 | 29 | initWg *sync.WaitGroup 30 | 31 | token string 32 | } 33 | 34 | func NewDiscovery(config interface{}) discovery.Discovery { 35 | 36 | if config == nil { 37 | config = DefaultConfig() 38 | } 39 | 40 | self := &memDiscovery{ 41 | config: config.(*Config), 42 | kvCache: make(map[string][]byte), 43 | svcCache: make(map[string][]*discovery.ServiceDesc), 44 | } 45 | 46 | model.Queue = cellnet.NewEventQueue() 47 | model.Queue.EnableCapturePanic(true) 48 | model.Queue.StartLoop() 49 | 50 | self.initWg = new(sync.WaitGroup) 51 | self.initWg.Add(1) 52 | 53 | self.connect(self.config.Address) 54 | 55 | // 等待拉取初始值 56 | self.initWg.Wait() 57 | self.initWg = nil 58 | 59 | return self 60 | } 61 | -------------------------------------------------------------------------------- /discovery/memsd/api/api_test.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | _ "github.com/davyxu/cellnet/peer/tcp" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestAPI(t *testing.T) { 11 | 12 | sd := NewDiscovery(nil) 13 | 14 | sd.SetValue("mykey", 123456) 15 | 16 | var a int 17 | sd.GetValue("mykey", &a) 18 | if a != 123456 { 19 | t.Fatalf("a != 123456") 20 | } 21 | 22 | sd.DeleteValue("mykey") 23 | 24 | time.Sleep(time.Millisecond * 100) 25 | 26 | err := sd.GetValue("mykey", &a) 27 | if err == nil { 28 | t.Fatalf("getvalue == nil") 29 | } 30 | 31 | if err.Error() != "value not exists" { 32 | t.Fatalf("value exists") 33 | } 34 | 35 | sd.Register(&discovery.ServiceDesc{ 36 | Name: "game", 37 | ID: "game0@dev", 38 | Host: "127.0.0.1", 39 | Port: 9091, 40 | Tags: []string{"blabla"}, 41 | Meta: map[string]string{"a": "b"}, 42 | }) 43 | 44 | time.Sleep(time.Millisecond * 100) 45 | 46 | list := sd.Query("game") 47 | if len(list) == 0 { 48 | t.Fatalf("len(list) == 0") 49 | } 50 | 51 | t.Log(*list[0]) 52 | 53 | if list[0].ID != "game0@dev" { 54 | t.Fatalf("list[0].ID != 'game0@dev'") 55 | } 56 | 57 | sd.Deregister("game0@dev") 58 | 59 | time.Sleep(time.Millisecond * 100) 60 | 61 | if len(sd.Query("game")) != 0 { 62 | t.Fatalf("len(sd.Query('game')) != 0") 63 | } 64 | 65 | time.Sleep(time.Second) 66 | } 67 | -------------------------------------------------------------------------------- /discovery/memsd/api/config.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import "time" 4 | 5 | type Config struct { 6 | Address string 7 | RequestTimeout time.Duration 8 | } 9 | 10 | func DefaultConfig() *Config { 11 | 12 | return &Config{ 13 | Address: ":8900", 14 | RequestTimeout: time.Second * 10, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discovery/memsd/api/conn.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellmesh/discovery/memsd/model" 6 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 7 | "github.com/davyxu/cellnet" 8 | "github.com/davyxu/cellnet/peer" 9 | "github.com/davyxu/cellnet/proc" 10 | "time" 11 | ) 12 | 13 | func (self *memDiscovery) clearCache() { 14 | 15 | self.svcCacheGuard.Lock() 16 | self.svcCache = map[string][]*discovery.ServiceDesc{} 17 | self.svcCacheGuard.Unlock() 18 | 19 | self.kvCacheGuard.Lock() 20 | self.kvCache = map[string][]byte{} 21 | self.kvCacheGuard.Unlock() 22 | } 23 | 24 | func (self *memDiscovery) connect(addr string) { 25 | p := peer.NewGenericPeer("tcp.Connector", "memsd", addr, model.Queue) 26 | 27 | proc.BindProcessorHandler(p, "memsd.cli", func(ev cellnet.Event) { 28 | 29 | switch msg := ev.Message().(type) { 30 | case *cellnet.SessionConnected: 31 | 32 | self.sesGuard.Lock() 33 | self.ses = ev.Session() 34 | self.sesGuard.Unlock() 35 | self.clearCache() 36 | ev.Session().Send(&proto.AuthREQ{ 37 | Token: self.token, 38 | }) 39 | case *cellnet.SessionClosed: 40 | self.token = "" 41 | log.Errorf("memsd discovery lost!") 42 | 43 | case *proto.AuthACK: 44 | 45 | self.token = msg.Token 46 | 47 | if self.initWg != nil { 48 | // Pull的消息还要在queue里处理,这里确认处理完成后才算初始化完成 49 | self.initWg.Done() 50 | } 51 | 52 | log.Infof("memsd discovery ready!") 53 | 54 | self.triggerNotify("ready", 0) 55 | 56 | case *proto.ValueChangeNotifyACK: 57 | 58 | if model.IsServiceKey(msg.Key) { 59 | self.updateSvcCache(msg.SvcName, msg.Value) 60 | } else { 61 | self.updateKVCache(msg.Key, msg.Value) 62 | } 63 | 64 | case *proto.ValueDeleteNotifyACK: 65 | 66 | if model.IsServiceKey(msg.Key) { 67 | svcid := model.GetSvcIDByServiceKey(msg.Key) 68 | self.deleteSvcCache(svcid, msg.SvcName) 69 | } else { 70 | self.deleteKVCache(msg.Key) 71 | } 72 | } 73 | }) 74 | 75 | // noDelay 76 | p.(cellnet.TCPSocketOption).SetSocketBuffer(1024*1024, 1024*1024, true) 77 | 78 | // 断线后自动重连 79 | p.(cellnet.TCPConnector).SetReconnectDuration(time.Second * 5) 80 | 81 | p.Start() 82 | 83 | for { 84 | 85 | if p.(cellnet.PeerReadyChecker).IsReady() { 86 | break 87 | } 88 | 89 | time.Sleep(time.Millisecond * 500) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /discovery/memsd/api/const.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import "errors" 4 | 5 | const ( 6 | MaxValueSize = 512 * 1024 7 | ) 8 | 9 | var ( 10 | ErrValueNotExists = errors.New("value not exists") 11 | ErrValueTooLarge = errors.New("value too large") 12 | ) 13 | -------------------------------------------------------------------------------- /discovery/memsd/api/kv.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 6 | "strings" 7 | ) 8 | 9 | type Option struct { 10 | PrettyPrint bool 11 | } 12 | 13 | func getOpt(optList ...interface{}) Option { 14 | 15 | for _, opt := range optList { 16 | 17 | switch raw := opt.(type) { 18 | case Option: 19 | return raw 20 | } 21 | } 22 | 23 | return Option{} 24 | } 25 | 26 | func (self *memDiscovery) getKVCache(key string) (value []byte, ok bool) { 27 | self.kvCacheGuard.RLock() 28 | defer self.kvCacheGuard.RUnlock() 29 | value, ok = self.kvCache[key] 30 | return 31 | } 32 | 33 | func (self *memDiscovery) updateKVCache(key string, value []byte) { 34 | self.kvCacheGuard.Lock() 35 | self.kvCache[key] = value 36 | self.kvCacheGuard.Unlock() 37 | } 38 | 39 | func (self *memDiscovery) deleteKVCache(key string) { 40 | self.kvCacheGuard.Lock() 41 | delete(self.kvCache, key) 42 | self.kvCacheGuard.Unlock() 43 | } 44 | 45 | func (self *memDiscovery) SetValue(key string, dataPtr interface{}, optList ...interface{}) (retErr error) { 46 | 47 | raw, err := discovery.AnyToBytes(dataPtr, getOpt(optList...).PrettyPrint) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | if len(raw) > MaxValueSize { 53 | return ErrValueTooLarge 54 | } 55 | 56 | callErr := self.remoteCall(&proto.SetValueREQ{ 57 | Key: key, 58 | Value: raw, 59 | }, func(ack *proto.SetValueACK) { 60 | retErr = codeToError(ack.Code) 61 | }) 62 | 63 | if retErr != nil { 64 | return 65 | } 66 | 67 | retErr = callErr 68 | 69 | return nil 70 | } 71 | 72 | func (self *memDiscovery) GetValue(key string, valuePtr interface{}) error { 73 | 74 | data, ok := self.getKVCache(key) 75 | 76 | if !ok { 77 | return ErrValueNotExists 78 | } 79 | 80 | return discovery.BytesToAny(data, valuePtr) 81 | } 82 | 83 | func (self *memDiscovery) GetValueDirect(key string, valuePtr interface{}) error { 84 | 85 | data, err := self.GetRawValue(key) 86 | 87 | if err != nil { 88 | return err 89 | } 90 | 91 | return discovery.BytesToAny(data, valuePtr) 92 | } 93 | 94 | func (self *memDiscovery) GetRawValue(key string) (retData []byte, retErr error) { 95 | 96 | callErr := self.remoteCall(&proto.GetValueREQ{ 97 | Key: key, 98 | }, func(ack *proto.GetValueACK) { 99 | retData = ack.Value 100 | retErr = codeToError(ack.Code) 101 | }) 102 | 103 | if retErr != nil { 104 | return 105 | } 106 | 107 | retErr = callErr 108 | 109 | return 110 | } 111 | 112 | func (self *memDiscovery) DeleteValue(key string) (ret error) { 113 | 114 | callErr := self.remoteCall(&proto.DeleteValueREQ{ 115 | Key: key, 116 | }, func(ack *proto.DeleteValueACK) { 117 | ret = codeToError(ack.Code) 118 | }) 119 | 120 | if ret != nil { 121 | return ret 122 | } 123 | 124 | return callErr 125 | } 126 | 127 | func (self *memDiscovery) GetRawValueList(prefix string) (ret []discovery.ValueMeta) { 128 | 129 | self.kvCacheGuard.RLock() 130 | 131 | for key, value := range self.kvCache { 132 | 133 | if strings.HasPrefix(key, prefix) { 134 | ret = append(ret, discovery.ValueMeta{ 135 | Key: key, 136 | Value: value, 137 | }) 138 | } 139 | 140 | } 141 | 142 | self.kvCacheGuard.RUnlock() 143 | 144 | return 145 | } 146 | 147 | func (self *memDiscovery) ClearKey() { 148 | self.remoteCall(&proto.ClearKeyREQ{}, func(ack *proto.ClearKeyACK) {}) 149 | } 150 | -------------------------------------------------------------------------------- /discovery/memsd/api/log.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var log = golog.New("memsdapi") 8 | -------------------------------------------------------------------------------- /discovery/memsd/api/packet.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "github.com/davyxu/cellnet" 7 | "github.com/davyxu/cellnet/codec" 8 | "github.com/davyxu/cellnet/util" 9 | "io" 10 | ) 11 | 12 | var ( 13 | ErrMaxPacket = errors.New("packet over size") 14 | ErrMinPacket = errors.New("packet short size") 15 | ErrShortMsgID = errors.New("short msgid") 16 | ) 17 | 18 | const ( 19 | bodySize = 4 // 包体大小字段 20 | msgIDSize = 2 // 消息ID字段 21 | ) 22 | 23 | // 接收Length-Type-Value格式的封包流程 24 | func RecvLTVPacket(reader io.Reader, maxPacketSize int) (msg interface{}, err error) { 25 | 26 | // Size为uint16,占2字节 27 | var sizeBuffer = make([]byte, bodySize) 28 | 29 | // 持续读取Size直到读到为止 30 | _, err = io.ReadFull(reader, sizeBuffer) 31 | 32 | // 发生错误时返回 33 | if err != nil { 34 | return 35 | } 36 | 37 | if len(sizeBuffer) < bodySize { 38 | return nil, ErrMinPacket 39 | } 40 | 41 | // 用小端格式读取Size 42 | size := binary.LittleEndian.Uint32(sizeBuffer) 43 | 44 | if maxPacketSize > 0 && int(size) >= maxPacketSize { 45 | return nil, ErrMaxPacket 46 | } 47 | 48 | // 分配包体大小 49 | body := make([]byte, size) 50 | 51 | // 读取包体数据 52 | _, err = io.ReadFull(reader, body) 53 | 54 | // 发生错误时返回 55 | if err != nil { 56 | return 57 | } 58 | 59 | if len(body) < msgIDSize { 60 | return nil, ErrShortMsgID 61 | } 62 | 63 | msgid := binary.LittleEndian.Uint16(body) 64 | 65 | msgData := body[msgIDSize:] 66 | 67 | // 将字节数组和消息ID用户解出消息 68 | msg, _, err = codec.DecodeMessage(int(msgid), msgData) 69 | if err != nil { 70 | // TODO 接收错误时,返回消息 71 | return nil, err 72 | } 73 | 74 | return 75 | } 76 | 77 | // 发送Length-Type-Value格式的封包流程 78 | func SendLTVPacket(writer io.Writer, ctx cellnet.ContextSet, data interface{}) error { 79 | 80 | var ( 81 | msgData []byte 82 | msgID int 83 | meta *cellnet.MessageMeta 84 | ) 85 | 86 | switch m := data.(type) { 87 | case *cellnet.RawPacket: // 发裸包 88 | msgData = m.MsgData 89 | msgID = m.MsgID 90 | default: // 发普通编码包 91 | var err error 92 | 93 | // 将用户数据转换为字节数组和消息ID 94 | msgData, meta, err = codec.EncodeMessage(data, ctx) 95 | 96 | if err != nil { 97 | return err 98 | } 99 | 100 | msgID = meta.ID 101 | } 102 | 103 | pkt := make([]byte, bodySize+msgIDSize+len(msgData)) 104 | 105 | // Length 106 | binary.LittleEndian.PutUint32(pkt, uint32(msgIDSize+len(msgData))) 107 | 108 | // Type 109 | binary.LittleEndian.PutUint16(pkt[bodySize:], uint16(msgID)) 110 | 111 | // Value 112 | copy(pkt[bodySize+msgIDSize:], msgData) 113 | 114 | // 将数据写入Socket 115 | err := util.WriteFull(writer, pkt) 116 | 117 | // Codec中使用内存池时的释放位置 118 | if meta != nil { 119 | codec.FreeCodecResource(meta.Codec, msgData, ctx) 120 | } 121 | 122 | return err 123 | } 124 | -------------------------------------------------------------------------------- /discovery/memsd/api/rpc.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "errors" 5 | "github.com/davyxu/cellnet" 6 | "reflect" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | var ( 12 | callByType sync.Map // map[reflect.Type]func(interface{}) 13 | ) 14 | 15 | type typeRPCHooker struct { 16 | } 17 | 18 | func (typeRPCHooker) OnInboundEvent(inputEvent cellnet.Event) (outputEvent cellnet.Event) { 19 | 20 | incomingMsgType := reflect.TypeOf(inputEvent.Message()).Elem() 21 | 22 | if rawFeedback, ok := callByType.Load(incomingMsgType); ok { 23 | feedBack := rawFeedback.(chan interface{}) 24 | feedBack <- inputEvent.Message() 25 | return inputEvent 26 | } 27 | 28 | return inputEvent 29 | } 30 | 31 | func (typeRPCHooker) OnOutboundEvent(inputEvent cellnet.Event) (outputEvent cellnet.Event) { 32 | 33 | return inputEvent 34 | } 35 | 36 | func (self *memDiscovery) Session() (ses cellnet.Session) { 37 | self.sesGuard.RLock() 38 | ses = self.ses 39 | self.sesGuard.RUnlock() 40 | return 41 | } 42 | 43 | // callback =func(ack *YouMsgACK) 44 | func (self *memDiscovery) remoteCall(req interface{}, callback interface{}) error { 45 | 46 | funcType := reflect.TypeOf(callback) 47 | if funcType.Kind() != reflect.Func { 48 | panic("callback require 'func'") 49 | } 50 | 51 | ses := self.Session() 52 | 53 | if ses == nil { 54 | return errors.New("memsd not connected") 55 | } 56 | 57 | feedBack := make(chan interface{}) 58 | 59 | // 获取回调第一个参数 60 | 61 | if funcType.NumIn() != 1 { 62 | panic("callback func param format like 'func(ack *YouMsgACK)'") 63 | } 64 | 65 | ackType := funcType.In(0) 66 | if ackType.Kind() != reflect.Ptr { 67 | panic("callback func param format like 'func(ack *YouMsgACK)'") 68 | } 69 | 70 | ackType = ackType.Elem() 71 | 72 | callByType.Store(ackType, feedBack) 73 | 74 | defer callByType.Delete(ackType) 75 | 76 | ses.Send(req) 77 | 78 | select { 79 | case ack := <-feedBack: 80 | 81 | vCall := reflect.ValueOf(callback) 82 | vCall.Call([]reflect.Value{reflect.ValueOf(ack)}) 83 | 84 | return nil 85 | case <-time.After(self.config.RequestTimeout): 86 | 87 | return errors.New("Request time out") 88 | } 89 | 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /discovery/memsd/api/setup.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery/memsd/model" 5 | "github.com/davyxu/cellnet" 6 | _ "github.com/davyxu/cellnet/peer/tcp" 7 | "github.com/davyxu/cellnet/proc" 8 | "github.com/davyxu/cellnet/proc/tcp" 9 | ) 10 | 11 | func init() { 12 | // 仅供demo使用的 13 | proc.RegisterProcessor("memsd.cli", func(bundle proc.ProcessorBundle, userCallback cellnet.EventCallback, args ...interface{}) { 14 | 15 | bundle.SetTransmitter(new(TCPMessageTransmitter)) 16 | 17 | if model.Debug { 18 | bundle.SetHooker(proc.NewMultiHooker(new(tcp.MsgHooker), new(typeRPCHooker))) 19 | } else { 20 | bundle.SetHooker(new(typeRPCHooker)) 21 | } 22 | 23 | bundle.SetCallback(proc.NewQueuedEventCallback(userCallback)) 24 | }) 25 | 26 | proc.RegisterProcessor("memsd.svc", func(bundle proc.ProcessorBundle, userCallback cellnet.EventCallback, args ...interface{}) { 27 | 28 | bundle.SetTransmitter(new(TCPMessageTransmitter)) 29 | if model.Debug { 30 | bundle.SetHooker(proc.NewMultiHooker(new(tcp.MsgHooker))) 31 | } 32 | 33 | bundle.SetCallback(proc.NewQueuedEventCallback(userCallback)) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /discovery/memsd/api/svc.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/davyxu/cellmesh/discovery" 7 | "github.com/davyxu/cellmesh/discovery/memsd/model" 8 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 9 | "github.com/davyxu/cellnet/util" 10 | "time" 11 | ) 12 | 13 | func (self *memDiscovery) Register(svc *discovery.ServiceDesc) (retErr error) { 14 | 15 | if svc.Name == "" { 16 | return errors.New("expect svc name") 17 | } 18 | 19 | if svc.ID == "" { 20 | return errors.New("expect svc id") 21 | } 22 | 23 | data, err := json.Marshal(svc) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | callErr := self.remoteCall(&proto.SetValueREQ{ 29 | Key: model.ServiceKeyPrefix + svc.ID, 30 | Value: data, 31 | SvcName: svc.Name, 32 | }, func(ack *proto.SetValueACK) { 33 | retErr = codeToError(ack.Code) 34 | }) 35 | 36 | if retErr != nil { 37 | return 38 | } 39 | 40 | return callErr 41 | } 42 | 43 | func (self *memDiscovery) Deregister(svcid string) error { 44 | 45 | return self.DeleteValue(model.ServiceKeyPrefix + svcid) 46 | } 47 | 48 | func (self *memDiscovery) Query(name string) (ret []*discovery.ServiceDesc) { 49 | 50 | self.svcCacheGuard.RLock() 51 | defer self.svcCacheGuard.RUnlock() 52 | 53 | return self.svcCache[name] 54 | } 55 | 56 | func (self *memDiscovery) QueryAll() (ret []*discovery.ServiceDesc) { 57 | 58 | self.svcCacheGuard.RLock() 59 | defer self.svcCacheGuard.RUnlock() 60 | 61 | for _, list := range self.svcCache { 62 | ret = append(ret, list...) 63 | } 64 | 65 | return 66 | } 67 | 68 | func (self *memDiscovery) ClearService() { 69 | self.remoteCall(&proto.ClearSvcREQ{}, func(ack *proto.ClearSvcACK) {}) 70 | } 71 | 72 | func (self *memDiscovery) triggerNotify(mode string, timeout time.Duration) { 73 | 74 | self.notifyMap.Range(func(key, value interface{}) bool { 75 | 76 | if value == nil { 77 | return true 78 | } 79 | 80 | ctx := value.(*notifyContext) 81 | 82 | if ctx.mode != mode { 83 | return true 84 | } 85 | 86 | c := key.(chan struct{}) 87 | 88 | if timeout == 0 { 89 | 90 | select { 91 | case c <- struct{}{}: 92 | default: 93 | } 94 | 95 | } else { 96 | select { 97 | case c <- struct{}{}: 98 | case <-time.After(timeout): 99 | // 接收通知阻塞太久,或者没有释放侦听的channel 100 | log.Errorf("notify(%s) timeout, not free? regstack: %s", ctx.mode, ctx.stack) 101 | } 102 | } 103 | 104 | return true 105 | }) 106 | 107 | } 108 | 109 | func (self *memDiscovery) RegisterNotify(mode string) (ret chan struct{}) { 110 | ret = make(chan struct{}, 10) 111 | 112 | switch mode { 113 | case "add", "ready": 114 | self.notifyMap.Store(ret, ¬ifyContext{ 115 | mode: mode, 116 | stack: util.StackToString(5), 117 | }) 118 | default: 119 | panic("unknown notify mode: " + mode) 120 | } 121 | 122 | return 123 | } 124 | 125 | func (self *memDiscovery) DeregisterNotify(mode string, c chan struct{}) { 126 | 127 | switch mode { 128 | case "add", "ready": 129 | self.notifyMap.Store(c, nil) 130 | default: 131 | panic("unknown notify mode: " + mode) 132 | } 133 | } 134 | 135 | func (self *memDiscovery) updateSvcCache(svcName string, value []byte) { 136 | self.svcCacheGuard.Lock() 137 | 138 | list := self.svcCache[svcName] 139 | 140 | var desc discovery.ServiceDesc 141 | err := json.Unmarshal(value, &desc) 142 | if err != nil { 143 | log.Errorf("ServiceDesc unmarshal failed, %s", err) 144 | self.svcCacheGuard.Unlock() 145 | return 146 | } 147 | 148 | var notfound = true 149 | for index, svc := range list { 150 | if svc.ID == desc.ID { 151 | list[index] = &desc 152 | notfound = false 153 | break 154 | } 155 | } 156 | 157 | if notfound { 158 | list = append(list, &desc) 159 | } 160 | 161 | self.svcCache[svcName] = list 162 | self.svcCacheGuard.Unlock() 163 | 164 | self.triggerNotify("add", time.Second*10) 165 | } 166 | 167 | func (self *memDiscovery) deleteSvcCache(svcid, svcName string) { 168 | 169 | list := self.svcCache[svcName] 170 | 171 | for index, svc := range list { 172 | if svc.ID == svcid { 173 | list = append(list[:index], list[index+1:]...) 174 | break 175 | } 176 | } 177 | 178 | self.svcCache[svcName] = list 179 | } 180 | -------------------------------------------------------------------------------- /discovery/memsd/api/transmitter.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "github.com/davyxu/cellnet" 5 | "io" 6 | "net" 7 | ) 8 | 9 | type TCPMessageTransmitter struct { 10 | } 11 | 12 | type socketOpt interface { 13 | MaxPacketSize() int 14 | ApplySocketReadTimeout(conn net.Conn, callback func()) 15 | ApplySocketWriteTimeout(conn net.Conn, callback func()) 16 | } 17 | 18 | func (TCPMessageTransmitter) OnRecvMessage(ses cellnet.Session) (msg interface{}, err error) { 19 | 20 | reader, ok := ses.Raw().(io.Reader) 21 | 22 | // 转换错误,或者连接已经关闭时退出 23 | if !ok || reader == nil { 24 | return nil, nil 25 | } 26 | 27 | opt := ses.Peer().(socketOpt) 28 | 29 | if conn, ok := reader.(net.Conn); ok { 30 | 31 | // 有读超时时,设置超时 32 | opt.ApplySocketReadTimeout(conn, func() { 33 | 34 | msg, err = RecvLTVPacket(reader, opt.MaxPacketSize()) 35 | 36 | }) 37 | } 38 | 39 | return 40 | } 41 | 42 | func (TCPMessageTransmitter) OnSendMessage(ses cellnet.Session, msg interface{}) (err error) { 43 | 44 | writer, ok := ses.Raw().(io.Writer) 45 | 46 | // 转换错误,或者连接已经关闭时退出 47 | if !ok || writer == nil { 48 | return nil 49 | } 50 | 51 | opt := ses.Peer().(socketOpt) 52 | 53 | // 有写超时时,设置超时 54 | opt.ApplySocketWriteTimeout(writer.(net.Conn), func() { 55 | 56 | err = SendLTVPacket(writer, ses.(cellnet.ContextSet), msg) 57 | 58 | }) 59 | 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /discovery/memsd/api/util.go: -------------------------------------------------------------------------------- 1 | package memsd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 6 | ) 7 | 8 | func codeToError(code proto.ResultCode) error { 9 | 10 | switch code { 11 | case proto.ResultCode_Result_OK: 12 | return nil 13 | case proto.ResultCode_Result_NotExists: 14 | return ErrValueNotExists 15 | } 16 | 17 | return fmt.Errorf("error %s", code.String()) 18 | } 19 | -------------------------------------------------------------------------------- /discovery/memsd/cmd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | ) 8 | 9 | func ViewSvc() { 10 | 11 | sd := initSD() 12 | 13 | list := sd.QueryAll() 14 | 15 | sort.Slice(list, func(i, j int) bool { 16 | 17 | a := list[i] 18 | b := list[j] 19 | 20 | if a.GetMeta("SvcGroup") != b.GetMeta("SvcGroup") { 21 | return a.GetMeta("SvcGroup") < b.GetMeta("SvcGroup") 22 | } 23 | 24 | if a.Port != b.Port { 25 | return a.Port < b.Port 26 | } 27 | 28 | if a.Host != b.Host { 29 | return a.Host < b.Host 30 | } 31 | 32 | return a.ID < b.ID 33 | }) 34 | 35 | for _, desc := range list { 36 | 37 | fmt.Println(desc.FormatString()) 38 | } 39 | } 40 | 41 | func ViewKey() { 42 | sd := initSD() 43 | list := sd.GetRawValueList("") 44 | sort.Slice(list, func(i, j int) bool { 45 | 46 | a := list[i] 47 | b := list[j] 48 | 49 | return a.Key < b.Key 50 | }) 51 | 52 | for _, meta := range list { 53 | fmt.Printf(" %s = (size %d)\n", meta.Key, len(meta.Value)) 54 | } 55 | } 56 | 57 | func GetValue(key string) { 58 | sd := initSD() 59 | var value string 60 | err := sd.GetValue(key, &value) 61 | if err != nil { 62 | fmt.Println(err.Error()) 63 | os.Exit(1) 64 | } 65 | 66 | fmt.Println(value) 67 | } 68 | 69 | func SetValue(key, value string) { 70 | sd := initSD() 71 | err := sd.SetValue(key, value) 72 | if err != nil { 73 | fmt.Println(err.Error()) 74 | os.Exit(1) 75 | } 76 | } 77 | 78 | func ClearSvc() { 79 | 80 | sd := initSD() 81 | sd.ClearService() 82 | } 83 | 84 | func ClearValue() { 85 | 86 | sd := initSD() 87 | sd.ClearKey() 88 | } 89 | 90 | func DeleteValue(key string) { 91 | 92 | sd := initSD() 93 | sd.DeleteValue(key) 94 | } 95 | -------------------------------------------------------------------------------- /discovery/memsd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/davyxu/cellmesh/discovery" 7 | "github.com/davyxu/cellmesh/discovery/memsd/api" 8 | "github.com/davyxu/cellmesh/discovery/memsd/model" 9 | "os" 10 | ) 11 | 12 | var ( 13 | flagCmd = flag.String("cmd", "", "sub command, empty to launch memsd service") 14 | flagAddr = flag.String("addr", "", "service discovery address") 15 | flagDataFile = flag.String("datafile", "", "persist values to file") 16 | flagDebug = flag.Bool("debug", false, "show debug info") 17 | flagVersion = flag.Bool("version", false, "show version") 18 | ) 19 | 20 | type DiscoveryExtend interface { 21 | discovery.Discovery 22 | 23 | QueryAll() (ret []*discovery.ServiceDesc) 24 | 25 | ClearKey() 26 | 27 | ClearService() 28 | 29 | GetRawValueList(prefix string) (ret []discovery.ValueMeta) 30 | } 31 | 32 | func initSD() DiscoveryExtend { 33 | config := memsd.DefaultConfig() 34 | if *flagAddr != "" { 35 | config.Address = *flagAddr 36 | } 37 | 38 | return memsd.NewDiscovery(config).(DiscoveryExtend) 39 | } 40 | 41 | func main() { 42 | 43 | flag.Parse() 44 | 45 | if *flagVersion { 46 | fmt.Println("version", model.Version) 47 | return 48 | } 49 | 50 | model.Debug = *flagDebug 51 | 52 | go startCheckRedundantValue() 53 | 54 | if *flagDataFile != "" { 55 | loadPersistFile(*flagDataFile) 56 | go startPersistCheck(*flagDataFile) 57 | } 58 | 59 | switch *flagCmd { 60 | case "": // addr 61 | startSvc() 62 | case "viewsvc": // addr 63 | ViewSvc() 64 | case "viewkey": // addr 65 | ViewKey() 66 | case "clearsvc": // addr 67 | ClearSvc() 68 | case "clearvalue": // addr 69 | ClearValue() 70 | case "deletevalue": 71 | if flag.NArg() < 1 { 72 | fmt.Println("deletevalue ") 73 | os.Exit(1) 74 | } 75 | DeleteValue(flag.Arg(0)) 76 | case "getvalue": 77 | if flag.NArg() < 1 { 78 | fmt.Println("getvalue ") 79 | os.Exit(1) 80 | } 81 | GetValue(flag.Arg(0)) 82 | case "setvalue": 83 | if flag.NArg() < 2 { 84 | fmt.Println("setvalue ") 85 | os.Exit(1) 86 | } 87 | 88 | SetValue(flag.Arg(0), flag.Arg(1)) 89 | default: 90 | fmt.Printf("Unknown command '%s'\n", *flagCmd) 91 | os.Exit(1) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /discovery/memsd/model/kv.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/davyxu/cellmesh/discovery" 6 | "io" 7 | "sort" 8 | ) 9 | 10 | type ValueMeta struct { 11 | Key string 12 | Value []byte 13 | SvcName string // 服务才有此名字 14 | Token string 15 | } 16 | 17 | var ErrDesc = discovery.ServiceDesc{Name: "invalid desc"} 18 | 19 | func (self *ValueMeta) ValueAsServiceDesc() *discovery.ServiceDesc { 20 | 21 | var desc discovery.ServiceDesc 22 | err := json.Unmarshal(self.Value, &desc) 23 | if err != nil { 24 | return &ErrDesc 25 | } 26 | 27 | return &desc 28 | } 29 | 30 | var ( 31 | valueByKey = map[string]*ValueMeta{} 32 | 33 | ValueDirty bool 34 | ) 35 | 36 | func SetValue(key string, meta *ValueMeta) { 37 | ValueDirty = true 38 | valueByKey[key] = meta 39 | } 40 | 41 | func GetValue(key string) *ValueMeta { 42 | 43 | return valueByKey[key] 44 | } 45 | 46 | func DeleteValue(key string) *ValueMeta { 47 | ValueDirty = true 48 | ret := valueByKey[key] 49 | delete(valueByKey, key) 50 | 51 | return ret 52 | } 53 | 54 | func ValueCount() int { 55 | return len(valueByKey) 56 | } 57 | 58 | func VisitValue(callback func(*ValueMeta) bool) { 59 | for _, vmeta := range valueByKey { 60 | if !callback(vmeta) { 61 | return 62 | } 63 | } 64 | } 65 | 66 | type PersistFile struct { 67 | Version int 68 | Values []*ValueMeta 69 | } 70 | 71 | var ( 72 | fileVersion = 1 73 | ) 74 | 75 | func SaveValue(writer io.Writer) error { 76 | 77 | encoder := json.NewEncoder(writer) 78 | encoder.SetIndent("", "\t") 79 | 80 | var file PersistFile 81 | file.Version = fileVersion 82 | for _, vmeta := range valueByKey { 83 | file.Values = append(file.Values, vmeta) 84 | } 85 | 86 | sort.SliceStable(file.Values, func(i, j int) bool { 87 | 88 | return file.Values[i].Key < file.Values[j].Key 89 | }) 90 | 91 | err := encoder.Encode(&file) 92 | 93 | if err != nil { 94 | return err 95 | } 96 | 97 | return nil 98 | } 99 | 100 | func LoadValue(reader io.Reader) error { 101 | 102 | decoder := json.NewDecoder(reader) 103 | 104 | var file PersistFile 105 | err := decoder.Decode(&file) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | valueByKey = map[string]*ValueMeta{} 111 | 112 | for _, v := range file.Values { 113 | valueByKey[v.Key] = v 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /discovery/memsd/model/svcmodel.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/util" 5 | "github.com/davyxu/cellnet" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | ServiceKeyPrefix = "_svcdesc_" 11 | ) 12 | 13 | var ( 14 | Queue cellnet.EventQueue 15 | IDGen = meshutil.NewUUID64Generator() 16 | 17 | Listener cellnet.Peer 18 | Debug bool 19 | 20 | Version = "0.1.0" 21 | ) 22 | 23 | func IsServiceKey(rawkey string) bool { 24 | 25 | return strings.HasPrefix(rawkey, ServiceKeyPrefix) 26 | } 27 | 28 | func GetSvcIDByServiceKey(rawkey string) string { 29 | 30 | if IsServiceKey(rawkey) { 31 | return rawkey[len(ServiceKeyPrefix):] 32 | } 33 | 34 | return "" 35 | } 36 | 37 | func init() { 38 | IDGen.AddTimeComponent(8) 39 | IDGen.AddSeqComponent(8, 0) 40 | } 41 | 42 | func GetSessionToken(ses cellnet.Session) (token string) { 43 | ses.(cellnet.ContextSet).FetchContext("token", &token) 44 | 45 | return 46 | } 47 | 48 | func Broadcast(msg interface{}) { 49 | Listener.(cellnet.TCPAcceptor).VisitSession(func(ses cellnet.Session) bool { 50 | ses.Send(msg) 51 | return true 52 | }) 53 | } 54 | 55 | func TokenExists(token string) (ret bool) { 56 | Listener.(cellnet.TCPAcceptor).VisitSession(func(ses cellnet.Session) bool { 57 | 58 | if GetSessionToken(ses) == token { 59 | ret = true 60 | return false 61 | } 62 | 63 | return true 64 | }) 65 | 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /discovery/memsd/persist.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery/memsd/model" 5 | "os" 6 | "time" 7 | ) 8 | 9 | func loadPersistFile(fileName string) { 10 | 11 | fileHandle, err := os.OpenFile(fileName, os.O_RDONLY, 0666) 12 | 13 | // 可能文件不存在,忽略 14 | if err != nil { 15 | return 16 | } 17 | 18 | log.Infoln("Load values...") 19 | 20 | err = model.LoadValue(fileHandle) 21 | if err != nil { 22 | log.Errorf("load values failed: %s %s", fileName, err.Error()) 23 | return 24 | } 25 | 26 | log.Infof("Load %d values", model.ValueCount()) 27 | } 28 | 29 | func startPersistCheck(fileName string) { 30 | 31 | ticker := time.NewTicker(time.Minute) 32 | 33 | for { 34 | 35 | <-ticker.C 36 | 37 | // 与收发在一个队列中,保证无锁 38 | model.Queue.Post(func() { 39 | 40 | if model.ValueDirty { 41 | 42 | log.Infoln("Save values...") 43 | 44 | fileHandle, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0666) 45 | if err != nil { 46 | log.Errorf("save persist file failed: %s %s", fileName, err.Error()) 47 | return 48 | } 49 | 50 | err = model.SaveValue(fileHandle) 51 | 52 | if err != nil { 53 | log.Errorf("save values failed: %s %s", fileName, err.Error()) 54 | return 55 | } 56 | 57 | log.Infof("Save %d values", model.ValueCount()) 58 | 59 | model.ValueDirty = false 60 | 61 | } 62 | 63 | }) 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /discovery/memsd/proto/MakeProto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRDIR=`pwd` 4 | cd ../../../../../../.. 5 | export GOPATH=`pwd` 6 | 7 | set -e 8 | Protoc=${GOPATH}/bin/protoc 9 | 10 | # cellmesh服务绑定 11 | CellMeshProtoGen=${GOPATH}/bin/cmprotogen 12 | go build -v -o ${CellMeshProtoGen} github.com/davyxu/cellmesh/tools/protogen 13 | 14 | # 协议生成 15 | ProtoPlusGen=${GOPATH}/bin/protoplus 16 | go build -v -o ${ProtoPlusGen} github.com/davyxu/protoplus 17 | 18 | cd ${CURRDIR} 19 | 20 | # windows下时,添加后缀名 21 | if [ `go env GOHOSTOS` == "windows" ];then 22 | EXESUFFIX=.exe 23 | fi 24 | 25 | echo "生成服务器协议的go消息..." 26 | ${ProtoPlusGen} -package=proto -go_out=msgsvc_gen.go `source ./protolist.sh svc` 27 | 28 | echo "生成服务器协议的消息绑定..." 29 | ${CellMeshProtoGen} -package=proto -cmgo_out=msgbind_gen.go `source ./protolist.sh svc` -------------------------------------------------------------------------------- /discovery/memsd/proto/msgbind_gen.go: -------------------------------------------------------------------------------- 1 | // Auto generated by github.com/davyxu/cellmesh/protogen 2 | // DO NOT EDIT! 3 | 4 | package proto 5 | 6 | import ( 7 | "github.com/davyxu/cellnet" 8 | "github.com/davyxu/cellnet/codec" 9 | _ "github.com/davyxu/cellnet/codec/protoplus" 10 | "reflect" 11 | ) 12 | 13 | // Make compiler import happy 14 | var ( 15 | _ cellnet.Event 16 | _ codec.CodecRecycler 17 | _ reflect.Type 18 | ) 19 | 20 | // memsd 21 | var ( 22 | Handle_Memsd_AuthREQ = func(ev cellnet.Event) { panic("'AuthREQ' not handled") } 23 | Handle_Memsd_ClearKeyREQ = func(ev cellnet.Event) { panic("'ClearKeyREQ' not handled") } 24 | Handle_Memsd_ClearSvcREQ = func(ev cellnet.Event) { panic("'ClearSvcREQ' not handled") } 25 | Handle_Memsd_DeleteValueREQ = func(ev cellnet.Event) { panic("'DeleteValueREQ' not handled") } 26 | Handle_Memsd_GetValueREQ = func(ev cellnet.Event) { panic("'GetValueREQ' not handled") } 27 | Handle_Memsd_SetValueREQ = func(ev cellnet.Event) { panic("'SetValueREQ' not handled") } 28 | Handle_Memsd_Default func(ev cellnet.Event) 29 | ) 30 | 31 | func GetMessageHandler(svcName string) cellnet.EventCallback { 32 | 33 | switch svcName { 34 | case "memsd": 35 | return func(ev cellnet.Event) { 36 | switch ev.Message().(type) { 37 | case *AuthREQ: 38 | Handle_Memsd_AuthREQ(ev) 39 | case *ClearKeyREQ: 40 | Handle_Memsd_ClearKeyREQ(ev) 41 | case *ClearSvcREQ: 42 | Handle_Memsd_ClearSvcREQ(ev) 43 | case *DeleteValueREQ: 44 | Handle_Memsd_DeleteValueREQ(ev) 45 | case *GetValueREQ: 46 | Handle_Memsd_GetValueREQ(ev) 47 | case *SetValueREQ: 48 | Handle_Memsd_SetValueREQ(ev) 49 | default: 50 | if Handle_Memsd_Default != nil { 51 | Handle_Memsd_Default(ev) 52 | } 53 | } 54 | } 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func init() { 61 | 62 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 63 | Codec: codec.MustGetCodec("protoplus"), 64 | Type: reflect.TypeOf((*SetValueREQ)(nil)).Elem(), 65 | ID: 44965, 66 | }) 67 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 68 | Codec: codec.MustGetCodec("protoplus"), 69 | Type: reflect.TypeOf((*SetValueACK)(nil)).Elem(), 70 | ID: 6796, 71 | }) 72 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 73 | Codec: codec.MustGetCodec("protoplus"), 74 | Type: reflect.TypeOf((*GetValueREQ)(nil)).Elem(), 75 | ID: 43673, 76 | }) 77 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 78 | Codec: codec.MustGetCodec("protoplus"), 79 | Type: reflect.TypeOf((*GetValueACK)(nil)).Elem(), 80 | ID: 5504, 81 | }) 82 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 83 | Codec: codec.MustGetCodec("protoplus"), 84 | Type: reflect.TypeOf((*DeleteValueREQ)(nil)).Elem(), 85 | ID: 64172, 86 | }) 87 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 88 | Codec: codec.MustGetCodec("protoplus"), 89 | Type: reflect.TypeOf((*DeleteValueACK)(nil)).Elem(), 90 | ID: 26003, 91 | }) 92 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 93 | Codec: codec.MustGetCodec("protoplus"), 94 | Type: reflect.TypeOf((*ValueChangeNotifyACK)(nil)).Elem(), 95 | ID: 52671, 96 | }) 97 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 98 | Codec: codec.MustGetCodec("protoplus"), 99 | Type: reflect.TypeOf((*ValueDeleteNotifyACK)(nil)).Elem(), 100 | ID: 35212, 101 | }) 102 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 103 | Codec: codec.MustGetCodec("protoplus"), 104 | Type: reflect.TypeOf((*AuthREQ)(nil)).Elem(), 105 | ID: 7726, 106 | }) 107 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 108 | Codec: codec.MustGetCodec("protoplus"), 109 | Type: reflect.TypeOf((*AuthACK)(nil)).Elem(), 110 | ID: 35093, 111 | }) 112 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 113 | Codec: codec.MustGetCodec("protoplus"), 114 | Type: reflect.TypeOf((*ClearSvcREQ)(nil)).Elem(), 115 | ID: 36847, 116 | }) 117 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 118 | Codec: codec.MustGetCodec("protoplus"), 119 | Type: reflect.TypeOf((*ClearSvcACK)(nil)).Elem(), 120 | ID: 64214, 121 | }) 122 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 123 | Codec: codec.MustGetCodec("protoplus"), 124 | Type: reflect.TypeOf((*ClearKeyREQ)(nil)).Elem(), 125 | ID: 6444, 126 | }) 127 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 128 | Codec: codec.MustGetCodec("protoplus"), 129 | Type: reflect.TypeOf((*ClearKeyACK)(nil)).Elem(), 130 | ID: 33811, 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /discovery/memsd/proto/msgsvc_gen.go: -------------------------------------------------------------------------------- 1 | // Generated by github.com/davyxu/protoplus 2 | // DO NOT EDIT! 3 | package proto 4 | 5 | import ( 6 | "github.com/davyxu/protoplus/proto" 7 | "unsafe" 8 | ) 9 | 10 | var ( 11 | _ *proto.Buffer 12 | _ unsafe.Pointer 13 | ) 14 | 15 | type ResultCode int32 16 | 17 | const ( 18 | ResultCode_Result_OK ResultCode = 0 19 | ResultCode_Result_NotExists ResultCode = 1 20 | ResultCode_Result_AuthRequire ResultCode = 2 21 | ) 22 | 23 | var ( 24 | ResultCodeMapperValueByName = map[string]int32{ 25 | "Result_OK": 0, 26 | "Result_NotExists": 1, 27 | "Result_AuthRequire": 2, 28 | } 29 | 30 | ResultCodeMapperNameByValue = map[int32]string{ 31 | 0: "Result_OK", 32 | 1: "Result_NotExists", 33 | 2: "Result_AuthRequire", 34 | } 35 | ) 36 | 37 | func (self ResultCode) String() string { 38 | return ResultCodeMapperNameByValue[int32(self)] 39 | } 40 | 41 | type SetValueREQ struct { 42 | Key string 43 | Value []byte 44 | SvcName string 45 | } 46 | 47 | func (self *SetValueREQ) String() string { return proto.CompactTextString(self) } 48 | 49 | func (self *SetValueREQ) Size() (ret int) { 50 | 51 | ret += proto.SizeString(0, self.Key) 52 | 53 | ret += proto.SizeBytes(1, self.Value) 54 | 55 | ret += proto.SizeString(2, self.SvcName) 56 | 57 | return 58 | } 59 | 60 | func (self *SetValueREQ) Marshal(buffer *proto.Buffer) error { 61 | 62 | proto.MarshalString(buffer, 0, self.Key) 63 | 64 | proto.MarshalBytes(buffer, 1, self.Value) 65 | 66 | proto.MarshalString(buffer, 2, self.SvcName) 67 | 68 | return nil 69 | } 70 | 71 | func (self *SetValueREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 72 | switch fieldIndex { 73 | case 0: 74 | return proto.UnmarshalString(buffer, wt, &self.Key) 75 | case 1: 76 | return proto.UnmarshalBytes(buffer, wt, &self.Value) 77 | case 2: 78 | return proto.UnmarshalString(buffer, wt, &self.SvcName) 79 | 80 | } 81 | 82 | return proto.ErrUnknownField 83 | } 84 | 85 | type SetValueACK struct { 86 | Code ResultCode 87 | } 88 | 89 | func (self *SetValueACK) String() string { return proto.CompactTextString(self) } 90 | 91 | func (self *SetValueACK) Size() (ret int) { 92 | 93 | ret += proto.SizeInt32(0, int32(self.Code)) 94 | 95 | return 96 | } 97 | 98 | func (self *SetValueACK) Marshal(buffer *proto.Buffer) error { 99 | 100 | proto.MarshalInt32(buffer, 0, int32(self.Code)) 101 | 102 | return nil 103 | } 104 | 105 | func (self *SetValueACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 106 | switch fieldIndex { 107 | case 0: 108 | return proto.UnmarshalInt32(buffer, wt, (*int32)(&self.Code)) 109 | 110 | } 111 | 112 | return proto.ErrUnknownField 113 | } 114 | 115 | type GetValueREQ struct { 116 | Key string 117 | } 118 | 119 | func (self *GetValueREQ) String() string { return proto.CompactTextString(self) } 120 | 121 | func (self *GetValueREQ) Size() (ret int) { 122 | 123 | ret += proto.SizeString(0, self.Key) 124 | 125 | return 126 | } 127 | 128 | func (self *GetValueREQ) Marshal(buffer *proto.Buffer) error { 129 | 130 | proto.MarshalString(buffer, 0, self.Key) 131 | 132 | return nil 133 | } 134 | 135 | func (self *GetValueREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 136 | switch fieldIndex { 137 | case 0: 138 | return proto.UnmarshalString(buffer, wt, &self.Key) 139 | 140 | } 141 | 142 | return proto.ErrUnknownField 143 | } 144 | 145 | type GetValueACK struct { 146 | Code ResultCode 147 | Key string 148 | Value []byte 149 | } 150 | 151 | func (self *GetValueACK) String() string { return proto.CompactTextString(self) } 152 | 153 | func (self *GetValueACK) Size() (ret int) { 154 | 155 | ret += proto.SizeInt32(0, int32(self.Code)) 156 | 157 | ret += proto.SizeString(1, self.Key) 158 | 159 | ret += proto.SizeBytes(2, self.Value) 160 | 161 | return 162 | } 163 | 164 | func (self *GetValueACK) Marshal(buffer *proto.Buffer) error { 165 | 166 | proto.MarshalInt32(buffer, 0, int32(self.Code)) 167 | 168 | proto.MarshalString(buffer, 1, self.Key) 169 | 170 | proto.MarshalBytes(buffer, 2, self.Value) 171 | 172 | return nil 173 | } 174 | 175 | func (self *GetValueACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 176 | switch fieldIndex { 177 | case 0: 178 | return proto.UnmarshalInt32(buffer, wt, (*int32)(&self.Code)) 179 | case 1: 180 | return proto.UnmarshalString(buffer, wt, &self.Key) 181 | case 2: 182 | return proto.UnmarshalBytes(buffer, wt, &self.Value) 183 | 184 | } 185 | 186 | return proto.ErrUnknownField 187 | } 188 | 189 | type DeleteValueREQ struct { 190 | Key string 191 | } 192 | 193 | func (self *DeleteValueREQ) String() string { return proto.CompactTextString(self) } 194 | 195 | func (self *DeleteValueREQ) Size() (ret int) { 196 | 197 | ret += proto.SizeString(0, self.Key) 198 | 199 | return 200 | } 201 | 202 | func (self *DeleteValueREQ) Marshal(buffer *proto.Buffer) error { 203 | 204 | proto.MarshalString(buffer, 0, self.Key) 205 | 206 | return nil 207 | } 208 | 209 | func (self *DeleteValueREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 210 | switch fieldIndex { 211 | case 0: 212 | return proto.UnmarshalString(buffer, wt, &self.Key) 213 | 214 | } 215 | 216 | return proto.ErrUnknownField 217 | } 218 | 219 | type DeleteValueACK struct { 220 | Code ResultCode 221 | Key string 222 | } 223 | 224 | func (self *DeleteValueACK) String() string { return proto.CompactTextString(self) } 225 | 226 | func (self *DeleteValueACK) Size() (ret int) { 227 | 228 | ret += proto.SizeInt32(0, int32(self.Code)) 229 | 230 | ret += proto.SizeString(1, self.Key) 231 | 232 | return 233 | } 234 | 235 | func (self *DeleteValueACK) Marshal(buffer *proto.Buffer) error { 236 | 237 | proto.MarshalInt32(buffer, 0, int32(self.Code)) 238 | 239 | proto.MarshalString(buffer, 1, self.Key) 240 | 241 | return nil 242 | } 243 | 244 | func (self *DeleteValueACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 245 | switch fieldIndex { 246 | case 0: 247 | return proto.UnmarshalInt32(buffer, wt, (*int32)(&self.Code)) 248 | case 1: 249 | return proto.UnmarshalString(buffer, wt, &self.Key) 250 | 251 | } 252 | 253 | return proto.ErrUnknownField 254 | } 255 | 256 | type ValueChangeNotifyACK struct { 257 | Key string 258 | Value []byte 259 | SvcName string 260 | } 261 | 262 | func (self *ValueChangeNotifyACK) String() string { return proto.CompactTextString(self) } 263 | 264 | func (self *ValueChangeNotifyACK) Size() (ret int) { 265 | 266 | ret += proto.SizeString(0, self.Key) 267 | 268 | ret += proto.SizeBytes(1, self.Value) 269 | 270 | ret += proto.SizeString(2, self.SvcName) 271 | 272 | return 273 | } 274 | 275 | func (self *ValueChangeNotifyACK) Marshal(buffer *proto.Buffer) error { 276 | 277 | proto.MarshalString(buffer, 0, self.Key) 278 | 279 | proto.MarshalBytes(buffer, 1, self.Value) 280 | 281 | proto.MarshalString(buffer, 2, self.SvcName) 282 | 283 | return nil 284 | } 285 | 286 | func (self *ValueChangeNotifyACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 287 | switch fieldIndex { 288 | case 0: 289 | return proto.UnmarshalString(buffer, wt, &self.Key) 290 | case 1: 291 | return proto.UnmarshalBytes(buffer, wt, &self.Value) 292 | case 2: 293 | return proto.UnmarshalString(buffer, wt, &self.SvcName) 294 | 295 | } 296 | 297 | return proto.ErrUnknownField 298 | } 299 | 300 | type ValueDeleteNotifyACK struct { 301 | Key string 302 | SvcName string 303 | } 304 | 305 | func (self *ValueDeleteNotifyACK) String() string { return proto.CompactTextString(self) } 306 | 307 | func (self *ValueDeleteNotifyACK) Size() (ret int) { 308 | 309 | ret += proto.SizeString(0, self.Key) 310 | 311 | ret += proto.SizeString(1, self.SvcName) 312 | 313 | return 314 | } 315 | 316 | func (self *ValueDeleteNotifyACK) Marshal(buffer *proto.Buffer) error { 317 | 318 | proto.MarshalString(buffer, 0, self.Key) 319 | 320 | proto.MarshalString(buffer, 1, self.SvcName) 321 | 322 | return nil 323 | } 324 | 325 | func (self *ValueDeleteNotifyACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 326 | switch fieldIndex { 327 | case 0: 328 | return proto.UnmarshalString(buffer, wt, &self.Key) 329 | case 1: 330 | return proto.UnmarshalString(buffer, wt, &self.SvcName) 331 | 332 | } 333 | 334 | return proto.ErrUnknownField 335 | } 336 | 337 | type AuthREQ struct { 338 | Token string 339 | } 340 | 341 | func (self *AuthREQ) String() string { return proto.CompactTextString(self) } 342 | 343 | func (self *AuthREQ) Size() (ret int) { 344 | 345 | ret += proto.SizeString(0, self.Token) 346 | 347 | return 348 | } 349 | 350 | func (self *AuthREQ) Marshal(buffer *proto.Buffer) error { 351 | 352 | proto.MarshalString(buffer, 0, self.Token) 353 | 354 | return nil 355 | } 356 | 357 | func (self *AuthREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 358 | switch fieldIndex { 359 | case 0: 360 | return proto.UnmarshalString(buffer, wt, &self.Token) 361 | 362 | } 363 | 364 | return proto.ErrUnknownField 365 | } 366 | 367 | type AuthACK struct { 368 | Token string 369 | } 370 | 371 | func (self *AuthACK) String() string { return proto.CompactTextString(self) } 372 | 373 | func (self *AuthACK) Size() (ret int) { 374 | 375 | ret += proto.SizeString(0, self.Token) 376 | 377 | return 378 | } 379 | 380 | func (self *AuthACK) Marshal(buffer *proto.Buffer) error { 381 | 382 | proto.MarshalString(buffer, 0, self.Token) 383 | 384 | return nil 385 | } 386 | 387 | func (self *AuthACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 388 | switch fieldIndex { 389 | case 0: 390 | return proto.UnmarshalString(buffer, wt, &self.Token) 391 | 392 | } 393 | 394 | return proto.ErrUnknownField 395 | } 396 | 397 | type ClearSvcREQ struct { 398 | } 399 | 400 | func (self *ClearSvcREQ) String() string { return proto.CompactTextString(self) } 401 | 402 | func (self *ClearSvcREQ) Size() (ret int) { 403 | 404 | return 405 | } 406 | 407 | func (self *ClearSvcREQ) Marshal(buffer *proto.Buffer) error { 408 | 409 | return nil 410 | } 411 | 412 | func (self *ClearSvcREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 413 | switch fieldIndex { 414 | 415 | } 416 | 417 | return proto.ErrUnknownField 418 | } 419 | 420 | type ClearSvcACK struct { 421 | Code ResultCode 422 | } 423 | 424 | func (self *ClearSvcACK) String() string { return proto.CompactTextString(self) } 425 | 426 | func (self *ClearSvcACK) Size() (ret int) { 427 | 428 | ret += proto.SizeInt32(0, int32(self.Code)) 429 | 430 | return 431 | } 432 | 433 | func (self *ClearSvcACK) Marshal(buffer *proto.Buffer) error { 434 | 435 | proto.MarshalInt32(buffer, 0, int32(self.Code)) 436 | 437 | return nil 438 | } 439 | 440 | func (self *ClearSvcACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 441 | switch fieldIndex { 442 | case 0: 443 | return proto.UnmarshalInt32(buffer, wt, (*int32)(&self.Code)) 444 | 445 | } 446 | 447 | return proto.ErrUnknownField 448 | } 449 | 450 | type ClearKeyREQ struct { 451 | } 452 | 453 | func (self *ClearKeyREQ) String() string { return proto.CompactTextString(self) } 454 | 455 | func (self *ClearKeyREQ) Size() (ret int) { 456 | 457 | return 458 | } 459 | 460 | func (self *ClearKeyREQ) Marshal(buffer *proto.Buffer) error { 461 | 462 | return nil 463 | } 464 | 465 | func (self *ClearKeyREQ) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 466 | switch fieldIndex { 467 | 468 | } 469 | 470 | return proto.ErrUnknownField 471 | } 472 | 473 | type ClearKeyACK struct { 474 | Code ResultCode 475 | } 476 | 477 | func (self *ClearKeyACK) String() string { return proto.CompactTextString(self) } 478 | 479 | func (self *ClearKeyACK) Size() (ret int) { 480 | 481 | ret += proto.SizeInt32(0, int32(self.Code)) 482 | 483 | return 484 | } 485 | 486 | func (self *ClearKeyACK) Marshal(buffer *proto.Buffer) error { 487 | 488 | proto.MarshalInt32(buffer, 0, int32(self.Code)) 489 | 490 | return nil 491 | } 492 | 493 | func (self *ClearKeyACK) Unmarshal(buffer *proto.Buffer, fieldIndex uint64, wt proto.WireType) error { 494 | switch fieldIndex { 495 | case 0: 496 | return proto.UnmarshalInt32(buffer, wt, (*int32)(&self.Code)) 497 | 498 | } 499 | 500 | return proto.ErrUnknownField 501 | } 502 | -------------------------------------------------------------------------------- /discovery/memsd/proto/proto_svc.txt: -------------------------------------------------------------------------------- 1 | #服务器间协议,一行一个 2 | sd.proto -------------------------------------------------------------------------------- /discovery/memsd/proto/protolist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # protolist.sh all 将proto_client.txt和proto_svc.txt的内容输出为行 4 | # protolist.sh XX 将proto_XX.txt的内容输出为行 5 | 6 | if [ "$1" == "all" ] 7 | then 8 | { grep -o '^[^#]*' proto_client.txt; echo " ";grep -o '^[^#]*' proto_svc.txt; }| tr -s "\r\n" " " 9 | else 10 | { grep -o '^[^#]*' proto_${1}.txt; echo " "; }| tr -s "\r\n" " " 11 | fi -------------------------------------------------------------------------------- /discovery/memsd/proto/sd.proto: -------------------------------------------------------------------------------- 1 | enum ResultCode 2 | { 3 | Result_OK = 0 4 | Result_NotExists 5 | Result_AuthRequire 6 | } 7 | 8 | 9 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 10 | struct SetValueREQ{ 11 | Key string 12 | Value bytes 13 | 14 | SvcName string 15 | } 16 | 17 | 18 | [AutoMsgID Codec:"protoplus"] 19 | struct SetValueACK{ 20 | Code ResultCode 21 | } 22 | 23 | 24 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 25 | struct GetValueREQ{ 26 | Key string 27 | } 28 | 29 | 30 | [AutoMsgID Codec:"protoplus"] 31 | struct GetValueACK{ 32 | Code ResultCode 33 | Key string 34 | Value bytes 35 | } 36 | 37 | 38 | 39 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 40 | struct DeleteValueREQ{ 41 | Key string 42 | } 43 | 44 | 45 | [AutoMsgID Codec:"protoplus"] 46 | struct DeleteValueACK{ 47 | Code ResultCode 48 | Key string 49 | 50 | 51 | } 52 | 53 | 54 | [AutoMsgID Codec:"protoplus"] 55 | struct ValueChangeNotifyACK{ 56 | Key string 57 | Value bytes 58 | 59 | SvcName string 60 | } 61 | 62 | [AutoMsgID Codec:"protoplus"] 63 | struct ValueDeleteNotifyACK{ 64 | Key string 65 | 66 | SvcName string 67 | } 68 | 69 | 70 | 71 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 72 | struct AuthREQ{ 73 | Token string 74 | } 75 | 76 | [AutoMsgID Codec:"protoplus"] 77 | struct AuthACK{ 78 | Token string 79 | } 80 | 81 | 82 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 83 | struct ClearSvcREQ{ 84 | } 85 | 86 | [AutoMsgID Codec:"protoplus"] 87 | struct ClearSvcACK{ 88 | Code ResultCode 89 | } 90 | 91 | 92 | [AutoMsgID Codec:"protoplus" Service: "memsd"] 93 | struct ClearKeyREQ{ 94 | } 95 | 96 | [AutoMsgID Codec:"protoplus"] 97 | struct ClearKeyACK{ 98 | Code ResultCode 99 | } -------------------------------------------------------------------------------- /discovery/memsd/redundant.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery/memsd/model" 5 | "time" 6 | ) 7 | 8 | // 移除token丢失的values 9 | func startCheckRedundantValue() { 10 | 11 | ticker := time.NewTicker(time.Minute) 12 | 13 | for { 14 | 15 | <-ticker.C 16 | 17 | // 与收发在一个队列中,保证无锁 18 | model.Queue.Post(func() { 19 | 20 | var svcToDelete []*model.ValueMeta 21 | 22 | model.VisitValue(func(meta *model.ValueMeta) bool { 23 | 24 | if meta.Token != "" && !model.TokenExists(meta.Token) && meta.ValueAsServiceDesc().GetMeta("@Persist") == "" { 25 | svcToDelete = append(svcToDelete, meta) 26 | } 27 | 28 | return true 29 | }) 30 | 31 | for _, meta := range svcToDelete { 32 | deleteNotify(meta.Key, "check redundant") 33 | } 34 | }) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /discovery/memsd/svc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery/memsd/api" 5 | "github.com/davyxu/cellmesh/discovery/memsd/model" 6 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 7 | "github.com/davyxu/cellmesh/service" 8 | "github.com/davyxu/cellnet" 9 | "github.com/davyxu/cellnet/peer" 10 | "github.com/davyxu/cellnet/proc" 11 | "github.com/davyxu/golog" 12 | "strings" 13 | ) 14 | 15 | var log = golog.New("memsd") 16 | 17 | func startSvc() { 18 | 19 | config := memsd.DefaultConfig() 20 | if *flagAddr != "" { 21 | config.Address = *flagAddr 22 | } 23 | 24 | model.Queue = cellnet.NewEventQueue() 25 | model.Queue.EnableCapturePanic(true) 26 | model.Queue.StartLoop() 27 | 28 | p := peer.NewGenericPeer("tcp.Acceptor", "memsd", config.Address, model.Queue) 29 | p.(cellnet.PeerCaptureIOPanic).EnableCaptureIOPanic(true) 30 | 31 | model.Listener = p 32 | msgFunc := proto.GetMessageHandler("memsd") 33 | 34 | proc.BindProcessorHandler(p, "memsd.svc", func(ev cellnet.Event) { 35 | 36 | if msgFunc != nil { 37 | msgFunc(ev) 38 | } 39 | }) 40 | 41 | // 100M封包大小 42 | p.(cellnet.TCPSocketOption).SetMaxPacketSize(1024 * 1024 * 100) 43 | p.(cellnet.TCPSocketOption).SetSocketBuffer(1024*1024, 1024*1024, true) 44 | p.(cellnet.PeerCaptureIOPanic).EnableCaptureIOPanic(true) 45 | p.Start() 46 | service.WaitExitSignal() 47 | } 48 | 49 | func deleteValueRecurse(key, reason string) { 50 | 51 | var keyToDelete []string 52 | model.VisitValue(func(meta *model.ValueMeta) bool { 53 | 54 | if strings.HasPrefix(meta.Key, key) { 55 | keyToDelete = append(keyToDelete, meta.Key) 56 | } 57 | 58 | return true 59 | }) 60 | 61 | for _, key := range keyToDelete { 62 | deleteNotify(key, reason) 63 | } 64 | } 65 | 66 | func deleteNotify(key, reason string) { 67 | valueMeta := model.DeleteValue(key) 68 | 69 | var ack proto.ValueDeleteNotifyACK 70 | ack.Key = key 71 | 72 | if valueMeta != nil { 73 | ack.SvcName = valueMeta.SvcName 74 | } 75 | 76 | if valueMeta != nil { 77 | 78 | if valueMeta.SvcName == "" { 79 | log.Infof("DeleteValue '%s' reason: %s", key, reason) 80 | } else { 81 | log.Infof("DeregisterService '%s' reason: %s", model.GetSvcIDByServiceKey(key), reason) 82 | } 83 | } 84 | 85 | model.Broadcast(&ack) 86 | 87 | } 88 | 89 | func checkAuth(ses cellnet.Session) bool { 90 | 91 | return model.GetSessionToken(ses) != "" 92 | } 93 | -------------------------------------------------------------------------------- /discovery/memsd/svc_msg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery/memsd/model" 5 | "github.com/davyxu/cellmesh/discovery/memsd/proto" 6 | "github.com/davyxu/cellnet" 7 | "strconv" 8 | ) 9 | 10 | func init() { 11 | 12 | proto.Handle_Memsd_SetValueREQ = func(ev cellnet.Event) { 13 | msg := ev.Message().(*proto.SetValueREQ) 14 | 15 | if !checkAuth(ev.Session()) { 16 | 17 | ev.Session().Send(&proto.SetValueACK{ 18 | Code: proto.ResultCode_Result_AuthRequire, 19 | }) 20 | return 21 | } 22 | 23 | meta := &model.ValueMeta{ 24 | Key: msg.Key, 25 | Value: msg.Value, 26 | } 27 | 28 | // 注册服务 29 | if model.IsServiceKey(msg.Key) { 30 | meta.SvcName = msg.SvcName 31 | meta.Token = model.GetSessionToken(ev.Session()) 32 | } 33 | 34 | model.SetValue(msg.Key, meta) 35 | 36 | if model.IsServiceKey(msg.Key) { 37 | log.Infof("RegisterService '%s'", meta.ValueAsServiceDesc().ID) 38 | } else { 39 | log.Infof("SetValue '%s' value(size:%d)", msg.Key, len(msg.Value)) 40 | } 41 | 42 | model.Broadcast(&proto.ValueChangeNotifyACK{ 43 | Key: msg.Key, 44 | Value: msg.Value, 45 | SvcName: msg.SvcName, 46 | }) 47 | 48 | ev.Session().Send(&proto.SetValueACK{}) 49 | 50 | } 51 | 52 | proto.Handle_Memsd_GetValueREQ = func(ev cellnet.Event) { 53 | msg := ev.Message().(*proto.GetValueREQ) 54 | 55 | if !checkAuth(ev.Session()) { 56 | 57 | ev.Session().Send(&proto.GetValueACK{ 58 | Code: proto.ResultCode_Result_AuthRequire, 59 | }) 60 | return 61 | } 62 | 63 | valueMeta := model.GetValue(msg.Key) 64 | if valueMeta != nil { 65 | ev.Session().Send(&proto.GetValueACK{ 66 | Key: msg.Key, 67 | Value: valueMeta.Value, 68 | }) 69 | } else { 70 | ev.Session().Send(&proto.GetValueACK{ 71 | Key: msg.Key, 72 | Code: proto.ResultCode_Result_NotExists, 73 | }) 74 | } 75 | 76 | } 77 | 78 | proto.Handle_Memsd_DeleteValueREQ = func(ev cellnet.Event) { 79 | msg := ev.Message().(*proto.DeleteValueREQ) 80 | 81 | if !checkAuth(ev.Session()) { 82 | 83 | ev.Session().Send(&proto.DeleteValueACK{ 84 | Code: proto.ResultCode_Result_AuthRequire, 85 | }) 86 | return 87 | } 88 | 89 | deleteValueRecurse(msg.Key, "api") 90 | 91 | ev.Session().Send(&proto.DeleteValueACK{ 92 | Key: msg.Key, 93 | }) 94 | } 95 | 96 | proto.Handle_Memsd_AuthREQ = func(ev cellnet.Event) { 97 | 98 | msg := ev.Message().(*proto.AuthREQ) 99 | 100 | model.VisitValue(func(meta *model.ValueMeta) bool { 101 | 102 | ev.Session().Send(&proto.ValueChangeNotifyACK{ 103 | Key: meta.Key, 104 | Value: meta.Value, 105 | SvcName: meta.SvcName, 106 | }) 107 | 108 | return true 109 | 110 | }) 111 | 112 | var ack proto.AuthACK 113 | 114 | // 首次生成token并与ses绑定 115 | if msg.Token == "" { 116 | ack.Token = strconv.Itoa(int(model.IDGen.Generate())) 117 | } 118 | 119 | ev.Session().(cellnet.ContextSet).SetContext("token", ack.Token) 120 | 121 | ev.Session().Send(&ack) 122 | } 123 | 124 | proto.Handle_Memsd_ClearSvcREQ = func(ev cellnet.Event) { 125 | 126 | if !checkAuth(ev.Session()) { 127 | ev.Session().Send(&proto.ClearSvcACK{ 128 | Code: proto.ResultCode_Result_AuthRequire, 129 | }) 130 | return 131 | } 132 | 133 | log.Infoln("ClearSvc") 134 | 135 | var svcToDelete []*model.ValueMeta 136 | model.VisitValue(func(meta *model.ValueMeta) bool { 137 | 138 | if meta.SvcName != "" { 139 | svcToDelete = append(svcToDelete, meta) 140 | } 141 | 142 | return true 143 | }) 144 | 145 | for _, meta := range svcToDelete { 146 | deleteNotify(meta.Key, "clearsvc") 147 | } 148 | 149 | ev.Session().Send(&proto.ClearSvcACK{}) 150 | } 151 | 152 | proto.Handle_Memsd_ClearKeyREQ = func(ev cellnet.Event) { 153 | 154 | if !checkAuth(ev.Session()) { 155 | ev.Session().Send(&proto.ClearKeyACK{ 156 | Code: proto.ResultCode_Result_AuthRequire, 157 | }) 158 | return 159 | } 160 | 161 | log.Infoln("ClearValue") 162 | 163 | var svcToDelete []*model.ValueMeta 164 | model.VisitValue(func(meta *model.ValueMeta) bool { 165 | 166 | if meta.SvcName == "" { 167 | svcToDelete = append(svcToDelete, meta) 168 | } 169 | 170 | return true 171 | }) 172 | 173 | for _, meta := range svcToDelete { 174 | deleteNotify(meta.Key, "clearkey") 175 | } 176 | 177 | ev.Session().Send(&proto.ClearKeyACK{}) 178 | } 179 | 180 | proto.Handle_Memsd_Default = func(ev cellnet.Event) { 181 | 182 | switch ev.Message().(type) { 183 | case *cellnet.SessionAccepted: 184 | 185 | case *cellnet.SessionClosed: 186 | 187 | if checkAuth(ev.Session()) { 188 | var svcToDelete []*model.ValueMeta 189 | model.VisitValue(func(meta *model.ValueMeta) bool { 190 | 191 | if meta.Token == model.GetSessionToken(ev.Session()) { 192 | 193 | // 工具写入的db服务,要持久化保存 194 | 195 | if meta.ValueAsServiceDesc().GetMeta("@Persist") == "" { 196 | svcToDelete = append(svcToDelete, meta) 197 | } 198 | } 199 | 200 | return true 201 | }) 202 | 203 | for _, meta := range svcToDelete { 204 | deleteNotify(meta.Key, "offline") 205 | } 206 | } 207 | 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /discovery/safevalue.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/cellnet/util" 6 | "reflect" 7 | ) 8 | 9 | //KV中的Value最大不超过512K, 10 | const ( 11 | // 不能直接保存二进制,底层用Json转base64,base64的二进制比原二进制要大最终二进制不到512K就会达到限制 12 | PackedValueSize = 300 * 1024 13 | ) 14 | 15 | type rawGetter interface { 16 | // 获取原始值 17 | GetRawValue(key string) ([]byte, error) 18 | GetValueDirect(key string, valuePtr interface{}) error 19 | } 20 | 21 | func getMultiKey(sd rawGetter, key string) (ret []string) { 22 | 23 | mainKey := key 24 | 25 | ret = append(ret, mainKey) 26 | 27 | for i := 1; ; i++ { 28 | 29 | key = fmt.Sprintf("%s.%d", mainKey, i) 30 | 31 | _, err := sd.GetRawValue(key) 32 | if err != nil && err.Error() == "value not exists" { 33 | return 34 | } 35 | 36 | ret = append(ret, key) 37 | } 38 | 39 | } 40 | 41 | // compress value按 key, key.1, key.2 ... 保存 42 | func SafeSetValue(sd Discovery, key string, value interface{}, compress bool) error { 43 | if compress { 44 | cData, err := util.CompressBytes(value.([]byte)) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | if len(cData) >= PackedValueSize { 50 | 51 | for _, multiKey := range getMultiKey(sd.(rawGetter), key) { 52 | 53 | err := sd.DeleteValue(multiKey) 54 | if err != nil { 55 | fmt.Printf("delete kv error, %s\n", err) 56 | } 57 | } 58 | 59 | var pos = PackedValueSize 60 | 61 | err = sd.SetValue(key, cData[:pos]) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | index := 1 67 | for len(cData)-pos > PackedValueSize { 68 | 69 | multiKey := fmt.Sprintf("%s.%d", key, index) 70 | err = sd.SetValue(multiKey, cData[pos:pos+PackedValueSize]) 71 | if err != nil { 72 | return err 73 | } 74 | pos += PackedValueSize 75 | index++ 76 | } 77 | 78 | if len(cData)-pos > 0 { 79 | multiKey := fmt.Sprintf("%s.%d", key, index) 80 | err = sd.SetValue(multiKey, cData[pos:]) 81 | if err != nil { 82 | return err 83 | } 84 | } 85 | 86 | return nil 87 | 88 | } else { 89 | return sd.SetValue(key, cData) 90 | } 91 | 92 | } else { 93 | return sd.SetValue(key, value) 94 | } 95 | } 96 | 97 | func SafeGetValue(sd Discovery, key string, valuePtr interface{}, decompress bool) error { 98 | 99 | rg := sd.(rawGetter) 100 | 101 | var ( 102 | finalData []byte 103 | err error 104 | ) 105 | 106 | if decompress { 107 | 108 | var data []byte 109 | for _, multiKey := range getMultiKey(rg, key) { 110 | 111 | var partData []byte 112 | err := rg.GetValueDirect(multiKey, &partData) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | data = append(data, partData...) 118 | } 119 | 120 | finalData, err = util.DecompressBytes(data) 121 | 122 | if err != nil { 123 | return err 124 | } 125 | 126 | reflect.ValueOf(valuePtr).Elem().Set(reflect.ValueOf(finalData)) 127 | } else { 128 | err = rg.GetValueDirect(key, &finalData) 129 | 130 | if err != nil { 131 | return err 132 | } 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /discovery/safevalue_test.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | memsd "github.com/davyxu/cellmesh/discovery/memsd/api" 5 | "github.com/davyxu/cellmesh/service" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestSafeGetValue(t *testing.T) { 11 | 12 | var origin []byte 13 | for i := 0; i < 12; i++ { 14 | //origin = append(origin, byte(rand.Int31n(127))) 15 | origin = append(origin, byte(i)) 16 | } 17 | 18 | sdConfig := memsd.DefaultConfig() 19 | sdConfig.Address = service.GetDiscoveryAddr() 20 | Default = memsd.NewDiscovery(sdConfig) 21 | 22 | err := SafeSetValue(Default, "config/test", origin, true) 23 | if err != nil { 24 | t.Error(err) 25 | t.FailNow() 26 | } 27 | 28 | var outData []byte 29 | err = SafeGetValue(Default, "config/test", &outData, true) 30 | if err != nil { 31 | t.Error(err) 32 | t.FailNow() 33 | } 34 | 35 | if !reflect.DeepEqual(origin, outData) { 36 | t.FailNow() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /discovery/util.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | ) 9 | 10 | func BytesToAny(data []byte, dataPtr interface{}) error { 11 | 12 | switch ret := dataPtr.(type) { 13 | case *int: 14 | v, err := strconv.ParseInt(string(data), 10, 64) 15 | if err != nil { 16 | return err 17 | } 18 | *ret = int(v) 19 | return nil 20 | case *float32: 21 | v, err := strconv.ParseFloat(string(data), 32) 22 | if err != nil { 23 | return err 24 | } 25 | *ret = float32(v) 26 | return nil 27 | case *float64: 28 | v, err := strconv.ParseFloat(string(data), 64) 29 | if err != nil { 30 | return err 31 | } 32 | *ret = float64(v) 33 | return nil 34 | case *bool: 35 | v, err := strconv.ParseBool(string(data)) 36 | if err != nil { 37 | return err 38 | } 39 | *ret = v 40 | return nil 41 | case *string: 42 | *ret = string(data) 43 | return nil 44 | default: 45 | return json.Unmarshal(data, dataPtr) 46 | } 47 | } 48 | 49 | func ValueMetaToSlice(pairs []ValueMeta, dataPtr interface{}) error { 50 | 51 | vdata := reflect.Indirect(reflect.ValueOf(dataPtr)) 52 | 53 | elementCount := len(pairs) 54 | 55 | slice := reflect.MakeSlice(vdata.Type(), elementCount, elementCount) 56 | 57 | for i := 0; i < elementCount; i++ { 58 | 59 | sliceValue := reflect.New(slice.Type().Elem()) 60 | 61 | err := BytesToAny(pairs[i].Value, sliceValue.Interface()) 62 | 63 | if err != nil { 64 | return err 65 | } 66 | 67 | slice.Index(i).Set(sliceValue.Elem()) 68 | } 69 | 70 | vdata.Set(slice) 71 | 72 | return nil 73 | 74 | } 75 | 76 | func AnyToBytes(data interface{}, prettyPrint bool) ([]byte, error) { 77 | 78 | switch v := data.(type) { 79 | case int, int32, int64, uint32, uint64, float32, float64, bool: 80 | return []byte(fmt.Sprint(data)), nil 81 | case string: 82 | return []byte(v), nil 83 | 84 | default: 85 | if prettyPrint { 86 | raw, err := json.MarshalIndent(data, "", "\t") 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | return raw, nil 92 | } else { 93 | raw, err := json.Marshal(data) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return raw, nil 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dummy.go: -------------------------------------------------------------------------------- 1 | package cellmesh 2 | 3 | // Make go get happy 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/davyxu/cellmesh 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/ahmetb/go-linq v3.0.0+incompatible 7 | github.com/davyxu/cellnet v0.0.0-20190628065413-a644d2409b6d 8 | github.com/davyxu/golexer v0.1.0 // indirect 9 | github.com/davyxu/golog v0.1.0 10 | github.com/davyxu/protoplus v0.1.0 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA= 2 | github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY= 3 | github.com/davyxu/cellnet v0.0.0-20190612112848-4f8cb890a9f8 h1:hnIRsbLMSkTNYPuA+ALM6R7yrV/1wl+J02dQ7pIv5Bw= 4 | github.com/davyxu/cellnet v0.0.0-20190612112848-4f8cb890a9f8/go.mod h1:veOb1czkqh9rfO/+KLeAIzRT9vOk9gg3LNXuCXV+QdQ= 5 | github.com/davyxu/cellnet v0.0.0-20190628065413-a644d2409b6d h1:M4eBkPiPdfcvRjhyMI9lwVdo63TxhiKwq0UWZO9/LI8= 6 | github.com/davyxu/cellnet v0.0.0-20190628065413-a644d2409b6d/go.mod h1:veOb1czkqh9rfO/+KLeAIzRT9vOk9gg3LNXuCXV+QdQ= 7 | github.com/davyxu/golexer v0.1.0 h1:PFavbgNV8PUH2HPU1G3o0QMcNJ3I4BkL8esbeenTyMU= 8 | github.com/davyxu/golexer v0.1.0/go.mod h1:K/1PVWqaQEwMs+N+VvaoS66o1BAAwso+jEgXKBIa8QY= 9 | github.com/davyxu/golog v0.1.0 h1:SsV3m2x37sCzFaQzq5OHc5S+PE2VMiL7XUx34JCa7mo= 10 | github.com/davyxu/golog v0.1.0/go.mod h1:YwChkFY5dCYt77yuPlWjcR6KlWqVJNbz3WkwC/8WgQk= 11 | github.com/davyxu/goobjfmt v0.1.0 h1:/Kz4X/UL4Jf5xOaQhP5DxzNtcwsfJqsz6ceoePeHBgA= 12 | github.com/davyxu/goobjfmt v0.1.0/go.mod h1:KKrytCtCXny2sEg3ojQfJ4NThhBP8hKw/qM9vhDwgog= 13 | github.com/davyxu/protoplus v0.1.0 h1:iKk94nwYZdEK8r1r4GZDkW7JnmLJTPYQSVUvBLBxsb8= 14 | github.com/davyxu/protoplus v0.1.0/go.mod h1:WzmNYPvYsyks3G81jCJ/vGY2ljs49qFMfCmXGwvxFLA= 15 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 16 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 17 | github.com/mediocregopher/radix.v2 v0.0.0-20181115013041-b67df6e626f9/go.mod h1:fLRUbhbSd5Px2yKUaGYYPltlyxi1guJz1vCmo1RQL50= 18 | -------------------------------------------------------------------------------- /service/discovery.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellnet" 6 | ) 7 | 8 | type DiscoveryOption struct { 9 | Rules []MatchRule 10 | MaxCount int // 连接数,默认发起多条连接 11 | MatchSvcGroup string // 空时,匹配所有同类服务,否则找指定组的服务 12 | } 13 | 14 | // 发现一个服务,服务可能拥有多个地址,每个地址返回时,创建一个connector并开启 15 | // DiscoveryService返回值返回持有多个Peer的peer, 判断Peer的IsReady可以得到所有连接准备好的状态 16 | func DiscoveryService(tgtSvcName string, opt DiscoveryOption, peerCreator func(MultiPeer, *discovery.ServiceDesc)) cellnet.Peer { 17 | 18 | // 从发现到连接有一个过程,需要用Map防止还没连上,又创建一个新的连接 19 | multiPeer := newMultiPeer() 20 | 21 | go func() { 22 | 23 | notify := discovery.Default.RegisterNotify("add") 24 | for { 25 | 26 | QueryService(tgtSvcName, 27 | Filter_MatchRule(opt.Rules), 28 | Filter_MatchSvcGroup(opt.MatchSvcGroup), 29 | func(desc *discovery.ServiceDesc) interface{} { 30 | 31 | //log.Infof("found '%s' address '%s' ", tgtSvcName, desc.Address()) 32 | 33 | prePeer := multiPeer.GetPeer(desc.ID) 34 | 35 | // 如果svcid重复汇报, 可能svcid内容有变化 36 | if prePeer != nil { 37 | 38 | var preDesc *discovery.ServiceDesc 39 | if prePeer.(cellnet.ContextSet).FetchContext("sd", &preDesc) && !preDesc.Equals(desc) { 40 | 41 | log.Infof("service '%s' change desc, %+v -> %+v...", desc.ID, preDesc, desc) 42 | 43 | // 移除之前的连接 44 | multiPeer.RemovePeer(desc.ID) 45 | 46 | // 停止重连 47 | prePeer.Stop() 48 | 49 | } else { 50 | return true 51 | } 52 | 53 | } 54 | 55 | // 达到最大连接 56 | if opt.MaxCount > 0 && len(multiPeer.GetPeers()) >= opt.MaxCount { 57 | return true 58 | } 59 | 60 | // 用户创建peer 61 | peerCreator(multiPeer, desc) 62 | 63 | return true 64 | }) 65 | 66 | <-notify 67 | } 68 | 69 | }() 70 | 71 | return multiPeer 72 | } 73 | -------------------------------------------------------------------------------- /service/flag.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | ) 7 | 8 | var ( 9 | // 独立出来避免污染工具类的flagset 10 | CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) 11 | 12 | // 服务发现地址 13 | flagDiscoveryAddr = CommandLine.String("sdaddr", "127.0.0.1:8900", "Discovery address") 14 | 15 | // 服务发现规则 16 | flagLinkRule = CommandLine.String("linkrule", "", "Discovery other node then connect it, format like: 'svcname:tgtnode|defaultnode'") 17 | 18 | // 服务所在组 19 | flagSvcGroup = CommandLine.String("svcgroup", "dev", "Represent one group server") 20 | 21 | // 服务索引 22 | flagSvcIndex = CommandLine.Int("svcindex", 0, "Multi proc in group use index to seperate each other") 23 | 24 | // 设置外网IP 25 | flagWANIP = CommandLine.String("wanip", "", "Client connect from extern ip") 26 | 27 | // 对日志上色 28 | flagLogColor = CommandLine.Bool("logcolor", false, "Make log in color in *nix") 29 | 30 | // 将日志输出到文件 31 | flagLogFile = CommandLine.String("logfile", "", "Print log to file by file name") 32 | 33 | // 单个日志文件大小, 超过设定值时创建新文件, 0表示单文件 34 | flagLogFileSize = CommandLine.String("logfilesize", "", "log max file size, can use B M G to represent size") 35 | 36 | // 设置日志级别,格式: 日志名称 日志级别, 名称支持正则表达式 37 | flagLogLevel = CommandLine.String("loglevel", "", "Set log level, format: 'name|level', name support regexp, level can be error, info") 38 | 39 | // 屏蔽消息日志 40 | flagMuteMsgLog = CommandLine.String("mutemsglog", "", "Do not show msg log, splite by '|', support regexp") 41 | 42 | // 批量设置flag 43 | flagFlagFile = CommandLine.String("flagfile", "../cfg/LocalFlag.cfg", "Flagfile to init flag values") 44 | ) 45 | -------------------------------------------------------------------------------- /service/hooker.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellnet" 6 | _ "github.com/davyxu/cellnet/peer/tcp" 7 | "github.com/davyxu/cellnet/proc" 8 | "github.com/davyxu/cellnet/proc/tcp" 9 | ) 10 | 11 | // 服务互联消息处理 12 | type SvcEventHooker struct { 13 | } 14 | 15 | func (SvcEventHooker) OnInboundEvent(inputEvent cellnet.Event) (outputEvent cellnet.Event) { 16 | 17 | switch msg := inputEvent.Message().(type) { 18 | case *ServiceIdentifyACK: 19 | 20 | if pre := GetRemoteService(msg.SvcID); pre == nil { 21 | 22 | // 添加连接上来的对方服务 23 | AddRemoteService(inputEvent.Session(), msg.SvcID, msg.SvcName) 24 | } 25 | case *cellnet.SessionConnected: 26 | 27 | ctx := inputEvent.Session().Peer().(cellnet.ContextSet) 28 | 29 | var sd *discovery.ServiceDesc 30 | if ctx.FetchContext("sd", &sd) { 31 | 32 | // 用Connector的名称(一般是ProcName)让远程知道自己是什么服务,用于网关等需要反向发送消息的标识 33 | inputEvent.Session().Send(&ServiceIdentifyACK{ 34 | SvcName: GetProcName(), 35 | SvcID: GetLocalSvcID(), 36 | }) 37 | 38 | AddRemoteService(inputEvent.Session(), sd.ID, sd.Name) 39 | } else { 40 | 41 | log.Errorf("Make sure call multi.AddPeer before peer.Start, peer: %s", inputEvent.Session().Peer().TypeName()) 42 | } 43 | 44 | case *cellnet.SessionClosed: 45 | 46 | RemoveRemoteService(inputEvent.Session()) 47 | } 48 | 49 | return inputEvent 50 | 51 | } 52 | 53 | func (SvcEventHooker) OnOutboundEvent(inputEvent cellnet.Event) (outputEvent cellnet.Event) { 54 | 55 | return inputEvent 56 | } 57 | 58 | func init() { 59 | 60 | // 服务器间通讯协议 61 | proc.RegisterProcessor("tcp.svc", func(bundle proc.ProcessorBundle, userCallback cellnet.EventCallback, args ...interface{}) { 62 | 63 | bundle.SetTransmitter(new(tcp.TCPMessageTransmitter)) 64 | bundle.SetHooker(proc.NewMultiHooker(new(SvcEventHooker), new(tcp.MsgHooker))) 65 | bundle.SetCallback(proc.NewQueuedEventCallback(userCallback)) 66 | }) 67 | 68 | // 与客户端通信的处理器 69 | proc.RegisterProcessor("tcp.client", func(bundle proc.ProcessorBundle, userCallback cellnet.EventCallback, args ...interface{}) { 70 | 71 | bundle.SetTransmitter(new(tcp.TCPMessageTransmitter)) 72 | bundle.SetHooker(proc.NewMultiHooker(new(tcp.MsgHooker))) 73 | bundle.SetCallback(proc.NewQueuedEventCallback(userCallback)) 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /service/init.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellmesh/discovery/memsd/api" 6 | "github.com/davyxu/cellmesh/util" 7 | "github.com/davyxu/cellnet/msglog" 8 | "github.com/davyxu/cellnet/util" 9 | "github.com/davyxu/golog" 10 | "os" 11 | "os/signal" 12 | "strings" 13 | "syscall" 14 | ) 15 | 16 | func Init(name string) { 17 | 18 | procName = name 19 | 20 | CommandLine.Parse(os.Args[1:]) 21 | 22 | // 开发期优先从LocalFlag作用flag 23 | meshutil.ApplyFlagFromFile(CommandLine, *flagFlagFile) 24 | 25 | CommandLine.Parse(os.Args[1:]) 26 | 27 | LinkRules = ParseMatchRule(getLinkRule()) 28 | 29 | // 设置文件日志 30 | if *flagLogFile != "" { 31 | 32 | if *flagLogFileSize == "" { 33 | log.Infof("LogFile: %s", *flagLogFile) 34 | golog.SetOutputToFile(*flagLogFile) 35 | 36 | } else { 37 | 38 | size, err := meshutil.ParseSizeString(*flagLogFileSize) 39 | if err == nil { 40 | log.Infof("LogFile: %s Size: %s", *flagLogFile, *flagLogFileSize) 41 | golog.SetOutputToFile(*flagLogFile, golog.OutputFileOption{ 42 | MaxFileSize: size, 43 | }) 44 | } else { 45 | log.Errorf("log file size err: %s", err) 46 | } 47 | 48 | } 49 | } 50 | 51 | // 彩色日志 52 | if *flagLogColor { 53 | golog.SetColorDefine(".", msglog.LogColorDefine) 54 | golog.EnableColorLogger(".", true) 55 | } 56 | 57 | // 设置日志级别 58 | if *flagLogLevel != "" { 59 | 60 | if rawstr := strings.Split(*flagLogLevel, "|"); len(rawstr) == 2 { 61 | 62 | if err := golog.SetLevelByString(rawstr[0], rawstr[1]); err != nil { 63 | log.Warnln("SetLevelByString:", err) 64 | } else { 65 | log.Infoln("SetLevelByString:", rawstr[0], rawstr[1]) 66 | } 67 | } else { 68 | log.Errorln("Invalid log level cli fomat, require 'name level'") 69 | } 70 | } 71 | 72 | // 禁用指定消息名的消息日志 73 | if *flagMuteMsgLog != "" { 74 | 75 | if err := msglog.SetMsgLogRule(*flagMuteMsgLog, msglog.MsgLogRule_BlackList); err != nil { 76 | log.Errorln("SetMsgLogRule: ", err) 77 | } else { 78 | log.Infoln("SetMsgLogRule:", *flagMuteMsgLog) 79 | } 80 | } 81 | 82 | } 83 | 84 | func getLinkRule() string { 85 | if *flagLinkRule == "" { 86 | return *flagSvcGroup 87 | } else { 88 | return *flagLinkRule 89 | } 90 | } 91 | 92 | func LogParameter() { 93 | workdir, _ := os.Getwd() 94 | log.Infof("Execuable: %s", os.Args[0]) 95 | log.Infof("WorkDir: %s", workdir) 96 | log.Infof("ProcName: '%s'", GetProcName()) 97 | log.Infof("PID: %d", os.Getpid()) 98 | log.Infof("Discovery: '%s'", *flagDiscoveryAddr) 99 | log.Infof("LinkRule: '%s'", getLinkRule()) 100 | log.Infof("SvcGroup: '%s'", GetSvcGroup()) 101 | log.Infof("SvcIndex: %d", GetSvcIndex()) 102 | log.Infof("LANIP: '%s'", util.GetLocalIP()) 103 | log.Infof("WANIP: '%s'", GetWANIP()) 104 | } 105 | 106 | // 连接到服务发现, 建议在service.Init后, 以及服务器逻辑开始前调用 107 | func ConnectDiscovery() { 108 | log.Debugf("Connecting to discovery '%s' ...", *flagDiscoveryAddr) 109 | sdConfig := memsd.DefaultConfig() 110 | sdConfig.Address = *flagDiscoveryAddr 111 | discovery.Default = memsd.NewDiscovery(sdConfig) 112 | } 113 | 114 | func WaitExitSignal() { 115 | ch := make(chan os.Signal, 1) 116 | signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) 117 | 118 | <-ch 119 | } 120 | -------------------------------------------------------------------------------- /service/log.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var log = golog.New("service") 8 | -------------------------------------------------------------------------------- /service/matchrule.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellmesh/util" 6 | "strings" 7 | ) 8 | 9 | type MatchRule struct { 10 | Target string 11 | } 12 | 13 | func matchTarget(rule *MatchRule, desc *discovery.ServiceDesc) bool { 14 | 15 | return meshutil.WildcardPatternMatch(desc.GetMeta("SvcGroup"), rule.Target) 16 | } 17 | func ParseMatchRule(rule string) (ret []MatchRule) { 18 | 19 | for _, ruleStr := range strings.Split(rule, "|") { 20 | var rule MatchRule 21 | rule.Target = ruleStr 22 | ret = append(ret, rule) 23 | } 24 | 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /service/model.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | var ( 4 | procName string 5 | LinkRules []MatchRule // 互联发现规则 6 | ) 7 | 8 | // 获取当前服务进程名称 9 | func GetProcName() string { 10 | return procName 11 | } 12 | 13 | // 获取外网IP 14 | func GetWANIP() string { 15 | return *flagWANIP 16 | } 17 | 18 | func GetSvcGroup() string { 19 | return *flagSvcGroup 20 | } 21 | 22 | func GetSvcIndex() int { 23 | return *flagSvcIndex 24 | } 25 | 26 | func GetDiscoveryAddr() string { 27 | return *flagDiscoveryAddr 28 | } 29 | -------------------------------------------------------------------------------- /service/msg.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/davyxu/cellnet" 7 | "github.com/davyxu/cellnet/codec" 8 | _ "github.com/davyxu/cellnet/codec/binary" 9 | "github.com/davyxu/cellnet/relay" 10 | "github.com/davyxu/cellnet/util" 11 | "reflect" 12 | ) 13 | 14 | type ServiceIdentifyACK struct { 15 | SvcName string 16 | SvcID string 17 | } 18 | 19 | func (self *ServiceIdentifyACK) String() string { return fmt.Sprintf("%+v", *self) } 20 | 21 | func init() { 22 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 23 | Codec: codec.MustGetCodec("binary"), 24 | Type: reflect.TypeOf((*ServiceIdentifyACK)(nil)).Elem(), 25 | ID: int(util.StringHash("service.ServiceIdentifyACK")), 26 | }) 27 | } 28 | 29 | var ( 30 | ErrInvalidRelayMessage = errors.New("invalid relay message") 31 | ErrInvalidRelayPassthroughType = errors.New("invalid relay passthrough type") 32 | ) 33 | 34 | // 获取Event中relay的透传数据 35 | func GetPassThrough(ev cellnet.Event, ptrList ...interface{}) error { 36 | if relayEvent, ok := ev.(*relay.RecvMsgEvent); ok { 37 | 38 | for _, ptr := range ptrList { 39 | 40 | switch valuePtr := ptr.(type) { 41 | case *int64: 42 | *valuePtr = relayEvent.PassThroughAsInt64() 43 | case *[]int64: 44 | *valuePtr = relayEvent.PassThroughAsInt64Slice() 45 | case *string: 46 | *valuePtr = relayEvent.PassThroughAsString() 47 | default: 48 | return ErrInvalidRelayPassthroughType 49 | } 50 | } 51 | 52 | return nil 53 | } else { 54 | return ErrInvalidRelayMessage 55 | } 56 | 57 | } 58 | 59 | // 回复event来源一个消息 60 | func Reply(ev cellnet.Event, msg interface{}) { 61 | 62 | type replyEvent interface { 63 | Reply(msg interface{}) 64 | } 65 | 66 | if replyEv, ok := ev.(replyEvent); ok { 67 | replyEv.Reply(msg) 68 | } else { 69 | panic("Require 'ReplyEvent' to reply event") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /service/multipeer.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellnet" 6 | "github.com/davyxu/cellnet/peer" 7 | "sync" 8 | ) 9 | 10 | // 一类服务发起多个连接(不是同一地址), 比如 login1 login2 11 | type MultiPeer interface { 12 | GetPeers() []cellnet.Peer 13 | 14 | cellnet.ContextSet 15 | 16 | AddPeer(sd *discovery.ServiceDesc, p cellnet.Peer) 17 | } 18 | 19 | type multiPeer struct { 20 | peer.CoreContextSet 21 | peers []cellnet.Peer 22 | peersGuard sync.RWMutex 23 | context interface{} 24 | } 25 | 26 | func (self *multiPeer) Start() cellnet.Peer { 27 | return self 28 | } 29 | 30 | func (self *multiPeer) Stop() { 31 | 32 | } 33 | 34 | func (self *multiPeer) TypeName() string { 35 | return "" 36 | } 37 | 38 | func (self *multiPeer) GetPeers() []cellnet.Peer { 39 | self.peersGuard.RLock() 40 | defer self.peersGuard.RUnlock() 41 | 42 | return self.peers 43 | } 44 | 45 | func (self *multiPeer) IsReady() bool { 46 | 47 | peers := self.GetPeers() 48 | 49 | if len(peers) == 0 { 50 | return false 51 | } 52 | 53 | for _, p := range peers { 54 | if !p.(cellnet.PeerReadyChecker).IsReady() { 55 | return false 56 | } 57 | } 58 | 59 | return true 60 | } 61 | 62 | // 保证AddPeer在Peer Start之前调用, 否则在连接上时因为没有sd,会导致不汇报服务信息 63 | func (self *multiPeer) AddPeer(sd *discovery.ServiceDesc, p cellnet.Peer) { 64 | 65 | contextSet := p.(cellnet.ContextSet) 66 | contextSet.SetContext("sd", sd) 67 | 68 | self.peersGuard.Lock() 69 | self.peers = append(self.peers, p) 70 | self.peersGuard.Unlock() 71 | } 72 | 73 | func (self *multiPeer) GetPeer(svcid string) cellnet.Peer { 74 | for _, p := range self.peers { 75 | 76 | if getSvcIDByPeer(p) == svcid { 77 | return p 78 | } 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func (self *multiPeer) RemovePeer(svcid string) { 85 | self.peersGuard.Lock() 86 | defer self.peersGuard.Unlock() 87 | for index, p := range self.peers { 88 | 89 | if getSvcIDByPeer(p) == svcid { 90 | self.peers = append(self.peers[:index], self.peers[index+1:]...) 91 | break 92 | } 93 | } 94 | } 95 | 96 | func getSvcIDByPeer(p cellnet.Peer) string { 97 | var sd *discovery.ServiceDesc 98 | if p.(cellnet.ContextSet).FetchContext("sd", &sd) { 99 | return sd.ID 100 | } 101 | 102 | return "" 103 | } 104 | 105 | func newMultiPeer() *multiPeer { 106 | return &multiPeer{} 107 | } 108 | -------------------------------------------------------------------------------- /service/query.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "sort" 6 | ) 7 | 8 | type QueryServiceOp int 9 | 10 | const ( 11 | QueryServiceOp_NextFilter QueryServiceOp = iota 12 | QueryServiceOp_NextDesc 13 | QueryServiceOp_End 14 | ) 15 | 16 | type QueryResult interface{} 17 | 18 | // 返回值含义: 19 | // 1. true等效于QueryServiceOp_NextFilter,转到下一个内层循环 20 | // 2. false等效于QueryServiceOp_NextDesc, 转到下一个外层循环 21 | // 3. QueryServiceOp_End: 终止所有遍历循环 22 | // 4. Filter中将类型转为QueryResult,则在QueryService函数返回 23 | type FilterFunc func(*discovery.ServiceDesc) interface{} 24 | 25 | // 根据给定的查询服务名,将结果经过各种过滤器处理后输出 26 | func QueryService(svcName string, filterList ...FilterFunc) (ret interface{}) { 27 | 28 | return QueryServiceEx(svcName, QueryServiceOption{}, filterList...) 29 | } 30 | 31 | type QueryServiceOption struct { 32 | Sort bool 33 | } 34 | 35 | func QueryServiceEx(svcName string, opt QueryServiceOption, filterList ...FilterFunc) (ret interface{}) { 36 | 37 | descList := discovery.Default.Query(svcName) 38 | 39 | if opt.Sort { 40 | sort.Slice(descList, func(i, j int) bool { 41 | 42 | a := descList[i] 43 | b := descList[j] 44 | 45 | aGroup := a.GetMeta("SvcGroup") 46 | bGroup := b.GetMeta("SvcGroup") 47 | 48 | if aGroup != bGroup { 49 | return aGroup < bGroup 50 | } 51 | 52 | aIndex := a.GetMeta("SvcIndex") 53 | bIndex := b.GetMeta("SvcIndex") 54 | 55 | return aIndex < bIndex 56 | }) 57 | } 58 | 59 | for _, desc := range descList { 60 | 61 | for _, filter := range filterList { 62 | 63 | if filter == nil { 64 | continue 65 | } 66 | 67 | op := filter(desc) 68 | 69 | switch raw := op.(type) { 70 | case QueryServiceOp: 71 | switch raw { 72 | case QueryServiceOp_NextFilter: 73 | case QueryServiceOp_NextDesc: 74 | goto NextDesc 75 | case QueryServiceOp_End: 76 | return 77 | } 78 | case bool: 79 | if !raw { 80 | goto NextDesc 81 | } 82 | case QueryResult: 83 | ret = raw 84 | default: 85 | panic("unknown filter result") 86 | } 87 | } 88 | 89 | NextDesc: 90 | } 91 | 92 | return 93 | } 94 | 95 | // 匹配指定的服务组,服务组空时,匹配所有 96 | func Filter_MatchSvcGroup(svcGroup string) FilterFunc { 97 | 98 | return func(desc *discovery.ServiceDesc) interface{} { 99 | 100 | if svcGroup == "" { 101 | return true 102 | } 103 | 104 | return desc.GetMeta("SvcGroup") == svcGroup 105 | } 106 | } 107 | 108 | // 匹配指定的服务ID 109 | func Filter_MatchSvcID(svcid string) FilterFunc { 110 | 111 | return func(desc *discovery.ServiceDesc) interface{} { 112 | 113 | if desc.ID == svcid { 114 | return QueryResult(desc) 115 | } 116 | 117 | return true 118 | } 119 | } 120 | 121 | // 匹配指定的规则,一般由命令行指定 122 | func Filter_MatchRule(rules []MatchRule) FilterFunc { 123 | 124 | return func(desc *discovery.ServiceDesc) interface{} { 125 | 126 | // 任意规则满足即可 127 | for _, rule := range rules { 128 | 129 | if matchTarget(&rule, desc) { 130 | return true 131 | } 132 | } 133 | 134 | return false 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /service/reg.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | "github.com/davyxu/cellnet" 6 | "github.com/davyxu/cellnet/util" 7 | "strconv" 8 | ) 9 | 10 | type peerListener interface { 11 | Port() int 12 | } 13 | 14 | type ServiceMeta map[string]string 15 | 16 | // 将Acceptor注册到服务发现,IP自动取本地IP 17 | func Register(p cellnet.Peer, options ...interface{}) *discovery.ServiceDesc { 18 | host := util.GetLocalIP() 19 | 20 | property := p.(cellnet.PeerProperty) 21 | 22 | sd := &discovery.ServiceDesc{ 23 | ID: MakeLocalSvcID(property.Name()), 24 | Name: property.Name(), 25 | Host: host, 26 | Port: p.(peerListener).Port(), 27 | } 28 | 29 | sd.SetMeta("SvcGroup", GetSvcGroup()) 30 | sd.SetMeta("SvcIndex", strconv.Itoa(GetSvcIndex())) 31 | 32 | for _, opt := range options { 33 | 34 | switch optValue := opt.(type) { 35 | case ServiceMeta: 36 | for metaKey, metaValue := range optValue { 37 | sd.SetMeta(metaKey, metaValue) 38 | } 39 | } 40 | } 41 | 42 | if GetWANIP() != "" { 43 | sd.SetMeta("WANAddress", util.JoinAddress(GetWANIP(), sd.Port)) 44 | } 45 | 46 | log.SetColor("green").Debugf("service '%s' listen at port: %d", sd.ID, sd.Port) 47 | 48 | p.(cellnet.ContextSet).SetContext("sd", sd) 49 | 50 | // 有同名的要先解除注册,再注册,防止watch不触发 51 | discovery.Default.Deregister(sd.ID) 52 | err := discovery.Default.Register(sd) 53 | if err != nil { 54 | log.Errorf("service register failed, %s %s", sd.String(), err.Error()) 55 | } 56 | 57 | return sd 58 | } 59 | 60 | // 解除peer注册 61 | func Unregister(p cellnet.Peer) { 62 | property := p.(cellnet.PeerProperty) 63 | discovery.Default.Deregister(MakeLocalSvcID(property.Name())) 64 | } 65 | -------------------------------------------------------------------------------- /service/remotesvc.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellnet" 5 | "sync" 6 | ) 7 | 8 | type RemoteServiceContext struct { 9 | Name string 10 | SvcID string 11 | } 12 | 13 | type NotifyFunc func(ctx *RemoteServiceContext, ses cellnet.Session) 14 | 15 | var ( 16 | connBySvcID = map[string]cellnet.Session{} 17 | connBySvcNameGuard sync.RWMutex 18 | removeNotify NotifyFunc 19 | ) 20 | 21 | func AddRemoteService(ses cellnet.Session, svcid, name string) { 22 | 23 | connBySvcNameGuard.Lock() 24 | ses.(cellnet.ContextSet).SetContext("ctx", &RemoteServiceContext{Name: name, SvcID: svcid}) 25 | connBySvcID[svcid] = ses 26 | connBySvcNameGuard.Unlock() 27 | 28 | log.SetColor("green").Infof("remote service added: '%s' sid: %d", svcid, ses.ID()) 29 | } 30 | 31 | func RemoveRemoteService(ses cellnet.Session) { 32 | 33 | if ses == nil { 34 | return 35 | } 36 | 37 | ctx := SessionToContext(ses) 38 | if ctx != nil { 39 | 40 | if removeNotify != nil { 41 | removeNotify(ctx, ses) 42 | } 43 | 44 | connBySvcNameGuard.Lock() 45 | delete(connBySvcID, ctx.SvcID) 46 | connBySvcNameGuard.Unlock() 47 | 48 | log.SetColor("yellow").Infof("remote service removed '%s' sid: %d", ctx.SvcID, ses.ID()) 49 | } else { 50 | log.SetColor("yellow").Infof("remote service removed sid: %d, context lost", ses.ID()) 51 | } 52 | } 53 | 54 | // 设置服务的通知 55 | func SetRemoteServiceNotify(mode string, callback NotifyFunc) { 56 | 57 | switch mode { 58 | case "remove": 59 | removeNotify = callback 60 | default: 61 | panic("unknown notify mode") 62 | } 63 | } 64 | 65 | // 取得其他服务器的会话对应的上下文 66 | func SessionToContext(ses cellnet.Session) *RemoteServiceContext { 67 | if ses == nil { 68 | return nil 69 | } 70 | 71 | if raw, ok := ses.(cellnet.ContextSet).GetContext("ctx"); ok { 72 | return raw.(*RemoteServiceContext) 73 | } 74 | 75 | return nil 76 | } 77 | 78 | // 根据svcid获取远程服务的会话 79 | func GetRemoteService(svcid string) cellnet.Session { 80 | connBySvcNameGuard.RLock() 81 | defer connBySvcNameGuard.RUnlock() 82 | 83 | if ses, ok := connBySvcID[svcid]; ok { 84 | 85 | return ses 86 | } 87 | 88 | return nil 89 | } 90 | 91 | // 遍历远程服务(已经连接到本进程) 92 | func VisitRemoteService(callback func(ses cellnet.Session, ctx *RemoteServiceContext) bool) { 93 | connBySvcNameGuard.RLock() 94 | 95 | for _, ses := range connBySvcID { 96 | 97 | if !callback(ses, SessionToContext(ses)) { 98 | break 99 | } 100 | } 101 | 102 | connBySvcNameGuard.RUnlock() 103 | } 104 | -------------------------------------------------------------------------------- /service/safevalue_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/davyxu/cellmesh/discovery" 5 | memsd "github.com/davyxu/cellmesh/discovery/memsd/api" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestSafeGetValue(t *testing.T) { 11 | 12 | var origin []byte 13 | for i := 0; i < 12; i++ { 14 | //origin = append(origin, byte(rand.Int31n(127))) 15 | origin = append(origin, byte(i)) 16 | } 17 | 18 | sdConfig := memsd.DefaultConfig() 19 | sdConfig.Address = GetDiscoveryAddr() 20 | discovery.Default = memsd.NewDiscovery(sdConfig) 21 | 22 | err := discovery.SafeSetValue(discovery.Default, "config/test", origin, true) 23 | if err != nil { 24 | t.Error(err) 25 | t.FailNow() 26 | } 27 | 28 | var outData []byte 29 | err = discovery.SafeGetValue(discovery.Default, "config/test", &outData, true) 30 | if err != nil { 31 | t.Error(err) 32 | t.FailNow() 33 | } 34 | 35 | if !reflect.DeepEqual(origin, outData) { 36 | t.FailNow() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /service/svcid.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // 全局唯一的svcid 格式: svcName#svcIndex@svcGroup 10 | 11 | // 构造服务ID 12 | func MakeSvcID(svcName string, svcIndex int, svcGroup string) string { 13 | return fmt.Sprintf("%s#%d@%s", svcName, svcIndex, svcGroup) 14 | } 15 | 16 | // 构造指定服务的ID 17 | func MakeLocalSvcID(svcName string) string { 18 | return MakeSvcID(svcName, *flagSvcIndex, *flagSvcGroup) 19 | } 20 | 21 | // 获得本进程的服务id 22 | func GetLocalSvcID() string { 23 | return MakeLocalSvcID(GetProcName()) 24 | } 25 | 26 | func ParseSvcID(svcid string) (svcName string, svcIndex int, svcGroup string, err error) { 27 | 28 | var sharpPos, atPos = -1, -1 29 | 30 | for pos, c := range svcid { 31 | switch c { 32 | case '#': 33 | sharpPos = pos 34 | svcName = svcid[:sharpPos] 35 | case '@': 36 | atPos = pos 37 | svcGroup = svcid[atPos+1:] 38 | 39 | if sharpPos == -1 { 40 | break 41 | } 42 | 43 | var n int64 44 | n, err = strconv.ParseInt(svcid[sharpPos+1:atPos], 10, 32) 45 | if err != nil { 46 | break 47 | } 48 | svcIndex = int(n) 49 | } 50 | } 51 | 52 | if sharpPos == -1 { 53 | err = errors.New("missing '#' in svcid") 54 | } 55 | 56 | if atPos == -1 { 57 | err = errors.New("missing '@' in svcid") 58 | } 59 | 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /service/svcid_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "testing" 4 | 5 | func TestParseSvcID(t *testing.T) { 6 | 7 | t.Log(ParseSvcID("game#100@xubo1")) 8 | } 9 | -------------------------------------------------------------------------------- /tool/protogen/gengo/func.go: -------------------------------------------------------------------------------- 1 | package gengo 2 | 3 | import ( 4 | "github.com/ahmetb/go-linq" 5 | "github.com/davyxu/protoplus/gen" 6 | "github.com/davyxu/protoplus/model" 7 | "sort" 8 | "strings" 9 | "text/template" 10 | ) 11 | 12 | var FuncMap = template.FuncMap{} 13 | 14 | func init() { 15 | FuncMap["StructCodec"] = func(d *model.Descriptor) string { 16 | return d.TagValueString("Codec") 17 | } 18 | 19 | FuncMap["StructService"] = func(d *model.Descriptor) string { 20 | return d.TagValueString("Service") 21 | } 22 | 23 | FuncMap["ProtoImportList"] = func(ctx *gen.Context) (ret []string) { 24 | 25 | linq.From(ctx.Structs()).WhereT(func(d *model.Descriptor) bool { 26 | return d.TagValueString("Codec") != "" 27 | }).SelectT(func(d *model.Descriptor) string { 28 | return d.TagValueString("Codec") 29 | }).DistinctByT(func(d string) string { 30 | return d 31 | }).ToSlice(&ret) 32 | 33 | return 34 | } 35 | 36 | FuncMap["ServiceGroup"] = func(ctx *gen.Context) (ret []linq.Group) { 37 | 38 | type RecvPair struct { 39 | Recv string 40 | d *model.Descriptor 41 | } 42 | 43 | var pairs []*RecvPair 44 | 45 | for _, d := range ctx.Structs() { 46 | 47 | recvList := d.TagValueString("Service") 48 | 49 | if recvList == "" { 50 | continue 51 | } 52 | 53 | for _, recv := range strings.Split(recvList, "|") { 54 | pairs = append(pairs, &RecvPair{recv, d}) 55 | } 56 | } 57 | 58 | linq.From(pairs).GroupByT(func(pair *RecvPair) interface{} { 59 | return pair.Recv 60 | }, func(pair *RecvPair) interface{} { 61 | return pair.d 62 | }).SortT(func(a, b linq.Group) bool { 63 | 64 | asvc := a.Key.(string) 65 | bsvc := b.Key.(string) 66 | 67 | return asvc < bsvc 68 | }).ToSlice(&ret) 69 | 70 | for _, g := range ret { 71 | sort.Slice(g.Group, func(i, j int) bool { 72 | a := g.Group[i].(*model.Descriptor) 73 | b := g.Group[j].(*model.Descriptor) 74 | 75 | return a.Name < b.Name 76 | }) 77 | } 78 | 79 | return 80 | } 81 | 82 | FuncMap["HasJsonCodec"] = func(ctx *gen.Context) bool { 83 | 84 | for _, d := range ctx.Structs() { 85 | if d.TagValueString("Codec") == "json" { 86 | return true 87 | } 88 | } 89 | 90 | return true 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tool/protogen/gengo/gen.go: -------------------------------------------------------------------------------- 1 | package gengo 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/protoplus/codegen" 6 | "github.com/davyxu/protoplus/gen" 7 | ) 8 | 9 | func GenGo(ctx *gen.Context) error { 10 | 11 | gen := codegen.NewCodeGen("cmgo"). 12 | RegisterTemplateFunc(codegen.UsefulFunc). 13 | RegisterTemplateFunc(FuncMap). 14 | ParseTemplate(goCodeTemplate, ctx). 15 | FormatGoCode() 16 | 17 | if gen.Error() != nil { 18 | fmt.Println(string(gen.Code())) 19 | return gen.Error() 20 | } 21 | 22 | return gen.WriteOutputFile(ctx.OutputFileName).Error() 23 | } 24 | -------------------------------------------------------------------------------- /tool/protogen/gengo/text.go: -------------------------------------------------------------------------------- 1 | package gengo 2 | 3 | // 报错行号+3 4 | const goCodeTemplate = `// Auto generated by github.com/davyxu/cellmesh/protogen 5 | // DO NOT EDIT! 6 | 7 | package {{.PackageName}} 8 | 9 | import ( 10 | "github.com/davyxu/cellnet" 11 | "github.com/davyxu/cellnet/codec"{{range ProtoImportList $}} 12 | _ "github.com/davyxu/cellnet/codec/{{.}}"{{end}} 13 | "reflect" 14 | ) 15 | 16 | // Make compiler import happy 17 | var( 18 | _ cellnet.Event 19 | _ codec.CodecRecycler 20 | _ reflect.Type 21 | ) 22 | 23 | {{range ServiceGroup $}} 24 | // {{$svcName := .Key}}{{$svcName}} 25 | var ( {{range .Group}} 26 | Handle_{{ExportSymbolName $svcName}}_{{.Name}} = func(ev cellnet.Event){ panic("'{{.Name}}' not handled") } {{end}} 27 | Handle_{{ExportSymbolName $svcName}}_Default func(ev cellnet.Event) 28 | ) 29 | {{end}} 30 | 31 | func GetMessageHandler(svcName string) cellnet.EventCallback { 32 | 33 | switch svcName { {{range ServiceGroup $}} 34 | case "{{$svcName := .Key}}{{$svcName}}": 35 | return func(ev cellnet.Event) { 36 | switch ev.Message().(type) { {{range .Group}} 37 | case *{{.Name}}: 38 | Handle_{{ExportSymbolName $svcName}}_{{.Name}}(ev) {{end}} 39 | default: 40 | if Handle_{{ExportSymbolName $svcName}}_Default != nil { 41 | Handle_{{ExportSymbolName $svcName}}_Default(ev) 42 | } 43 | } 44 | } {{end}} 45 | } 46 | 47 | return nil 48 | } 49 | 50 | 51 | func init() { 52 | {{range .Structs}} {{ if IsMessage . }} 53 | cellnet.RegisterMessageMeta(&cellnet.MessageMeta{ 54 | Codec: codec.MustGetCodec("{{StructCodec .}}"), 55 | Type: reflect.TypeOf((*{{.Name}})(nil)).Elem(), 56 | ID: {{StructMsgID .}}, 57 | }) {{end}} {{end}} 58 | } 59 | 60 | ` 61 | -------------------------------------------------------------------------------- /tool/protogen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/davyxu/cellmesh/tool/protogen/gengo" 7 | "github.com/davyxu/protoplus/gen" 8 | "github.com/davyxu/protoplus/model" 9 | _ "github.com/davyxu/protoplus/msgidutil" 10 | "github.com/davyxu/protoplus/util" 11 | "os" 12 | ) 13 | 14 | var ( 15 | flagPackage = flag.String("package", "", "package name in source files") 16 | flagGoOut = flag.String("cmgo_out", "", "cellmesh binding for golang") 17 | ) 18 | 19 | func main() { 20 | 21 | flag.Parse() 22 | 23 | var err error 24 | var ctx gen.Context 25 | ctx.DescriptorSet = new(model.DescriptorSet) 26 | ctx.DescriptorSet.PackageName = *flagPackage 27 | ctx.PackageName = *flagPackage 28 | 29 | err = util.ParseFileList(ctx.DescriptorSet) 30 | 31 | if err != nil { 32 | goto OnError 33 | } 34 | 35 | ctx.OutputFileName = *flagGoOut 36 | if ctx.OutputFileName != "" { 37 | err = gengo.GenGo(&ctx) 38 | if err != nil { 39 | goto OnError 40 | } 41 | } 42 | 43 | return 44 | 45 | OnError: 46 | fmt.Println(err) 47 | os.Exit(1) 48 | } 49 | -------------------------------------------------------------------------------- /util/flagfile.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import ( 4 | "flag" 5 | "github.com/davyxu/cellnet/util" 6 | ) 7 | 8 | func ApplyFlagFromFile(fs *flag.FlagSet, filename string) error { 9 | 10 | return util.ReadKVFile(filename, func(key, value string) bool { 11 | 12 | // 设置flagm 13 | fg := fs.Lookup(key) 14 | if fg != nil { 15 | log.Infof("ApplyFlagFromFile: %s=%s", key, value) 16 | fg.Value.Set(value) 17 | } else { 18 | log.Errorf("ApplyFlagFromFile: flag not found, %s", key) 19 | } 20 | 21 | return true 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /util/log.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | var log = golog.New("meshutil") 10 | 11 | func sizeLevel(sizeStr, levelStr string, multi int, sizePtr *int, errPtr *error) bool { 12 | if strings.HasSuffix(sizeStr, levelStr) { 13 | size, err := strconv.Atoi(strings.TrimSuffix(sizeStr, levelStr)) 14 | 15 | if err != nil { 16 | *errPtr = err 17 | return true 18 | } 19 | 20 | *sizePtr = size * multi 21 | return true 22 | } 23 | 24 | return false 25 | } 26 | 27 | func ParseSizeString(sizeStr string) (size int, err error) { 28 | 29 | sizeStr = strings.TrimSpace(sizeStr) 30 | sizeStr = strings.ToUpper(sizeStr) 31 | 32 | if sizeLevel(sizeStr, "M", 1024*1024, &size, &err) { 33 | return 34 | } 35 | 36 | if sizeLevel(sizeStr, "K", 1024, &size, &err) { 37 | return 38 | } 39 | 40 | if sizeLevel(sizeStr, "G", 1024*1024*1024, &size, &err) { 41 | return 42 | } 43 | 44 | return strconv.Atoi(sizeStr) 45 | } 46 | -------------------------------------------------------------------------------- /util/log_test.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import "testing" 4 | 5 | func TestSize(t *testing.T) { 6 | 7 | t.Log(ParseSizeString("10k")) 8 | t.Log(ParseSizeString("10M")) 9 | t.Log(ParseSizeString("10g")) 10 | 11 | t.Log(ParseSizeString("10")) 12 | 13 | } 14 | -------------------------------------------------------------------------------- /util/uuid64.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // numF=半字节 11 | func numFToMask(numF uint) uint64 { 12 | switch numF { 13 | case 1: 14 | return 0xF 15 | case 2: 16 | return 0xFF 17 | case 3: 18 | return 0xFFF 19 | case 4: 20 | return 0xFFFF 21 | case 5: 22 | return 0xFFFFF 23 | case 6: 24 | return 0xFFFFFF 25 | case 7: 26 | return 0xFFFFFFF 27 | case 8: 28 | return 0xFFFFFFFF 29 | case 9: 30 | return 0xFFFFFFFFF 31 | case 10: 32 | return 0xFFFFFFFFFF 33 | case 11: 34 | return 0xFFFFFFFFFFF 35 | case 12: 36 | return 0xFFFFFFFFFFFF 37 | case 13: 38 | return 0xFFFFFFFFFFFFF 39 | case 14: 40 | return 0xFFFFFFFFFFFFFF 41 | case 15: 42 | return 0xFFFFFFFFFFFFFFF 43 | case 16: 44 | return 0xFFFFFFFFFFFFFFFF 45 | default: 46 | panic("numF shound in range 1~16") 47 | } 48 | } 49 | 50 | type UUID64Component struct { 51 | ValueSrc func() uint64 // 数值来源 52 | NumF uint // 占用多少个F 1个F=0.5个字节=4位 53 | } 54 | 55 | type UUID64Generator struct { 56 | seqGen uint64 57 | comSet []*UUID64Component 58 | genGuard sync.Mutex 59 | } 60 | 61 | const ( 62 | MaxNumFInt64 = 16 63 | ) 64 | 65 | func (self *UUID64Generator) AddComponent(com *UUID64Component) { 66 | 67 | // 检查范围 68 | numFToMask(com.NumF) 69 | 70 | self.comSet = append(self.comSet, com) 71 | 72 | // 检查总组件超过位数 73 | if self.UsedNumF() > MaxNumFInt64 { 74 | panic("total bit over int64(8bit) range") 75 | } 76 | } 77 | 78 | func (self *UUID64Generator) UsedNumF() (ret uint) { 79 | 80 | for _, com := range self.comSet { 81 | ret += com.NumF 82 | } 83 | 84 | return 85 | } 86 | 87 | func (self *UUID64Generator) LeftNumF() (ret uint) { 88 | return MaxNumFInt64 - self.UsedNumF() 89 | } 90 | 91 | // 序列号组件 92 | func (self *UUID64Generator) AddSeqComponent(numF uint, init uint64) { 93 | 94 | if init&numFToMask(numF) != init { 95 | panic(fmt.Sprintf("const component out of range, expect 0~%d, got, %d", numFRange(numF), init)) 96 | } 97 | 98 | self.seqGen = init 99 | self.AddComponent(&UUID64Component{ 100 | ValueSrc: func() uint64 { 101 | self.seqGen++ 102 | return self.seqGen 103 | }, 104 | 105 | NumF: numF, 106 | }) 107 | 108 | } 109 | 110 | func numFRange(numF uint) uint { 111 | 112 | return uint(math.Pow(16, float64(numF))) - 1 113 | } 114 | 115 | // 固定值组件 116 | func (self *UUID64Generator) AddConstComponent(numF uint, constNumber uint64) { 117 | 118 | uconst := uint64(constNumber) 119 | 120 | if uconst&numFToMask(numF) != constNumber { 121 | panic(fmt.Sprintf("const component out of range, expect 0~%d, got, %d", numFRange(numF), constNumber)) 122 | } 123 | 124 | self.AddComponent(&UUID64Component{ 125 | ValueSrc: func() uint64 { 126 | return uconst 127 | }, 128 | 129 | NumF: numF, 130 | }) 131 | 132 | } 133 | 134 | const timeStartPoint = 946656000 // 这里设置参考点为2000/1/1 0:0:0,延迟出现2039年Unix时间戳溢出问题 135 | 136 | // 添加时间组件 137 | func (self *UUID64Generator) AddTimeComponent(numF uint) { 138 | self.AddComponent(&UUID64Component{ 139 | ValueSrc: func() uint64 { 140 | return uint64(time.Now().Unix() - timeStartPoint) 141 | }, 142 | 143 | NumF: numF, 144 | }) 145 | 146 | } 147 | 148 | // 按给定的组件规则生成一个UUID 149 | func (self *UUID64Generator) Generate() (ret uint64) { 150 | 151 | self.genGuard.Lock() 152 | var offset uint 153 | for i := len(self.comSet) - 1; i >= 0; i-- { 154 | g := self.comSet[i] 155 | mask := numFToMask(g.NumF) 156 | part := (g.ValueSrc() & mask) << offset 157 | ret |= part 158 | offset += g.NumF * 4 159 | } 160 | 161 | self.genGuard.Unlock() 162 | 163 | return 164 | } 165 | 166 | func NewUUID64Generator() *UUID64Generator { 167 | 168 | return &UUID64Generator{} 169 | } 170 | -------------------------------------------------------------------------------- /util/uuid64_test.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import "testing" 4 | 5 | func TestUUIDGenerator_Generate(t *testing.T) { 6 | gen := NewUUID64Generator() 7 | gen.AddSeqComponent(3, 0) 8 | gen.AddTimeComponent(8) 9 | gen.AddComponent(&UUID64Component{func() uint64 { return 1 }, 2}) 10 | gen.AddComponent(&UUID64Component{func() uint64 { return 2 }, 3}) 11 | t.Logf("%x", gen.Generate()) 12 | } 13 | 14 | func TestTimeSeqGen(t *testing.T) { 15 | gen := NewUUID64Generator() 16 | gen.AddTimeComponent(8) 17 | gen.AddSeqComponent(8, 0) 18 | 19 | t.Logf("%x", gen.Generate()) 20 | } 21 | -------------------------------------------------------------------------------- /util/wilecard.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | // https://siongui.github.io/2017/04/11/go-wildcard-pattern-matching/ 4 | func stringToRuneSlice(s string) []rune { 5 | var r []rune 6 | for _, runeValue := range s { 7 | r = append(r, runeValue) 8 | } 9 | return r 10 | } 11 | 12 | func initLookupTable(row, column int) [][]bool { 13 | lookup := make([][]bool, row) 14 | for i := range lookup { 15 | lookup[i] = make([]bool, column) 16 | } 17 | return lookup 18 | } 19 | 20 | // Function that matches input str with given wildcard pattern 21 | func WildcardPatternMatch(str, pattern string) bool { 22 | s := stringToRuneSlice(str) 23 | p := stringToRuneSlice(pattern) 24 | 25 | // empty pattern can only match with empty string 26 | if len(p) == 0 { 27 | return len(s) == 0 28 | } 29 | 30 | // lookup table for storing results of subproblems 31 | // zero value of lookup is false 32 | lookup := initLookupTable(len(s)+1, len(p)+1) 33 | 34 | // empty pattern can match with empty string 35 | lookup[0][0] = true 36 | 37 | // Only '*' can match with empty string 38 | for j := 1; j < len(p)+1; j++ { 39 | if p[j-1] == '*' { 40 | lookup[0][j] = lookup[0][j-1] 41 | } 42 | } 43 | 44 | // fill the table in bottom-up fashion 45 | for i := 1; i < len(s)+1; i++ { 46 | for j := 1; j < len(p)+1; j++ { 47 | if p[j-1] == '*' { 48 | // Two cases if we see a '*' 49 | // a) We ignore ‘*’ character and move 50 | // to next character in the pattern, 51 | // i.e., ‘*’ indicates an empty sequence. 52 | // b) '*' character matches with ith 53 | // character in input 54 | lookup[i][j] = lookup[i][j-1] || lookup[i-1][j] 55 | 56 | } else if p[j-1] == '?' || s[i-1] == p[j-1] { 57 | // Current characters are considered as 58 | // matching in two cases 59 | // (a) current character of pattern is '?' 60 | // (b) characters actually match 61 | lookup[i][j] = lookup[i-1][j-1] 62 | 63 | } else { 64 | // If characters don't match 65 | lookup[i][j] = false 66 | } 67 | } 68 | } 69 | 70 | return lookup[len(s)][len(p)] 71 | } 72 | -------------------------------------------------------------------------------- /util/wilecard_test.go: -------------------------------------------------------------------------------- 1 | package meshutil 2 | 3 | import "testing" 4 | 5 | func TestWildcardPatternMatch(t *testing.T) { 6 | if WildcardPatternMatch("", "") != true { 7 | t.Error("err") 8 | } 9 | if WildcardPatternMatch("a", "") != false { 10 | t.Error("err") 11 | } 12 | if WildcardPatternMatch("baaabab", "*****ba*****ab") != true { 13 | t.Error("err") 14 | } 15 | if WildcardPatternMatch("baaabab", "baaa?ab") != true { 16 | t.Error("err") 17 | } 18 | if WildcardPatternMatch("baaabab", "ba*a?") != true { 19 | t.Error("err") 20 | } 21 | if WildcardPatternMatch("baaabab", "a*ab") != false { 22 | t.Error("err") 23 | } 24 | if WildcardPatternMatch("aa", "a") != false { 25 | t.Error("err") 26 | } 27 | if WildcardPatternMatch("aa", "aa") != true { 28 | t.Error("err") 29 | } 30 | if WildcardPatternMatch("aaa", "aa") != false { 31 | t.Error("err") 32 | } 33 | if WildcardPatternMatch("aa", "*") != true { 34 | t.Error("err") 35 | } 36 | if WildcardPatternMatch("aa", "a*") != true { 37 | t.Error("err") 38 | } 39 | if WildcardPatternMatch("ab", "?*") != true { 40 | t.Error("err") 41 | } 42 | if WildcardPatternMatch("aab", "c*a*b") != false { 43 | t.Error("err") 44 | } 45 | } 46 | --------------------------------------------------------------------------------