├── .gitignore ├── README.md ├── README_CN.md ├── conf └── conf.go ├── core ├── callhelper.go ├── core.go ├── defs.go ├── handler.go ├── module.go ├── module_test.go ├── msg.go ├── service.go ├── topology.go ├── uuid.go └── uuid_test.go ├── encoding ├── binary │ ├── binary_test.go │ ├── decoder.go │ └── encoder.go └── gob │ ├── decoder.go │ ├── encoder.go │ ├── gob.go │ ├── gob_test.go │ ├── gobhelper.go │ ├── type.go │ ├── type_test.go │ └── userType.go ├── helper └── helper.go ├── log ├── log.go ├── log_logger.go └── log_test.go ├── lotou.go ├── master_test.go ├── network └── tcp │ ├── agent.go │ ├── client.go │ ├── client_test.go │ ├── msgmethod.go │ ├── server.go │ ├── server_test.go │ └── subpackage.go ├── slave_test.go ├── timer ├── timer.go ├── timer_test.go └── timerschedule.go ├── topology ├── master.go ├── master_test.go ├── slave.go ├── slave_test.go └── slaveb_test.go └── vector ├── vector.go └── vector_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .pro 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lotou is a lightweight framework for game server implemented in golang. 2 | It utilizes the communication between nodes by abstracting exchangings of messages 3 | so that the multi-nodes and multi-services can be easily achieved. 4 | 5 | 6 | [中文博客](http://blog.csdn.net/sydnash/article/details/66033983) 7 | ------------------- 8 | # lotou 9 | Lotou is inspired by the framework SKYNET written by cloudwu. 10 | Within this framework, all services communicates each other by messaging. 11 | Functions supplied by services are not exposed by API calling. 12 | In current version, module core is responsible for routing messages, 13 | and every service has its own message chan for receiving and sending. 14 | 15 | # features 16 | 17 | 1. message based on communication between service(idea from share memory through communication in golang) 18 | 2. multicore support 19 | 3. cluster support 20 | 4. extremely simple user interface 21 | 22 | # main functional package 23 | 24 | ## core 25 | core supplies the communication between services. 26 | All services are registered to `core`, and send message to others by core.send. 27 | 28 | ## binary 29 | 30 | `binary` encodes data into binary stream for communication between servers and clients. 31 | Usage of this encoding convention is somewhat like the usage of json marshalling. 32 | 33 | gob(not gob in golang's own lib) 34 | Gob encoding aims to sustain nodes' communication. 35 | Communication of services can be trans-nodes, so the encoding messages into binary stream is necessary. 36 | Message switching within nodes do not need to be encoded for better performance. 37 | 38 | All primitive types in golang can be encoded. 39 | Any combination of parameters is easily supported 40 | as long as the sender and the receiver complies with the same signature 41 | 42 | self-defined struct can also be supported, but the struct must be registered in advanced. 43 | If different nodes need to use the same struct, the registration of those structs must 44 | be registered in a fixed and unique sequence. 45 | 46 | At present, encoding performances for slice and map are not so good. 47 | In order to encode type of `[]interface{}`, each elements of which are encoded separately. 48 | To be more specific, type info for each elements are all marshalled. 49 | So if there are elements of one certain same type, type info are marshalled redundantly. 50 | 51 | Marshalling of gob(Lotou) is achieved by reflect. 52 | 53 | ## log 54 | 55 | Module of log is for printing debug and error information. 56 | Currently, log is Synchronized mode, if we have too many log which needs to wirte to file, it may block the main logic. 57 | (it may be changed to asynchronized mode some day) 58 | 59 | ## network 60 | network implements the routes between nodes in TCP. 61 | Roles of `server` and `client` work as what they are called. 62 | Heartbeat is not implemented yet. 63 | `client` establishes connection to its `server` at the first time of sending message. 64 | 65 | ## topology 66 | Multi-node servers are based on topoloy. 67 | There are two types of nodes in Lotou 68 | and different types of nodes are clusted into the same network. 69 | Within a certain network there is only one master node and serveral slave nodes. 70 | Services can be run in any type of node and there are two types of services: 71 | Local service and global service. 72 | Local service are ones within the same node known to each other by name. 73 | Global service are ones run by different nodes 74 | and they are exposed to all services of all nodes by being registered to master. 75 | 76 | 77 | # contact us 78 | 79 | If you are interested in developing Lotou, feel free to contact me via 80 | 81 | QQ 157621271 82 | 83 | Wechat ID daijun_1234. 84 | 85 | QQ group is also available 362112175. (Fill verification message with `Lotou`) 86 | 87 | twitter @sydnash1 88 | 89 | Advices and geeks are always welcomed. 90 | 91 | # next 92 | 93 | use `etcd` as service discover. 94 | 95 | use `nats` as node message diliver. 96 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # lotou 2 | 3 | 4 | 5 | lotou是一个基于golang的轻量级游戏服务器框架,主要对游戏内部的消息传递进行了封装,方便多服务、多节点游戏开发。 6 | 一些关于lotou的介绍可以看我的相关博文[lotou介绍](http://blog.csdn.net/sydnash/article/details/66033983) 7 | 8 | 9 | 如果有兴趣,可以联系我 10 | QQ:157621271 11 | wechat:daijun_1234   12 | golang&lotou 交流群号:362112175    欢迎达人进来分享技术 13 | 14 | 申请请写lotou   15 | 16 | a golang game server framework 17 | 18 | I want write it as a game server. 19 | 20 | lotou模仿了一部分skynet的模式,服务分为多个service,service之间通过消息进行通信,不会进行 21 | 直接的函数调用。目前通过core对消息进行转发,每一个service都有一个chan用于接收core转发的消 22 | 息。 23 | 24 | ## encoding 25 | 26 | encoding is used to encode and decode socket message, here provide to encode type: 27 | 1. a binary encode like go's json, but it encode a struct to binary, and decode from binary stream. 28 | 2. a gob encode, it encode each value's type, can auto parse to interface{} of that type. 29 | 30 | ### binary 31 | binary用于进行二进制编码,主要用户客户端、服务器通信。 32 | 使用方式和golang的json有点类似。 33 | [useage](https://github.com/sydnash/lotou/blob/master/encoding/binary/binary_test.go) 34 | 35 | ### gob 36 | gob用于实现服务节点间的通信,由于服务间的通信有可能在本机有可能跨节点, 37 | 本地消息通信不需要进行编码,跨节点通信的时候则需要进行编码。gob会同时将 38 | 数据的类型和值编码到package中,这样就不需要对消息进行注册,不过消息中的 39 | 结构体类型必须先注册到gob中进行,为了保证struct的ID一致,每一个节点都要 40 | 使用相同的顺序注册所有结构体。 41 | 42 | 目前对于slice、map的编码不够高效,为了可以编解码[]interface{}类型的slice, 43 | 目前会对slice每一个元素都对类型进行编码,希望可以根据elem类型进行适当的 44 | 调整。 45 | 46 | 内部使用reflect实现。 47 | [useage](https://github.com/sydnash/lotou/blob/master/encoding/gob/type_test.go) 48 | 49 | ## log 50 | 51 | log模块用于打印调试和错误信息,目前使用的是异步打印方式,暂不支持同步打印,这种模式会在程序panic的时候有些关键信息无法打印出来,还在考虑是否换为同步打印模式。 52 | 53 | ##network 54 | 55 | network模块实现了一个基于tcp的server和client,暂时还未加入心跳机制,client会在需要发送数据的时候自动建立tcp连接。 56 | 57 | ## core 58 | 59 | core提供service之间通信的桥梁,所有的service都会在core进行注册,然后service之间通过core.send进行消息发送。 60 | 61 | ## topology 62 | 63 | topology,用于支持拓扑结构的服务器,一个master和N个slave,master会作为数据交互中心,不同logic主机上的消息会通过 master进行中转。 64 | 目前可以通过slave master模块,使节点可以给本地节点的服务注册全局名字,可以通过全局名字获取服务ID,同时对发送给非 65 | 本地节点的消息进行转发。 66 | -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "reflect" 10 | 11 | "github.com/sydnash/lotou/log" 12 | ) 13 | 14 | var ( 15 | LogFilePath string = "loger" //log's file path 16 | LogFileLevel int = log.LEVEL_MAX //log's file level 17 | LogShellLevel int = log.DEBUG_LEVEL //log's shell level 18 | LogMaxLine int = 10000 //log's max line per file 19 | LogBufferSize int = 1000 // 20 | LogHasColor bool = true 21 | CoreIsStandalone bool = false //set system is a standalone or multinode 22 | CoreIsMaster bool = true //set node is master 23 | MasterListenIp string = "127.0.0.1" //master listen ip 24 | MultiNodePort string = "4000" //master listen port 25 | SlaveConnectIp string = "127.0.0.1" //master ip 26 | SlaveWhiteIPList []string = []string{} //slave white ip list 27 | CallTimeOut int = 10000 //global timeout for Call fucntion 28 | ) 29 | 30 | func PrintCurrentConfSetToStd() { 31 | fmt.Printf("LogFilePath = %v\n", LogFilePath) 32 | fmt.Printf("LogFileLevel = %v\n", LogFileLevel) 33 | fmt.Printf("LogShellLevel = %v\n", LogShellLevel) 34 | fmt.Printf("LogMaxLine = %v\n", LogMaxLine) 35 | fmt.Printf("LogBufferSize = %v\n", LogBufferSize) 36 | fmt.Printf("LogHasColor = %v\n", LogHasColor) 37 | fmt.Printf("CoreIsStandalone = %v\n", CoreIsStandalone) 38 | fmt.Printf("CoreIsMaster = %v\n", CoreIsMaster) 39 | fmt.Printf("MasterListenIp = %v\n", MasterListenIp) 40 | fmt.Printf("SlaveConnectIp = %v\n", SlaveConnectIp) 41 | fmt.Printf("MultiNodePort = %v\n", MultiNodePort) 42 | fmt.Printf("CallTimeOut = %v\n", CallTimeOut) 43 | } 44 | 45 | //~ 46 | func assignTo(r map[string]interface{}, target interface{}, name string) { 47 | if t := reflect.TypeOf(target); t.Kind() != reflect.Ptr { 48 | fmt.Println("Kind is", t.Kind()) 49 | panic("Not a proper type") 50 | } 51 | if s, ok := r[name]; ok { 52 | t0 := reflect.TypeOf(s) 53 | t1 := reflect.TypeOf(target).Elem() 54 | // var doOK bool = true 55 | tSet := reflect.ValueOf(target).Elem() 56 | if t0.AssignableTo(t1) { 57 | tSet.Set(reflect.ValueOf(s)) 58 | } else if t1.Kind() == reflect.Int { 59 | if t0.Kind() == reflect.Float64 { 60 | f := reflect.ValueOf(s).Float() 61 | tSet.Set(reflect.ValueOf(int(f))) 62 | } else { 63 | // doOK = false 64 | } 65 | } else { 66 | // doOK = false 67 | } 68 | // if doOK { 69 | // fmt.Printf("Set of <%v> to %v\n", name, s) 70 | // } else { 71 | // fmt.Printf("Cannot assign %v to %v\n", t0.Name(), t1.Name()) 72 | // } 73 | } 74 | } 75 | 76 | const ( 77 | privateConfiguraPath = ".private/svrconf.json" 78 | ) 79 | 80 | //~ If you wish to alter the configuration filepath 81 | //~ Overwrite the configures by configuration file. 82 | func init() { 83 | goPath := os.ExpandEnv("$GOPATH") 84 | if len(goPath) <= 0 { 85 | return 86 | } 87 | fname := path.Join(goPath, privateConfiguraPath) 88 | fin, err := os.Open(fname) 89 | if err != nil { 90 | //The configure file may not exist 91 | return 92 | } 93 | defer fin.Close() 94 | chunk, r_err := ioutil.ReadAll(fin) 95 | if r_err != nil { 96 | return 97 | } 98 | var intfs interface{} 99 | jErr := json.Unmarshal(chunk, &intfs) 100 | if jErr != nil { 101 | return 102 | } 103 | 104 | if mIntfs, ok := intfs.(map[string]interface{}); ok { 105 | assignTo(mIntfs, &LogFilePath, `LogFilePath`) 106 | assignTo(mIntfs, &LogHasColor, `LogHasCoor`) 107 | assignTo(mIntfs, &CoreIsStandalone, `IsStandalone`) 108 | assignTo(mIntfs, &CoreIsMaster, `IsMaster`) 109 | assignTo(mIntfs, &MasterListenIp, `MasterIP`) 110 | assignTo(mIntfs, &SlaveConnectIp, `SlaveIP`) 111 | assignTo(mIntfs, &MultiNodePort, `MasterPort`) 112 | assignTo(mIntfs, &CallTimeOut, `CallTimeout`) 113 | } 114 | } 115 | 116 | func SetMasterMode() { 117 | CoreIsMaster = true 118 | CoreIsStandalone = false 119 | } 120 | 121 | func SetStandaloneMode() { 122 | CoreIsMaster = false 123 | CoreIsStandalone = true 124 | } 125 | 126 | func SetSlaveMode() { 127 | CoreIsMaster = false 128 | CoreIsStandalone = false 129 | } 130 | -------------------------------------------------------------------------------- /core/callhelper.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/sydnash/lotou/helper" 7 | "github.com/sydnash/lotou/log" 8 | "reflect" 9 | ) 10 | 11 | //CallHelper use reflect.Call to invoke a function. 12 | //it's not thread safe 13 | const ( 14 | ReplyFuncPosition = 1 15 | ) 16 | 17 | type callbackDesc struct { 18 | cb reflect.Value 19 | isAutoReply bool 20 | } 21 | type CallHelper struct { 22 | funcMap map[CmdType]*callbackDesc 23 | hostServiceName string //help to locate which callback is not registered. 24 | } 25 | 26 | type ReplyFunc func(data ...interface{}) 27 | 28 | var ( 29 | FuncNotFound = errors.New("func not found.") 30 | ) 31 | 32 | func NewCallHelper(name string) *CallHelper { 33 | return &CallHelper{ 34 | hostServiceName: name, 35 | funcMap: make(map[CmdType]*callbackDesc), 36 | } 37 | } 38 | 39 | //AddFunc add callback with normal function 40 | func (c *CallHelper) AddFunc(cmd CmdType, fun interface{}) { 41 | f := reflect.ValueOf(fun) 42 | helper.PanicWhen(f.Kind() != reflect.Func, "fun must be a function type.") 43 | c.funcMap[cmd] = &callbackDesc{f, true} 44 | } 45 | 46 | //AddMethod add callback with struct's method by method name 47 | //method name muse be exported 48 | func (c *CallHelper) AddMethod(cmd CmdType, v interface{}, methodName string) { 49 | self := reflect.ValueOf(v) 50 | f := self.MethodByName(methodName) 51 | helper.PanicWhen(f.Kind() != reflect.Func, fmt.Sprintf("[CallHelper:AddMethod] cmd{%v} method must be a function type.", cmd)) 52 | c.funcMap[cmd] = &callbackDesc{f, true} 53 | } 54 | 55 | //setIsAutoReply recode special cmd is auto reply after Call is return 56 | func (c *CallHelper) setIsAutoReply(cmd CmdType, isAutoReply bool) { 57 | cb := c.findCallbackDesc(cmd) 58 | cb.isAutoReply = isAutoReply 59 | if !isAutoReply { 60 | t := reflect.New(cb.cb.Type().In(ReplyFuncPosition)) 61 | _ = t.Elem().Interface().(ReplyFunc) 62 | } 63 | } 64 | 65 | func (c *CallHelper) getIsAutoReply(cmd CmdType) bool { 66 | return c.findCallbackDesc(cmd).isAutoReply 67 | } 68 | 69 | func (c *CallHelper) findCallbackDesc(cmd CmdType) *callbackDesc { 70 | cb, ok := c.funcMap[cmd] 71 | if !ok { 72 | if cb, ok = c.funcMap[Cmd_Default]; ok { 73 | //log.Info("func: <%v>:%d is not found in {%v}, use default cmd handler.", cmd, len(cmd), c.hostServiceName) 74 | } else { 75 | log.Fatal("func: <%v>:%d is not found in {%v}", cmd, len(cmd), c.hostServiceName) 76 | } 77 | } 78 | return cb 79 | } 80 | 81 | //Call invoke special function for cmd 82 | func (c *CallHelper) Call(cmd CmdType, src ServiceID, param ...interface{}) []interface{} { 83 | cb := c.findCallbackDesc(cmd) 84 | defer func() { 85 | if err := recover(); err != nil { 86 | log.Fatal("CallHelper.Call err: method: %v %v", cmd, err) 87 | } 88 | }() 89 | 90 | //addition one param for source service id 91 | p := make([]reflect.Value, len(param)+1) 92 | p[0] = reflect.ValueOf(src) //append src service id 93 | HelperFunctionToUseReflectCall(cb.cb, p, 1, param) 94 | 95 | ret := cb.cb.Call(p) 96 | 97 | out := make([]interface{}, len(ret)) 98 | for i, v := range ret { 99 | out[i] = v.Interface() 100 | } 101 | return out 102 | } 103 | 104 | //CallWithReplyFunc invoke special function for cmd with a reply function which is used to reply Call or Request. 105 | func (c *CallHelper) CallWithReplyFunc(cmd CmdType, src ServiceID, replyFunc ReplyFunc, param ...interface{}) { 106 | cb := c.findCallbackDesc(cmd) 107 | //addition two param for source service id and reply function 108 | p := make([]reflect.Value, len(param)+2) 109 | p[0] = reflect.ValueOf(src) 110 | p[1] = reflect.ValueOf(replyFunc) 111 | 112 | HelperFunctionToUseReflectCall(cb.cb, p, 2, param) 113 | defer func() { 114 | if err := recover(); err != nil { 115 | log.Fatal("CallHelper.Call err: method: %v %v", cmd, err) 116 | } 117 | }() 118 | cb.cb.Call(p) 119 | } 120 | -------------------------------------------------------------------------------- /core/core.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/sydnash/lotou/helper" 8 | "github.com/sydnash/lotou/log" 9 | ) 10 | 11 | type ModuleParam struct { 12 | N string 13 | M Module 14 | L int 15 | } 16 | 17 | // StartService starts the given modules with specific names 18 | //`service' will call module's OnInit after registration 19 | // and registers name to master if the name(of service) is a global name (starting without a dot) 20 | // and starts msg loop in an another goroutine 21 | func StartService(m *ModuleParam) ServiceID { 22 | s := newService(m.N, m.L) 23 | s.m = m.M 24 | id := registerService(s) 25 | m.M.setService(s) 26 | d := m.M.getDuration() 27 | if !checkIsLocalName(m.N) { 28 | globalName(id, m.N) 29 | } 30 | s.m.OnModuleStartup(id, m.N) 31 | if d > 0 { 32 | s.runWithLoop(d) 33 | } else { 34 | s.run() 35 | } 36 | return id 37 | } 38 | 39 | //HelperFunctionToUseReflectCall helps to convert realparam like([]interface{}) to reflect.Call's param([]reflect.Value 40 | //and if param is nil, then use reflect.New to create an empty to avoid crash when reflect.Call invokes. 41 | //and genrates more readable error messages if param is not ok. 42 | func HelperFunctionToUseReflectCall(f reflect.Value, callParam []reflect.Value, startNum int, realParam []interface{}) { 43 | n := len(realParam) 44 | lastCallParamIdx := f.Type().NumIn() - 1 45 | isVariadic := f.Type().IsVariadic() 46 | for i := 0; i < n; i++ { 47 | paramIndex := i + startNum 48 | if !isVariadic && paramIndex >= f.Type().NumIn() { 49 | panic(fmt.Sprintf("InvocationCausedPanic(%v): called param count(%v) is len than reciver function's parma count(%v)", f.Type().String(), len(callParam), f.Type().NumIn())) 50 | } 51 | var expectedType reflect.Type 52 | if isVariadic && paramIndex >= lastCallParamIdx { //variadic function's last param is []T 53 | expectedType = f.Type().In(lastCallParamIdx) 54 | expectedType = expectedType.Elem() 55 | } else { 56 | expectedType = f.Type().In(paramIndex) 57 | } 58 | //if param is nil, create a empty reflect.Value 59 | if realParam[i] == nil { 60 | callParam[paramIndex] = reflect.New(expectedType).Elem() 61 | } else { 62 | callParam[paramIndex] = reflect.ValueOf(realParam[i]) 63 | } 64 | actualType := callParam[paramIndex].Type() 65 | if !actualType.AssignableTo(expectedType) { 66 | //panic if param is not assignable to Call 67 | errStr := fmt.Sprintf("InvocationCausedPanic(%v): called with a mismatched parameter type [parameter #%v: expected %v; got %v].", f.Type().String(), paramIndex, expectedType, actualType) 68 | panic(errStr) 69 | } 70 | } 71 | } 72 | 73 | func PrintArgListForFunc(f reflect.Value) { 74 | t := f.Type() 75 | if t.Kind() != reflect.Func { 76 | fmt.Println("Not a func") 77 | return 78 | } 79 | inCount := t.NumIn() 80 | var str string 81 | for i := 0; i < inCount; i++ { 82 | et := t.In(i) 83 | str = str + ":" + et.Name() 84 | } 85 | fmt.Println(str) 86 | } 87 | 88 | //Parse Node Id parse node id from service id 89 | func ParseNodeId(id ServiceID) uint64 { 90 | return id.parseNodeId() 91 | } 92 | 93 | //Send send a message to dst service no src service. 94 | func Send(dst ServiceID, msgType MsgType, encType EncType, cmd CmdType, data ...interface{}) error { 95 | return lowLevelSend(INVALID_SERVICE_ID, dst, msgType, encType, 0, cmd, data...) 96 | } 97 | 98 | //SendCloseToAll simple send a close msg to all service 99 | func SendCloseToAll() { 100 | h.dicMutex.Lock() 101 | defer h.dicMutex.Unlock() 102 | for _, ser := range h.dic { 103 | localSendWithoutMutex(INVALID_SERVICE_ID, ser, MSG_TYPE_CLOSE, MSG_ENC_TYPE_NO, 0, Cmd_None, false) 104 | } 105 | } 106 | 107 | func Exit() { 108 | if isStandalone { 109 | SendCloseToAll() 110 | } else { 111 | route(Cmd_Exit) 112 | } 113 | } 114 | 115 | func ExitNodeByName(nodeName string) { 116 | if isStandalone { 117 | SendCloseToAll() 118 | } else { 119 | route(Cmd_Exit_Node, nodeName) 120 | } 121 | } 122 | 123 | func RefreshSlaveWhiteIPList(ips []string) { 124 | if isStandalone { 125 | } else { 126 | route(Cmd_RefreshSlaveWhiteIPList, ips) 127 | } 128 | } 129 | 130 | //Wait wait on a sync.WaitGroup, until all service is closed. 131 | func Wait() { 132 | exitGroup.Wait() 133 | } 134 | 135 | //CheckIsLocalServiceId heck a given service id is a local service 136 | func CheckIsLocalServiceId(id ServiceID) bool { 137 | return checkIsLocalId(id) 138 | } 139 | 140 | //SafeGo start a groutine, and handle all panic within it. 141 | func SafeGo(f func()) { 142 | go func() { 143 | defer func() { 144 | if err := recover(); err != nil { 145 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 146 | } 147 | return 148 | }() 149 | f() 150 | }() 151 | } 152 | -------------------------------------------------------------------------------- /core/defs.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | const ( 4 | Cmd_None CmdType = "CmdType.Core.None" 5 | Cmd_Forward CmdType = "CmdType.Core.Forward" 6 | Cmd_Distribute CmdType = "CmdType.Core.Distribute" 7 | Cmd_RegisterNode CmdType = "CmdType.Core.RegisterNode" 8 | Cmd_RegisterNodeRet CmdType = "CmdType.Core.RegisterNodeRet" 9 | Cmd_RegisterName CmdType = "CmdType.Core.RegisterName" 10 | Cmd_GetIdByName CmdType = "CmdType.Core.GetIdByName" 11 | Cmd_GetIdByNameRet CmdType = "CmdType.Core.GetIdByNameRet" 12 | Cmd_NameAdd CmdType = "CmdType.Core.NameAdd" 13 | Cmd_NameDeleted CmdType = "CmdType.Core.NameDeleted" 14 | Cmd_Exit CmdType = "CmdType.Core.Exit" 15 | Cmd_Exit_Node CmdType = "CmdType.Core.ExitNode" 16 | Cmd_Default CmdType = "CmdType.Core.Default" 17 | Cmd_RefreshSlaveWhiteIPList CmdType = "CmdType.Core.RefreshSlaveWhiteIPList" 18 | ) 19 | 20 | /* 21 | const ( 22 | MSG_TYPE_NORMAL = iota 23 | MSG_TYPE_REQUEST 24 | MSG_TYPE_RESPOND 25 | MSG_TYPE_TIMEOUT 26 | MSG_TYPE_CALL 27 | MSG_TYPE_RET 28 | MSG_TYPE_CLOSE 29 | MSG_TYPE_SOCKET 30 | MSG_TYPE_ERR 31 | MSG_TYPE_DISTRIBUTE 32 | MSG_TYPE_MAX 33 | )*/ 34 | 35 | const ( 36 | MSG_TYPE_NORMAL MsgType = "MsgType.Normal" 37 | MSG_TYPE_REQUEST MsgType = "MsgType.Request" 38 | MSG_TYPE_RESPOND MsgType = "MsgType.Respond" 39 | MSG_TYPE_TIMEOUT MsgType = "MsgType.TimeOut" 40 | MSG_TYPE_CALL MsgType = "MsgType.Call" 41 | MSG_TYPE_RET MsgType = "MsgType.Ret" 42 | MSG_TYPE_CLOSE MsgType = "MsgType.Close" 43 | MSG_TYPE_SOCKET MsgType = "MsgType.Socket" 44 | MSG_TYPE_ERR MsgType = "MsgType.Error" 45 | MSG_TYPE_DISTRIBUTE MsgType = "MsgType.Distribute" 46 | //MSG_TYPE_MAX="MsgType.Max" 47 | ) 48 | 49 | /* 50 | const ( 51 | MSG_ENC_TYPE_NO = iota 52 | MSG_ENC_TYPE_GO 53 | ) 54 | */ 55 | const ( 56 | MSG_ENC_TYPE_NO EncType = "EncType.No" 57 | MSG_ENC_TYPE_GO EncType = "EncType.LotouGob" 58 | ) 59 | -------------------------------------------------------------------------------- /core/handler.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "github.com/sydnash/lotou/helper" 6 | "github.com/sydnash/lotou/vector" 7 | "sync" 8 | ) 9 | 10 | //definition for node id 11 | //if system is standalone, then it's node id is DEFAULT_NODE_ID 12 | //if system is multi node, master's node id is MASTER_NODE_ID, slave's node is allocation by master service. 13 | const ( 14 | NODE_ID_OFF = 64 - 16 15 | NODE_ID_MASK = 0xFFFF << NODE_ID_OFF 16 | INVALID_SERVICE_ID = NODE_ID_MASK 17 | DEFAULT_NODE_ID = 0XFFFF 18 | MASTER_NODE_ID = 0 19 | INIT_SERVICE_ID ServiceID = 10 20 | ) 21 | 22 | type handleDic map[uint64]*service 23 | 24 | //a storage that stores all local services 25 | type handleStorage struct { 26 | dicMutex sync.Mutex 27 | dic handleDic 28 | nodeId uint64 29 | curId uint64 30 | baseServiceIdCache *vector.Vector 31 | } 32 | 33 | var ( 34 | h *handleStorage 35 | ServiceNotFindError = errors.New("service is not find.") 36 | exitGroup sync.WaitGroup 37 | ) 38 | 39 | func newHandleStorage() *handleStorage { 40 | h := &handleStorage{} 41 | h.nodeId = DEFAULT_NODE_ID 42 | h.dic = make(map[uint64]*service) 43 | h.curId = uint64(INIT_SERVICE_ID) 44 | h.baseServiceIdCache = vector.NewCap(1000) 45 | return h 46 | } 47 | 48 | //checkIsLocalId checks a given service id is a local service's id 49 | //a serviceId's node id is equal to DEFAULT_NODE_ID or nodeId is a local service's id 50 | func checkIsLocalId(id ServiceID) bool { 51 | nodeId := id.parseNodeId() 52 | if nodeId == DEFAULT_NODE_ID { 53 | return true 54 | } 55 | if nodeId == h.nodeId { 56 | return true 57 | } 58 | return false 59 | } 60 | 61 | //checkIsLocalName checks a given name is a local name. 62 | //a name start with '.' or empty is a local name. others a all global name 63 | func checkIsLocalName(name string) bool { 64 | if len(name) == 0 { 65 | return true 66 | } 67 | if name[0] == '.' { 68 | return true 69 | } 70 | return false 71 | } 72 | 73 | func init() { 74 | h = newHandleStorage() 75 | } 76 | 77 | //registerService register a service and allocate a service id to the given service. 78 | func registerService(s *service) ServiceID { 79 | h.dicMutex.Lock() 80 | defer h.dicMutex.Unlock() 81 | var baseServiceId uint64 82 | //if h.baseServiceIdCache.Empty() { 83 | h.curId++ 84 | baseServiceId = h.curId 85 | //} else { 86 | // baseServiceId = h.baseServiceIdCache.Pop().(uint64) 87 | //} 88 | id := h.nodeId< 0, millisecond 89 | //after receiver call Respond, the responseCb will be called 90 | func (s *Skeleton) Request(dst ServiceID, encType EncType, timeout int, responseCb interface{}, cmd CmdType, data ...interface{}) { 91 | s.s.request(dst, encType, timeout, responseCb, cmd, data...) 92 | } 93 | 94 | //Respond used to respond request msg 95 | func (s *Skeleton) Respond(dst ServiceID, encType EncType, rid uint64, data ...interface{}) { 96 | s.s.respond(dst, encType, rid, data...) 97 | } 98 | 99 | //Call send a call msg to dst, and start a timeout function with the conf.CallTimeOut 100 | //after receiver call Ret, it will return 101 | func (s *Skeleton) Call(dst ServiceID, encType EncType, cmd CmdType, data ...interface{}) ([]interface{}, error) { 102 | return s.s.call(dst, encType, cmd, data...) 103 | } 104 | 105 | //CallWithTimeout send a call msg to dst, and start a timeout function with the timeout millisecond 106 | //after receiver call Ret, it will return 107 | func (s *Skeleton) CallWithTimeout(dst ServiceID, encType EncType, timeout int, cmd CmdType, data ...interface{}) ([]interface{}, error) { 108 | return s.s.callWithTimeout(dst, encType, timeout, cmd, data...) 109 | } 110 | 111 | //Schedule schedule a time with given parameter. 112 | func (s *Skeleton) Schedule(interval, repeat int, cb timer.TimerCallback) *timer.Timer { 113 | if s.s == nil { 114 | panic("Schedule must call after OnInit is called(not contain OnInit)") 115 | } 116 | return s.s.schedule(interval, repeat, cb) 117 | } 118 | 119 | //Ret used to ret call msg 120 | func (s *Skeleton) Ret(dst ServiceID, encType EncType, cid uint64, data ...interface{}) { 121 | s.s.ret(dst, encType, cid, data...) 122 | } 123 | 124 | func (s *Skeleton) OnDestroy() { 125 | } 126 | func (s *Skeleton) OnMainLoop(dt int) { 127 | } 128 | func (s *Skeleton) OnNormalMSG(msg *Message) { 129 | s.normalDispatcher.Call(msg.Cmd, msg.Src, msg.Data...) 130 | } 131 | func (s *Skeleton) OnInit() { 132 | } 133 | func (s *Skeleton) OnSocketMSG(msg *Message) { 134 | } 135 | func (s *Skeleton) OnRequestMSG(msg *Message) { 136 | isAutoReply := s.requestDispatcher.getIsAutoReply(msg.Cmd) 137 | if isAutoReply { //if auto reply is set, auto respond when user's callback is return. 138 | ret := s.requestDispatcher.Call(msg.Cmd, msg.Src, msg.Data...) 139 | s.Respond(msg.Src, msg.EncType, msg.Id, ret...) 140 | } else { //pass a closure to the user's callback, when to call depends on the user. 141 | s.requestDispatcher.CallWithReplyFunc(msg.Cmd, msg.Src, func(ret ...interface{}) { 142 | s.Respond(msg.Src, msg.EncType, msg.Id, ret...) 143 | }, msg.Data...) 144 | } 145 | } 146 | func (s *Skeleton) OnCallMSG(msg *Message) { 147 | isAutoReply := s.callDispatcher.getIsAutoReply(msg.Cmd) 148 | if isAutoReply { 149 | ret := s.callDispatcher.Call(msg.Cmd, msg.Src, msg.Data...) 150 | s.Ret(msg.Src, msg.EncType, msg.Id, ret...) 151 | } else { 152 | s.callDispatcher.CallWithReplyFunc(msg.Cmd, msg.Src, func(ret ...interface{}) { 153 | s.Ret(msg.Src, msg.EncType, msg.Id, ret...) 154 | }, msg.Data...) 155 | } 156 | } 157 | 158 | func (s *Skeleton) findCallerByType(msgType MsgType) *CallHelper { 159 | var caller *CallHelper 160 | switch msgType { 161 | case MSG_TYPE_NORMAL: 162 | caller = s.normalDispatcher 163 | case MSG_TYPE_REQUEST: 164 | caller = s.requestDispatcher 165 | case MSG_TYPE_CALL: 166 | caller = s.callDispatcher 167 | default: 168 | panic("not support msgType") 169 | } 170 | return caller 171 | } 172 | 173 | //function's first parameter must ServiceID 174 | //isAutoReply: is auto reply when msgType is request or call. 175 | func (s *Skeleton) RegisterHandlerFunc(msgType MsgType, cmd CmdType, fun interface{}, isAutoReply bool) { 176 | caller := s.findCallerByType(msgType) 177 | caller.AddFunc(cmd, fun) 178 | caller.setIsAutoReply(cmd, isAutoReply) 179 | } 180 | 181 | //method's first parameter must ServiceID 182 | //isAutoReply: is auto reply when msgType is request or call. 183 | func (s *Skeleton) RegisterHandlerMethod(msgType MsgType, cmd CmdType, v interface{}, methodName string, isAutoReply bool) { 184 | caller := s.findCallerByType(msgType) 185 | caller.AddMethod(cmd, v, methodName) 186 | caller.setIsAutoReply(cmd, isAutoReply) 187 | } 188 | 189 | func (s *Skeleton) OnDistributeMSG(msg *Message) { 190 | } 191 | func (s *Skeleton) OnCloseNotify() { 192 | s.SendClose(s.s.getId(), true) 193 | } 194 | -------------------------------------------------------------------------------- /core/module_test.go: -------------------------------------------------------------------------------- 1 | package core_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sydnash/lotou/conf" 6 | "github.com/sydnash/lotou/core" 7 | "github.com/sydnash/lotou/log" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | type Game struct { 13 | *core.Skeleton 14 | Dst core.ServiceID 15 | } 16 | 17 | type XMsg struct { 18 | A int32 19 | B string 20 | C int64 21 | } 22 | 23 | func (g *Game) OnMainLoop(dt int) { 24 | g.Send(g.Dst, core.MSG_TYPE_NORMAL, core.MSG_ENC_TYPE_GO, "testNormal", g.Name, []byte{1, 2, 3, 4, 56}) 25 | g.RawSend(g.Dst, core.MSG_TYPE_NORMAL, "testNormal", g.Name, g.Id) 26 | 27 | t := func(timeout bool, data ...interface{}) { 28 | fmt.Println("request respond ", timeout, data) 29 | } 30 | g.Request(g.Dst, core.MSG_ENC_TYPE_GO, 10, t, "testRequest", "hello") 31 | 32 | fmt.Println(g.Call(g.Dst, core.MSG_ENC_TYPE_GO, "testCall", "hello")) 33 | } 34 | 35 | func (g *Game) OnInit() { 36 | //test for go and no enc 37 | g.RegisterHandlerFunc(core.MSG_TYPE_NORMAL, "testNormal", func(src core.ServiceID, data ...interface{}) { 38 | log.Info("%v, %v", src, data) 39 | }, true) 40 | g.RegisterHandlerFunc(core.MSG_TYPE_REQUEST, "testRequest", func(src core.ServiceID, data ...interface{}) string { 41 | return "world" 42 | }, true) 43 | g.RegisterHandlerFunc(core.MSG_TYPE_CALL, "testCall", func(src core.ServiceID, data ...interface{}) (string, string) { 44 | return "hello", "world" 45 | }, true) 46 | } 47 | 48 | func TestModule(t *testing.T) { 49 | log.Init(conf.LogFilePath, conf.LogFileLevel, conf.LogShellLevel, conf.LogMaxLine, conf.LogBufferSize) 50 | id1 := core.StartService(&core.ModuleParam{ 51 | N: "g1", 52 | M: &Game{Skeleton: core.NewSkeleton(0)}, 53 | L: 0, 54 | }) 55 | core.StartService(&core.ModuleParam{ 56 | N: "g2", 57 | M: &Game{Skeleton: core.NewSkeleton(1000), Dst: id1}, 58 | L: 0, 59 | }) 60 | 61 | ch := make(chan int) 62 | go func() { 63 | time.Sleep(10 * time.Second) 64 | ch <- 1 65 | }() 66 | 67 | <-ch 68 | } 69 | -------------------------------------------------------------------------------- /core/msg.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/sydnash/lotou/encoding/gob" 5 | ) 6 | 7 | type MsgType string 8 | type EncType string 9 | type CmdType string 10 | 11 | //Message is the based struct of msg through all service 12 | //by convention, the first value of Data is a string as the method name 13 | type Message struct { 14 | Src ServiceID 15 | Dst ServiceID 16 | Type MsgType // Used to be int32 17 | EncType EncType 18 | Id uint64 //request id or call id 19 | Cmd CmdType 20 | Data []interface{} 21 | } 22 | 23 | type NodeInfo struct { 24 | Name string 25 | Id ServiceID 26 | } 27 | 28 | func NewMessage(src, dst ServiceID, msgType MsgType, encType EncType, id uint64, cmd CmdType, data ...interface{}) *Message { 29 | switch encType { 30 | case MSG_ENC_TYPE_NO: 31 | case MSG_ENC_TYPE_GO: 32 | data = append([]interface{}(nil), gob.Pack(data...)) 33 | } 34 | msg := &Message{src, dst, msgType, encType, id, cmd, data} 35 | return msg 36 | } 37 | 38 | func init() { 39 | gob.RegisterStructType(Message{}) 40 | gob.RegisterStructType(NodeInfo{}) 41 | } 42 | 43 | func sendNoEnc(src ServiceID, dst ServiceID, msgType MsgType, id uint64, cmd CmdType, data ...interface{}) error { 44 | return lowLevelSend(src, dst, msgType, MSG_ENC_TYPE_NO, id, cmd, data...) 45 | } 46 | 47 | func send(src ServiceID, dst ServiceID, msgType MsgType, encType EncType, id uint64, cmd CmdType, data ...interface{}) error { 48 | return lowLevelSend(src, dst, msgType, encType, id, cmd, data...) 49 | } 50 | 51 | func lowLevelSend(src, dst ServiceID, msgType MsgType, encType EncType, id uint64, cmd CmdType, data ...interface{}) error { 52 | dsts, err := findServiceById(dst) 53 | isLocal := checkIsLocalId(dst) 54 | //a local service is not been found 55 | if err != nil && isLocal { 56 | return err 57 | } 58 | var msg *Message 59 | msg = NewMessage(src, dst, msgType, encType, id, cmd, data...) 60 | if err != nil { 61 | //doesn't find service and dstid is remote id, send a forward msg to router. 62 | route(Cmd_Forward, msg) 63 | return nil 64 | } 65 | dsts.pushMSG(msg) 66 | return nil 67 | } 68 | 69 | //send msg to dst by dst's service name 70 | func sendName(src ServiceID, dst string, msgType MsgType, cmd CmdType, data ...interface{}) error { 71 | dsts, err := findServiceByName(dst) 72 | if err != nil { 73 | return err 74 | } 75 | return lowLevelSend(src, dsts.getId(), msgType, MSG_ENC_TYPE_GO, 0, cmd, data...) 76 | } 77 | 78 | //ForwardLocal forward the message to the specified local sevice. 79 | func ForwardLocal(m *Message) { 80 | dsts, err := findServiceById(ServiceID(m.Dst)) 81 | if err != nil { 82 | return 83 | } 84 | switch m.Type { 85 | case MSG_TYPE_NORMAL, 86 | MSG_TYPE_REQUEST, 87 | MSG_TYPE_RESPOND, 88 | MSG_TYPE_CALL, 89 | MSG_TYPE_DISTRIBUTE: 90 | dsts.pushMSG(m) 91 | case MSG_TYPE_RET: 92 | if m.EncType == MSG_ENC_TYPE_GO { 93 | t, err := gob.Unpack(m.Data[0].([]byte)) 94 | if err != nil { 95 | panic(err) 96 | } 97 | m.Data = t.([]interface{}) 98 | } 99 | cid := m.Id 100 | dsts.dispatchRet(cid, m.Data...) 101 | } 102 | } 103 | 104 | //DistributeMSG distribute the message to all local sevice 105 | func DistributeMSG(src ServiceID, cmd CmdType, data ...interface{}) { 106 | h.dicMutex.Lock() 107 | defer h.dicMutex.Unlock() 108 | for dst, ser := range h.dic { 109 | if ServiceID(dst) != src { 110 | localSendWithoutMutex(src, ser, MSG_TYPE_DISTRIBUTE, MSG_ENC_TYPE_NO, 0, cmd, data...) 111 | } 112 | } 113 | } 114 | 115 | //localSendWithoutMutex send a message to the local service with no mutex. 116 | func localSendWithoutMutex(src ServiceID, dstService *service, msgType MsgType, encType EncType, id uint64, cmd CmdType, data ...interface{}) { 117 | msg := NewMessage(src, dstService.getId(), msgType, encType, id, cmd, data...) 118 | dstService.pushMSG(msg) 119 | } 120 | -------------------------------------------------------------------------------- /core/service.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | "sync" 8 | "time" 9 | 10 | "github.com/sydnash/lotou/conf" 11 | "github.com/sydnash/lotou/encoding/gob" 12 | "github.com/sydnash/lotou/helper" 13 | "github.com/sydnash/lotou/log" 14 | "github.com/sydnash/lotou/timer" 15 | ) 16 | 17 | type ServiceID uint64 18 | 19 | func (id ServiceID) parseNodeId() uint64 { 20 | return (uint64(id) & NODE_ID_MASK) >> NODE_ID_OFF 21 | } 22 | func (id ServiceID) parseBaseId() uint64 { 23 | return uint64(id) & (^uint64(NODE_ID_MASK)) 24 | } 25 | func (id ServiceID) IsValid() bool { 26 | return !(id == INVALID_SERVICE_ID || id == 0) 27 | } 28 | func (id ServiceID) InValid() bool { 29 | return id == INVALID_SERVICE_ID || id == 0 30 | } 31 | 32 | type requestCB struct { 33 | respond reflect.Value 34 | //timeout reflect.Value 35 | } 36 | type service struct { 37 | id ServiceID 38 | name string 39 | msgChan chan *Message 40 | loopTicker *time.Ticker 41 | loopDuration int //unit is Millisecond 42 | m Module 43 | requestId uint64 44 | requestMap map[uint64]requestCB 45 | requestMutex sync.Mutex 46 | callId uint64 47 | callChanMap map[uint64]chan []interface{} 48 | callMutex sync.Mutex 49 | ts *timer.TimerSchedule 50 | } 51 | 52 | var ( 53 | ServiceCallTimeout = errors.New("call time out") 54 | ) 55 | 56 | func newService(name string, len int) *service { 57 | s := &service{name: name} 58 | if len <= 1024 { 59 | len = 1024 60 | } 61 | s.msgChan = make(chan *Message, len) 62 | s.requestId = 0 63 | s.requestMap = make(map[uint64]requestCB) 64 | s.callChanMap = make(map[uint64]chan []interface{}) 65 | return s 66 | } 67 | 68 | func (s *service) setModule(m Module) { 69 | s.m = m 70 | } 71 | 72 | func (s *service) getName() string { 73 | return s.name 74 | } 75 | 76 | func (s *service) setId(id ServiceID) { 77 | s.id = id 78 | } 79 | 80 | func (s *service) getId() ServiceID { 81 | return s.id 82 | } 83 | 84 | func (s *service) pushMSG(m *Message) { 85 | select { 86 | case s.msgChan <- m: 87 | default: 88 | if s.msgChan == nil { 89 | log.Warn("msg chan is closed.<%s>", s.getName()) 90 | } else { 91 | panic(fmt.Sprintf("service is full.<%s>", s.getName())) 92 | } 93 | } 94 | } 95 | 96 | func (s *service) destroy() { 97 | unregisterService(s) 98 | msgChan := s.msgChan 99 | s.msgChan = nil 100 | close(msgChan) 101 | if s.loopTicker != nil { 102 | s.loopTicker.Stop() 103 | } 104 | } 105 | 106 | func (s *service) dispatchMSG(msg *Message) bool { 107 | if msg.EncType == MSG_ENC_TYPE_GO { 108 | t, err := gob.Unpack(msg.Data[0].([]byte)) 109 | if err != nil { 110 | panic(err) 111 | } 112 | msg.Data = t.([]interface{}) 113 | } 114 | switch msg.Type { 115 | case MSG_TYPE_NORMAL: 116 | s.m.OnNormalMSG(msg) 117 | case MSG_TYPE_CLOSE: 118 | if msg.Data[0].(bool) { 119 | return true 120 | } 121 | s.m.OnCloseNotify() 122 | case MSG_TYPE_SOCKET: 123 | s.m.OnSocketMSG(msg) 124 | case MSG_TYPE_REQUEST: 125 | s.dispatchRequest(msg) 126 | case MSG_TYPE_RESPOND: 127 | s.dispatchRespond(msg) 128 | case MSG_TYPE_CALL: 129 | s.dispatchCall(msg) 130 | case MSG_TYPE_DISTRIBUTE: 131 | s.m.OnDistributeMSG(msg) 132 | case MSG_TYPE_TIMEOUT: 133 | s.dispatchTimeout(msg) 134 | } 135 | return false 136 | } 137 | 138 | //select on msgChan only 139 | func (s *service) loopSelect() (ret bool) { 140 | ret = true 141 | defer func() { 142 | if err := recover(); err != nil { 143 | log.Error("error in service<%v>", s.getName()) 144 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 145 | } 146 | }() 147 | select { 148 | case msg, ok := <-s.msgChan: 149 | if !ok { 150 | return false 151 | } 152 | isClose := s.dispatchMSG(msg) 153 | if isClose { 154 | return false 155 | } 156 | } 157 | return true 158 | } 159 | 160 | func (s *service) loop() { 161 | s.m.OnInit() 162 | for { 163 | if !s.loopSelect() { 164 | break 165 | } 166 | } 167 | s.m.OnDestroy() 168 | s.destroy() 169 | } 170 | 171 | //select on msgChan and a loop ticker 172 | func (s *service) loopWithLoopSelect() (ret bool) { 173 | ret = true 174 | defer func() { 175 | if err := recover(); err != nil { 176 | log.Error("error in service<%v>", s.getName()) 177 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 178 | } 179 | }() 180 | select { 181 | case msg, ok := <-s.msgChan: 182 | if !ok { 183 | return false 184 | } 185 | isClose := s.dispatchMSG(msg) 186 | if isClose { 187 | return false 188 | } 189 | case <-s.loopTicker.C: 190 | s.ts.Update(s.loopDuration) 191 | s.m.OnMainLoop(s.loopDuration) 192 | } 193 | return true 194 | } 195 | 196 | func (s *service) loopWithLoop() { 197 | s.m.OnInit() 198 | for { 199 | if !s.loopWithLoopSelect() { 200 | break 201 | } 202 | } 203 | s.loopTicker.Stop() 204 | s.m.OnDestroy() 205 | s.destroy() 206 | } 207 | 208 | //start a goroutinue with no ticker for main loop 209 | func (s *service) run() { 210 | SafeGo(s.loop) 211 | } 212 | 213 | //start a goroutinue with ticker for main loop 214 | func (s *service) runWithLoop(d int) { 215 | s.loopDuration = d 216 | s.loopTicker = time.NewTicker(time.Duration(d) * time.Millisecond) 217 | s.ts = timer.NewTS() 218 | SafeGo(s.loopWithLoop) 219 | } 220 | 221 | //respndCb is a function like: func(isok bool, ...interface{}) the first param must be a bool 222 | func (s *service) request(dst ServiceID, encType EncType, timeout int, respondCb interface{}, cmd CmdType, data ...interface{}) { 223 | s.requestMutex.Lock() 224 | id := s.requestId 225 | s.requestId++ 226 | cbp := requestCB{reflect.ValueOf(respondCb)} 227 | s.requestMap[id] = cbp 228 | s.requestMutex.Unlock() 229 | helper.PanicWhen(cbp.respond.Kind() != reflect.Func, "respond cb must function.") 230 | 231 | lowLevelSend(s.getId(), dst, MSG_TYPE_REQUEST, encType, id, cmd, data...) 232 | 233 | if timeout > 0 { 234 | time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() { 235 | s.requestMutex.Lock() 236 | _, ok := s.requestMap[id] 237 | s.requestMutex.Unlock() 238 | if ok { 239 | lowLevelSend(INVALID_SERVICE_ID, s.getId(), MSG_TYPE_TIMEOUT, MSG_ENC_TYPE_NO, id, Cmd_None) 240 | } 241 | }) 242 | } 243 | } 244 | 245 | func (s *service) dispatchTimeout(m *Message) { 246 | rid := m.Id 247 | cbp, ok := s.getDeleteRequestCb(rid) 248 | if !ok { 249 | return 250 | } 251 | cb := cbp.respond 252 | var param []reflect.Value 253 | param = append(param, reflect.ValueOf(true)) 254 | plen := cb.Type().NumIn() 255 | for i := 1; i < plen; i++ { 256 | param = append(param, reflect.New(cb.Type().In(i)).Elem()) 257 | } 258 | cb.Call(param) 259 | } 260 | 261 | func (s *service) dispatchRequest(msg *Message) { 262 | s.m.OnRequestMSG(msg) 263 | } 264 | 265 | func (s *service) respond(dst ServiceID, encType EncType, rid uint64, data ...interface{}) { 266 | lowLevelSend(s.getId(), dst, MSG_TYPE_RESPOND, encType, rid, Cmd_None, data...) 267 | } 268 | 269 | //return request callback by request id 270 | func (s *service) getDeleteRequestCb(id uint64) (requestCB, bool) { 271 | s.requestMutex.Lock() 272 | cb, ok := s.requestMap[id] 273 | delete(s.requestMap, id) 274 | s.requestMutex.Unlock() 275 | return cb, ok 276 | } 277 | 278 | func (s *service) dispatchRespond(m *Message) { 279 | var rid uint64 280 | var data []interface{} 281 | rid = m.Id 282 | data = m.Data 283 | 284 | cbp, ok := s.getDeleteRequestCb(rid) 285 | if !ok { 286 | return 287 | } 288 | cb := cbp.respond 289 | n := len(data) 290 | param := make([]reflect.Value, n+1) 291 | param[0] = reflect.ValueOf(false) 292 | HelperFunctionToUseReflectCall(cb, param, 1, data) 293 | cb.Call(param) 294 | } 295 | 296 | func (s *service) call(dst ServiceID, encType EncType, cmd CmdType, data ...interface{}) ([]interface{}, error) { 297 | helper.PanicWhen(dst == s.getId(), "dst must equal to s's id") 298 | s.callMutex.Lock() 299 | id := s.callId 300 | s.callId++ 301 | s.callMutex.Unlock() 302 | 303 | //ch has one buffer, make ret service not block on it. 304 | ch := make(chan []interface{}, 1) 305 | s.callMutex.Lock() 306 | s.callChanMap[id] = ch 307 | s.callMutex.Unlock() 308 | if err := lowLevelSend(s.getId(), dst, MSG_TYPE_CALL, encType, id, cmd, data...); err != nil { 309 | return nil, err 310 | } 311 | if conf.CallTimeOut > 0 { 312 | time.AfterFunc(time.Duration(conf.CallTimeOut)*time.Millisecond, func() { 313 | s.dispatchRet(id, ServiceCallTimeout) 314 | }) 315 | } 316 | ret := <-ch 317 | s.callMutex.Lock() 318 | delete(s.callChanMap, id) 319 | s.callMutex.Unlock() 320 | 321 | close(ch) 322 | if err, ok := ret[0].(error); ok { 323 | return ret[1:], err 324 | } 325 | return ret, nil 326 | } 327 | 328 | func (s *service) callWithTimeout(dst ServiceID, encType EncType, timeout int, cmd CmdType, data ...interface{}) ([]interface{}, error) { 329 | helper.PanicWhen(dst == s.getId(), "dst must equal to s's id") 330 | s.callMutex.Lock() 331 | id := s.callId 332 | s.callId++ 333 | s.callMutex.Unlock() 334 | 335 | //ch has one buffer, make ret service not block on it. 336 | ch := make(chan []interface{}, 1) 337 | s.callMutex.Lock() 338 | s.callChanMap[id] = ch 339 | s.callMutex.Unlock() 340 | if err := lowLevelSend(s.getId(), dst, MSG_TYPE_CALL, encType, id, cmd, data...); err != nil { 341 | return nil, err 342 | } 343 | if timeout > 0 { 344 | time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() { 345 | s.dispatchRet(id, ServiceCallTimeout) 346 | }) 347 | } 348 | ret := <-ch 349 | s.callMutex.Lock() 350 | delete(s.callChanMap, id) 351 | s.callMutex.Unlock() 352 | 353 | close(ch) 354 | if err, ok := ret[0].(error); ok { 355 | return ret[1:], err 356 | } 357 | return ret, nil 358 | } 359 | 360 | func (s *service) dispatchCall(msg *Message) { 361 | s.m.OnCallMSG(msg) 362 | } 363 | 364 | func (s *service) ret(dst ServiceID, encType EncType, cid uint64, data ...interface{}) { 365 | var dstService *service 366 | dstService, err := findServiceById(dst) 367 | if err != nil { 368 | lowLevelSend(s.getId(), dst, MSG_TYPE_RET, encType, cid, Cmd_None, data...) 369 | return 370 | } 371 | dstService.dispatchRet(cid, data...) 372 | } 373 | 374 | func (s *service) dispatchRet(cid uint64, data ...interface{}) { 375 | s.callMutex.Lock() 376 | ch, ok := s.callChanMap[cid] 377 | s.callMutex.Unlock() 378 | 379 | if ok { 380 | select { 381 | case ch <- data: 382 | default: 383 | helper.PanicWhen(true, "dispatchRet failed on ch.") 384 | } 385 | } 386 | } 387 | 388 | func (s *service) schedule(interval, repeat int, cb timer.TimerCallback) *timer.Timer { 389 | helper.PanicWhen(s.loopDuration <= 0, "loopDuraton must greater than zero.") 390 | return s.ts.Schedule(interval, repeat, cb) 391 | } 392 | -------------------------------------------------------------------------------- /core/topology.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/sydnash/lotou/log" 5 | "github.com/sydnash/lotou/vector" 6 | "sync" 7 | ) 8 | 9 | type nameRet struct { 10 | id ServiceID 11 | ok bool 12 | name string 13 | } 14 | 15 | const ( 16 | StartingNodeID = 10 17 | ) 18 | 19 | var ( 20 | once sync.Once 21 | isStandalone, isMaster bool 22 | registerNodeChan chan uint64 23 | nameChanMap map[uint]chan *nameRet 24 | nameMapMutex sync.Mutex 25 | nameRequestId uint 26 | beginNodeId uint64 27 | validNodeIdVec *vector.Vector 28 | ) 29 | 30 | func init() { 31 | registerNodeChan = make(chan uint64, 1) 32 | 33 | nameChanMap = make(map[uint]chan *nameRet) 34 | nameRequestId = 0 35 | 36 | isStandalone = true 37 | 38 | beginNodeId = StartingNodeID 39 | validNodeIdVec = vector.New() 40 | } 41 | 42 | //InitNode set node's information. 43 | func InitNode(_isStandalone, _isMaster bool) { 44 | isStandalone = _isStandalone 45 | isMaster = _isMaster 46 | if !isStandalone && isMaster { 47 | h.nodeId = MASTER_NODE_ID 48 | } 49 | } 50 | 51 | //RegisterNode : register slave node to master, and get a node id 52 | // block until register success 53 | func RegisterNode(nodeName string) { 54 | once.Do(func() { 55 | if !isStandalone && !isMaster { 56 | route(Cmd_RegisterNode, nodeName) 57 | h.nodeId = <-registerNodeChan 58 | worker, _ = NewIdWorker(int64(h.nodeId)) 59 | log.Info("SlaveNode register ndoe success: nodeId: %v, nodeName: {%v}", h.nodeId, nodeName) 60 | } 61 | }) 62 | } 63 | 64 | //DispatchRegisterNodeRet send RegisterNode's return to channel which RegisterNode is wait for 65 | func DispatchRegisterNodeRet(id uint64) { 66 | registerNodeChan <- id 67 | } 68 | 69 | //globalName regist name to master 70 | //it will notify all exist service through distribute msg. 71 | func globalName(id ServiceID, name string) { 72 | route(Cmd_RegisterName, uint64(id), name) 73 | } 74 | 75 | //route send msg to master 76 | //if node is not a master node, it send to .slave node first, .slave will forward msg to master. 77 | func route(cmd CmdType, data ...interface{}) bool { 78 | router, err := findServiceByName(".router") 79 | if err != nil { 80 | return false 81 | } 82 | localSendWithoutMutex(INVALID_SERVICE_ID, router, MSG_TYPE_NORMAL, MSG_ENC_TYPE_NO, 0, cmd, data...) 83 | return true 84 | } 85 | 86 | //NameToId couldn't guarantee get the correct id for name. 87 | //it will return err if the named server is until now. 88 | func NameToId(name string) (ServiceID, error) { 89 | ser, err := findServiceByName(name) 90 | if err == nil { 91 | return ser.getId(), nil 92 | } 93 | if !checkIsLocalName(name) { 94 | nameMapMutex.Lock() 95 | nameRequestId++ 96 | tmp := nameRequestId 97 | nameMapMutex.Unlock() 98 | 99 | ch := make(chan *nameRet, 1) 100 | nameMapMutex.Lock() 101 | nameChanMap[tmp] = ch 102 | nameMapMutex.Unlock() 103 | 104 | route(Cmd_GetIdByName, name, tmp) 105 | ret := <-ch 106 | close(ch) 107 | if !ret.ok { 108 | return INVALID_SERVICE_ID, ServiceNotFindError 109 | } 110 | return ret.id, nil 111 | } 112 | return INVALID_SERVICE_ID, ServiceNotFindError 113 | } 114 | 115 | func DispatchGetIdByNameRet(id ServiceID, ok bool, name string, rid uint) { 116 | nameMapMutex.Lock() 117 | ch := nameChanMap[rid] 118 | delete(nameChanMap, rid) 119 | nameMapMutex.Unlock() 120 | ch <- &nameRet{id, ok, name} 121 | } 122 | 123 | func GenerateNodeId() uint64 { 124 | var ret uint64 125 | if validNodeIdVec.Empty() { 126 | ret = beginNodeId 127 | beginNodeId++ 128 | } else { 129 | ret = validNodeIdVec.Pop().(uint64) 130 | } 131 | return ret 132 | } 133 | 134 | func CollectNodeId(recycledNodeID uint64) { 135 | if recycledNodeID >= StartingNodeID { 136 | log.Info("Recycled of NodeID<%v>", recycledNodeID) 137 | validNodeIdVec.Push(recycledNodeID) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /core/uuid.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const ( 11 | CEpoch = 1474802888000 12 | CWorkerIdBits = 10 // Num of WorkerId Bits 13 | CSenquenceBits = 12 // Num of Sequence Bits 14 | 15 | CWorkerIdShift = 12 16 | CTimeStampShift = 22 17 | 18 | CSequenceMask = 0xfff // equal as getSequenceMask() 19 | CMaxWorker = 0x3ff // equal as getMaxWorkerId() 20 | ) 21 | 22 | // IdWorker Struct 23 | type IdWorker struct { 24 | workerId int64 25 | lastTimeStamp int64 26 | sequence int64 27 | maxWorkerId int64 28 | lock *sync.Mutex 29 | } 30 | 31 | var worker *IdWorker 32 | 33 | func init() { 34 | worker, _ = NewIdWorker(0) 35 | } 36 | 37 | func UUID() uint64 { 38 | u, _ := worker.NextId() 39 | return uint64(u) 40 | } 41 | func UUIDString() string { 42 | u, _ := worker.NextId() 43 | return fmt.Sprintf("%v", uint64(u)) 44 | } 45 | 46 | // NewIdWorker Func: Generate NewIdWorker with Given workerid 47 | func NewIdWorker(workerid int64) (iw *IdWorker, err error) { 48 | iw = new(IdWorker) 49 | 50 | iw.maxWorkerId = getMaxWorkerId() 51 | 52 | if workerid > iw.maxWorkerId || workerid < 0 { 53 | return nil, errors.New("worker not fit") 54 | } 55 | iw.workerId = workerid 56 | iw.lastTimeStamp = -1 57 | iw.sequence = 0 58 | iw.lock = new(sync.Mutex) 59 | return iw, nil 60 | } 61 | 62 | func getMaxWorkerId() int64 { 63 | return -1 ^ -1<> CWorkerIdShift) & CMaxWorker 114 | ts = (id >> CTimeStampShift) + CEpoch 115 | t = time.Unix(ts/1000, (ts%1000)*1000000) 116 | return 117 | } 118 | -------------------------------------------------------------------------------- /core/uuid_test.go: -------------------------------------------------------------------------------- 1 | package core_test 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/base64" 6 | "encoding/binary" 7 | "fmt" 8 | "github.com/sydnash/lotou/core" 9 | "testing" 10 | ) 11 | 12 | func TestUUID(t *testing.T) { 13 | pp := core.UUID() 14 | var p []byte = make([]byte, 8) 15 | binary.LittleEndian.PutUint64(p, pp) 16 | 17 | str := base64.StdEncoding.EncodeToString(p) 18 | fmt.Println(string(str)) 19 | 20 | tt := md5.Sum([]byte(str)) 21 | 22 | str = base64.StdEncoding.EncodeToString(tt[:]) 23 | fmt.Println(string(str)) 24 | } 25 | -------------------------------------------------------------------------------- /encoding/binary/binary_test.go: -------------------------------------------------------------------------------- 1 | package binary_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sydnash/lotou/encoding/binary" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestEncode(t *testing.T) { 11 | enc := binary.NewEncoder() 12 | 13 | a := make([]interface{}, 0, 10) 14 | a = append(a, int(-5)) 15 | a = append(a, int8(-1)) 16 | a = append(a, int16(-2)) 17 | a = append(a, int32(-3)) 18 | a = append(a, int64(-4)) 19 | a = append(a, uint(6)) 20 | a = append(a, uint8(7)) 21 | a = append(a, uint16(8)) 22 | a = append(a, uint32(9)) 23 | a = append(a, uint64(10)) 24 | a = append(a, float32(0.99999)) 25 | a = append(a, float64(0.9999999999)) 26 | a = append(a, "this is a string") 27 | a = append(a, "这也是一个string") 28 | t1 := struct { 29 | a string 30 | B []byte 31 | }{a: "结构体的字符串", B: []byte("结构体的字符串")} 32 | a = append(a, t1) 33 | 34 | for _, v := range a { 35 | enc.Encode(v) 36 | } 37 | 38 | dec := binary.NewDecoder() 39 | dec.SetBuffer(enc.Buffer()) 40 | 41 | for i := 0; i < len(a); i++ { 42 | typ := reflect.TypeOf(a[i]) 43 | v := reflect.New(typ) 44 | dec.Decode(v.Interface()) 45 | fmt.Println(v.Elem().Interface()) 46 | if !reflect.DeepEqual(v.Elem().Interface(), a[i]) { 47 | t.Errorf("%v is not equal to %v at idx %v", v.Elem().Interface(), a[i], i) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /encoding/binary/decoder.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | ) 7 | 8 | type Decoder struct { 9 | b []byte 10 | r, w int 11 | } 12 | 13 | func NewDecoder() *Decoder { 14 | a := &Decoder{} 15 | return a 16 | } 17 | 18 | func (dec *Decoder) SetBuffer(b []byte) { 19 | dec.b = b 20 | dec.reset() 21 | } 22 | 23 | func (dec *Decoder) reset() { 24 | dec.r = 4 25 | dec.w = len(dec.b) 26 | } 27 | 28 | var ( 29 | decoderMap map[reflect.Kind]func(*Decoder, reflect.Value) 30 | ) 31 | 32 | func (dec *Decoder) Decode(i interface{}) { 33 | v := reflect.ValueOf(i) 34 | if v.Kind() != reflect.Ptr { 35 | panic("must ptr") 36 | } 37 | v = v.Elem() 38 | if v.Kind() == reflect.Ptr { 39 | panic("must only one depth of ptr") 40 | } 41 | dec.decodeValue(v) 42 | } 43 | 44 | func (enc *Decoder) decodeValue(v reflect.Value) { 45 | typ := v.Type() 46 | decodeFunc := findDecoder(typ) 47 | decodeFunc(enc, v) 48 | } 49 | 50 | func readUInt32(dec *Decoder) (a uint32) { 51 | idx := dec.r 52 | var i uint 53 | for i = 0; i < 4; i++ { 54 | a = a | uint32(dec.b[idx+int(i)])<<(i*8) 55 | } 56 | dec.r += 4 57 | return 58 | } 59 | func readUInt16(dec *Decoder) (a uint16) { 60 | idx := dec.r 61 | var i uint 62 | for i = 0; i < 2; i++ { 63 | a = a | uint16(dec.b[idx+int(i)])<<(i*8) 64 | } 65 | dec.r += 2 66 | return 67 | } 68 | func readUInt64(dec *Decoder) (a uint64) { 69 | idx := dec.r 70 | var i uint 71 | for i = 0; i < 8; i++ { 72 | a = a | uint64(dec.b[idx+int(i)])<<(i*8) 73 | } 74 | dec.r += 8 75 | return 76 | } 77 | 78 | func decodeInt32(dec *Decoder, v reflect.Value) { 79 | var a int32 = int32(readUInt32(dec)) 80 | v.SetInt(int64(a)) 81 | } 82 | func decodeInt16(dec *Decoder, v reflect.Value) { 83 | var a int16 = int16(readUInt16(dec)) 84 | v.SetInt(int64(a)) 85 | } 86 | func decodeInt64(dec *Decoder, v reflect.Value) { 87 | var a int64 = int64(readUInt64(dec)) 88 | v.SetInt(a) 89 | } 90 | func decodeInt8(dec *Decoder, v reflect.Value) { 91 | var a int8 = int8(dec.b[dec.r]) 92 | dec.r += 1 93 | v.SetInt(int64(a)) 94 | } 95 | func decodeUInt32(dec *Decoder, v reflect.Value) { 96 | var a uint64 = uint64(readUInt32(dec)) 97 | v.SetUint(a) 98 | } 99 | func decodeUInt16(dec *Decoder, v reflect.Value) { 100 | var a uint64 = uint64(readUInt16(dec)) 101 | v.SetUint(a) 102 | } 103 | func decodeUInt64(dec *Decoder, v reflect.Value) { 104 | var a uint64 = uint64(readUInt64(dec)) 105 | v.SetUint(a) 106 | } 107 | func decodeUInt8(dec *Decoder, v reflect.Value) { 108 | var a uint64 = uint64(dec.b[dec.r]) 109 | dec.r += 1 110 | v.SetUint(a) 111 | } 112 | func decodeFloat32(dec *Decoder, v reflect.Value) { 113 | a := readUInt32(dec) 114 | f := math.Float32frombits(a) 115 | v.SetFloat(float64(f)) 116 | } 117 | func decodeFloat64(dec *Decoder, v reflect.Value) { 118 | a := readUInt64(dec) 119 | ua := math.Float64frombits(a) 120 | v.SetFloat(ua) 121 | } 122 | 123 | func decodeString(dec *Decoder, v reflect.Value) { 124 | var count int = int(readUInt16(dec)) 125 | str := string(dec.b[dec.r : dec.r+count]) 126 | dec.r += count 127 | v.SetString(str) 128 | } 129 | func decodeBytes(dec *Decoder, v reflect.Value) { 130 | var count int = int(readUInt16(dec)) 131 | str := string(dec.b[dec.r : dec.r+count]) 132 | dec.r += count 133 | v.SetBytes([]byte(str)) 134 | } 135 | func decodeStruct(dec *Decoder, v reflect.Value) { 136 | num := v.NumField() 137 | for i := 0; i < num; i++ { 138 | f := v.Field(i) 139 | if f.CanInterface() { 140 | dec.decodeValue(f) 141 | } 142 | } 143 | } 144 | func decodeInterface(dec *Decoder, v reflect.Value) { 145 | rv := v.Elem() 146 | dec.decodeValue(rv) 147 | } 148 | func decodeBool(dec *Decoder, v reflect.Value) { 149 | a := dec.b[dec.r] 150 | dec.r += 1 151 | var b bool = false 152 | if a != 0 { 153 | b = true 154 | } 155 | v.SetBool(b) 156 | } 157 | func decodeInt(dec *Decoder, v reflect.Value) { 158 | decodeInt32(dec, v) 159 | } 160 | func decodeUInt(dec *Decoder, v reflect.Value) { 161 | decodeUInt32(dec, v) 162 | } 163 | func decodeSlice(dec *Decoder, v reflect.Value) { 164 | count := int(readUInt32(dec)) 165 | v.SetLen(count) 166 | for i := 0; i < count; i++ { 167 | elem := v.Index(i) 168 | dec.decodeValue(elem) 169 | } 170 | } 171 | func decodeMap(dec *Decoder, v reflect.Value) { 172 | count := int(readUInt32(dec)) 173 | typ := v.Type() 174 | for i := 0; i < count; i++ { 175 | key := reflect.New(typ.Key()).Elem() 176 | dec.decodeValue(key) 177 | value := reflect.New(typ.Elem()).Elem() 178 | dec.decodeValue(value) 179 | v.SetMapIndex(key, value) 180 | } 181 | } 182 | 183 | func init() { 184 | decoderMap = make(map[reflect.Kind]func(*Decoder, reflect.Value)) 185 | decoderMap[reflect.Bool] = decodeBool 186 | decoderMap[reflect.Int] = decodeInt 187 | decoderMap[reflect.Int8] = decodeInt8 188 | decoderMap[reflect.Int16] = decodeInt16 189 | decoderMap[reflect.Int32] = decodeInt32 190 | decoderMap[reflect.Int64] = decodeInt64 191 | decoderMap[reflect.Uint] = decodeUInt 192 | decoderMap[reflect.Uint8] = decodeUInt8 193 | decoderMap[reflect.Uint16] = decodeUInt16 194 | decoderMap[reflect.Uint32] = decodeUInt32 195 | decoderMap[reflect.Uint64] = decodeUInt64 196 | decoderMap[reflect.Float32] = decodeFloat32 197 | decoderMap[reflect.Float64] = decodeFloat64 198 | decoderMap[reflect.Struct] = decodeStruct 199 | decoderMap[reflect.String] = decodeString 200 | decoderMap[reflect.Slice] = decodeSlice 201 | decoderMap[reflect.Map] = decodeMap 202 | } 203 | 204 | func findDecoder(typ reflect.Type) func(*Decoder, reflect.Value) { 205 | if typ == reflect.TypeOf([]byte{}) { 206 | return decodeBytes 207 | } 208 | return decoderMap[typ.Kind()] 209 | } 210 | -------------------------------------------------------------------------------- /encoding/binary/encoder.go: -------------------------------------------------------------------------------- 1 | package binary 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | ) 7 | 8 | //Encoder encode value to binary start with 4 byte for binary len. 9 | //and only 2 byte to encode map, slice's len. 10 | type Encoder struct { 11 | b []byte 12 | r, w int 13 | } 14 | 15 | func (enc *Encoder) Reset() { 16 | if enc.b == nil { 17 | enc.b = make([]byte, 1024, 1024) 18 | } 19 | enc.r = 0 20 | enc.w = 4 21 | enc.b = enc.b[:] 22 | } 23 | 24 | func NewEncoder() *Encoder { 25 | a := &Encoder{} 26 | a.Reset() 27 | return a 28 | } 29 | 30 | var ( 31 | encoderMap map[reflect.Kind]func(*Encoder, reflect.Value) 32 | ) 33 | 34 | func (enc *Encoder) Buffer() []byte { 35 | return enc.b[:enc.w] 36 | } 37 | 38 | func (enc *Encoder) Encode(i interface{}) { 39 | enc.encodeValue(reflect.ValueOf(i)) 40 | } 41 | 42 | func (enc *Encoder) encodeValue(v reflect.Value) { 43 | typ := v.Type() 44 | encodeFunc := findEncoder(typ) 45 | encodeFunc(enc, v) 46 | } 47 | 48 | func (enc *Encoder) grow(n int) { 49 | if enc.w+n > cap(enc.b) { 50 | buf := make([]byte, 2*cap(enc.b), 2*cap(enc.b)) 51 | copy(buf, enc.b[:enc.w]) 52 | enc.b = buf[:] 53 | } 54 | } 55 | func intToByteSlice(v uint32) []byte { 56 | a := make([]byte, 4) 57 | a[3] = byte((v >> 24) & 0xFF) 58 | a[2] = byte((v >> 16) & 0XFF) 59 | a[1] = byte((v >> 8) & 0XFF) 60 | a[0] = byte(v & 0XFF) 61 | return a 62 | } 63 | func (enc *Encoder) UpdateLen() { 64 | l := enc.w 65 | b := intToByteSlice(uint32(l)) 66 | copy(enc.b[:4], b[:]) 67 | } 68 | 69 | func encodeInt32(enc *Encoder, v reflect.Value) { 70 | a := uint32(v.Interface().(int32)) 71 | enc.grow(4) 72 | idx := enc.w 73 | for i := 0; i < 4; i++ { 74 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 75 | } 76 | enc.w += 4 77 | } 78 | func encodeInt16(enc *Encoder, v reflect.Value) { 79 | a := uint16(v.Interface().(int16)) 80 | enc.grow(2) 81 | idx := enc.w 82 | for i := 0; i < 2; i++ { 83 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 84 | } 85 | enc.w += 2 86 | } 87 | func encodeInt64(enc *Encoder, v reflect.Value) { 88 | a := uint64(v.Interface().(int64)) 89 | enc.grow(8) 90 | idx := enc.w 91 | for i := 0; i < 8; i++ { 92 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 93 | } 94 | enc.w += 8 95 | } 96 | func encodeInt8(enc *Encoder, v reflect.Value) { 97 | a := uint8(v.Interface().(int8)) 98 | enc.grow(1) 99 | enc.b[enc.w] = a 100 | enc.w += 1 101 | } 102 | func encodeUInt32(enc *Encoder, v reflect.Value) { 103 | a := v.Interface().(uint32) 104 | enc.grow(4) 105 | idx := enc.w 106 | for i := 0; i < 4; i++ { 107 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 108 | } 109 | enc.w += 4 110 | } 111 | func encodeUInt16(enc *Encoder, v reflect.Value) { 112 | a := v.Interface().(uint16) 113 | enc.grow(2) 114 | idx := enc.w 115 | for i := 0; i < 2; i++ { 116 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 117 | } 118 | enc.w += 2 119 | } 120 | func encodeUInt64(enc *Encoder, v reflect.Value) { 121 | a := v.Interface().(uint64) 122 | enc.grow(8) 123 | idx := enc.w 124 | for i := 0; i < 8; i++ { 125 | enc.b[idx+i] = byte(a >> (uint(i) * 8)) 126 | } 127 | enc.w += 8 128 | } 129 | func encodeUInt8(enc *Encoder, v reflect.Value) { 130 | a := v.Interface().(uint8) 131 | enc.grow(1) 132 | enc.b[enc.w] = a 133 | enc.w += 1 134 | } 135 | func encodeFloat32(enc *Encoder, v reflect.Value) { 136 | a := v.Interface().(float32) 137 | ua := math.Float32bits(a) 138 | encodeUInt32(enc, reflect.ValueOf(ua)) 139 | } 140 | func encodeFloat64(enc *Encoder, v reflect.Value) { 141 | a := v.Interface().(float64) 142 | ua := math.Float64bits(a) 143 | encodeUInt64(enc, reflect.ValueOf(ua)) 144 | } 145 | 146 | func encodeString(enc *Encoder, v reflect.Value) { 147 | str := v.Interface().(string) 148 | encodeBytes(enc, reflect.ValueOf([]byte(str))) 149 | } 150 | func encodeBytes(enc *Encoder, v reflect.Value) { 151 | bytes := v.Bytes() 152 | count := len(bytes) 153 | encodeInt16(enc, reflect.ValueOf(int16(count))) 154 | enc.grow(count) 155 | copy(enc.b[enc.w:], bytes[:]) 156 | enc.w += count 157 | } 158 | func encodeStruct(enc *Encoder, v reflect.Value) { 159 | num := v.NumField() 160 | for i := 0; i < num; i++ { 161 | f := v.Field(i) 162 | if f.CanInterface() { 163 | enc.encodeValue(f) 164 | } 165 | } 166 | } 167 | func encodeInterface(enc *Encoder, v reflect.Value) { 168 | rv := v.Elem() 169 | enc.encodeValue(rv) 170 | } 171 | func encodeBool(enc *Encoder, v reflect.Value) { 172 | b := v.Bool() 173 | var a uint8 = 0 174 | if b { 175 | a = 1 176 | } 177 | encodeUInt8(enc, reflect.ValueOf(a)) 178 | } 179 | func encodeInt(enc *Encoder, v reflect.Value) { 180 | var a uint32 = uint32(v.Interface().(int)) 181 | encodeUInt32(enc, reflect.ValueOf(a)) 182 | } 183 | func encodeUInt(enc *Encoder, v reflect.Value) { 184 | var a uint32 = uint32(v.Interface().(uint)) 185 | encodeUInt32(enc, reflect.ValueOf(a)) 186 | } 187 | func encodeSlice(enc *Encoder, v reflect.Value) { 188 | count := v.Len() 189 | encodeInt32(enc, reflect.ValueOf(int32(count))) 190 | for i := 0; i < count; i++ { 191 | elem := v.Index(i) 192 | enc.encodeValue(elem) 193 | } 194 | } 195 | func encodeMap(enc *Encoder, v reflect.Value) { 196 | count := v.Len() 197 | encodeInt32(enc, reflect.ValueOf(int32(count))) 198 | keys := v.MapKeys() 199 | for _, k := range keys { 200 | enc.encodeValue(k) 201 | elem := v.MapIndex(k) 202 | enc.encodeValue(elem) 203 | } 204 | } 205 | 206 | func init() { 207 | encoderMap = make(map[reflect.Kind]func(*Encoder, reflect.Value)) 208 | encoderMap[reflect.Bool] = encodeBool 209 | encoderMap[reflect.Int] = encodeInt 210 | encoderMap[reflect.Int8] = encodeInt8 211 | encoderMap[reflect.Int16] = encodeInt16 212 | encoderMap[reflect.Int32] = encodeInt32 213 | encoderMap[reflect.Int64] = encodeInt64 214 | encoderMap[reflect.Uint] = encodeUInt 215 | encoderMap[reflect.Uint8] = encodeUInt8 216 | encoderMap[reflect.Uint16] = encodeUInt16 217 | encoderMap[reflect.Uint32] = encodeUInt32 218 | encoderMap[reflect.Uint64] = encodeUInt64 219 | encoderMap[reflect.Float32] = encodeFloat32 220 | encoderMap[reflect.Float64] = encodeFloat64 221 | encoderMap[reflect.Struct] = encodeStruct 222 | encoderMap[reflect.String] = encodeString 223 | encoderMap[reflect.Slice] = encodeSlice 224 | encoderMap[reflect.Map] = encodeMap 225 | } 226 | 227 | func findEncoder(typ reflect.Type) func(*Encoder, reflect.Value) { 228 | if typ == reflect.TypeOf([]byte{}) { 229 | return encodeBytes 230 | } 231 | return encoderMap[typ.Kind()] 232 | } 233 | -------------------------------------------------------------------------------- /encoding/gob/decoder.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "reflect" 7 | ) 8 | 9 | type Decoder struct { 10 | b []byte 11 | r, w int 12 | } 13 | 14 | func NewDecoder() *Decoder { 15 | return &Decoder{} 16 | } 17 | 18 | func (dec *Decoder) SetBuffer(b []byte) { 19 | dec.b = b 20 | dec.reset() 21 | } 22 | 23 | func (dec *Decoder) reset() { 24 | dec.r = 4 25 | dec.w = len(dec.b) 26 | } 27 | 28 | func (dec *Decoder) Decode() (ret interface{}, ok bool) { 29 | if dec.r >= dec.w { 30 | return nil, false 31 | } 32 | v := dec.decodeValue() 33 | return v.Interface(), true 34 | } 35 | 36 | func (dec *Decoder) decodeValue() (value reflect.Value) { 37 | kind, depth, structId, typ := dec.decodeType(false) 38 | value = dec.decodeConcreteValue(kind, depth, structId, typ) 39 | return 40 | } 41 | 42 | func (dec *Decoder) decodeConcreteValue(kind, depth, structId uint, typ reflect.Type) (value reflect.Value) { 43 | _ = structId 44 | tk := reflect.Kind(kind) 45 | switch tk { 46 | case reflect.Int8: 47 | t := int8(dec.decodeInt()) 48 | value = reflect.ValueOf(t) 49 | case reflect.Int16: 50 | t := int16(dec.decodeInt()) 51 | value = reflect.ValueOf(t) 52 | case reflect.Int32: 53 | t := int32(dec.decodeInt()) 54 | value = reflect.ValueOf(t) 55 | case reflect.Int64: 56 | t := dec.decodeInt() 57 | value = reflect.ValueOf(t) 58 | case reflect.Int: 59 | t := int(dec.decodeInt()) 60 | value = reflect.ValueOf(t) 61 | case reflect.Uint8: 62 | t := uint8(dec.decodeUInt()) 63 | value = reflect.ValueOf(t) 64 | case reflect.Uint16: 65 | t := uint16(dec.decodeUInt()) 66 | value = reflect.ValueOf(t) 67 | case reflect.Uint32: 68 | t := uint32(dec.decodeUInt()) 69 | value = reflect.ValueOf(t) 70 | case reflect.Uint64: 71 | t := dec.decodeUInt() 72 | value = reflect.ValueOf(t) 73 | case reflect.Uint: 74 | t := uint(dec.decodeUInt()) 75 | value = reflect.ValueOf(t) 76 | case reflect.Float32: 77 | t := dec.decodeUInt() 78 | f32 := float32(math.Float64frombits(t)) 79 | value = reflect.ValueOf(f32) 80 | case reflect.Float64: 81 | t := dec.decodeUInt() 82 | f64 := math.Float64frombits(t) 83 | value = reflect.ValueOf(f64) 84 | case reflect.Bool: 85 | v := dec.decodeUInt() 86 | var b bool = false 87 | if v != 0 { 88 | b = true 89 | } 90 | value = reflect.ValueOf(b) 91 | case reflect.String: 92 | str := dec.decodeString() 93 | value = reflect.ValueOf(str) 94 | case reflect.Struct: 95 | value = dec.decodeStruct(structId) 96 | case reflect.Slice: 97 | value = dec.decodeSlice(typ) 98 | case reflect.Array: 99 | value = dec.decodeArray(typ) 100 | case reflect.Map: 101 | value = dec.decodeMap(typ) 102 | default: 103 | panic(fmt.Sprintf("not support type: %s", tk)) 104 | } 105 | for i := 0; i < int(depth); i++ { 106 | typ := value.Type() 107 | valuep := reflect.New(typ) 108 | valuep.Elem().Set(value) 109 | value = valuep 110 | } 111 | return 112 | } 113 | 114 | func (dec *Decoder) decodeType(hasDepth bool) (uint, uint, uint, reflect.Type) { 115 | id := uint16(dec.decodeUInt()) 116 | kind, depth, structId := parseTypeId(id) 117 | var count uint 118 | var key reflect.Type 119 | var elem reflect.Type 120 | 121 | switch reflect.Kind(kind) { 122 | case reflect.Slice: 123 | _, _, _, elem = dec.decodeType(true) 124 | case reflect.Map: 125 | _, _, _, key = dec.decodeType(true) 126 | _, _, _, elem = dec.decodeType(true) 127 | case reflect.Array: 128 | _, _, _, elem = dec.decodeType(true) 129 | count = uint(dec.decodeUInt()) 130 | /* case reflect.Chan: 131 | et := typ.Elem() 132 | enc.encodeType(et) 133 | */ 134 | default: 135 | } 136 | var typ reflect.Type 137 | typ = createType(&TypeDesc{kind, depth, structId, count, key, elem, hasDepth}) 138 | 139 | return kind, depth, structId, typ 140 | } 141 | 142 | func (dec *Decoder) decodeUInt() uint64 { 143 | var x uint64 144 | var s uint 145 | t := 0 146 | for i, b := range dec.b[dec.r:] { 147 | if b < 0x80 { 148 | if i > 9 || i == 9 && b > 1 { 149 | panic("decode uint error.") 150 | } 151 | t = i + 1 152 | dec.r = dec.r + t 153 | return x | uint64(b)<> 1) 164 | if ux&1 != 0 { 165 | x = ^x 166 | } 167 | return x 168 | } 169 | 170 | func (dec *Decoder) decodeString() string { 171 | l := dec.decodeUInt() 172 | str := string(dec.b[dec.r : dec.r+int(l)]) 173 | dec.r = dec.r + int(l) 174 | return str 175 | } 176 | 177 | func (dec *Decoder) decodeStruct(structId uint) (value reflect.Value) { 178 | typ, ok := typeIdToUt[structId] 179 | if !ok { 180 | panic(fmt.Sprintf("struct type %v is not registed.", structId)) 181 | } 182 | 183 | pv := reflect.New(typ) 184 | value = pv.Elem() 185 | 186 | num := value.NumField() 187 | for i := 0; i < num; i++ { 188 | f := value.Field(i) 189 | if f.CanInterface() { 190 | v := dec.decodeValue() 191 | f.Set(v.Convert(f.Type())) 192 | } 193 | } 194 | 195 | return 196 | } 197 | 198 | func (dec *Decoder) decodeSlice(typ reflect.Type) (value reflect.Value) { 199 | count := int(dec.decodeUInt()) 200 | if typ == reflect.TypeOf([]byte{}) { 201 | v := reflect.New(typ) 202 | sli := make([]byte, count) 203 | copy(sli, dec.b[dec.r:]) 204 | dec.r += count 205 | v.Elem().Set(reflect.ValueOf(sli)) 206 | return v.Elem() 207 | } else { 208 | value = reflect.MakeSlice(typ, 0, count) 209 | for i := 0; i < count; i++ { 210 | v := dec.decodeValue() 211 | value = reflect.Append(value, v) 212 | } 213 | } 214 | return 215 | } 216 | 217 | func (dec *Decoder) decodeArray(typ reflect.Type) (value reflect.Value) { 218 | count := int(dec.decodeUInt()) 219 | value = reflect.New(typ) 220 | value = value.Elem() 221 | for i := 0; i < count; i++ { 222 | v := dec.decodeValue() 223 | value.Index(i).Set(v) 224 | } 225 | return 226 | } 227 | 228 | func (dec *Decoder) decodeMap(typ reflect.Type) (value reflect.Value) { 229 | count := int(dec.decodeUInt()) 230 | value = reflect.MakeMap(typ) 231 | for i := 0; i < count; i++ { 232 | key := dec.decodeValue() 233 | elem := dec.decodeValue() 234 | value.SetMapIndex(key, elem) 235 | } 236 | return 237 | } 238 | -------------------------------------------------------------------------------- /encoding/gob/encoder.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "reflect" 7 | ) 8 | 9 | type Encoder struct { 10 | b []byte 11 | r, w int 12 | } 13 | 14 | func NewEncoder() *Encoder { 15 | a := &Encoder{} 16 | a.Reset() 17 | return a 18 | } 19 | 20 | func intToByteSlice(v uint32) []byte { 21 | a := make([]byte, 4) 22 | a[3] = byte((v >> 24) & 0xFF) 23 | a[2] = byte((v >> 16) & 0XFF) 24 | a[1] = byte((v >> 8) & 0XFF) 25 | a[0] = byte(v & 0XFF) 26 | return a 27 | } 28 | func ByteSliceToInt(s []byte) (v uint32) { 29 | v = uint32(s[3])<<24 | uint32(s[2])<<16 | uint32(s[1])<<8 | uint32(s[0]) 30 | return v 31 | } 32 | 33 | func (enc *Encoder) SetBuffer(b []byte) { 34 | enc.b = b 35 | enc.Reset() 36 | } 37 | func (enc *Encoder) Buffer() []byte { 38 | return enc.b[:enc.w] 39 | } 40 | 41 | func (enc *Encoder) UpdateLen() { 42 | l := enc.w 43 | b := intToByteSlice(uint32(l)) 44 | copy(enc.b[:4], b[:]) 45 | } 46 | 47 | func (enc *Encoder) Reset() { 48 | if enc.b == nil { 49 | enc.b = make([]byte, 1024, 1024) 50 | } 51 | enc.r = 0 52 | enc.w = 4 53 | enc.b = enc.b[:] 54 | } 55 | 56 | func (enc *Encoder) grow(n int) { 57 | if enc.w+n > cap(enc.b) { 58 | newLen := 2 * cap(enc.b) 59 | for { 60 | if newLen >= enc.w+n { 61 | break 62 | } 63 | newLen = 2 * newLen 64 | } 65 | buf := make([]byte, newLen, newLen) 66 | copy(buf, enc.b[:enc.w]) 67 | enc.b = buf[:] 68 | } 69 | } 70 | 71 | func (enc *Encoder) Encode(i interface{}) { 72 | value := reflect.ValueOf(i) 73 | enc.encodeValue(value) 74 | } 75 | 76 | func (enc *Encoder) encodeValue(v reflect.Value) { 77 | rt, depth := findBaseAndDepth(v.Type()) 78 | enc.encodeType(v.Type()) 79 | enc.encodeConcreteValue(rt, depth, v) 80 | } 81 | 82 | func (enc *Encoder) encodeConcreteValue(rt reflect.Type, depth uint, v reflect.Value) { 83 | value := v 84 | for i := 0; i < int(depth); i++ { 85 | value = value.Elem() 86 | } 87 | switch rt.Kind() { 88 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 89 | enc.encodeInt(value.Int()) 90 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 91 | enc.encodeUInt(value.Uint()) 92 | case reflect.Float64, reflect.Float32: 93 | tmp := value.Float() 94 | tmp64 := math.Float64bits(float64(tmp)) 95 | enc.encodeUInt(tmp64) 96 | case reflect.Bool: 97 | t := value.Bool() 98 | var v uint64 = 0 99 | if t { 100 | v = 1 101 | } 102 | enc.encodeUInt(v) 103 | case reflect.String: 104 | str := value.String() 105 | enc.encodeString(str) 106 | case reflect.Struct: 107 | enc.encodeStruct(value) 108 | case reflect.Array, reflect.Slice: 109 | enc.encodeArrayLike(value) 110 | case reflect.Map: 111 | enc.encodeMap(value) 112 | case reflect.Interface: 113 | default: 114 | panic(fmt.Sprintf("not support type to send. %v", rt)) 115 | } 116 | } 117 | 118 | func (enc *Encoder) encodeType(typ reflect.Type) { 119 | id := valueToId(typ) 120 | enc.encodeUInt(uint64(id)) 121 | kind, _, _ := parseTypeId(id) 122 | switch reflect.Kind(kind) { 123 | case reflect.Slice: 124 | et := typ.Elem() 125 | enc.encodeType(et) 126 | case reflect.Map: 127 | kt := typ.Key() 128 | enc.encodeType(kt) 129 | et := typ.Elem() 130 | enc.encodeType(et) 131 | case reflect.Array: 132 | et := typ.Elem() 133 | enc.encodeType(et) 134 | le := typ.Len() 135 | enc.encodeUInt(uint64(le)) 136 | /* case reflect.Chan: 137 | et := typ.Elem() 138 | enc.encodeType(et) 139 | */ 140 | default: 141 | return 142 | } 143 | } 144 | func (enc *Encoder) encodeInt(v int64) { 145 | enc.grow(9) 146 | ux := uint64(v) << 1 147 | if v < 0 { 148 | ux = ^ux 149 | } 150 | enc.encodeUInt(ux) 151 | } 152 | func (enc *Encoder) encodeUInt(v uint64) { 153 | enc.grow(9) 154 | for v >= 0x80 { 155 | enc.b[enc.w] = byte(v) | 0x80 156 | v >>= 7 157 | enc.w++ 158 | } 159 | enc.b[enc.w] = byte(v) 160 | enc.w++ 161 | } 162 | func (enc *Encoder) encodeString(str string) { 163 | l := len(str) 164 | enc.encodeUInt(uint64(l)) 165 | 166 | enc.grow(l) 167 | b := []byte(str) 168 | copy(enc.b[enc.w:], b[:]) 169 | enc.w = enc.w + l 170 | } 171 | func (enc *Encoder) encodeStruct(value reflect.Value) { 172 | num := value.NumField() 173 | for i := 0; i < num; i++ { 174 | v := value.Field(i) 175 | if v.CanInterface() { 176 | enc.encodeValue(reflect.ValueOf(v.Interface())) 177 | } 178 | } 179 | } 180 | func (enc *Encoder) encodeArrayLike(value reflect.Value) { 181 | num := value.Len() 182 | enc.encodeUInt(uint64(num)) 183 | 184 | if value.Type() == reflect.TypeOf([]byte{}) { 185 | enc.grow(num) 186 | b := value.Interface().([]byte) 187 | copy(enc.b[enc.w:], b[:]) 188 | enc.w = enc.w + num 189 | } else { 190 | for i := 0; i < num; i++ { 191 | v := value.Index(i) 192 | enc.encodeValue(reflect.ValueOf(v.Interface())) //if pass v direct, it's type kind will be interface when the slice is []interface{} 193 | } 194 | } 195 | } 196 | 197 | func (enc *Encoder) encodeMap(value reflect.Value) { 198 | num := value.Len() 199 | enc.encodeUInt(uint64(num)) 200 | keys := value.MapKeys() 201 | for _, k := range keys { 202 | v := value.MapIndex(k) 203 | enc.encodeValue(reflect.ValueOf(k.Interface())) 204 | enc.encodeValue(reflect.ValueOf(v.Interface())) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /encoding/gob/gob.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | //Pack pack data to bytes, if has error, it will panic 9 | func Pack(data ...interface{}) []byte { 10 | encoder := NewEncoder() 11 | encoder.Reset() 12 | encoder.Encode(data) 13 | encoder.UpdateLen() 14 | buf := encoder.Buffer() 15 | return buf 16 | } 17 | 18 | //Pack pack data to bytes, if has error, it will return a error 19 | func PackWithErr(data ...interface{}) (ret []byte, err error) { 20 | defer func() { 21 | if e := recover(); e != nil { 22 | err = errors.New(fmt.Sprintf("%v", e)) 23 | ret = nil 24 | } 25 | }() 26 | 27 | ret = Pack(data...) 28 | return ret, nil 29 | } 30 | 31 | //Unpack unpack bytes to value, return error 32 | func Unpack(data []byte) (ret interface{}, err error) { 33 | defer func() { 34 | if e := recover(); e != nil { 35 | err = errors.New(fmt.Sprintf("%v", e)) 36 | ret = nil 37 | } 38 | }() 39 | decoder := NewDecoder() 40 | decoder.SetBuffer(data) 41 | ret, ok := decoder.Decode() 42 | if !ok { 43 | return nil, errors.New("decode error.") 44 | } 45 | return ret, nil 46 | } 47 | -------------------------------------------------------------------------------- /encoding/gob/gob_test.go: -------------------------------------------------------------------------------- 1 | package gob_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/encoding/gob" 7 | "testing" 8 | ) 9 | 10 | func TestPackMore(t *testing.T) { 11 | specificType := core.MSG_TYPE_NORMAL 12 | m1 := &core.Message{ 13 | Type: specificType, 14 | Cmd: "", //~ This line can not be ignored. 15 | } 16 | bytes := gob.Pack(m1) 17 | fmt.Println(bytes) 18 | m2, _ := gob.Unpack(bytes) 19 | m3 := m2.([]interface{})[0].(*core.Message) 20 | if m3.Type != specificType { 21 | t.Error("Not the same") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /encoding/gob/gobhelper.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | /* 4 | import ( 5 | "fmt" 6 | "hash/fnv" 7 | "reflect" 8 | ) 9 | 10 | func GetTypeRepr(x interface{}) string { 11 | return getTypeFrag(x) 12 | } 13 | 14 | func getTypeFrag(x interface{}) string { 15 | return getTypeFragStr(reflect.TypeOf(x)) 16 | } 17 | 18 | //~ Should be private 19 | func getTypeFragStr(t reflect.Type) string { 20 | var frag string 21 | switch t.Kind() { 22 | //~ Primitive types. 23 | case reflect.Bool: 24 | fallthrough 25 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 26 | fallthrough 27 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 28 | fallthrough 29 | case reflect.Uintptr, reflect.Float32, reflect.Float64: 30 | fallthrough 31 | case reflect.Complex64, reflect.Complex128: 32 | fallthrough 33 | case reflect.Interface: 34 | fallthrough 35 | case reflect.String: 36 | frag = fmt.Sprintf("%v", t) 37 | break 38 | //~ Types that do not proceed 39 | case reflect.Chan, reflect.Func: 40 | panic(fmt.Sprintf("Canno process such type %v", t.Kind())) 41 | case reflect.Ptr: 42 | frag = getPtrFlatStr(t) 43 | case reflect.Array: 44 | frag = getArrayFlatStr(t) 45 | case reflect.Map: 46 | frag = getMapFlatStr(t) 47 | case reflect.Slice: 48 | frag = getSliceFlatStr(t) 49 | break 50 | case reflect.Struct: 51 | frag = getStructFlatStr(t) 52 | default: 53 | panic(fmt.Sprintf("Unknown case of %v", t.Kind())) 54 | } 55 | if len(frag) == 0 { 56 | panic("Cannot get type of this:Something missing") 57 | } 58 | return frag 59 | } 60 | 61 | func getSliceFlatStr(t reflect.Type) string { 62 | if t.Kind() != reflect.Slice { 63 | panic(`Not a slice type`) 64 | } 65 | return fmt.Sprintf("[-%v]", getTypeFragStr(t.Elem())) 66 | } 67 | 68 | func getMapFlatStr(t reflect.Type) string { 69 | if t.Kind() != reflect.Map { 70 | panic(`Not a map as expected`) 71 | } 72 | return fmt.Sprintf("{map[%v]%v}", getTypeFragStr(t.Key()), getTypeFragStr(t.Elem())) 73 | } 74 | 75 | func getPtrFlatStr(t reflect.Type) string { 76 | if t.Kind() != reflect.Ptr { 77 | panic(`Not a ptr as expected`) 78 | } 79 | return fmt.Sprintf("*%v", getTypeFragStr(t.Elem())) 80 | } 81 | 82 | func getArrayFlatStr(t reflect.Type) string { 83 | if t.Kind() != reflect.Array { 84 | panic(`Not an array as expected`) 85 | } 86 | return fmt.Sprintf("[(%v)-%v]", t.Len(), getTypeFragStr(t.Elem())) 87 | } 88 | 89 | func getStructFlatStr(t reflect.Type) string { 90 | if t.Kind() != reflect.Struct { 91 | panic(`Not a struct as expected`) 92 | } 93 | n := t.NumField() 94 | var s string = fmt.Sprintf("%v.%v=Struct{<%v>::", t.PkgPath(), t.Name(), n) 95 | for i := 0; i < n; i++ { 96 | f := t.Field(i) //~ StructField 97 | s += fmt.Sprintf("%v<%v>;", f.Name, getTypeFragStr(f.Type)) 98 | } 99 | s += "}" 100 | return s 101 | } 102 | 103 | func getStructID(t reflect.Type) uint { 104 | strrpr := getTypeFragStr(t) 105 | fmt.Println(strrpr) 106 | h := fnv.New64a() 107 | h.Sum([]byte(strrpr)) 108 | return uint(h.Sum64()) 109 | } 110 | */ 111 | 112 | // Uncomment the rest to get the original implementation 113 | var ( 114 | _baseID uint 115 | ) 116 | 117 | func getStructID() uint { 118 | _baseID++ 119 | return _baseID 120 | } 121 | -------------------------------------------------------------------------------- /encoding/gob/type.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sync" 7 | ) 8 | 9 | var ( 10 | rtypeToUtMutxt sync.Mutex 11 | typeIdToUt map[uint]reflect.Type 12 | baseRtToTypeId map[reflect.Type]uint 13 | kindToReflectType map[reflect.Kind]reflect.Type 14 | ) 15 | 16 | func RegisterStructType(i interface{}) { 17 | value := reflect.ValueOf(i) 18 | rt, depth := findBaseAndDepth(value.Type()) 19 | _ = depth 20 | if rt.Kind() != reflect.Struct { 21 | return 22 | } 23 | _, ok := baseRtToTypeId[rt] 24 | if ok { 25 | return 26 | } 27 | typeId := getStructID() 28 | baseRtToTypeId[rt] = typeId 29 | typeIdToUt[typeId] = rt 30 | } 31 | 32 | func findBaseAndDepth(typ reflect.Type) (rt reflect.Type, depth uint) { 33 | for rt = typ; rt.Kind() == reflect.Ptr; { 34 | rt = rt.Elem() 35 | depth++ 36 | } 37 | return rt, depth 38 | } 39 | 40 | func valueToId(typ reflect.Type) uint16 { 41 | rt, depth := findBaseAndDepth(typ) 42 | kind := rt.Kind() 43 | var structId uint 44 | var ok bool 45 | if kind == reflect.Struct { 46 | structId, ok = baseRtToTypeId[rt] 47 | if !ok { 48 | panic(fmt.Sprintf("%v is not register.", rt)) 49 | } 50 | } 51 | id := uint16(structId)<<8 | uint16(depth)<<5 | uint16(kind) 52 | return id 53 | } 54 | 55 | func gernerateId(kind, depth, structId uint) uint16 { 56 | id := uint16(structId)<<8 | uint16(depth)<<5 | uint16(kind) 57 | return id 58 | } 59 | 60 | func parseTypeId(id uint16) (kind uint, depth uint, structId uint) { 61 | kind = uint(id & 0x1F) 62 | depth = uint((id >> 5) & 0x07) 63 | structId = uint((id >> 8) & 0xFF) 64 | return 65 | } 66 | 67 | type TypeDesc struct { 68 | kind, depth, structId, count uint 69 | key, elem reflect.Type 70 | hasDepth bool 71 | } 72 | 73 | func createType(desc *TypeDesc) (typ reflect.Type) { 74 | var ok bool 75 | typ, ok = kindToReflectType[reflect.Kind(desc.kind)] 76 | if !ok { 77 | switch reflect.Kind(desc.kind) { 78 | case reflect.Struct: 79 | id := gernerateId(desc.kind, desc.depth, desc.structId) 80 | typ = typeIdToUt[uint(id)] 81 | case reflect.Array: 82 | typ = reflect.ArrayOf(int(desc.count), desc.elem) 83 | case reflect.Slice: 84 | typ = reflect.SliceOf(desc.elem) 85 | case reflect.Map: 86 | typ = reflect.MapOf(desc.key, desc.elem) 87 | } 88 | } 89 | if desc.hasDepth { 90 | for i := 0; i < int(desc.depth); i++ { 91 | typ = reflect.PtrTo(typ) 92 | } 93 | } 94 | return typ 95 | } 96 | 97 | func init() { 98 | typeIdToUt = make(map[uint]reflect.Type) 99 | baseRtToTypeId = make(map[reflect.Type]uint) 100 | kindToReflectType = make(map[reflect.Kind]reflect.Type) 101 | 102 | kindToReflectType[reflect.Bool] = reflect.TypeOf(false) 103 | kindToReflectType[reflect.Int] = reflect.TypeOf(int(0)) 104 | kindToReflectType[reflect.Int8] = reflect.TypeOf(int8(0)) 105 | kindToReflectType[reflect.Int16] = reflect.TypeOf(int16(0)) 106 | kindToReflectType[reflect.Int32] = reflect.TypeOf(int32(0)) 107 | kindToReflectType[reflect.Int64] = reflect.TypeOf(int64(0)) 108 | kindToReflectType[reflect.Uint] = reflect.TypeOf(uint(0)) 109 | kindToReflectType[reflect.Uint8] = reflect.TypeOf(uint8(0)) 110 | kindToReflectType[reflect.Uint16] = reflect.TypeOf(uint16(0)) 111 | kindToReflectType[reflect.Uint32] = reflect.TypeOf(uint32(0)) 112 | kindToReflectType[reflect.Uint64] = reflect.TypeOf(uint64(0)) 113 | kindToReflectType[reflect.Float32] = reflect.TypeOf(float32(0.0)) 114 | kindToReflectType[reflect.Float64] = reflect.TypeOf(float64(0.0)) 115 | kindToReflectType[reflect.String] = reflect.TypeOf("") 116 | t := make([]interface{}, 1) 117 | kindToReflectType[reflect.Interface] = reflect.TypeOf(t).Elem() 118 | } 119 | -------------------------------------------------------------------------------- /encoding/gob/type_test.go: -------------------------------------------------------------------------------- 1 | package gob_test 2 | 3 | import "github.com/sydnash/lotou/encoding/gob" 4 | import "fmt" 5 | import "testing" 6 | import "reflect" 7 | 8 | func TestType(t *testing.T) { 9 | enc := gob.NewEncoder() 10 | 11 | a := make([]interface{}, 0, 10) 12 | a = append(a, int(-5)) 13 | a = append(a, int8(-1)) 14 | a = append(a, int16(-2)) 15 | a = append(a, int32(-3)) 16 | a = append(a, int64(-4)) 17 | a = append(a, uint(6)) 18 | a = append(a, uint8(7)) 19 | a = append(a, uint16(8)) 20 | a = append(a, uint32(9)) 21 | a = append(a, uint64(10)) 22 | a = append(a, float32(0.99999)) 23 | a = append(a, float64(0.9999999999)) 24 | a = append(a, "this is a string") 25 | a = append(a, "这也是一个string") 26 | a = append(a, &gob.T1{10, "哈哈,这都可以?", 1.5, -100}) 27 | a = append(a, &gob.T2{gob.T1{10, "哈哈,这都可以?", 1.5, -100}, "那么这样还可以吗?"}) 28 | a = append(a, gob.T1{10, "哈哈,这都可以?", 1.5, -100}) 29 | a = append(a, gob.T2{gob.T1{10, "哈哈,这都可以?", 1.5, -100}, "那么这样还可以吗?"}) 30 | a = append(a, true) 31 | a = append(a, false) 32 | a = append(a, [3]int{1, 2, 3}) 33 | a = append(a, []byte{}) 34 | m := make(map[int]string) 35 | m[1] = "map的第一个元素" 36 | m[1] = "map的第二个元素" 37 | a = append(a, m) 38 | s := make([]string, 0, 2) 39 | s = append(s, "这是slice的元素") 40 | a = append(a, s) 41 | str := "这是一个[]byte" 42 | s1 := []byte(str) 43 | a = append(a, s1) 44 | 45 | b := make([]interface{}, 0, 10) 46 | b = append(b, m) 47 | b = append(b, s) 48 | b = append(b, s1) 49 | a = append(a, b) 50 | a = append(a, a) 51 | a = append(a, [3]byte{'a', 'b', 'c'}) 52 | for _, v := range a { 53 | enc.Encode(v) 54 | } 55 | dec := gob.NewDecoder() 56 | dec.SetBuffer(enc.Buffer()) 57 | 58 | var ok bool = true 59 | var r interface{} 60 | idx := 0 61 | for ok { 62 | r, ok = dec.Decode() 63 | fmt.Println(r, reflect.TypeOf(r), ok) 64 | if ok { 65 | if !reflect.DeepEqual(r, a[idx]) { 66 | t.Errorf("%v is not equal to %v at idx %v", r, a[idx], idx) 67 | } 68 | if reflect.TypeOf(r) != reflect.TypeOf(a[idx]) { 69 | t.Errorf("%v is not equal to %v at idx %v", reflect.TypeOf(r), reflect.TypeOf(a[idx]), idx) 70 | } 71 | idx++ 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /encoding/gob/userType.go: -------------------------------------------------------------------------------- 1 | package gob 2 | 3 | func init() { 4 | RegisterStructType(T1{}) 5 | RegisterStructType(T2{}) 6 | } 7 | 8 | type T1 struct { 9 | A uint 10 | B string 11 | C float64 12 | E int32 13 | } 14 | 15 | type T2 struct { 16 | T1 17 | F string 18 | } 19 | -------------------------------------------------------------------------------- /helper/helper.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "regexp" 5 | "runtime/debug" 6 | ) 7 | 8 | var ( 9 | panicReg *regexp.Regexp 10 | logReg *regexp.Regexp 11 | ) 12 | 13 | func init() { 14 | regStr := `(?m)^panic\(.*\)$` 15 | panicReg = regexp.MustCompile(regStr) 16 | 17 | logReg = regexp.MustCompile(`(?m)^.*lotou/log/log\.go:.*$`) 18 | } 19 | 20 | func PanicWhen(b bool, s string) { 21 | if b { 22 | panic(s) 23 | } 24 | } 25 | 26 | //GetStack return calling stack as string in which messages before log and panic are tripped. 27 | func GetStack() string { 28 | stack := string(debug.Stack()) 29 | for { 30 | find := panicReg.FindStringIndex(stack) 31 | if find == nil { 32 | break 33 | } 34 | stack = stack[find[1]:] 35 | } 36 | for { 37 | find := logReg.FindStringIndex(stack) 38 | if find == nil { 39 | break 40 | } 41 | stack = stack[find[1]:] 42 | } 43 | return stack 44 | } 45 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "runtime" 7 | "sync" 8 | ) 9 | 10 | // levels 11 | const ( 12 | DEBUG_LEVEL = iota 13 | INFO_LEVEL 14 | WARN_LEVEL 15 | ERROR_LEVEL 16 | FATAL_LEVEL 17 | LEVEL_MAX 18 | ) 19 | 20 | const ( 21 | DEBUG_LEVEL_DESC = "[debug] " 22 | INFO_LEVEL_DESC = "[info ] " 23 | WARN_LEVEL_DESC = "[warn ] " 24 | ERROR_LEVEL_DESC = "[error] " 25 | FATAL_LEVEL_DESC = "[fatal] " 26 | ) 27 | 28 | type Msg struct { 29 | level int 30 | levelDesc string 31 | msg string 32 | } 33 | 34 | type Logger interface { 35 | DoPrintf(level int, levelDesc, msg string) 36 | SetColored(colored bool) 37 | Close() 38 | } 39 | 40 | var glogger Logger 41 | var gloggerMut sync.Mutex 42 | 43 | var HasCallerPos bool 44 | 45 | func do(level int, desc, format string, param ...interface{}) { 46 | if glogger == nil { 47 | log.Fatal("log is not init, please call log.Init first.") 48 | return 49 | } 50 | m := &Msg{level, desc, fmt.Sprintf(format, param...)} 51 | gloggerMut.Lock() 52 | glogger.DoPrintf(m.level, m.levelDesc, m.msg) 53 | gloggerMut.Unlock() 54 | 55 | if level == FATAL_LEVEL { 56 | format = desc + format 57 | panic(fmt.Sprintf(format, param...)) 58 | } 59 | } 60 | func SetLogger(logger Logger) { 61 | gloggerMut.Lock() 62 | glogger = logger 63 | gloggerMut.Unlock() 64 | } 65 | 66 | func preProcess(format string, level int) string { 67 | if level == INFO_LEVEL || level == WARN_LEVEL || level == FATAL_LEVEL { 68 | return format 69 | } 70 | if !HasCallerPos { 71 | return format 72 | } 73 | pc, file, line, ok := runtime.Caller(2) 74 | if ok { 75 | t := runtime.FuncForPC(pc) 76 | name := t.Name() 77 | format = fmt.Sprintf("[%v:%v +%v] ", file, line, name) + format 78 | } 79 | return format 80 | } 81 | 82 | func Debug(format string, param ...interface{}) { 83 | format = preProcess(format, DEBUG_LEVEL) 84 | do(DEBUG_LEVEL, DEBUG_LEVEL_DESC, format, param...) 85 | } 86 | 87 | func Info(format string, param ...interface{}) { 88 | format = preProcess(format, INFO_LEVEL) 89 | do(INFO_LEVEL, INFO_LEVEL_DESC, format, param...) 90 | } 91 | 92 | func Warn(format string, param ...interface{}) { 93 | format = preProcess(format, WARN_LEVEL) 94 | do(WARN_LEVEL, WARN_LEVEL_DESC, format, param...) 95 | } 96 | 97 | func Error(format string, param ...interface{}) { 98 | format = preProcess(format, ERROR_LEVEL) 99 | do(ERROR_LEVEL, ERROR_LEVEL_DESC, format, param...) 100 | } 101 | 102 | func Fatal(format string, param ...interface{}) { 103 | format = preProcess(format, FATAL_LEVEL) 104 | do(FATAL_LEVEL, FATAL_LEVEL_DESC, format, param...) 105 | } 106 | 107 | func Close() { 108 | if glogger == nil { 109 | return 110 | } 111 | gloggerMut.Lock() 112 | glogger.Close() 113 | gloggerMut.Unlock() 114 | } 115 | 116 | //init log with SimpleLogger 117 | func Init(path string, fileLevel, shellLevel, maxLine, bufSize int) Logger { 118 | logger := CreateLogger(path, fileLevel, shellLevel, maxLine, bufSize) 119 | SetLogger(logger) 120 | return logger 121 | } 122 | 123 | //init log with default logger 124 | func init() { 125 | HasCallerPos = true 126 | SetLogger(DefaultLogger) 127 | } 128 | -------------------------------------------------------------------------------- /log/log_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path" 8 | "runtime" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type SimpleLogger struct { 14 | //file log 15 | fileLevel int 16 | fileLogger *log.Logger 17 | baseFile *os.File 18 | outputPath string 19 | maxLine int 20 | logLine int 21 | //shell log 22 | shellLevel int 23 | isColored bool 24 | buffer chan *Msg 25 | wg sync.WaitGroup 26 | } 27 | 28 | const ( 29 | COLOR_DEBUG_LEVEL_DESC = "[\x1b[32mdebug\x1b[0m] " 30 | COLOR_INFO_LEVEL_DESC = "[\x1b[36minfo \x1b[0m] " 31 | COLOR_WARN_LEVEL_DESC = "[\x1b[33mwarn \x1b[0m] " 32 | COLOR_ERROR_LEVEL_DESC = "[\x1b[31merror\x1b[0m] " 33 | COLOR_FATAL_LEVEL_DESC = "[\x1b[31mfatal\x1b[0m] " 34 | ) 35 | 36 | var color_format = []string{ 37 | COLOR_DEBUG_LEVEL_DESC, 38 | COLOR_INFO_LEVEL_DESC, 39 | COLOR_WARN_LEVEL_DESC, 40 | COLOR_ERROR_LEVEL_DESC, 41 | COLOR_FATAL_LEVEL_DESC, 42 | } 43 | 44 | var std_format = []string{ 45 | DEBUG_LEVEL_DESC, 46 | INFO_LEVEL_DESC, 47 | WARN_LEVEL_DESC, 48 | ERROR_LEVEL_DESC, 49 | FATAL_LEVEL_DESC, 50 | } 51 | 52 | func (self *SimpleLogger) SetColored(colored bool) { 53 | self.isColored = colored 54 | } 55 | 56 | func (self *SimpleLogger) doPrintf(level int, levelDesc, msg string) { 57 | nformat := levelDesc + msg 58 | if level >= self.fileLevel { 59 | if self.logLine > self.maxLine { 60 | if self.baseFile != nil { 61 | self.baseFile.Sync() 62 | self.baseFile.Close() 63 | } 64 | self.baseFile, _ = self.createLogFile(self.outputPath) 65 | self.fileLogger = log.New(self.baseFile, "", log.Ldate|log.Lmicroseconds) 66 | self.logLine = 0 67 | } 68 | if self.fileLogger != nil { 69 | self.fileLogger.Printf(nformat) 70 | self.logLine++ 71 | } 72 | } 73 | if level >= self.shellLevel { 74 | sel_fmt := color_format 75 | if !self.isColored { 76 | sel_fmt = std_format 77 | } 78 | nformat := sel_fmt[level] + msg 79 | log.Printf(nformat) 80 | } 81 | } 82 | 83 | func (self *SimpleLogger) DoPrintf(level int, levelDesc, msg string) { 84 | if self.buffer == nil { 85 | self.doPrintf(level, levelDesc, msg) 86 | return 87 | } 88 | self.buffer <- &Msg{level, levelDesc, msg} 89 | } 90 | 91 | func (self *SimpleLogger) setFileOutDir(path string) { 92 | self.outputPath = path 93 | } 94 | 95 | func (self *SimpleLogger) setFileLogLevel(level int) { 96 | self.fileLevel = level 97 | } 98 | func (self *SimpleLogger) setShellLogLevel(level int) { 99 | self.shellLevel = level 100 | } 101 | 102 | func (self *SimpleLogger) createLogFile(dir string) (*os.File, error) { 103 | now := time.Now() 104 | filename := fmt.Sprintf("_%d-%02d-%02d_%02d-%02d-%02d.log", 105 | now.Year(), 106 | now.Month(), 107 | now.Day(), 108 | now.Hour(), 109 | now.Minute(), 110 | now.Second()) 111 | if dir != "" { 112 | os.MkdirAll(dir, os.ModePerm) 113 | } 114 | file, err := os.Create(path.Join(dir, filename)) 115 | if err != nil { 116 | log.Printf("create file failed %v", err) 117 | return nil, err 118 | } 119 | return file, nil 120 | } 121 | 122 | func (self *SimpleLogger) Close() { 123 | for len(self.buffer) > 0 { 124 | runtime.Gosched() 125 | } 126 | close(self.buffer) 127 | self.wg.Wait() 128 | } 129 | 130 | func (self *SimpleLogger) run() { 131 | go func() { 132 | self.wg.Add(1) 133 | for { 134 | m, ok := <-self.buffer 135 | if ok { 136 | self.doPrintf(m.level, m.levelDesc, m.msg) 137 | } else { 138 | break 139 | } 140 | } 141 | self.wg.Done() 142 | }() 143 | } 144 | 145 | func CreateLogger(path string, fileLevel, shellLevel, maxLine, bufSize int) *SimpleLogger { 146 | logger := &SimpleLogger{} 147 | log.SetFlags(log.Ldate | log.Lmicroseconds) 148 | logger.setFileOutDir(path) 149 | logger.setFileLogLevel(fileLevel) 150 | logger.setShellLogLevel(shellLevel) 151 | logger.maxLine = maxLine 152 | logger.logLine = maxLine + 1 153 | if bufSize > 10 { 154 | log.Printf("log start with async mode.") 155 | logger.buffer = make(chan *Msg, bufSize) 156 | logger.run() 157 | } 158 | return logger 159 | } 160 | 161 | var DefaultLogger = CreateLogger("test", FATAL_LEVEL, DEBUG_LEVEL, 10000, 1000) 162 | -------------------------------------------------------------------------------- /log/log_test.go: -------------------------------------------------------------------------------- 1 | package log_test 2 | 3 | import ( 4 | "github.com/sydnash/lotou/log" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestLog(t *testing.T) { 10 | log.Init("test", log.DEBUG_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 11 | for { 12 | time.Sleep(time.Second) 13 | log.Debug("hahaha %v, %v", 2, 3) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lotou.go: -------------------------------------------------------------------------------- 1 | package lotou 2 | 3 | import ( 4 | "github.com/sydnash/lotou/conf" 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/topology" 8 | "math/rand" 9 | _ "net/http" 10 | _ "net/http/pprof" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | "time" 15 | ) 16 | 17 | type CloseFunc func() 18 | 19 | //Start start lotou with given modules which in is in data. 20 | //initialize log by config param 21 | //if lotou's network is standalone, then only start master service. 22 | //if lotou's network is not standalone, 23 | // if node is master, then start master service 24 | // if node is slave, then start slave service, and register node to get a nodeid from master which will block until it success 25 | //capture system's SIGKILL SIGTERM signal 26 | //and wait until all service are closed. 27 | //f will be called when SIGKILL or SIGTERM is received. 28 | func Start(f CloseFunc, data ...*core.ModuleParam) { 29 | StartWithName("", f, nil, data...) 30 | } 31 | 32 | func StartWithName(nodeName string, f CloseFunc, customLogger log.Logger, data ...*core.ModuleParam) { 33 | rand.Seed(time.Now().UnixNano()) 34 | 35 | if customLogger == nil { 36 | logFilePath := nodeName + "_" + conf.LogFilePath 37 | logger := log.Init(logFilePath, conf.LogFileLevel, conf.LogShellLevel, conf.LogMaxLine, conf.LogBufferSize) 38 | logger.SetColored(conf.LogHasColor) 39 | } else { 40 | log.SetLogger(customLogger) 41 | } 42 | core.InitNode(conf.CoreIsStandalone, conf.CoreIsMaster) 43 | 44 | log.Info("starting node: {%v}", nodeName) 45 | if !conf.CoreIsStandalone { 46 | if conf.CoreIsMaster { 47 | topology.StartMaster(conf.MasterListenIp, conf.MultiNodePort) 48 | } else { 49 | topology.StartSlave(conf.SlaveConnectIp, conf.MultiNodePort) 50 | } 51 | core.RegisterNode(nodeName) 52 | } else { 53 | topology.StartMaster(conf.MasterListenIp, conf.MultiNodePort) 54 | } 55 | 56 | for _, m := range data { 57 | core.StartService(m) 58 | } 59 | 60 | /*err := http.ListenAndServe(":10000", nil) 61 | if err != nil { 62 | log.Error("ListenAndServe: %v", err) 63 | }*/ 64 | 65 | c := make(chan os.Signal, 1) 66 | signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM) 67 | 68 | if f == nil { 69 | f = core.SendCloseToAll 70 | } 71 | go func() { 72 | for { 73 | sig := <-c 74 | log.Info("lotou closing down (signal: %v)", sig) 75 | f() 76 | } 77 | }() 78 | 79 | core.Wait() 80 | 81 | log.Close() 82 | } 83 | 84 | //RawStart start lotou, with no wait 85 | func RawStart(nodeName string, data ...*core.ModuleParam) { 86 | log.Init(conf.LogFilePath, conf.LogFileLevel, conf.LogShellLevel, conf.LogMaxLine, conf.LogBufferSize) 87 | 88 | core.InitNode(conf.CoreIsStandalone, conf.CoreIsMaster) 89 | 90 | log.Info("start node: {%v}", nodeName) 91 | if !conf.CoreIsStandalone { 92 | if conf.CoreIsMaster { 93 | topology.StartMaster(conf.MasterListenIp, conf.MultiNodePort) 94 | } else { 95 | topology.StartSlave(conf.SlaveConnectIp, conf.MultiNodePort) 96 | } 97 | core.RegisterNode(nodeName) 98 | } 99 | 100 | for _, m := range data { 101 | core.StartService(m) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /master_test.go: -------------------------------------------------------------------------------- 1 | package lotou_test 2 | 3 | import ( 4 | "github.com/sydnash/lotou" 5 | "github.com/sydnash/lotou/conf" 6 | "github.com/sydnash/lotou/core" 7 | "github.com/sydnash/lotou/log" 8 | "testing" 9 | ) 10 | 11 | type Game struct { 12 | *core.Skeleton 13 | remoteId core.ServiceID 14 | } 15 | 16 | /* 17 | func (g *Game) OnRequestMSG(src core.ServiceID, rid uint64, data ...interface{}) { 18 | g.Respond(src, rid, "world") 19 | } 20 | func (g *Game) OnCallMSG(src core.ServiceID, cid uint64, data ...interface{}) { 21 | g.Ret(src, cid, "world") 22 | } 23 | 24 | func (g *Game) OnNormalMSG(src core.ServiceID, data ...interface{}) { 25 | log.Info("%v, %v", src, data) 26 | //g.RawSend(src, core.MSG_TYPE_NORMAL, "222") 27 | }*/ 28 | func (g *Game) OnDistributeMSG(msg *core.Message) { 29 | log.Info("%v", msg) 30 | } 31 | func (g *Game) OnInit() { 32 | log.Info("OnInit: name:%v id:%v", g.Name, g.Id) 33 | g.remoteId, _ = core.NameToId("game1") 34 | log.Info("name2id: game1:%v", g.remoteId) 35 | if g.D > 0 { 36 | g.Schedule(100, 0, func(dt int) { 37 | log.Info("time schedule.") 38 | }) 39 | } 40 | g.RegisterHandlerFunc(core.MSG_TYPE_NORMAL, "testNormal", func(src core.ServiceID, data ...interface{}) { 41 | log.Info("%v, %v", src, data) 42 | }, true) 43 | g.RegisterHandlerFunc(core.MSG_TYPE_REQUEST, "testRequest", func(src core.ServiceID, data ...interface{}) string { 44 | return "world" 45 | }, true) 46 | g.RegisterHandlerFunc(core.MSG_TYPE_CALL, "testCall", func(src core.ServiceID, data ...interface{}) (string, string) { 47 | return "hello", "world" 48 | }, true) 49 | } 50 | 51 | func TestMaster(t *testing.T) { 52 | conf.CoreIsStandalone = false 53 | conf.CoreIsMaster = true 54 | game := &Game{core.NewSkeleton(0), 0} 55 | lotou.Start(nil, &core.ModuleParam{ 56 | N: "game1", 57 | M: game, 58 | L: 0, 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /network/tcp/agent.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "bufio" 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/helper" 7 | "github.com/sydnash/lotou/log" 8 | "net" 9 | "time" 10 | ) 11 | 12 | /* 13 | recieve message from network 14 | split message into package 15 | send package to dest service 16 | 17 | reciev other inner message and process(such as write to network; close; change dest service) 18 | 19 | agent has two goroutine: 20 | one to read message from tcpConnector and send to dest 21 | one read inner message chan and process 22 | 23 | this also has a timeout for first data arrived after agent create. 24 | */ 25 | 26 | type Agent struct { 27 | *core.Skeleton 28 | Con *net.TCPConn 29 | closeing bool 30 | hostService core.ServiceID 31 | hasDataArrived bool 32 | leftTimeBeforArrived int 33 | inbuffer []byte 34 | outbuffer *bufio.Writer 35 | parseCache *ParseCache 36 | } 37 | 38 | const AgentNoDataHoldtime = 5000 39 | 40 | func (a *Agent) OnInit() { 41 | a.hasDataArrived = false 42 | a.leftTimeBeforArrived = AgentNoDataHoldtime 43 | a.inbuffer = make([]byte, DEFAULT_RECV_BUFF_LEN) 44 | a.outbuffer = bufio.NewWriter(a.Con) 45 | a.parseCache = &ParseCache{} 46 | log.Info("receive a connect at: %v->%v", a.Con.LocalAddr(), a.Con.RemoteAddr()) 47 | a.sendToHost(core.MSG_TYPE_SOCKET, AGENT_ARRIVE, a.Con.LocalAddr().String(), a.Con.RemoteAddr().String()) 48 | go func() { 49 | defer func() { 50 | if err := recover(); err != nil { 51 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 52 | } 53 | }() 54 | for { 55 | pack, err := Subpackage(a.inbuffer, a.Con, a.parseCache) 56 | if err != nil { 57 | log.Error("agent read msg failed: %s", err) 58 | a.onConnectError() 59 | break 60 | } 61 | if !a.hasDataArrived { 62 | a.hasDataArrived = true 63 | } 64 | for _, v := range pack { 65 | a.sendToHost(core.MSG_TYPE_SOCKET, AGENT_DATA, v) 66 | } 67 | } 68 | }() 69 | } 70 | 71 | func NewAgent(con *net.TCPConn, hostID core.ServiceID) *Agent { 72 | a := &Agent{ 73 | Con: con, 74 | hostService: hostID, 75 | Skeleton: core.NewSkeleton(5000), 76 | } 77 | return a 78 | } 79 | 80 | func (a *Agent) OnMainLoop(dt int) { 81 | if !a.hasDataArrived { 82 | a.leftTimeBeforArrived -= dt 83 | if a.leftTimeBeforArrived < 0 { 84 | log.Error("agent hasn't got any data within %v ms", AgentNoDataHoldtime) 85 | a.SendClose(a.Id, false) 86 | } 87 | } 88 | } 89 | 90 | func (a *Agent) OnNormalMSG(msg *core.Message) { 91 | if a.closeing { 92 | return 93 | } 94 | data := msg.Data 95 | cmd := msg.Cmd 96 | if cmd == AGENT_CMD_SEND { 97 | a.Con.SetWriteDeadline(time.Now().Add(time.Second * 20)) 98 | msg := data[0].([]byte) 99 | if _, err := a.outbuffer.Write(msg); err != nil { 100 | log.Error("agent write msg failed: %s", err) 101 | a.onConnectError() 102 | return 103 | } 104 | if err := a.outbuffer.Flush(); err != nil { 105 | log.Error("agent flush msg failed: %s", err) 106 | a.onConnectError() 107 | return 108 | } 109 | } 110 | } 111 | 112 | func (a *Agent) OnDestroy() { 113 | a.close() 114 | } 115 | 116 | func (a *Agent) onConnectError() { 117 | a.sendToHost(core.MSG_TYPE_SOCKET, AGENT_CLOSED) 118 | a.SendClose(a.Id, false) 119 | a.closeing = true 120 | } 121 | 122 | func (a *Agent) close() { 123 | log.Info("close agent. %v", a.Con.RemoteAddr()) 124 | a.Con.Close() 125 | } 126 | 127 | func (a *Agent) sendToHost(msgType core.MsgType, cmd core.CmdType, data ...interface{}) { 128 | a.RawSend(a.hostService, msgType, cmd, data...) 129 | } 130 | -------------------------------------------------------------------------------- /network/tcp/client.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "github.com/sydnash/lotou/core" 7 | "github.com/sydnash/lotou/helper" 8 | "github.com/sydnash/lotou/log" 9 | "net" 10 | "time" 11 | ) 12 | 13 | const ( 14 | CLIENT_STATUS_NOT_CONNECT = iota 15 | CLIENT_STATUS_CONNECTING 16 | CLIENT_STATUS_CONNECTED 17 | ) 18 | 19 | type Client struct { 20 | *core.Skeleton 21 | Con *net.TCPConn 22 | RemoteAddress *net.TCPAddr 23 | hostService core.ServiceID 24 | inbuffer []byte 25 | outbuffer *bufio.Writer 26 | parseCache *ParseCache 27 | status int32 28 | bufferForOutMsg *bytes.Buffer 29 | isNeedExit bool 30 | } 31 | 32 | func NewClient(host, port string, hostID core.ServiceID) *Client { 33 | c := &Client{ 34 | Skeleton: core.NewSkeleton(0), 35 | hostService: hostID, 36 | } 37 | address := net.JoinHostPort(host, port) 38 | tcpAddress, err := net.ResolveTCPAddr("tcp", address) 39 | if err != nil { 40 | log.Error("tcp resolve failed.") 41 | return nil 42 | } 43 | c.RemoteAddress = tcpAddress 44 | //status modify on main goroutinue 45 | c.status = CLIENT_STATUS_NOT_CONNECT 46 | c.bufferForOutMsg = bytes.NewBuffer([]byte{}) 47 | return c 48 | } 49 | 50 | func (c *Client) OnInit() { 51 | c.inbuffer = make([]byte, DEFAULT_RECV_BUFF_LEN) 52 | } 53 | 54 | func (c *Client) OnDestroy() { 55 | c.isNeedExit = true 56 | if c.Con != nil { 57 | c.Con.Close() 58 | c.Con = nil 59 | } 60 | } 61 | func (c *Client) onConnect(n int) { 62 | c.connect(n) 63 | } 64 | 65 | func (c *Client) sendBufferOutMsgAndData(data []byte) { 66 | if c.Con != nil { 67 | c.Con.SetWriteDeadline(time.Now().Add(time.Second * 5)) 68 | if c.bufferForOutMsg.Len() > 0 { 69 | _, err := c.outbuffer.Write(c.bufferForOutMsg.Bytes()) 70 | if err != nil { 71 | log.Error("client onSend tmp out msg err: %s", err) 72 | c.onConError() 73 | } 74 | c.bufferForOutMsg.Reset() 75 | } 76 | if data != nil { 77 | _, err := c.outbuffer.Write(data) 78 | if err != nil { 79 | log.Error("client onSend writebuff err: %s", err) 80 | c.onConError() 81 | } 82 | } 83 | if c.Con != nil { 84 | err := c.outbuffer.Flush() 85 | if err != nil { 86 | log.Error("client onSend err: %s", err) 87 | c.onConError() 88 | } 89 | } 90 | } 91 | } 92 | 93 | func (c *Client) onSend(src core.ServiceID, param ...interface{}) { 94 | //status check and modify on main goroutinue 95 | if c.status != CLIENT_STATUS_CONNECTED { 96 | if c.status == CLIENT_STATUS_NOT_CONNECT { 97 | c.status = CLIENT_STATUS_CONNECTING 98 | c.isNeedExit = false 99 | go c.connect(-1) 100 | } 101 | data := param[0].([]byte) 102 | defer func() { 103 | if err := recover(); err != nil { 104 | if err == bytes.ErrTooLarge { 105 | log.Error("client out msg buffer is too large, we will reset it.") 106 | c.bufferForOutMsg.Reset() 107 | } else { 108 | panic(err) 109 | } 110 | } 111 | }() 112 | c.bufferForOutMsg.Write(data) 113 | return 114 | } 115 | //sendBufferOutMsgAndData on main goroutinue 116 | c.sendBufferOutMsgAndData(param[0].([]byte)) 117 | } 118 | 119 | func (c *Client) OnNormalMSG(msg *core.Message) { 120 | src := msg.Src 121 | cmd := msg.Cmd 122 | param := msg.Data 123 | switch cmd { 124 | case CLIENT_CMD_CONNECT: 125 | n := param[0].(int) 126 | c.onConnect(n) 127 | case CLIENT_CMD_SEND: 128 | c.onSend(src, param...) 129 | case CLIENT_SELF_CONNECTED: 130 | //status modify on main goroutinue 131 | c.status = CLIENT_STATUS_CONNECTED 132 | //sendBufferOutMsgAndData on main goroutinue 133 | c.sendBufferOutMsgAndData(nil) 134 | case CLIENT_SELF_DISCONNECTED: 135 | //status modify on goroutinue 136 | c.status = CLIENT_STATUS_NOT_CONNECT 137 | c.OnDestroy() 138 | } 139 | } 140 | 141 | func (c *Client) connect(n int) { 142 | defer func() { 143 | if err := recover(); err != nil { 144 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 145 | } 146 | }() 147 | i := 0 148 | for { 149 | if c.isNeedExit { 150 | return 151 | } 152 | if c.Con == nil { 153 | var err error 154 | c.Con, err = net.DialTCP("tcp", nil, c.RemoteAddress) 155 | if err != nil { 156 | //log.Error("client connect failed: %s", err) 157 | } else { 158 | break 159 | } 160 | } 161 | time.Sleep(time.Second * 2) 162 | i++ 163 | if n > 0 && i >= n { 164 | break 165 | } 166 | } 167 | if c.Con == nil { 168 | c.sendToHost(core.MSG_TYPE_SOCKET, CLIENT_CONNECT_FAILED) //connect failed 169 | } else { 170 | if c.outbuffer == nil { 171 | c.outbuffer = bufio.NewWriter(c.Con) 172 | c.parseCache = &ParseCache{} 173 | } else { 174 | c.outbuffer.Reset(c.Con) 175 | c.parseCache.reset() 176 | } 177 | c.sendToHost(core.MSG_TYPE_SOCKET, CLIENT_CONNECTED) //connect success 178 | //send normal msg to tell self tcp is connected. 179 | c.RawSend(c.Id, core.MSG_TYPE_NORMAL, CLIENT_SELF_CONNECTED) 180 | go func() { 181 | defer func() { 182 | if err := recover(); err != nil { 183 | log.Error("recover: stack: %v\n, %v", helper.GetStack(), err) 184 | } 185 | }() 186 | for { 187 | //split package 188 | pack, err := Subpackage(c.inbuffer, c.Con, c.parseCache) 189 | if err != nil { 190 | log.Error("client read msg failed: %s", err) 191 | c.onConError() 192 | break 193 | } 194 | for _, v := range pack { 195 | c.sendToHost(core.MSG_TYPE_SOCKET, CLIENT_DATA, v) //recv message 196 | } 197 | } 198 | }() 199 | } 200 | } 201 | 202 | func (c *Client) onConError() { 203 | c.sendToHost(core.MSG_TYPE_SOCKET, CLIENT_DISCONNECTED) //disconnected 204 | //send normal msg to tell self tcp is diconnected. 205 | c.RawSend(c.Id, core.MSG_TYPE_NORMAL, CLIENT_SELF_DISCONNECTED) 206 | } 207 | 208 | func (c *Client) sendToHost(msgType core.MsgType, cmd core.CmdType, data ...interface{}) { 209 | c.RawSend(c.hostService, msgType, cmd, data...) 210 | } 211 | -------------------------------------------------------------------------------- /network/tcp/client_test.go: -------------------------------------------------------------------------------- 1 | package tcp_test 2 | 3 | import ( 4 | "github.com/sydnash/lotou/core" 5 | "github.com/sydnash/lotou/encoding/binary" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/network/tcp" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | type C struct { 13 | *core.Skeleton 14 | client core.ServiceID 15 | encoder *binary.Encoder 16 | decoder *binary.Decoder 17 | } 18 | 19 | func (c *C) OnMainLoop(dt int) { 20 | var a []byte = []byte("alsdkjfladjflkasdjf") 21 | c.encoder.Reset() 22 | c.encoder.Encode(a) 23 | c.encoder.UpdateLen() 24 | t := c.encoder.Buffer() 25 | t1 := make([]byte, len(t)) 26 | copy(t1[:], t[:]) 27 | c.RawSend(c.client, core.MSG_TYPE_NORMAL, tcp.CLIENT_CMD_SEND, t1) 28 | } 29 | 30 | func (c *C) OnNormalMSG(msg *core.Message) { 31 | data := msg.Data 32 | if len(data) >= 2 { 33 | log.Info("recv data :%s", string(data[0].([]byte))) 34 | } 35 | } 36 | 37 | func (c *C) OnSocketMSG(msg *core.Message) { 38 | cmd := msg.Cmd 39 | data := msg.Data 40 | if cmd == tcp.CLIENT_DATA { 41 | data := data[0].([]byte) 42 | c.decoder.SetBuffer(data) 43 | var msg []byte = []byte{} 44 | c.decoder.Decode(&msg) 45 | log.Info(string(msg)) 46 | } 47 | } 48 | 49 | func TestClient(t *testing.T) { 50 | log.Init("test", log.FATAL_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 51 | 52 | for i := 0; i < 1; i++ { 53 | c := &C{Skeleton: core.NewSkeleton(10)} 54 | core.StartService(&core.ModuleParam{ 55 | N: ".client", 56 | M: c, 57 | L: 0, 58 | }) 59 | c.encoder = binary.NewEncoder() 60 | c.decoder = binary.NewDecoder() 61 | 62 | client := tcp.NewClient("127.0.0.1", "3333", c.Id) 63 | c.client = core.StartService(&core.ModuleParam{ 64 | N: ".cc", 65 | M: client, 66 | L: 0, 67 | }) 68 | } 69 | 70 | for { 71 | time.Sleep(time.Minute) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /network/tcp/msgmethod.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "github.com/sydnash/lotou/core" 5 | ) 6 | 7 | const ( 8 | AGENT_CLOSED core.CmdType = "CmdType.Tcp.AgentClosed" 9 | AGENT_DATA core.CmdType = "CmdType.Tcp.AgentData" 10 | AGENT_ARRIVE core.CmdType = "CmdType.Tcp.AgentArrive" 11 | AGENT_CMD_SEND core.CmdType = "CmdType.Tcp.AgentSend" 12 | CLIENT_CONNECT_FAILED core.CmdType = "CmdType.Tcp.ClientConnectFailed" 13 | CLIENT_CONNECTED core.CmdType = "CmdType.Tcp.ClientConnected" 14 | CLIENT_SELF_CONNECTED core.CmdType = "CmdType.Tcp.ClientSelfConnected" 15 | CLIENT_SELF_DISCONNECTED core.CmdType = "CmdType.Tcp.ClientSelfDisconnected" 16 | CLIENT_DISCONNECTED core.CmdType = "CmdType.Tcp.ClientDisconnected" 17 | CLIENT_DATA core.CmdType = "CmdType.Tcp.ClientData" 18 | CLIENT_CMD_CONNECT core.CmdType = "CmdType.Tcp.ClientConnect" 19 | CLIENT_CMD_SEND core.CmdType = "CmdType.Tcp.ClientSend" 20 | ) 21 | 22 | const ( 23 | MAX_PACKET_LEN = 1024 * 1024 * 100 //100M 24 | DEFAULT_RECV_BUFF_LEN = 1 * 1024 25 | ) 26 | -------------------------------------------------------------------------------- /network/tcp/server.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | /* 4 | server listen on a tcp port 5 | when a tcp connect comming in 6 | create a agent 7 | */ 8 | import ( 9 | "net" 10 | "time" 11 | 12 | "github.com/sydnash/lotou/core" 13 | "github.com/sydnash/lotou/log" 14 | ) 15 | 16 | type Server struct { 17 | Host string 18 | Port string 19 | AcceptWhiteIpList []net.IP 20 | hostService core.ServiceID 21 | listener *net.TCPListener 22 | } 23 | 24 | const ( 25 | TCPServerClosed = "TCPServerClosed" 26 | ) 27 | 28 | func NewServer(host, port string, hsID core.ServiceID) *Server { 29 | s := &Server{ 30 | Host: host, 31 | Port: port, 32 | hostService: hsID, 33 | } 34 | return s 35 | } 36 | 37 | func (self *Server) SetAcceptWhiteIPList(ipList []string) { 38 | self.AcceptWhiteIpList = self.AcceptWhiteIpList[0:0] 39 | for _, ipStr := range ipList { 40 | ip := net.ParseIP(ipStr) 41 | if ip != nil { 42 | self.AcceptWhiteIpList = append(self.AcceptWhiteIpList, ip) 43 | } 44 | } 45 | } 46 | 47 | func (self *Server) Listen() error { 48 | address := net.JoinHostPort(self.Host, self.Port) 49 | tcpAddress, err := net.ResolveTCPAddr("tcp", address) 50 | if err != nil { 51 | log.Error("tcp server: resolve tcp address failed, %s", err) 52 | return err 53 | } 54 | self.listener, err = net.ListenTCP("tcp", tcpAddress) 55 | if err != nil { 56 | log.Error("tcp server: listen tcp failed %s", err) 57 | return err 58 | } 59 | 60 | go func() { 61 | var tempDelay time.Duration 62 | for { 63 | tcpCon, err := self.listener.AcceptTCP() 64 | if err != nil { 65 | if ne, ok := err.(net.Error); ok && ne.Temporary() { 66 | if tempDelay == 0 { 67 | tempDelay = 5 * time.Millisecond 68 | } else { 69 | tempDelay *= 2 70 | } 71 | if max := 1 * time.Second; tempDelay > max { 72 | tempDelay = max 73 | } 74 | log.Warn("tcp server: accept error: %v; retrying in %v", err, tempDelay) 75 | time.Sleep(tempDelay) 76 | continue 77 | } 78 | log.Error("tcp server: accept err %s, server closed.", err) 79 | core.Send(self.hostService, core.MSG_TYPE_NORMAL, core.MSG_ENC_TYPE_NO, TCPServerClosed) 80 | break 81 | } 82 | if len(self.AcceptWhiteIpList) > 0 { 83 | remoteAddr := tcpCon.RemoteAddr() 84 | tcpAddr, e := net.ResolveTCPAddr("tcp", remoteAddr.String()) 85 | if e != nil { 86 | log.Error("tcp server: receive remote address error. %v ", e) 87 | continue 88 | } 89 | ip := tcpAddr.IP 90 | isAllowAccept := false 91 | for _, allowIP := range self.AcceptWhiteIpList { 92 | if allowIP.Equal(ip) { 93 | isAllowAccept = true 94 | break 95 | } 96 | } 97 | if !isAllowAccept { 98 | log.Error("tcp server: receive a remote connect with ip: [ %v ] which is not in white ip list. [ %v ], so close it.", ip, self.AcceptWhiteIpList) 99 | tcpCon.Close() 100 | continue 101 | } 102 | } 103 | a := NewAgent(tcpCon, self.hostService) 104 | core.StartService(&core.ModuleParam{ 105 | N: "", 106 | M: a, 107 | L: 0, 108 | }) 109 | } 110 | }() 111 | 112 | return nil 113 | } 114 | 115 | func (self *Server) Close() { 116 | if self.listener != nil { 117 | self.listener.Close() 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /network/tcp/server_test.go: -------------------------------------------------------------------------------- 1 | package tcp_test 2 | 3 | import ( 4 | "github.com/sydnash/lotou/core" 5 | "github.com/sydnash/lotou/encoding/binary" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/network/tcp" 8 | "testing" 9 | ) 10 | 11 | type M struct { 12 | *core.Skeleton 13 | decoder *binary.Decoder 14 | } 15 | 16 | func (m *M) OnNormalMSG(msg *core.Message) { 17 | cmd := msg.Cmd 18 | if cmd == tcp.AGENT_CLOSED { 19 | log.Info("agent closed") 20 | } 21 | } 22 | 23 | func (m *M) OnSocketMSG(msg *core.Message) { 24 | src := msg.Src 25 | cmd := msg.Cmd 26 | data := msg.Data 27 | if cmd == tcp.AGENT_DATA { 28 | data := data[0].([]byte) 29 | m.decoder.SetBuffer(data) 30 | var msg []byte = []byte{} 31 | m.decoder.Decode(&msg) 32 | log.Info("%v, %v", src, string(msg)) 33 | 34 | m.RawSend(src, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, data) 35 | } 36 | } 37 | 38 | func TestServer(t *testing.T) { 39 | log.Init("test", log.FATAL_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 40 | m := &M{Skeleton: core.NewSkeleton(0)} 41 | m.decoder = binary.NewDecoder() 42 | core.StartService(&core.ModuleParam{ 43 | N: ".m", 44 | M: m, 45 | L: 0, 46 | }) 47 | 48 | s := tcp.NewServer("", "3333", m.Id) 49 | s.Listen() 50 | 51 | ch := make(chan int) 52 | <-ch 53 | 54 | s.Close() 55 | } 56 | -------------------------------------------------------------------------------- /network/tcp/subpackage.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "errors" 5 | "github.com/sydnash/lotou/log" 6 | "net" 7 | ) 8 | 9 | const ( 10 | PARSE_STATUS_LEN int = iota 11 | PARSE_STATUS_MSG 12 | PARSE_STATUS_END 13 | ) 14 | 15 | const ( 16 | PACKAGE_LEN_COUNT int = 4 17 | ) 18 | 19 | var ErrPacketLenExceed = errors.New("packet length exceed") 20 | 21 | type ParseCache struct { 22 | msg []byte 23 | msgLen int 24 | copyLen int 25 | status int 26 | msgHeader [PACKAGE_LEN_COUNT]byte 27 | copyHeaderLen int 28 | } 29 | 30 | func (p *ParseCache) reset() { 31 | p.msg = nil 32 | p.msgLen = 0 33 | p.copyLen = 0 34 | p.copyHeaderLen = 0 35 | p.status = PARSE_STATUS_LEN 36 | } 37 | 38 | func IntToByteSlice(v uint32) []byte { 39 | a := make([]byte, 4) 40 | a[3] = byte((v >> 24) & 0xFF) 41 | a[2] = byte((v >> 16) & 0XFF) 42 | a[1] = byte((v >> 8) & 0XFF) 43 | a[0] = byte(v & 0XFF) 44 | return a 45 | } 46 | func ByteSliceToInt(s []byte) (v uint32) { 47 | v = uint32(s[3])<<24 | uint32(s[2])<<16 | uint32(s[1])<<8 | uint32(s[0]) 48 | return v 49 | } 50 | 51 | func Subpackage(cache []byte, in net.Conn, status *ParseCache) (pack [][]byte, err error) { 52 | READ_LOOP: 53 | for { 54 | if len(pack) > 0 { 55 | return pack, nil 56 | } 57 | n, err := in.Read(cache) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | startPos := 0 63 | for { 64 | switch status.status { 65 | case PARSE_STATUS_LEN: 66 | if len(cache[startPos:n]) < PACKAGE_LEN_COUNT-status.copyHeaderLen { 67 | copyLen := copy(status.msgHeader[status.copyHeaderLen:], cache[startPos:n]) 68 | status.copyHeaderLen += copyLen 69 | if len(pack) == 0 { 70 | continue READ_LOOP 71 | } else { 72 | return pack, nil 73 | } 74 | } 75 | if status.copyHeaderLen == 0 { 76 | status.msgLen = int(ByteSliceToInt(cache[startPos:n])) 77 | startPos += PACKAGE_LEN_COUNT 78 | } else { 79 | copyLen := copy(status.msgHeader[status.copyHeaderLen:], cache[startPos:n]) 80 | startPos += copyLen 81 | status.msgLen = int(ByteSliceToInt(status.msgHeader[:])) 82 | } 83 | if status.msgLen > MAX_PACKET_LEN { 84 | log.Error("packet length(%v) exceeds the maximum message length %v", status.msgLen, MAX_PACKET_LEN) 85 | return pack, ErrPacketLenExceed 86 | } 87 | tmp := make([]byte, status.msgLen) 88 | if status.copyHeaderLen != 0 { 89 | copy(tmp, status.msgHeader[:]) 90 | } else { 91 | copy(tmp[0:PACKAGE_LEN_COUNT], cache[startPos-PACKAGE_LEN_COUNT:startPos]) 92 | } 93 | status.status = PARSE_STATUS_MSG 94 | status.msg = tmp 95 | status.copyLen = PACKAGE_LEN_COUNT 96 | case PARSE_STATUS_MSG: 97 | copyLen := copy(status.msg[status.copyLen:], cache[startPos:n]) 98 | status.copyLen += copyLen 99 | startPos += copyLen 100 | if status.copyLen != status.msgLen { 101 | if len(pack) == 0 { 102 | continue READ_LOOP 103 | } else { 104 | return pack, nil 105 | } 106 | } 107 | status.status = PARSE_STATUS_END 108 | case PARSE_STATUS_END: 109 | pack = append(pack, status.msg) 110 | status.reset() 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /slave_test.go: -------------------------------------------------------------------------------- 1 | package lotou_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sydnash/lotou" 6 | "github.com/sydnash/lotou/conf" 7 | "github.com/sydnash/lotou/core" 8 | "github.com/sydnash/lotou/log" 9 | "testing" 10 | ) 11 | 12 | func (g *Game) OnMainLoop(dt int) { 13 | log.Info("OnMainLoop %d", dt) 14 | if g.remoteId != 0 { 15 | go func() { 16 | log.Info("send remoteId: %v", g.remoteId) 17 | g.RawSend(g.remoteId, core.MSG_TYPE_NORMAL, "testNormal", 1, 2, 3, 4, "are you ok?") 18 | 19 | t := func(timeout bool, data string) { 20 | fmt.Println("request respond ", timeout, data) 21 | } 22 | g.Request(g.remoteId, core.MSG_ENC_TYPE_GO, 10, t, "testRequest", "hello") 23 | 24 | fmt.Println(g.Call(g.remoteId, core.MSG_ENC_TYPE_GO, "testCall", "hello")) 25 | }() 26 | } 27 | } 28 | 29 | func (g *Game) OnCloseNotify() { 30 | log.Info("recieve close notify") 31 | g.SendClose(g.Id, true) 32 | } 33 | 34 | func TestSlave(t *testing.T) { 35 | conf.CoreIsStandalone = false 36 | conf.CoreIsMaster = false 37 | 38 | game := &Game{core.NewSkeleton(1000), 0} 39 | lotou.Start(nil, &core.ModuleParam{ 40 | N: "game2", 41 | M: game, 42 | L: 0, 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /timer/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "errors" 5 | "github.com/sydnash/lotou/helper" 6 | "github.com/sydnash/lotou/log" 7 | ) 8 | 9 | var ( 10 | TimerIsComplete = errors.New("timer is complete") 11 | ) 12 | 13 | type TimerCallback func(int) 14 | type Timer struct { 15 | cb TimerCallback 16 | interval int //interval time of milloseconds per trigger 17 | elapsed int //time elapsed 18 | repeat int //repeat times, <= 0 forever 19 | repeated int //allready repeated times 20 | isComplete bool 21 | isForever bool 22 | } 23 | 24 | func NewTimer(interval, repeat int, cb TimerCallback) *Timer { 25 | if interval <= 0 { 26 | panic("NewTimer: interval is negative or zero.") 27 | } 28 | t := &Timer{} 29 | t.interval = interval 30 | t.cb = cb 31 | t.repeat = repeat 32 | t.isForever = (t.repeat <= 0) 33 | return t 34 | } 35 | 36 | func (t *Timer) update(dt int) { 37 | if t.isComplete { 38 | return 39 | } 40 | 41 | t.elapsed += dt 42 | if t.elapsed < t.interval { 43 | return 44 | } 45 | 46 | for t.elapsed >= t.interval { 47 | t.elapsed -= t.interval 48 | t.repeated += 1 49 | 50 | t.trigger() 51 | 52 | if !t.isForever { 53 | if t.repeated >= t.repeat { 54 | t.isComplete = true 55 | return 56 | } 57 | } 58 | } 59 | } 60 | 61 | func (t *Timer) trigger() { 62 | defer func() { 63 | if err := recover(); err != nil { 64 | log.Error("Timer:trigger stack: %v\n, %v", helper.GetStack(), err) 65 | } 66 | }() 67 | t.cb(t.interval) 68 | } 69 | 70 | //Reset reset timer's time elapsed and repeated times. 71 | func (t *Timer) Reset() error { 72 | if t.isComplete { 73 | return TimerIsComplete 74 | } 75 | t.elapsed = 0 76 | t.repeated = 0 77 | return nil 78 | } 79 | 80 | func (t *Timer) cancel() { 81 | t.isComplete = true 82 | } 83 | -------------------------------------------------------------------------------- /timer/timer_test.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestTimer(t *testing.T) { 10 | tick := time.NewTicker(time.Duration(100) * time.Millisecond) 11 | 12 | ts := NewTS() 13 | 14 | ts.Schedule(100, 10, func(dt int) { 15 | fmt.Println("time1") 16 | }) 17 | ts.Schedule(10, 10, func(dt int) { 18 | fmt.Println("timet-----") 19 | }) 20 | ts.Schedule(1000, 1, func(dt int) { 21 | fmt.Println("time2") 22 | }) 23 | ts.Schedule(100, -1, func(dt int) { 24 | fmt.Println("time3") 25 | }) 26 | 27 | go func() { 28 | for { 29 | <-tick.C 30 | ts.Update(10) 31 | } 32 | }() 33 | 34 | ch := make(chan int) 35 | <-ch 36 | } 37 | -------------------------------------------------------------------------------- /timer/timerschedule.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "github.com/sydnash/lotou/vector" 5 | "sync" 6 | ) 7 | 8 | type TimerSchedule struct { 9 | timers *vector.Vector 10 | addedCache *vector.Vector 11 | deleteCache *vector.Vector 12 | mutex sync.Mutex 13 | } 14 | 15 | func NewTS() *TimerSchedule { 16 | ts := &TimerSchedule{} 17 | ts.timers = vector.New() 18 | ts.addedCache = vector.New() 19 | ts.deleteCache = vector.New() 20 | return ts 21 | } 22 | 23 | //Update update all timers 24 | func (ts *TimerSchedule) Update(dt int) { 25 | ts.mutex.Lock() 26 | ts.timers.AppendVec(ts.addedCache) 27 | ts.addedCache.Clear() 28 | ts.mutex.Unlock() 29 | for i := 0; i < ts.timers.Len(); i++ { 30 | t := ts.timers.At(i).(*Timer) 31 | t.update(dt) 32 | if t.isComplete { 33 | ts.Unschedule(t) 34 | } 35 | } 36 | ts.mutex.Lock() 37 | for i := 0; i < ts.deleteCache.Len(); i++ { 38 | t := ts.deleteCache.At(i) 39 | for i := 0; i < ts.timers.Len(); i++ { 40 | if ts.timers.At(i) == t { 41 | ts.timers.Delete(i) 42 | break 43 | } 44 | } 45 | } 46 | ts.deleteCache.Clear() 47 | ts.mutex.Unlock() 48 | } 49 | 50 | //Schedule start a timer with interval and repeat. 51 | //callback will be triggerd each interval, and timer will delete after trigger repeat times 52 | //if interval is small than schedule's interval 53 | //it may trigger multitimes at a update. 54 | func (ts *TimerSchedule) Schedule(interval, repeat int, cb TimerCallback) *Timer { 55 | t := NewTimer(interval, repeat, cb) 56 | ts.mutex.Lock() 57 | ts.addedCache.Push(t) 58 | ts.mutex.Unlock() 59 | return t 60 | } 61 | 62 | func (ts *TimerSchedule) Unschedule(t *Timer) { 63 | ts.mutex.Lock() 64 | ts.deleteCache.Push(t) 65 | ts.mutex.Unlock() 66 | t.cancel() 67 | } 68 | -------------------------------------------------------------------------------- /topology/master.go: -------------------------------------------------------------------------------- 1 | package topology 2 | 3 | import ( 4 | "github.com/sydnash/lotou/conf" 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/encoding/gob" 7 | "github.com/sydnash/lotou/log" 8 | "github.com/sydnash/lotou/network/tcp" 9 | ) 10 | 11 | type Node struct { 12 | Agent core.ServiceID 13 | Name string 14 | } 15 | 16 | type master struct { 17 | *core.Skeleton 18 | nodesMap map[uint64]Node //nodeid : Node struct 19 | globalNameMap map[string]core.ServiceID 20 | tcpServer *tcp.Server 21 | isNeedExit bool 22 | } 23 | 24 | func StartMaster(ip, port string) { 25 | m := &master{Skeleton: core.NewSkeleton(0)} 26 | m.nodesMap = make(map[uint64]Node) 27 | m.globalNameMap = make(map[string]core.ServiceID) 28 | core.StartService(&core.ModuleParam{ 29 | N: ".router", 30 | M: m, 31 | L: 0, 32 | }) 33 | 34 | if !conf.CoreIsStandalone { 35 | m.tcpServer = tcp.NewServer(ip, port, m.Id) 36 | m.tcpServer.SetAcceptWhiteIPList(conf.SlaveWhiteIPList) 37 | m.tcpServer.Listen() 38 | } 39 | } 40 | 41 | func (m *master) OnNormalMSG(msg *core.Message) { 42 | //cmd such as (registerName, getIdByName, syncName, forward ...) 43 | cmd := msg.Cmd 44 | data := msg.Data 45 | switch cmd { 46 | case core.Cmd_Forward: 47 | msg := data[0].(*core.Message) 48 | m.forwardM(msg, nil) 49 | case core.Cmd_RegisterName: 50 | id := data[0].(uint64) 51 | name := data[1].(string) 52 | m.onRegisterName(core.ServiceID(id), name) 53 | case core.Cmd_GetIdByName: 54 | name := data[0].(string) 55 | rid := data[1].(uint) 56 | id, ok := m.globalNameMap[name] 57 | core.DispatchGetIdByNameRet(id, ok, name, rid) 58 | case core.Cmd_Exit: 59 | m.closeAll() 60 | case core.Cmd_Exit_Node: 61 | nodeName := data[0].(string) 62 | m.closeNode(nodeName) 63 | case core.Cmd_RefreshSlaveWhiteIPList: 64 | ips := data[0].([]string) 65 | m.tcpServer.SetAcceptWhiteIPList(ips) 66 | default: 67 | log.Info("Unknown command for master: %v", cmd) 68 | } 69 | } 70 | 71 | func (m *master) onRegisterNode(src core.ServiceID, nodeName string) { 72 | //generate node id 73 | nodeId := core.GenerateNodeId() 74 | log.Info("register node: nodeId: %v, nodeName: %v", nodeId, nodeName) 75 | m.nodesMap[nodeId] = Node{ 76 | Agent: src, 77 | Name: nodeName, 78 | } 79 | msg := core.NewMessage(core.INVALID_SERVICE_ID, core.INVALID_SERVICE_ID, core.MSG_TYPE_NORMAL, core.MSG_ENC_TYPE_NO, 0, core.Cmd_RegisterNodeRet, nodeId) 80 | sendData := gob.Pack(msg) 81 | m.RawSend(src, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, sendData) 82 | } 83 | 84 | func (m *master) onRegisterName(serviceId core.ServiceID, serviceName string) { 85 | m.globalNameMap[serviceName] = serviceId 86 | m.distributeM(core.Cmd_NameAdd, core.NodeInfo{serviceName, serviceId}) 87 | } 88 | 89 | func (m *master) onGetIdByName(src core.ServiceID, name string, rId uint) { 90 | id, ok := m.globalNameMap[name] 91 | msg := core.NewMessage(core.INVALID_SERVICE_ID, core.INVALID_SERVICE_ID, core.MSG_TYPE_NORMAL, core.MSG_ENC_TYPE_NO, 0, core.Cmd_GetIdByNameRet, id, ok, name, rId) 92 | sendData := gob.Pack(msg) 93 | m.RawSend(src, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, sendData) 94 | } 95 | 96 | func (m *master) OnSocketMSG(msg *core.Message) { 97 | //src is slave's agent's serviceid 98 | src := msg.Src 99 | //cmd is socket status 100 | cmd := msg.Cmd 101 | //data[0] is a gob encode with message 102 | data := msg.Data 103 | //it's first encode value is cmd such as (registerNode, regeisterName, getIdByName, forward...) 104 | if cmd == tcp.AGENT_DATA { 105 | sdata, err := gob.Unpack(data[0].([]byte)) 106 | if err != nil { 107 | m.SendClose(src, false) 108 | return 109 | } 110 | slaveMSG := sdata.([]interface{})[0].(*core.Message) 111 | scmd := slaveMSG.Cmd 112 | array := slaveMSG.Data 113 | switch scmd { 114 | case core.Cmd_RegisterNode: 115 | nodeName := array[0].(string) 116 | m.onRegisterNode(src, nodeName) 117 | case core.Cmd_RegisterName: 118 | serviceId := array[0].(uint64) 119 | serviceName := array[1].(string) 120 | m.onRegisterName(core.ServiceID(serviceId), serviceName) 121 | case core.Cmd_GetIdByName: 122 | name := array[0].(string) 123 | rId := array[1].(uint) 124 | m.onGetIdByName(src, name, rId) 125 | case core.Cmd_Forward: 126 | //find correct agent and send msg to that node. 127 | forwardMsg := array[0].(*core.Message) 128 | m.forwardM(forwardMsg, data[0].([]byte)) 129 | case core.Cmd_Exit: 130 | m.closeAll() 131 | case core.Cmd_Exit_Node: 132 | nodeName := array[0].(string) 133 | m.closeNode(nodeName) 134 | case core.Cmd_RefreshSlaveWhiteIPList: 135 | ips := array[0].([]string) 136 | m.tcpServer.SetAcceptWhiteIPList(ips) 137 | } 138 | } else if cmd == tcp.AGENT_CLOSED { 139 | //on agent disconnected 140 | //delet node from nodesMap 141 | var nodeId uint64 = 0 142 | hasFind := false 143 | for id, v := range m.nodesMap { 144 | if v.Agent == src { 145 | hasFind = true 146 | nodeId = id 147 | } 148 | } 149 | if !hasFind { 150 | return 151 | } 152 | delete(m.nodesMap, nodeId) 153 | core.CollectNodeId(nodeId) 154 | 155 | //notify other services delete name's id on agent which is disconnected. 156 | deletedNames := []interface{}{} 157 | for name, id := range m.globalNameMap { 158 | nid := core.ParseNodeId(id) 159 | if nid == nodeId { 160 | log.Warn("service is delete: name: %v id: %v", name, id) 161 | deletedNames = append(deletedNames, core.NodeInfo{name, id}) 162 | delete(m.globalNameMap, name) 163 | } 164 | } 165 | m.distributeM(core.Cmd_NameDeleted, deletedNames...) 166 | 167 | if len(m.nodesMap) == 0 && m.isNeedExit { 168 | core.SendCloseToAll() 169 | } 170 | } 171 | } 172 | 173 | func (m *master) distributeM(cmd core.CmdType, data ...interface{}) { 174 | for _, node := range m.nodesMap { 175 | msg := &core.Message{} 176 | msg.Cmd = core.Cmd_Distribute 177 | msg.Data = append(msg.Data, cmd) 178 | msg.Data = append(msg.Data, data...) 179 | sendData := gob.Pack(msg) 180 | m.RawSend(node.Agent, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, sendData) 181 | } 182 | core.DistributeMSG(m.Id, cmd, data...) 183 | } 184 | 185 | func (m *master) closeNode(nodeName string) { 186 | for _, node := range m.nodesMap { 187 | if node.Name == nodeName { 188 | msg := &core.Message{} 189 | msg.Cmd = core.Cmd_Exit 190 | sendData := gob.Pack(msg) 191 | m.RawSend(node.Agent, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, sendData) 192 | } 193 | } 194 | } 195 | 196 | func (m *master) closeAll() { 197 | m.isNeedExit = true 198 | for _, node := range m.nodesMap { 199 | msg := &core.Message{} 200 | msg.Cmd = core.Cmd_Exit 201 | sendData := gob.Pack(msg) 202 | m.RawSend(node.Agent, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, sendData) 203 | } 204 | if len(m.nodesMap) == 0 { 205 | core.SendCloseToAll() 206 | } 207 | } 208 | 209 | func (m *master) forwardM(msg *core.Message, data []byte) { 210 | nodeId := core.ParseNodeId(core.ServiceID(msg.Dst)) 211 | isLcoal := core.CheckIsLocalServiceId(core.ServiceID(msg.Dst)) 212 | //log.Debug("master forwardM is send to master: %v, nodeid: %d", isLcoal, nodeId) 213 | if isLcoal { 214 | core.ForwardLocal(msg) 215 | return 216 | } 217 | node, ok := m.nodesMap[nodeId] 218 | if !ok { 219 | log.Debug("node:%v is disconnected.", nodeId) 220 | return 221 | } 222 | //if has no encode data, encode it first. 223 | if data == nil { 224 | ret := &core.Message{ 225 | Cmd: core.Cmd_Forward, 226 | } 227 | ret.Data = append(ret.Data, msg) 228 | data = gob.Pack(ret) 229 | } 230 | m.RawSend(node.Agent, core.MSG_TYPE_NORMAL, tcp.AGENT_CMD_SEND, data) 231 | } 232 | 233 | func (m *master) OnDestroy() { 234 | if m.tcpServer != nil { 235 | m.tcpServer.Close() 236 | } 237 | for _, v := range m.nodesMap { 238 | m.SendClose(v.Agent, false) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /topology/master_test.go: -------------------------------------------------------------------------------- 1 | package topology_test 2 | 3 | import ( 4 | "github.com/sydnash/lotou/core" 5 | "github.com/sydnash/lotou/log" 6 | "github.com/sydnash/lotou/topology" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | type Game struct { 12 | *core.Skeleton 13 | } 14 | 15 | func (g *Game) OnRequestMSG(msg *core.Message) { 16 | g.Respond(msg.Src, core.MSG_ENC_TYPE_GO, msg.Id, "world") 17 | } 18 | func (g *Game) OnCallMSG(msg *core.Message) { 19 | g.Ret(msg.Src, core.MSG_ENC_TYPE_GO, msg.Id, "world") 20 | } 21 | 22 | func (g *Game) OnNormalMSG(msg *core.Message) { 23 | log.Info("%v", msg) 24 | //g.RawSend(src, core.MSG_TYPE_NORMAL, "222") 25 | } 26 | func (g *Game) OnDistributeMSG(msg *core.Message) { 27 | log.Info("%v", msg) 28 | } 29 | func TestMaster(t *testing.T) { 30 | log.Init("test", log.FATAL_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 31 | 32 | core.InitNode(false, true) 33 | topology.StartMaster("127.0.0.1", "4000") 34 | core.RegisterNode() 35 | 36 | game := &Game{core.NewSkeleton(0)} 37 | id := core.StartService(&core.ModuleParam{ 38 | N: "game1", 39 | M: game, 40 | L: 0, 41 | }) 42 | log.Info("game1's id :%v", id) 43 | 44 | log.Info("test") 45 | for { 46 | time.Sleep(time.Minute * 10) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /topology/slave.go: -------------------------------------------------------------------------------- 1 | package topology 2 | 3 | import ( 4 | "github.com/sydnash/lotou/core" 5 | "github.com/sydnash/lotou/encoding/gob" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/network/tcp" 8 | ) 9 | 10 | type slave struct { 11 | *core.Skeleton 12 | client core.ServiceID 13 | } 14 | 15 | func StartSlave(ip, port string) { 16 | m := &slave{Skeleton: core.NewSkeleton(0)} 17 | core.StartService(&core.ModuleParam{ 18 | N: ".router", 19 | M: m, 20 | L: 0, 21 | }) 22 | c := tcp.NewClient(ip, port, m.Id) 23 | m.client = core.StartService(&core.ModuleParam{ 24 | N: "", 25 | M: c, 26 | L: 0, 27 | }) 28 | } 29 | 30 | func (s *slave) OnNormalMSG(msg *core.Message) { 31 | //dest is master's id, src is core's id 32 | //data[0] is cmd such as (registerNode, regeisterName, getIdByName...) 33 | t1 := gob.Pack(msg) 34 | s.RawSend(s.client, core.MSG_TYPE_NORMAL, tcp.CLIENT_CMD_SEND, t1) 35 | } 36 | func (s *slave) OnSocketMSG(msg *core.Message) { 37 | //cmd is socket status 38 | cmd := msg.Cmd 39 | //data[0] is a gob encode data of Message 40 | data := msg.Data 41 | if cmd == tcp.CLIENT_DATA { 42 | sdata, err := gob.Unpack(data[0].([]byte)) 43 | if err != nil { 44 | return 45 | } 46 | masterMSG := sdata.([]interface{})[0].(*core.Message) 47 | scmd := masterMSG.Cmd 48 | array := masterMSG.Data 49 | switch scmd { 50 | case core.Cmd_RegisterNodeRet: 51 | nodeId := array[0].(uint64) 52 | core.DispatchRegisterNodeRet(nodeId) 53 | case core.Cmd_Distribute: 54 | core.DistributeMSG(s.Id, core.CmdType(array[0].(string)), array[1:]...) 55 | case core.Cmd_GetIdByNameRet: 56 | id := array[0].(uint64) 57 | ok := array[1].(bool) 58 | name := array[2].(string) 59 | rid := array[3].(uint) 60 | core.DispatchGetIdByNameRet(core.ServiceID(id), ok, name, rid) 61 | case core.Cmd_Forward: 62 | msg := array[0].(*core.Message) 63 | s.forwardM(msg) 64 | case core.Cmd_Exit: 65 | log.Info("receive exit command, node will exit now.") 66 | core.SendCloseToAll() 67 | } 68 | } 69 | } 70 | 71 | func (s *slave) forwardM(msg *core.Message) { 72 | isLcoal := core.CheckIsLocalServiceId(core.ServiceID(msg.Dst)) 73 | if isLcoal { 74 | core.ForwardLocal(msg) 75 | return 76 | } 77 | log.Warn("recv msg not forward to this node.") 78 | } 79 | 80 | func (s *slave) OnDestroy() { 81 | s.SendClose(s.client, false) 82 | } 83 | -------------------------------------------------------------------------------- /topology/slave_test.go: -------------------------------------------------------------------------------- 1 | package topology_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/topology" 8 | "testing" 9 | ) 10 | 11 | var ( 12 | remoteId core.ServiceID = 100 13 | ) 14 | 15 | func (g *Game) OnMainLoop(dt int) { 16 | if remoteId != 0 { 17 | log.Info("send") 18 | g.RawSend(remoteId, core.MSG_TYPE_NORMAL, "haha", 1, 2, 3, 4) 19 | 20 | t := func(timeout bool, data ...interface{}) { 21 | fmt.Println("request respond ", timeout, data) 22 | } 23 | g.Request(remoteId, core.MSG_ENC_TYPE_GO, 10, t, "hello") 24 | 25 | fmt.Println(g.Call(remoteId, core.MSG_ENC_TYPE_GO, "hello")) 26 | } 27 | } 28 | 29 | func TestSlavea(t *testing.T) { 30 | log.Init("test", log.FATAL_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 31 | 32 | remoteId = 0 33 | core.InitNode(false, false) 34 | topology.StartSlave("127.0.0.1", "4000") 35 | 36 | log.Info("start register node") 37 | core.RegisterNode() 38 | log.Info("start create service") 39 | game := &Game{core.NewSkeleton(1000)} 40 | core.StartService(&core.ModuleParam{ 41 | N: "game2", 42 | M: game, 43 | L: 0, 44 | }) 45 | log.Info("game2's id: %v", game.Id) 46 | 47 | var err error 48 | remoteId, err = core.NameToId("game1") 49 | log.Info("NameToId: %v, %v %v", "game1", remoteId, err) 50 | 51 | ch := make(chan int) 52 | <-ch 53 | } 54 | -------------------------------------------------------------------------------- /topology/slaveb_test.go: -------------------------------------------------------------------------------- 1 | package topology_test 2 | 3 | /* 4 | import ( 5 | "github.com/sydnash/lotou/core" 6 | "github.com/sydnash/lotou/log" 7 | "github.com/sydnash/lotou/topology" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestSlaveb(t *testing.T) { 13 | log.Init("test", log.FATAL_LEVEL, log.DEBUG_LEVEL, 10000, 1000) 14 | log.Debug("start slave") 15 | topology.StartSlave("127.0.0.1", "4000") 16 | core.RegisterNode() 17 | core.Name(1, "我是服务1") 18 | 19 | game := &Game{core.NewBase()} 20 | core.RegisterService(game) 21 | core.Name(game.Id(), "game1") 22 | game.SetDispatcher(game) 23 | go func() { 24 | for msg := range game.In() { 25 | game.DispatchM(msg) 26 | } 27 | }() 28 | 29 | for { 30 | time.Sleep(time.Minute * 10) 31 | } 32 | 33 | } 34 | 35 | */ 36 | -------------------------------------------------------------------------------- /vector/vector.go: -------------------------------------------------------------------------------- 1 | package vector 2 | 3 | type Comparable interface { 4 | Equal(Comparable) bool 5 | } 6 | 7 | type Vector struct { 8 | s []interface{} 9 | } 10 | 11 | func New() *Vector { 12 | return &Vector{} 13 | } 14 | 15 | func NewCap(n int) *Vector { 16 | v := &Vector{} 17 | v.s = make([]interface{}, 0, n) 18 | return v 19 | } 20 | 21 | func (v *Vector) Empty() bool { 22 | return v.Len() == 0 23 | } 24 | 25 | func (v *Vector) Len() int { 26 | return len(v.s) 27 | } 28 | 29 | func (v *Vector) Cap() int { 30 | return cap(v.s) 31 | } 32 | 33 | func (v *Vector) Append(data ...interface{}) { 34 | v.s = append(v.s, data...) 35 | } 36 | 37 | func (v *Vector) AppendVec(o *Vector) { 38 | v.Append(o.s...) 39 | } 40 | 41 | func (v *Vector) Clone() *Vector { 42 | t := NewCap(v.Len()) 43 | t.Append(v.s...) 44 | return t 45 | } 46 | 47 | func (v *Vector) Copy(src *Vector) { 48 | v.s = make([]interface{}, len(src.s)) 49 | copy(v.s[:], src.s[:]) 50 | } 51 | 52 | //Delete delete value at position i 53 | //if i is out of range, Delete will panic 54 | func (v *Vector) Delete(i int) { 55 | copy(v.s[i:], v.s[i+1:]) 56 | v.s[len(v.s)-1] = nil 57 | v.s = v.s[:len(v.s)-1] 58 | } 59 | 60 | func (v *Vector) Insert(i int, d interface{}) { 61 | v.s = append(v.s, nil) 62 | copy(v.s[i+1:], v.s[i:]) 63 | v.s[i] = d 64 | } 65 | func (v *Vector) InsertVariant(i int, d ...interface{}) { 66 | v.s = append(v.s[0:i], append(d, v.s[i:]...)...) 67 | } 68 | func (v *Vector) InsertVector(i int, d *Vector) { 69 | v.InsertVariant(i, d.s...) 70 | } 71 | 72 | //At will panic while i is out of range 73 | func (v *Vector) At(i int) interface{} { 74 | return v.s[i] 75 | } 76 | 77 | //Extend extend j space at tail 78 | func (v *Vector) Extend(j int) { 79 | v.s = append(v.s, make([]interface{}, j)...) 80 | } 81 | 82 | //ExtendAt extend j space after position i. 83 | func (v *Vector) ExtendAt(i, j int) { 84 | v.s = append(v.s[:i], append(make([]interface{}, j), v.s[i:]...)...) 85 | } 86 | 87 | func (v *Vector) Pop() (r interface{}) { 88 | r = v.s[len(v.s)-1] 89 | v.Delete(len(v.s) - 1) 90 | return r 91 | } 92 | 93 | func (v *Vector) Push(d interface{}) { 94 | v.Append(d) 95 | } 96 | 97 | func (v *Vector) PopFront() (r interface{}) { 98 | r = v.s[0] 99 | v.Delete(0) 100 | return r 101 | } 102 | 103 | func (v *Vector) PushFront(d interface{}) { 104 | v.Insert(0, d) 105 | } 106 | 107 | func (v *Vector) Reverse() { 108 | for left, right := 0, len(v.s)-1; left < right; left, right = left+1, right-1 { 109 | v.s[left], v.s[right] = v.s[right], v.s[left] 110 | } 111 | } 112 | 113 | func (v *Vector) Front() (r interface{}) { 114 | r = v.s[0] 115 | return r 116 | } 117 | 118 | //Back return last element in vector 119 | //it panics if vector is empty 120 | func (v *Vector) Back() (r interface{}) { 121 | r = v.s[len(v.s)-1] 122 | return r 123 | } 124 | 125 | func (v *Vector) Clear() { 126 | for i, _ := range v.s { 127 | v.s[i] = nil 128 | } 129 | v.s = v.s[0:0] 130 | } 131 | 132 | func isEqual(d1, d2 interface{}) bool { 133 | p, ok := d1.(Comparable) 134 | if ok { 135 | return p.Equal(d2.(Comparable)) 136 | } else { 137 | return d1 == d2 138 | } 139 | } 140 | 141 | func (v *Vector) IndexOf(d interface{}) int { 142 | for i, t := range v.s { 143 | if isEqual(t, d) { 144 | return i 145 | } 146 | } 147 | return -1 148 | } 149 | 150 | func (v *Vector) DeleteByValue(d interface{}) { 151 | for i, t := range v.s { 152 | if isEqual(t, d) { 153 | v.Delete(i) 154 | return 155 | } 156 | } 157 | } 158 | 159 | func (v *Vector) Raw() []interface{} { 160 | return v.s 161 | } 162 | -------------------------------------------------------------------------------- /vector/vector_test.go: -------------------------------------------------------------------------------- 1 | package vector 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | "testing" 7 | ) 8 | 9 | func TestLenAndCap(t *testing.T) { 10 | v := New() 11 | 12 | if v.Len() != 0 || v.Cap() != 0 { 13 | t.Error("len and cap must be 0") 14 | } 15 | 16 | v = NewCap(10) 17 | if v.Len() != 0 || v.Cap() != 10 { 18 | t.Error("len must be 0 and cap must 10") 19 | } 20 | } 21 | 22 | func errorOnDeepEqual(a []interface{}, b []interface{}, s string, t *testing.T) { 23 | if !reflect.DeepEqual(a, b) { 24 | t.Error(s) 25 | } 26 | } 27 | 28 | func TestAppend(t *testing.T) { 29 | v := New() 30 | v.Append([]interface{}{1, 2, 3}...) 31 | if !reflect.DeepEqual(v.s, []interface{}{1, 2, 3}) { 32 | t.Error("append") 33 | } 34 | v.Append(4) 35 | if !reflect.DeepEqual(v.s, []interface{}{1, 2, 3, 4}) { 36 | t.Error("append") 37 | } 38 | v.AppendVec(v) 39 | if !reflect.DeepEqual(v.s, []interface{}{1, 2, 3, 4, 1, 2, 3, 4}) { 40 | t.Error("append vec") 41 | } 42 | } 43 | 44 | func TestClone(t *testing.T) { 45 | v := NewCap(10) 46 | v.Append([]interface{}{1, 2, 3, 4}...) 47 | v1 := v.Clone() 48 | if !reflect.DeepEqual(v.s, v1.s) { 49 | t.Error("clone") 50 | } 51 | } 52 | 53 | func TestDelete(t *testing.T) { 54 | v := NewCap(10) 55 | v.Append([]interface{}{1, 2, 3, 4}...) 56 | 57 | v.Delete(0) 58 | 59 | if !reflect.DeepEqual(v.s, []interface{}{2, 3, 4}) { 60 | t.Error("delete") 61 | } 62 | 63 | v.Insert(0, 5) 64 | errorOnDeepEqual(v.s, []interface{}{5, 2, 3, 4}, "insert", t) 65 | 66 | v.InsertVariant(2, 6, 6) 67 | errorOnDeepEqual(v.s, []interface{}{5, 2, 6, 6, 3, 4}, "Insert", t) 68 | 69 | e2 := v.At(1) 70 | if e2 != 2 { 71 | t.Error("at") 72 | } 73 | 74 | v.Reverse() 75 | errorOnDeepEqual(v.s, []interface{}{4, 3, 6, 6, 2, 5}, "reverse", t) 76 | 77 | v.PushFront(0) 78 | v.Push(0) 79 | 80 | errorOnDeepEqual(v.s, []interface{}{0, 4, 3, 6, 6, 2, 5, 0}, "push", t) 81 | 82 | v.Pop() 83 | v.PopFront() 84 | 85 | errorOnDeepEqual(v.s, []interface{}{4, 3, 6, 6, 2, 5}, "reverse", t) 86 | 87 | n := New() 88 | n.Copy(v) 89 | errorOnDeepEqual(n.s, []interface{}{4, 3, 6, 6, 2, 5}, "Copy", t) 90 | 91 | n.DeleteByValue(4) 92 | errorOnDeepEqual(n.s, []interface{}{3, 6, 6, 2, 5}, "delete by value", t) 93 | } 94 | --------------------------------------------------------------------------------