├── .gitignore ├── LICENSE ├── README.md ├── args └── args.go ├── astar └── astar.go ├── bpool ├── bpool.go ├── bpool_test.go └── writer.go ├── crypto └── crypto.go ├── db ├── README.md ├── db.go ├── db_check.go ├── db_def.go ├── db_encode.go ├── db_mod.go ├── db_sync.go ├── db_sync_worker.go ├── db_test.go ├── env.go └── type.go ├── gate ├── README.md ├── acceptor.go ├── config.go ├── connect_starter.go ├── gate.go ├── gate_app.go ├── gate_epoll_test.go ├── gate_test.go ├── listener.go ├── nb_conn.go ├── net_conn.go ├── pb │ ├── decoder.go │ ├── encoder.go │ ├── pb.go │ └── type.go ├── reader.go └── type.go ├── go.mod ├── go.sum ├── gutil ├── sort.go ├── type.go ├── util.go └── util_test.go ├── httpc ├── httpc.go ├── httpc_manager.go ├── httpc_test.go ├── httpc_worker.go └── type.go ├── httpd ├── README.md ├── body.go ├── config.go ├── ejson │ └── json.go ├── error.go ├── example │ ├── http.go │ ├── websocket.go │ └── websocket.html ├── httpd.go ├── httpd_app.go ├── manager.go ├── parser.go ├── request.go ├── router.go ├── state.go ├── table.go ├── type.go ├── websocket │ ├── compression.go │ ├── config.go │ ├── conn.go │ ├── const.go │ ├── error.go │ └── upgrader.go └── worker.go ├── kernel ├── README.md ├── actor.go ├── application.go ├── console.go ├── context.go ├── deep_copy.go ├── env.go ├── init.go ├── kct │ ├── bmap.go │ ├── list.go │ ├── set.go │ └── string.go ├── kernel.go ├── kernel_test.go ├── logger.go ├── name_map.go ├── node.go ├── pid.go ├── self_sender.go ├── supervisor.go ├── time.go ├── timer.go └── type.go ├── node ├── env.go ├── gmpd │ ├── bin │ │ └── gmpd.sh │ ├── build.sh │ └── main.go ├── monitor.go ├── node.go ├── node_app.go ├── node_client.go ├── node_client_worker.go ├── proto_def.go ├── rpc.go ├── rpc_server.go └── type.go ├── pfun ├── ptr.go └── type.go ├── rand ├── rand.go ├── rand_test.go └── type.go ├── ringbuffer ├── ring_buffer.go └── single_ring_buffer.go ├── shell ├── README.md ├── build.sh └── gshell.go └── timer ├── timer.go └── type.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 liangmanlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang/OTP 2 | 3 | ## 提供`actor`模型编程思路 4 | 5 | ## go simple otp 6 | 7 | ### `kernel`提供`supervisor` `gen_server`类似的行为 8 | 9 | ### `node`提供多节点功能 10 | 11 | - 需要自行拷贝`node/gmpd/bin`目录下的文件到环境变量`PATH`目录下,例如:`/usr/bin` 12 | 13 | - 目前暂时没有支持windows的想法 14 | 15 | ### 更多的功能更新请自行查阅 16 | 17 | ## 传送门 [WIKI](https://github.com/liangmanlin/gootp/wikis/home) 18 | -------------------------------------------------------------------------------- /args/args.go: -------------------------------------------------------------------------------- 1 | package args 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/pfun" 5 | "os" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | "unsafe" 10 | ) 11 | 12 | /* 13 | 提供一个比较方便获取命令行参数的方法,支持等号赋值和shell模式赋值 14 | 15 | -key=value or -key value 16 | 17 | 支持多次赋值 18 | 19 | -key value1 -key value2 20 | 21 | func FillEvn(env interface{}) 22 | 23 | 支持自动填入命令行参数到对象如 24 | type Env struct{ 25 | k int 26 | v int `command:"v"` 27 | } 28 | var env = &Env{} 29 | FillEvn(env) 30 | 31 | ./main -k 2 -v 1 32 | 33 | 系统只会填入tag:command的字段 34 | */ 35 | 36 | var _args = make(map[string][]string) 37 | var _other []string 38 | 39 | func init() { 40 | l := os.Args[1:] 41 | for i := 0; i < len(l); { 42 | v := l[i] 43 | if v[0] == '-' { 44 | // 读取下一个 45 | i = readValue(v[1:], l, i) 46 | } else { 47 | // 把余下的放到一个大列表中 48 | _other = append(_other, v) 49 | i++ 50 | } 51 | } 52 | } 53 | 54 | func readValue(key string, l []string, i int) int { 55 | if len(key) == 0 { 56 | return i + 1 57 | } 58 | // 支持golang 等号(=)赋值 59 | spl := strings.Split(key, "=") 60 | if len(spl) > 1 { 61 | key = spl[0] 62 | appendValue(spl[0], spl[1]) 63 | return i + 1 64 | } 65 | // 最后一个 66 | if len(l) == i+1 { 67 | appendValue(key, "") 68 | return i + 1 69 | } 70 | v := l[i+1] 71 | if v[0] == '-' { 72 | appendValue(key, "") 73 | return i + 1 74 | } else { 75 | appendValue(key, v) 76 | return i + 2 77 | } 78 | } 79 | 80 | func GetInt(key string) (v int, ok bool) { 81 | if vl, ok := _args[key]; ok && len(vl) > 0 { 82 | // 获取最后一个 83 | if i, err := strconv.Atoi(vl[len(vl)-1]); err == nil { 84 | return i, ok 85 | } 86 | } 87 | return 88 | } 89 | 90 | func GetString(key string) (v string, ok bool) { 91 | if vl, ok := _args[key]; ok && len(vl) > 0 { 92 | return vl[len(vl)-1], ok 93 | } 94 | return 95 | } 96 | func GetIntDefault(key string,df int) int{ 97 | if vl, ok := _args[key]; ok && len(vl) > 0 { 98 | // 获取最后一个 99 | if i, err := strconv.Atoi(vl[len(vl)-1]); err == nil { 100 | return i 101 | } 102 | } 103 | return df 104 | } 105 | 106 | func GetStringDefault(key string,df string) string { 107 | if vl, ok := _args[key]; ok && len(vl) > 0 { 108 | return vl[len(vl)-1] 109 | } 110 | return df 111 | } 112 | 113 | func GetValues(key string) []string { 114 | if v, ok := _args[key]; ok { 115 | return v 116 | } 117 | return nil 118 | } 119 | 120 | func GetOther() []string { 121 | return _other 122 | } 123 | 124 | func appendValue(key, value string) { 125 | vl := _args[key] 126 | vl = append(vl, value) 127 | _args[key] = vl 128 | } 129 | 130 | // 根据命令行参数,自动填充,目前仅仅支持 整数,bool,字符串 131 | // env 需要是指针 132 | func FillEvn(env interface{}) { 133 | vt := reflect.ValueOf(env) 134 | if vt.Kind() != reflect.Ptr { 135 | return 136 | } 137 | vt = vt.Elem() 138 | ft := vt.Type() 139 | fieldNum := ft.NumField() 140 | // 利用指针,不导出的字段也可以更新 141 | ptr := pfun.Ptr(env) 142 | for i := 0; i < fieldNum; i++ { 143 | fieldType := ft.Field(i) 144 | if name, ok := fieldType.Tag.Lookup("command"); ok { 145 | setValue(fieldType.Type, name, ptr, fieldType.Offset) 146 | } 147 | } 148 | } 149 | 150 | func setValue(fieldType reflect.Type, name string, ptr unsafe.Pointer, offset uintptr) { 151 | var ( 152 | v int 153 | vs string 154 | ok bool 155 | ) 156 | switch fieldType.Kind() { 157 | case reflect.Int8: 158 | if v, ok = GetInt(name); ok { 159 | *(*int8)(unsafe.Pointer(uintptr(ptr) + offset)) = int8(v) 160 | } 161 | case reflect.Uint8: 162 | if v, ok = GetInt(name); ok { 163 | *(*uint8)(unsafe.Pointer(uintptr(ptr) + offset)) = uint8(v) 164 | } 165 | case reflect.Int16: 166 | if v, ok = GetInt(name); ok { 167 | *(*int16)(unsafe.Pointer(uintptr(ptr) + offset)) = int16(v) 168 | } 169 | case reflect.Uint16: 170 | if v, ok = GetInt(name); ok { 171 | *(*uint16)(unsafe.Pointer(uintptr(ptr) + offset)) = uint16(v) 172 | } 173 | case reflect.Int32: 174 | if v, ok = GetInt(name); ok { 175 | *(*int32)(unsafe.Pointer(uintptr(ptr) + offset)) = int32(v) 176 | } 177 | case reflect.Uint32: 178 | if v, ok = GetInt(name); ok { 179 | *(*uint32)(unsafe.Pointer(uintptr(ptr) + offset)) = uint32(v) 180 | } 181 | case reflect.Int64: 182 | if v, ok = GetInt(name); ok { 183 | *(*int64)(unsafe.Pointer(uintptr(ptr) + offset)) = int64(v) 184 | } 185 | case reflect.Uint64: 186 | if v, ok = GetInt(name); ok { 187 | *(*uint64)(unsafe.Pointer(uintptr(ptr) + offset)) = uint64(v) 188 | } 189 | case reflect.Int: 190 | if v, ok = GetInt(name); ok { 191 | *(*int)(unsafe.Pointer(uintptr(ptr) + offset)) = v 192 | } 193 | case reflect.Uint: 194 | if v, ok = GetInt(name); ok { 195 | *(*uint)(unsafe.Pointer(uintptr(ptr) + offset)) = uint(v) 196 | } 197 | case reflect.String: 198 | if vs, ok = GetString(name); ok { 199 | *(*string)(unsafe.Pointer(uintptr(ptr) + offset)) = vs 200 | } 201 | case reflect.Bool: 202 | if vs, ok = GetString(name); ok { 203 | *(*bool)(unsafe.Pointer(uintptr(ptr) + offset)) = vs == "true" 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /bpool/bpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "io" 5 | "math/bits" 6 | "sync" 7 | ) 8 | 9 | /* 10 | 提供一个用于接收网络数据的缓冲池 11 | 小于64k的数据将会被重用 12 | 13 | 为什么是64k? 14 | 因为uint16的最大值是64k 15 | */ 16 | const ( 17 | min_size = 32 18 | max_size = 64 * 1024 19 | pool_size = 12 //32,64,128,256,512,1k,2k,4k,8k,16k,32k,64k 20 | ) 21 | 22 | var pool [pool_size]sync.Pool 23 | 24 | type Buff struct { 25 | b []byte 26 | poolIdx int8 27 | } 28 | 29 | func init() { 30 | for i := 0; i < pool_size; i++ { 31 | size := getSize(i) 32 | idx := i 33 | pool[i].New = func() interface{} { 34 | return &Buff{poolIdx: int8(idx), b: make([]byte, size)} 35 | } 36 | } 37 | } 38 | 39 | func New(size int) *Buff { 40 | if size >= max_size { 41 | // 理论上很少这么大的数据,重用意义不大,所以,直接申请 42 | b := make([]byte, 0, size) 43 | return &Buff{poolIdx: -1, b: b} 44 | } 45 | idx := getIndex(size) 46 | buf := pool[idx].Get().(*Buff) 47 | buf.b = buf.b[0:0] 48 | return buf 49 | } 50 | 51 | func NewBuf(buf []byte) *Buff { 52 | size := len(buf) 53 | b := New(size) 54 | copy(b.b[0:size], buf) 55 | b.b = b.b[:size] 56 | return b 57 | } 58 | 59 | func getIndex(size int) int { 60 | if size < min_size { 61 | return 0 62 | } 63 | return bits.Len32(uint32(size-1)) - 5 64 | } 65 | 66 | // 调用该方法后,不能继续使用buff,否则有不可预料的bug 67 | func (b *Buff) Free() { 68 | if b.poolIdx < 0 { 69 | return 70 | } 71 | pool[b.poolIdx].Put(b) 72 | } 73 | 74 | func (b *Buff) Size() int { 75 | return len(b.b) 76 | } 77 | 78 | func (b *Buff) Cap() int { 79 | return cap(b.b) 80 | } 81 | 82 | func (b *Buff) Reset() { 83 | b.b = b.b[0:0] 84 | } 85 | 86 | func (b *Buff) Read(r io.Reader, size int) (n int, err error) { 87 | if cap(b.b) < size { 88 | return 0, io.ErrShortBuffer 89 | } 90 | b.b = b.b[0:size] 91 | for n < size && err == nil { 92 | var nn int 93 | nn, err = r.Read(b.b[n:]) 94 | n += nn 95 | } 96 | if n >= size { 97 | err = nil 98 | } else if n > 0 && err == io.EOF { 99 | err = io.ErrUnexpectedEOF 100 | } 101 | return 102 | } 103 | 104 | func (b *Buff) Append(buf ...byte) *Buff { 105 | totalSize := len(buf) + b.Size() 106 | if totalSize > b.Cap() { 107 | newCache := New(totalSize) 108 | newCache = newCache.Append(b.b...).Append(buf...) 109 | b.Free() 110 | return newCache 111 | } 112 | b.b = append(b.b, buf...) 113 | return b 114 | } 115 | 116 | func (b *Buff) ToBytes() []byte { 117 | return b.b 118 | } 119 | 120 | func (b *Buff) Copy() (buf []byte) { 121 | return append(buf, b.b...) 122 | } 123 | 124 | func (b *Buff) SetSize(size int) { 125 | b.b = b.b[:size] 126 | } 127 | 128 | func getSize(i int) int { 129 | return min_size << i 130 | } 131 | 132 | func ReadAll(r io.Reader, size int) (bp *Buff, err error) { 133 | const maxAppendSize = 1024 * 1024 * 4 134 | b := New(size) 135 | var n int 136 | for { 137 | n, err = r.Read(b.b[len(b.b):cap(b.b)]) 138 | if n > 0 { 139 | b.b = b.b[:len(b.b)+n] 140 | } 141 | if err != nil { 142 | if err == io.EOF { 143 | err = nil 144 | } 145 | return b, err 146 | } 147 | if len(b.b) == cap(b.b) { 148 | l := len(b.b) 149 | al := l 150 | if al > maxAppendSize { 151 | al = maxAppendSize 152 | } 153 | tmp := b 154 | b = New(l + al).Append(b.b...) 155 | tmp.Free() 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /bpool/bpool_test.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import "testing" 4 | 5 | func BenchmarkNewAndFree(b *testing.B) { 6 | for i := 0; i < b.N; i++ { 7 | buf := New(128) 8 | buf.Free() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bpool/writer.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import "io" 4 | 5 | const defaultBufSize = 4*1024 6 | 7 | type Writer struct { 8 | err error 9 | buf *Buff 10 | n int 11 | wr io.Writer 12 | } 13 | 14 | // NewWriterSize returns a new Writer whose buffer has at least the specified 15 | // size, it returns the underlying Writer. 16 | func NewWriterSize(w io.Writer, size int) *Writer { 17 | if size <= 0 { 18 | size = defaultBufSize-1 19 | } 20 | b := New(size) 21 | b.b = b.b[0:size] 22 | return &Writer{ 23 | buf: b, 24 | wr: w, 25 | } 26 | } 27 | 28 | // NewWriter returns a new Writer whose buffer has the default size. 29 | func NewWriter(w io.Writer) *Writer { 30 | return NewWriterSize(w, defaultBufSize) 31 | } 32 | 33 | // Size returns the size of the underlying buffer in bytes. 34 | func (b *Writer) Size() int { return len(b.buf.b) } 35 | 36 | // Reset discards any unflushed buffered data, clears any error, and 37 | // resets b to write its output to w. 38 | func (b *Writer) Reset(w io.Writer) { 39 | b.err = nil 40 | b.n = 0 41 | b.wr = w 42 | } 43 | 44 | // Flush writes any buffered data to the underlying io.Writer. 45 | func (b *Writer) Flush() error { 46 | if b.err != nil { 47 | return b.err 48 | } 49 | if b.n == 0 { 50 | return nil 51 | } 52 | n, err := b.wr.Write(b.buf.b[0:b.n]) 53 | if n < b.n && err == nil { 54 | err = io.ErrShortWrite 55 | } 56 | if err != nil { 57 | if n > 0 && n < b.n { 58 | copy(b.buf.b[0:b.n-n], b.buf.b[n:b.n]) 59 | } 60 | b.n -= n 61 | b.err = err 62 | return err 63 | } 64 | b.n = 0 65 | return nil 66 | } 67 | 68 | // Available returns how many bytes are unused in the buffer. 69 | func (b *Writer) Available() int { return b.buf.Cap() - b.n } 70 | 71 | // Buffered returns the number of bytes that have been written into the current buffer. 72 | func (b *Writer) Buffered() int { return b.n } 73 | 74 | // Write writes the contents of p into the buffer. 75 | // It returns the number of bytes written. 76 | // If nn < len(p), it also returns an error explaining 77 | // why the write is short. 78 | func (b *Writer) Write(p []byte) (nn int, err error) { 79 | for len(p) > b.Available() && b.err == nil { 80 | var n int 81 | if b.Buffered() == 0 { 82 | // Large write, empty buffer. 83 | // Write directly from p to avoid copy. 84 | n, b.err = b.wr.Write(p) 85 | } else { 86 | n = copy(b.buf.b[b.n:], p) 87 | b.n += n 88 | b.Flush() 89 | } 90 | nn += n 91 | p = p[n:] 92 | } 93 | if b.err != nil { 94 | return nn, b.err 95 | } 96 | n := copy(b.buf.b[b.n:], p) 97 | b.n += n 98 | nn += n 99 | return nn, nil 100 | } 101 | 102 | func (b *Writer) Free() { 103 | b.buf.Free() 104 | } -------------------------------------------------------------------------------- /crypto/crypto.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func Md5(in []byte) string { 9 | m := md5.Sum(in) 10 | return hex.EncodeToString(m[:]) 11 | } 12 | -------------------------------------------------------------------------------- /db/README.md: -------------------------------------------------------------------------------- 1 | # Db 2 | 3 | ## go mysql service 4 | 5 | ## useage 6 | 7 | ### insert 8 | 9 | ```go 10 | SyncInsert(tab string, indexKey int64, data interface{}) 11 | ModInsert(db *sql.DB, tab string, data interface{}) (sql.Result, error) 12 | ``` 13 | 14 | ### select 15 | ```go 16 | SyncSelectRow(context *kernel.Context, tab string, indexKey int64, key ...interface{}) interface{} 17 | SyncSelect(context *kernel.Context, tab string, indexKey int64, key ...interface{}) []interface{} 18 | ModSelectRow(db *sql.DB, tab string, key []interface{}) interface{} 19 | ModSelectAll(db *sql.DB, tab string, key []interface{}) []interface{} 20 | ``` 21 | 22 | ### update 23 | 24 | ```go 25 | SyncUpdate(tab string, indexKey int64, data interface{}) 26 | ModUpdate(db *sql.DB, tab string, data interface{}) (sql.Result, error) 27 | ``` 28 | 29 | ### delete 30 | 31 | ```go 32 | SyncDelete(tab string, indexKey int64, data interface{}) 33 | SyncDeletePKey(tab string, indexKey int64, pkey ...interface{}) 34 | ModDelete(db *sql.DB, tab string, data interface{}) (sql.Result, error) 35 | ModDeletePKey(db *sql.DB, tab string, pkey []interface{}) (sql.Result, error) 36 | ``` 37 | 38 | ## Test see: [db_test](db_test.go) 39 | -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | _ "github.com/go-sql-driver/mysql" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "log" 9 | ) 10 | 11 | var groups []*Group 12 | 13 | func Start(idx int,DBConfig Config,defSlice []*TabDef, dbName string,syncNum int,mode Mode) *Group { 14 | g := newGroup(idx) 15 | g.mode = mode 16 | Env.dbConfig = DBConfig 17 | initDef(g,defSlice) 18 | cn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",DBConfig.User,DBConfig.PWD,DBConfig.Host,DBConfig.Port, dbName) 19 | db,err := sql.Open("mysql",cn) 20 | g.db = db 21 | if err != nil { 22 | log.Panic(err) 23 | } 24 | rows,err := db.Query("show tables;") 25 | if err !=nil { 26 | log.Panic(err) 27 | } 28 | rows.Close() 29 | connNum := DBConfig.ConnNum 30 | if connNum == 0 { 31 | connNum = Env.ConnNum 32 | } 33 | db.SetMaxOpenConns(connNum) 34 | db.SetMaxIdleConns(connNum) 35 | startSync(g,syncNum) 36 | // 检查数据库表版本号 37 | tableCheck(g) 38 | kernel.ErrorLog("db start on database: %s",dbName) 39 | return g 40 | } 41 | 42 | func newGroup(idx int) *Group { 43 | if cap(groups) <= idx{ 44 | groups = append(groups,&Group{}) 45 | } 46 | if groups[idx].db != nil{ 47 | log.Panicf("duplicate db idx:%d",idx) 48 | } 49 | return groups[idx] 50 | } 51 | 52 | func GetGroup(idx int) *Group { 53 | return groups[idx] 54 | } -------------------------------------------------------------------------------- /db/db_check.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/crypto" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "log" 8 | "reflect" 9 | "strings" 10 | ) 11 | 12 | func tableCheck(group *Group) { 13 | rows, err := group.db.Query("show tables;") 14 | if err != nil { 15 | if rows != nil { 16 | rows.Close() 17 | } 18 | kernel.ErrorLog(err.Error()) 19 | log.Panic(err.Error()) 20 | } 21 | tabs := make(map[string]string, len(group.dbTabDef)) 22 | for rows.Next() { 23 | var tabName string 24 | err = rows.Scan(&tabName) 25 | ifExit(err) 26 | tabs[tabName] = "1" 27 | } 28 | _ = rows.Close() 29 | if _, ok := tabs["db_version"]; !ok { 30 | tabs["db_version"] = createTable(group, "db_version") 31 | } 32 | vers := group.SyncSelect(kernel.Call, "db_version", 1, true) 33 | for _, v := range vers { 34 | v2 := v.(*dbVersion) 35 | tabs[v2.TabName] = v2.Version 36 | } 37 | for tabName, def := range group.dbTabDef { 38 | md5, ok := tabs[tabName] 39 | if !ok { 40 | tabs[tabName] = createTable(group, tabName) 41 | continue 42 | } 43 | md52 := tabMd5(def.DataStruct) 44 | if md5 != md52 { 45 | kernel.ErrorLog("tab:[%s] check fields,ver: %s,old ver: %s", tabName, md5, md52) 46 | tabs[tabName] = md52 47 | checkField(group, def, md52) 48 | } 49 | } 50 | } 51 | 52 | func createTable(group *Group, tab string) string { 53 | def := group.GetDef(tab) 54 | md5Str := tabMd5(def.DataStruct) 55 | sqlStr := genCreateSql(def) 56 | _, err := group.db.Exec(sqlStr) 57 | ifExit(err) 58 | group.SyncInsert("db_version", 1, &dbVersion{TabName: tab, Version: md5Str}) 59 | return md5Str 60 | } 61 | 62 | func genCreateSql(def *TabDef) string { 63 | vf := reflect.ValueOf(def.DataStruct) 64 | vt := reflect.TypeOf(def.DataStruct) 65 | if vf.Kind() == reflect.Ptr { 66 | vf = vf.Elem() 67 | vt = vt.Elem() 68 | } 69 | fNum := vf.NumField() 70 | fsl := make([]string, fNum, fNum) 71 | for i := 0; i < fNum; i++ { 72 | f := vf.Field(i) 73 | t := vt.Field(i) 74 | fsl[i] = getFileDef(&f, t.Name) 75 | } 76 | if len(def.Pkey) > 0 { 77 | psl := make([]string, 0, 2) 78 | for _, pk := range def.Pkey { 79 | psl = append(psl, pk) 80 | } 81 | fsl = append(fsl, fmt.Sprintf("PRIMARY KEY(%s)", strings.Join(psl, ","))) 82 | } 83 | for _, k := range def.Keys { 84 | fsl = append(fsl, fmt.Sprintf("KEY `%s` (`%s`)", k, k)) 85 | } 86 | return fmt.Sprintf("create table if not exists `%s` (%s) ENGINE=InnoDB DEFAULT CHARSET=utf8;", def.Name, strings.Join(fsl, ",\n")) 87 | } 88 | 89 | func checkField(group *Group, def *TabDef, md5 string) { 90 | rows, err := group.db.Query(fmt.Sprintf("desc %s;", def.Name)) 91 | ifExit(err) 92 | fmap := make(map[string]bool) 93 | for rows.Next() { 94 | var field, a, b, c, d, e string 95 | rows.Scan(&field, &a, &b, &c, &d, &e) 96 | fmap[field] = true 97 | } 98 | rows.Close() 99 | rt := reflect.TypeOf(def.DataStruct) 100 | rf := reflect.ValueOf(def.DataStruct) 101 | if rf.Kind() == reflect.Ptr { 102 | rt = rt.Elem() 103 | rf = rf.Elem() 104 | } 105 | fNum := rt.NumField() 106 | add := make([]string, 0, 0) 107 | for i := 0; i < fNum; i++ { 108 | t := rt.Field(i) 109 | f := rf.Field(i) 110 | if _, ok := fmap[t.Name]; !ok { 111 | add = append(add, fmt.Sprintf("Add %s AFTER `%s`", getFileDef(&f, t.Name), rt.Field(i-1).Name)) 112 | } else { 113 | delete(fmap, t.Name) 114 | } 115 | } 116 | if len(add) > 0 { 117 | sqlAdd := fmt.Sprintf("ALTER TABLE `%s` %s;", def.Name, strings.Join(add, ",")) 118 | _, err = group.db.Exec(sqlAdd) 119 | ifExit(err) 120 | } 121 | if len(fmap) > 0 { 122 | del := make([]string, 0, 0) 123 | for fn, _ := range fmap { 124 | del = append(del, fmt.Sprintf("DROP `%s`", fn)) 125 | } 126 | sqlDel := fmt.Sprintf("ALTER TABLE `%s` %s;", def.Name, strings.Join(del, ",")) 127 | _, err = group.db.Exec(sqlDel) 128 | ifExit(err) 129 | } 130 | group.SyncUpdate("db_version", 1, &dbVersion{TabName: def.Name, Version: md5}) 131 | 132 | } 133 | 134 | func getFileDef(f *reflect.Value, fieldName string) string { 135 | var fs string 136 | switch f.Kind() { 137 | case reflect.Bool,reflect.Int,reflect.Uint,reflect.Int32: 138 | fs = fmt.Sprintf("`%s` INT( 11 ) NOT NULL", fieldName) 139 | case reflect.Int64,reflect.Uint64: 140 | fs = fmt.Sprintf("`%s` BIGINT( 23 ) NOT NULL", fieldName) 141 | case reflect.Ptr,reflect.Slice,reflect.Map,reflect.Struct: 142 | fs = fmt.Sprintf("`%s` mediumblob", fieldName) 143 | case reflect.String: 144 | fs = fmt.Sprintf("`%s` VARCHAR( 250 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL", fieldName) 145 | default: 146 | log.Panic(fmt.Errorf("%s not support type,%s", fieldName, f.Type())) 147 | } 148 | return fs 149 | } 150 | 151 | func ifExit(err error) { 152 | if err != nil { 153 | kernel.ErrorLog(err.Error()) 154 | log.Panic(err) 155 | } 156 | } 157 | 158 | func tabMd5(src interface{}) string { 159 | rt := reflect.TypeOf(src) 160 | if rt.Kind() == reflect.Ptr { 161 | rt = rt.Elem() 162 | } 163 | n := rt.NumField() 164 | sl := make([]string,0,n) 165 | for i := 0; i < n; i++ { 166 | t := rt.Field(i) 167 | sl = append(sl, t.Name) 168 | } 169 | return crypto.Md5([]byte(strings.Join(sl, "_"))) 170 | } 171 | -------------------------------------------------------------------------------- /db/db_def.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import "reflect" 4 | 5 | type TabDef struct { 6 | Name string 7 | Pkey []string 8 | Keys []string 9 | DataStruct interface{} 10 | nameType map[string]reflect.Type 11 | } 12 | 13 | type dbVersion struct { 14 | TabName string 15 | Version string 16 | } 17 | 18 | func initDef(g *Group,defSlice []*TabDef) { 19 | g.dbTabDef = make(map[string]*TabDef, 100) 20 | version := &TabDef{Name: "db_version", DataStruct: &dbVersion{}, Pkey: []string{"TabName"}} 21 | version.buildMap() 22 | g.dbTabDef["db_version"] = version 23 | for _, v := range defSlice { 24 | v.buildMap() 25 | g.dbTabDef[v.Name] = v 26 | } 27 | } 28 | 29 | func (g *Group)GetDef(tab string) *TabDef { 30 | return g.dbTabDef[tab] 31 | } 32 | 33 | func (g *Group)GetAllDef() []*TabDef { 34 | l := make([]*TabDef,0,len(g.dbTabDef)) 35 | for _,v := range g.dbTabDef { 36 | l = append(l,v) 37 | } 38 | return l 39 | } 40 | 41 | func (t *TabDef) buildMap() { 42 | vf := reflect.TypeOf(t.DataStruct).Elem() 43 | t.nameType = make(map[string]reflect.Type) 44 | num := vf.NumField() 45 | for i := 0; i < num; i++ { 46 | f := vf.Field(i) 47 | t.nameType[f.Name] = f.Type 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /db/db_encode.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "reflect" 6 | "strconv" 7 | ) 8 | 9 | func Encode(v interface{}) string { 10 | switch v2 := v.(type) { 11 | case int: 12 | return strconv.Itoa(v2) 13 | case int32: 14 | return strconv.FormatInt(int64(v2),10) 15 | case int64: 16 | return strconv.FormatInt(v2,10) 17 | case uint: 18 | return strconv.FormatUint(uint64(v2),10) 19 | case uint32: 20 | return strconv.FormatUint(uint64(v2),10) 21 | case uint64: 22 | return strconv.FormatUint(v2,10) 23 | case string: 24 | return quote([]byte(v2)) 25 | case []byte: 26 | return quote(v2) 27 | default: 28 | kernel.ErrorLog("db encode error:%#v",v2) 29 | return "1" 30 | } 31 | } 32 | 33 | func quote(bin []byte)string{ 34 | sl := make([]byte,1,len(bin)+2) 35 | sl[0] = '\'' 36 | //sl = append(sl,bin...) 37 | for _,b := range bin{ 38 | switch b { 39 | case 0: 40 | sl = append(sl,92,48) // \\\0 41 | //case 10: 42 | // sl = append(sl,92,110) // \\n 43 | //case 13: 44 | // sl = append(sl,92,114) // \\r 45 | //case 26: 46 | // sl = append(sl,92,90) // \\Z 47 | case 34: 48 | sl = append(sl,92,34) // \\\" 49 | case 39: 50 | sl = append(sl,92,39) // \\\' 51 | case 92: 52 | sl = append(sl,92,92) // \\\\ 53 | default: 54 | sl = append(sl,b) 55 | } 56 | } 57 | sl = append(sl,'\'') 58 | return string(sl) 59 | } 60 | 61 | func encodeValue(f *reflect.Value) string { 62 | switch f.Kind() { 63 | case reflect.Bool: 64 | var v = int32(0) 65 | if f.Bool() { 66 | v = 1 67 | } 68 | return Encode(v) 69 | case reflect.Slice,reflect.Map,reflect.Struct,reflect.Ptr: 70 | return Encode(toBinary(f)) 71 | default: 72 | return Encode(f.Interface()) 73 | } 74 | } -------------------------------------------------------------------------------- /db/db_sync.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/args" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "log" 7 | "time" 8 | ) 9 | 10 | type selectRow struct { 11 | index int64 12 | table string 13 | key []interface{} 14 | } 15 | 16 | type selectAll struct { 17 | index int64 18 | table string 19 | key []interface{} 20 | } 21 | 22 | type updateRow struct { 23 | index int64 24 | table string 25 | data interface{} 26 | } 27 | 28 | type insertRow struct { 29 | index int64 30 | table string 31 | data interface{} 32 | } 33 | 34 | type deleteRow struct { 35 | index int64 36 | table string 37 | data interface{} 38 | } 39 | type deletePKey struct { 40 | index int64 41 | table string 42 | pkey []interface{} 43 | } 44 | 45 | // 查询单行 46 | func (g *Group) SyncSelectRow(c Call, tab string, indexKey int64, key ...interface{}) interface{} { 47 | proc := g.syncPool[indexKey%g.syncNum] 48 | var ok bool 49 | var rs interface{} 50 | q := &selectRow{index: indexKey, table: tab, key: key} 51 | ok, rs = c(proc, q) 52 | if ok { 53 | return rs 54 | } 55 | kernel.ErrorLog("select %s error:%#v", tab, rs) 56 | return nil 57 | } 58 | 59 | // 查询多条记录 60 | func (g *Group) SyncSelect(c Call, tab string, indexKey int64, key ...interface{}) []interface{} { 61 | proc := g.syncPool[indexKey%g.syncNum] 62 | var ok bool 63 | var rs interface{} 64 | q := &selectAll{index: indexKey, table: tab, key: key} 65 | ok, rs = c(proc, q) 66 | if ok { 67 | return rs.([]interface{}) 68 | } 69 | kernel.ErrorLog("select %s error:%#v", tab, rs) 70 | return nil 71 | } 72 | 73 | func (g *Group) SyncUpdate(tab string, indexKey int64, data interface{}) { 74 | proc := g.syncPool[indexKey%g.syncNum] 75 | kernel.Cast(proc, &updateRow{index: indexKey, table: tab, data: kernel.DeepCopy(data)}) 76 | } 77 | 78 | func (g *Group) SyncInsert(tab string, indexKey int64, data interface{}) { 79 | proc := g.syncPool[indexKey%g.syncNum] 80 | if g.mode != MODE_MULTI_INSERT { 81 | data = kernel.DeepCopy(data) 82 | } 83 | kernel.Cast(proc, &insertRow{index: indexKey, table: tab, data: data}) 84 | } 85 | 86 | func (g *Group) SyncDelete(tab string, indexKey int64, data interface{}) { 87 | proc := g.syncPool[indexKey%g.syncNum] 88 | kernel.Cast(proc, &deleteRow{index: indexKey, table: tab, data: kernel.DeepCopy(data)}) 89 | } 90 | 91 | func (g *Group) SyncDeletePKey(tab string, indexKey int64, pkey ...interface{}) { 92 | proc := g.syncPool[indexKey%g.syncNum] 93 | kernel.Cast(proc, &deletePKey{index: indexKey, table: tab, pkey: pkey}) 94 | } 95 | 96 | func startSync(group *Group, num int) { 97 | group.syncPool = make([]*kernel.Pid, num) 98 | group.syncNum = int64(num) 99 | for i := 0; i < num; i++ { 100 | pid, err := kernel.Start(dbSyncActor, group, i) 101 | if err != nil { 102 | log.Panic(err) 103 | } 104 | group.syncPool[i] = pid 105 | } 106 | } 107 | 108 | type dbSync struct { 109 | g *Group 110 | worker *kernel.Pid 111 | cache map[string][]interface{} 112 | } 113 | 114 | var dbSyncActor = &kernel.Actor{ 115 | Init: func(context *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 116 | d := dbSync{} 117 | d.g = args[0].(*Group) 118 | if d.g.mode == MODE_MULTI_INSERT { 119 | syncTime := getSyncTime() 120 | go func() { 121 | i := args[1].(int) 122 | time.Sleep(time.Duration(i+1) * 5 * time.Second) 123 | kernel.SendAfterForever(pid, syncTime, kernel.Loop{}) 124 | }() 125 | d.cache = make(map[string][]interface{}, 10) 126 | // start child 127 | d.worker, _ = context.StartLinkOpt(dbSyncWorker, kernel.ActorOpt(kernel.ActorChanCacheSize(1000)), pid, d.g) 128 | } 129 | return &d 130 | }, 131 | HandleCast: func(context *kernel.Context, msg interface{}) { 132 | d := context.State.(*dbSync) 133 | switch m := msg.(type) { 134 | case *updateRow: 135 | d.g.ModUpdate(m.table, m.data) 136 | case *insertRow: 137 | if d.g.mode == MODE_MULTI_INSERT { 138 | // 缓存起来,后续批量插入 139 | d.insertCache(m.table, m.data) 140 | } else { 141 | d.g.ModInsert(m.table, m.data) 142 | } 143 | case *deleteRow: 144 | d.g.ModDelete(m.table, m.data) 145 | case *deletePKey: 146 | d.g.ModDeletePKey(m.table, m.pkey...) 147 | case kernel.Loop: 148 | d.multiInsert() 149 | case syncData: 150 | if d.g.mode == MODE_MULTI_INSERT { 151 | // 缓存起来,后续批量插入 152 | d.insertCache(m.tab, m.dataList...) 153 | } 154 | } 155 | }, 156 | HandleCall: func(context *kernel.Context, request interface{}) interface{} { 157 | d := context.State.(*dbSync) 158 | switch req := request.(type) { 159 | case *selectRow: 160 | return d.selectRow(req.table, req.key) 161 | case *selectAll: 162 | return d.selectAll(req.table, req.key) 163 | } 164 | return nil 165 | }, 166 | Terminate: func(context *kernel.Context, reason *kernel.Terminate) { 167 | 168 | }, 169 | ErrorHandler: func(context *kernel.Context, err interface{}) bool { 170 | return true 171 | }, 172 | } 173 | 174 | func (d *dbSync) selectRow(tab string, key []interface{}) interface{} { 175 | return d.g.ModSelectRow(tab, key...) 176 | } 177 | 178 | func (d *dbSync) selectAll(tab string, key []interface{}) interface{} { 179 | return d.g.ModSelectAll(tab, key...) 180 | } 181 | 182 | func (d *dbSync) insertCache(tab string, data ...interface{}) { 183 | s := d.cache[tab] 184 | s = append(s, data...) 185 | d.cache[tab] = s 186 | } 187 | func (d *dbSync) multiInsert() { 188 | for tab, dl := range d.cache { 189 | if len(dl) > 0 { 190 | d.worker.Cast(syncData{tab: tab, dataList: dl}) 191 | } 192 | delete(d.cache, tab) 193 | } 194 | } 195 | 196 | func getSyncTime() int64 { 197 | if v, ok := args.GetInt("db_sync_time"); ok { 198 | return int64(v) * 1000 199 | } 200 | return 30 * 1000 201 | } 202 | -------------------------------------------------------------------------------- /db/db_sync_worker.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql/driver" 5 | "errors" 6 | "github.com/liangmanlin/gootp/gutil" 7 | "github.com/liangmanlin/gootp/kernel" 8 | ) 9 | 10 | type syncWorker struct { 11 | father *kernel.Pid 12 | g *Group 13 | } 14 | 15 | var dbSyncWorker = &kernel.Actor{ 16 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 17 | state := syncWorker{} 18 | state.father = args[0].(*kernel.Pid) 19 | state.g = args[1].(*Group) 20 | return &state 21 | }, 22 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 23 | state := ctx.State.(*syncWorker) 24 | switch m := msg.(type) { 25 | case syncData: 26 | state.multiInsert(m.tab,m.dataList) 27 | } 28 | }, 29 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 30 | return nil 31 | }, 32 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 33 | return true 34 | }, 35 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 36 | kernel.ErrorLog("sync worker terminate") 37 | }, 38 | } 39 | 40 | func (s *syncWorker)multiInsert(tab string,dataList []interface{}) { 41 | size := len(dataList) 42 | if size == 0 { 43 | return 44 | } 45 | // 最多300条插入 46 | const div_size = 300 47 | var start, end int32 48 | var fail []interface{} 49 | div := gutil.Ceil(float32(size) / div_size) 50 | for i := int32(0); i < div; i++ { 51 | start = i * div_size 52 | end = gutil.MinInt32((i+1)*div_size, int32(size)) 53 | tmp := dataList[start:end] 54 | _, err := s.g.ModMultiInsert(tab, tmp) 55 | if err != nil && errors.Is(err, driver.ErrBadConn) { 56 | // 说明断开了,我们等待重连 57 | fail = append(fail, tmp...) 58 | } 59 | } 60 | if len(fail) > 0 { 61 | s.father.Cast(syncData{tab: tab,dataList: fail}) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /db/db_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type Account struct { 10 | Account string 11 | AgentID int32 12 | ServerID int32 13 | LastRole int64 14 | LastOffline int32 15 | BanTime int32 16 | OT []int32 17 | } 18 | 19 | var tabSlice = []*TabDef{ 20 | {Name: "account2", DataStruct: &Account{}, Pkey: []string{"Account"}, Keys: []string{"AgentID"}}, 21 | } 22 | 23 | func TestStart(t *testing.T) { 24 | go func() { 25 | time.Sleep(3 * time.Second) 26 | kernel.ErrorLog("test init stop now") 27 | kernel.InitStop() 28 | }() 29 | kernel.Env.LogPath = "" 30 | kernel.KernelStart(func() { 31 | config := Config{Host: "127.0.0.1", Port: 3306, User: "tttt", PWD: "tttt"} 32 | g := Start(1, config, tabSlice, "pkfr2", 3, MODE_NORMAL) 33 | if rs := g.ModSelectRow("account2", "b"); rs == nil { 34 | g.ModInsert("account2", &Account{Account: "b", OT: []int32{1, 2}}) 35 | } else { 36 | kernel.ErrorLog("%#v", rs) 37 | rs.(*Account).AgentID = 1 38 | rs.(*Account).OT = []int32{1, 2, 3} 39 | g.ModUpdate("account2", rs) 40 | } 41 | }, nil) 42 | } 43 | -------------------------------------------------------------------------------- /db/env.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import "github.com/liangmanlin/gootp/args" 4 | 5 | var Env = &env{ 6 | ConnNum: 8, 7 | IsOpenCache: true, 8 | } 9 | 10 | func init() { 11 | args.FillEvn(Env) 12 | } 13 | 14 | -------------------------------------------------------------------------------- /db/type.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/liangmanlin/gootp/kernel" 6 | ) 7 | 8 | type env struct { 9 | dbConfig Config 10 | ConnNum int `command:"db_conn_num"` 11 | IsOpenCache bool `command:"db_is_open_cache"` 12 | } 13 | 14 | type Config struct { 15 | Host string 16 | Port int 17 | User string 18 | PWD string 19 | ConnNum int 20 | } 21 | 22 | type Mode int 23 | 24 | const ( 25 | MODE_NORMAL Mode = iota + 1 26 | MODE_MULTI_INSERT //适用是日志库,有批量插入缓存 27 | ) 28 | 29 | type Group struct { 30 | db *sql.DB 31 | dbTabDef map[string]*TabDef 32 | mode Mode 33 | syncNum int64 34 | syncPool []*kernel.Pid 35 | } 36 | 37 | type Call func(pid *kernel.Pid, req interface{}) (bool, interface{}) 38 | 39 | type syncData struct { 40 | tab string 41 | dataList []interface{} 42 | } 43 | -------------------------------------------------------------------------------- /gate/README.md: -------------------------------------------------------------------------------- 1 | # Gate 2 | 3 | go gate service 4 | 5 | - 可以切换为epoll模式 6 | 7 | - `WithUseEpoll()` -------------------------------------------------------------------------------- /gate/acceptor.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "net" 6 | ) 7 | 8 | type acceptor struct { 9 | clientSup *kernel.Pid 10 | listener net.Listener 11 | handler *kernel.Actor 12 | clientArgs []interface{} 13 | } 14 | 15 | var acceptorActor = &kernel.Actor{ 16 | Init: func(context *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 17 | a := acceptor{} 18 | a.clientSup = args[0].(*kernel.Pid) 19 | a.listener = args[1].(net.Listener) 20 | a.handler = args[2].(*kernel.Actor) 21 | a.clientArgs = args[3].([]interface{}) 22 | return &a 23 | }, 24 | HandleCast: func(context *kernel.Context, msg interface{}) { 25 | switch msg.(type) { 26 | case bool: 27 | context.State.(*acceptor).accept(context) 28 | } 29 | }, 30 | HandleCall: func(context *kernel.Context, request interface{}) interface{} { 31 | return nil 32 | }, 33 | Terminate: func(context *kernel.Context, reason *kernel.Terminate) { 34 | 35 | }, 36 | ErrorHandler: func(context *kernel.Context, err interface{}) bool { 37 | return true 38 | }, 39 | } 40 | 41 | func (a *acceptor) accept(context *kernel.Context) { 42 | conn, err := a.listener.Accept() 43 | if err == nil { 44 | svr := a.handler 45 | c := NewConn(conn) 46 | initArgs := append(a.clientArgs, c) 47 | child := &kernel.SupChild{ChildType: kernel.SupChildTypeWorker, ReStart: false, Svr: svr, InitArgs: initArgs} 48 | e, pid := kernel.SupStartChild(a.clientSup, child) 49 | if e != nil { 50 | kernel.ErrorLog("accept error: %#v", e) 51 | } else { 52 | kernel.Cast(pid, true) 53 | } 54 | context.CastSelf(true) 55 | } else { 56 | if e, ok := err.(*net.OpError); ok && e.Op == "accept" { 57 | 58 | } else { 59 | kernel.ErrorLog("accept error: %s", err.Error()) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /gate/config.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | func WithAcceptNum(num int) optFun { 4 | return func(o *optStruct) { 5 | o.acceptNum = num 6 | } 7 | } 8 | 9 | func WithClientArgs(args ...interface{}) optFun { 10 | return func(o *optStruct) { 11 | o.clientArgs = args 12 | } 13 | } 14 | 15 | // 如果使用了epoll,那么返回的bpool.Buff的前面几个字节是head,需要逻辑代码注意 16 | func WithUseEpoll() optFun { 17 | return func(o *optStruct) { 18 | o.isUseNbio = true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gate/connect_starter.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "net" 6 | ) 7 | 8 | type starterState struct { 9 | clientSup *kernel.Pid 10 | handler *kernel.Actor 11 | clientArgs []interface{} 12 | } 13 | 14 | var starterActor = &kernel.Actor{ 15 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 16 | state := starterState{} 17 | state.handler = args[0].(*kernel.Actor) 18 | state.clientSup = args[1].(*kernel.Pid) 19 | state.clientArgs = args[2].([]interface{}) 20 | return &state 21 | }, 22 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 23 | state := ctx.State.(*starterState) 24 | switch m := msg.(type) { 25 | case net.Conn: 26 | initArgs := append(state.clientArgs, m) 27 | child := &kernel.SupChild{ChildType: kernel.SupChildTypeWorker, ReStart: false, Svr: state.handler, InitArgs: initArgs} 28 | e, pid := kernel.SupStartChild(state.clientSup, child) 29 | if e != nil { 30 | kernel.ErrorLog("start client error: %#v", e) 31 | } else { 32 | kernel.Cast(pid, true) 33 | } 34 | } 35 | }, 36 | 37 | } 38 | -------------------------------------------------------------------------------- /gate/gate.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "log" 6 | ) 7 | 8 | const gateSupName = "gate_sup" 9 | 10 | var addrMap = make(map[string]string) 11 | 12 | func Start(name string, handler *kernel.Actor, port int, opt ...optFun) { 13 | start(name,handler,port,opt) 14 | } 15 | 16 | func start(name string, handler *kernel.Actor, port int, opt []optFun) { 17 | ensureSupStart() 18 | a := &app{name: name,handler: handler,port: port,opt: opt} 19 | kernel.AppStart(a) 20 | } 21 | 22 | func Stop(name string) { 23 | kernel.AppStop(name) 24 | } 25 | 26 | func ensureSupStart() { 27 | if kernel.WhereIs(gateSupName) == nil { 28 | child := &kernel.SupChild{Name: gateSupName, ReStart: true, ChildType: kernel.SupChildTypeSup} 29 | if err, _ := kernel.SupStartChild("kernel", child); err != nil { 30 | log.Panic(err) 31 | } 32 | } 33 | } 34 | 35 | func WriteSize(buf []byte, head int, size int) { 36 | switch head { 37 | case 2: 38 | buf[0] = uint8(size >> 8) 39 | buf[1] = uint8(size) 40 | case 4: 41 | buf[0] = uint8(size >> 24) 42 | buf[1] = uint8(size >> 16) 43 | buf[2] = uint8(size >> 8) 44 | buf[3] = uint8(size) 45 | } 46 | } 47 | 48 | func GetAddr(flag string) string { 49 | return addrMap[flag] 50 | } 51 | -------------------------------------------------------------------------------- /gate/gate_app.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "log" 7 | ) 8 | 9 | func (a *app)Name() string { 10 | return a.name 11 | } 12 | 13 | func (a *app)Start(bootType kernel.AppBootType) *kernel.Pid { 14 | supName := fmt.Sprintf("gate_child_sup_%s", a.name) 15 | child := &kernel.SupChild{Name: supName, ReStart: false, ChildType: kernel.SupChildTypeSup} 16 | _, childSup := kernel.SupStartChild(gateSupName, child) 17 | clientSup := fmt.Sprintf("gate_client_sup_%s", a.name) 18 | child = &kernel.SupChild{Name: clientSup, ReStart: false, ChildType: kernel.SupChildTypeSup} 19 | _, csPid := kernel.SupStartChild(supName, child) 20 | // 启动侦听进程 21 | listenerName := fmt.Sprintf("gate_listener_%s", a.name) 22 | args := kernel.MakeArgs(a.name, a.handler, a.port, csPid, childSup, a.opt) 23 | child = &kernel.SupChild{Name: listenerName, ReStart: true, ChildType: kernel.SupChildTypeWorker, Svr: listenerActor, InitArgs: args} 24 | err, _ := kernel.SupStartChild(supName, child) 25 | if err != nil { 26 | kernel.ErrorLog("%#v", err) 27 | log.Panic(err) 28 | } 29 | if a.port > 0 { 30 | kernel.ErrorLog("[%s] listening on port: [0.0.0.0:%d]", a.name, a.port) 31 | } 32 | return childSup 33 | } 34 | 35 | func (a *app)Stop(stopType kernel.AppStopType) { 36 | listenerName := fmt.Sprintf("gate_listener_%s", a.name) 37 | kernel.CallName(listenerName,stopType) 38 | kernel.ErrorLog("gate %s stop",a.name) 39 | } 40 | 41 | func (a *app)SetEnv(key string,value interface{}) { 42 | 43 | } 44 | 45 | func (a *app)GetEnv(key string) interface{} { 46 | return nil 47 | } -------------------------------------------------------------------------------- /gate/gate_epoll_test.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/bpool" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | type stateEcho struct { 11 | conn Conn 12 | } 13 | 14 | func TestEpollStart(t *testing.T) { 15 | go func() { 16 | time.Sleep(30 * time.Second) 17 | kernel.ErrorLog("test init stop now") 18 | kernel.InitStop() 19 | }() 20 | kernel.Env.LogPath = "" 21 | kernel.KernelStart(func() { 22 | kernel.SetLogLevel(1) 23 | echo := kernel.DefaultActor() 24 | echo.Init = func(context *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 25 | state := stateEcho{} 26 | state.conn = args[0].(Conn) 27 | kernel.ErrorLog("connect") 28 | return &state 29 | } 30 | echo.HandleCast = func(context *kernel.Context, msg interface{}) { 31 | state := context.State.(*stateEcho) 32 | switch m := msg.(type) { 33 | case bool: 34 | // start 35 | kernel.ErrorLog("recv") 36 | state.conn.SetHead(2) 37 | buf, err := state.conn.Recv(0, 0) 38 | if err == nil { 39 | kernel.ErrorLog("%s", string(buf)) 40 | state.conn.StartReader(context.Self()) 41 | }else{ 42 | context.Exit("normal") 43 | } 44 | case *TcpError: 45 | context.Exit("normal") 46 | case int: 47 | context.Exit("normal") 48 | case *bpool.Buff: 49 | if _, err := state.conn.Send(m.ToBytes()[2:]); err != nil { 50 | context.Exit("normal") 51 | } 52 | kernel.ErrorLog("%s", string(m.ToBytes()[2:])) 53 | m.Free() 54 | default: 55 | kernel.ErrorLog("un handle msg: %#v", m) 56 | } 57 | } 58 | echo.Terminate = func(context *kernel.Context, reason *kernel.Terminate) { 59 | kernel.ErrorLog("client exit :%s", reason.Reason) 60 | context.State.(*stateEcho).conn.Close() 61 | } 62 | gatePort := 8000 63 | Start("echo", echo, gatePort, WithUseEpoll()) 64 | }, func() { 65 | Stop("echo") 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /gate/gate_test.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/bpool" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | type stateNet struct { 11 | Conn 12 | } 13 | 14 | func TestStart(t *testing.T) { 15 | go func() { 16 | time.Sleep(30 * time.Second) 17 | kernel.ErrorLog("test init stop now") 18 | kernel.InitStop() 19 | }() 20 | kernel.Env.LogPath = "" 21 | kernel.KernelStart(func() { 22 | echo := kernel.DefaultActor() 23 | echo.Init = func(context *kernel.Context,pid *kernel.Pid, args ...interface{}) interface{} { 24 | state := stateNet{} 25 | state.Conn = args[0].(Conn) 26 | kernel.ErrorLog("connect") 27 | return &state 28 | } 29 | echo.HandleCast = func(context *kernel.Context, msg interface{}) { 30 | state := context.State.(*stateNet) 31 | switch m := msg.(type) { 32 | case bool: 33 | // start 34 | state.SetHead(2) 35 | state.StartReader(context.Self()) 36 | case *TcpError: 37 | context.Exit("normal") 38 | case int: 39 | context.Exit("normal") 40 | case *bpool.Buff: 41 | if _,err := state.Write(m.ToBytes());err != nil { 42 | context.Exit("normal") 43 | } 44 | kernel.ErrorLog("%s",string(m.ToBytes())) 45 | m.Free() 46 | default: 47 | kernel.ErrorLog("un handle msg: %#v", m) 48 | } 49 | } 50 | echo.Terminate = func(context *kernel.Context, reason *kernel.Terminate) { 51 | state := context.State.(*stateNet) 52 | kernel.ErrorLog("exit:%s",state.RemoteAddr().String()) 53 | state.Conn.Close() 54 | } 55 | gatePort := 8000 56 | Start("echo", echo, gatePort, WithAcceptNum(5)) 57 | }, func() { 58 | Stop("echo") 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /gate/listener.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lesismal/nbio" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "log" 8 | "net" 9 | "strconv" 10 | ) 11 | 12 | type listener struct { 13 | isUseNbio bool 14 | g *nbio.Gopher 15 | 16 | ls net.Listener 17 | } 18 | 19 | var listenerActor = &kernel.Actor{ 20 | Init: func(context *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 21 | name := args[0].(string) 22 | handler := args[1].(*kernel.Actor) 23 | port := args[2].(int) 24 | clientSup := args[3].(*kernel.Pid) 25 | childSup := args[4].(*kernel.Pid) // 二级根sup 26 | opt := args[5].([]optFun) 27 | optStruct := parseOpt(opt) 28 | l := listener{} 29 | if optStruct.isUseNbio { 30 | l.isUseNbio = true 31 | connectStarter, _ := kernel.Start(starterActor, handler, clientSup, optStruct.clientArgs) 32 | g := nbio.NewGopher(nbio.Config{ 33 | Name: name, 34 | Network: "tcp", 35 | Addrs: []string{":" + strconv.Itoa(port)}, 36 | ReadBufferSize: 1024, 37 | EpollMod: nbio.EPOLLET, // 边缘模式 38 | }) 39 | l.g = g 40 | g.OnOpen(func(c *nbio.Conn) { 41 | conn := NewNbConn(c) 42 | c.SetSession(conn) 43 | connectStarter.Cast(conn) 44 | }) 45 | g.OnClose(func(c *nbio.Conn, err error) { 46 | kernel.ErrorLog("on closed :%s",c.RemoteAddr().String()) 47 | if c.Session() != nil { 48 | s := c.Session().(*ConnNbio) 49 | if s.mod == nb_mode_active { // 暂时不需要考虑race 50 | s.recvPid.Cast(&TcpError{ErrType: ErrClosed}) 51 | }else{ 52 | s.Close() 53 | } 54 | } 55 | }) 56 | g.OnRead(func(c *nbio.Conn) { 57 | c.Session().(*ConnNbio).OnRead(c) 58 | }) 59 | g.Start() 60 | kernel.ErrorLog("gate start on nbio") 61 | } else { 62 | ls, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) 63 | if err != nil { 64 | kernel.ErrorLog(err.Error()) 65 | log.Panic(err) 66 | 67 | } 68 | // 假如port=0,那么端口是随机的,记录一下 69 | addrMap[name] = ls.Addr().String() 70 | l.ls = ls 71 | l.startAccept(ls, handler, clientSup, childSup, optStruct) 72 | } 73 | return &l 74 | }, 75 | HandleCast: func(context *kernel.Context, msg interface{}) { 76 | }, 77 | HandleCall: func(context *kernel.Context, request interface{}) interface{} { 78 | state := context.State.(*listener) 79 | switch request.(type) { 80 | case kernel.AppStopType: 81 | if state.isUseNbio { 82 | state.g.Stop() 83 | state.g = nil 84 | }else { 85 | // 停止接收连接 86 | state.ls.Close() 87 | state.ls = nil 88 | } 89 | } 90 | return nil 91 | }, 92 | Terminate: func(context *kernel.Context, reason *kernel.Terminate) { 93 | state := context.State.(*listener) 94 | if state.g !=nil { 95 | state.g.Stop() 96 | } 97 | if state.ls != nil { 98 | state.ls.Close() 99 | } 100 | }, 101 | ErrorHandler: func(context *kernel.Context, err interface{}) bool { 102 | return true 103 | }, 104 | } 105 | 106 | func (l *listener) startAccept(ls net.Listener, handler *kernel.Actor, clientSup *kernel.Pid, childSup *kernel.Pid, opt *optStruct) { 107 | for i := 0; i < opt.acceptNum; i++ { 108 | pid, err := kernel.Start(acceptorActor, clientSup, ls, handler, opt.clientArgs) 109 | if err != nil { 110 | log.Panic(err) 111 | } 112 | kernel.Cast(pid, true) 113 | } 114 | } 115 | 116 | func parseOpt(opt []optFun) *optStruct { 117 | df := &optStruct{acceptNum: 10, clientArgs: nil} 118 | for _, f := range opt { 119 | f(df) 120 | } 121 | return df 122 | } 123 | 124 | type optStruct struct { 125 | isUseNbio bool 126 | acceptNum int 127 | clientArgs []interface{} 128 | } 129 | -------------------------------------------------------------------------------- /gate/nb_conn.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "errors" 5 | "github.com/lesismal/nbio" 6 | "github.com/liangmanlin/gootp/bpool" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "io" 9 | "sync" 10 | "sync/atomic" 11 | "syscall" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | var ( 17 | ErrSocketActiveMode = errors.New("active mode") 18 | ) 19 | 20 | type ConnNbio struct { 21 | mux sync.Mutex 22 | *nbio.Conn 23 | recvPid *kernel.Pid 24 | 25 | recvState bool 26 | recvChan chan kernel.Empty 27 | 28 | head int 29 | totalSize int 30 | buffer *bpool.Buff 31 | } 32 | 33 | func NewNbConn(conn *nbio.Conn) Conn { 34 | return &ConnNbio{Conn: conn, recvChan: make(chan kernel.Empty, 1)} 35 | } 36 | 37 | func (c *ConnNbio) getHandler() *kernel.Pid { 38 | return (*kernel.Pid)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&c.recvPid)))) 39 | } 40 | 41 | // 仅仅被动模式可用 42 | func (c *ConnNbio) Read(buf []byte) (n int, err error) { 43 | if c.getHandler() != nil { 44 | err = ErrSocketActiveMode 45 | return 46 | } 47 | c.mux.Lock() 48 | size := len(buf) 49 | var rn int 50 | for n < size { 51 | if c.Conn == nil { 52 | err = ErrSocketClosed 53 | break 54 | } 55 | rn, err = c.Conn.Read(buf[n:]) 56 | if rn > 0 { 57 | n += rn 58 | // 再继续尝试 59 | } 60 | if err != nil { 61 | if errors.Is(err, syscall.EINTR) { 62 | continue 63 | } 64 | if errors.Is(err, syscall.EAGAIN) { 65 | // 这里需要阻塞等待数据 66 | c.recvState = true 67 | c.mux.Unlock() 68 | <-c.recvChan 69 | c.mux.Lock() 70 | if c.Conn == nil { 71 | err = ErrSocketClosed 72 | break 73 | } 74 | continue 75 | } else { 76 | break 77 | } 78 | } 79 | } 80 | c.mux.Unlock() 81 | return 82 | } 83 | 84 | func (c *ConnNbio) Recv(len int, timeOutMS int) ([]byte, error) { 85 | var err error 86 | if c.getHandler() != nil { 87 | return nil, ErrSocketActiveMode 88 | } 89 | 90 | if timeOutMS > 0 { 91 | err = c.SetReadDeadline(time.Now().Add(time.Duration(timeOutMS) * time.Millisecond)) 92 | if err != nil { 93 | return nil, err 94 | } 95 | } 96 | if c.head == 0 { 97 | buf := make([]byte, len) 98 | _, err = io.ReadAtLeast(c, buf, len) 99 | return buf, err 100 | } 101 | headBuf := make([]byte, c.head) 102 | _, err = io.ReadAtLeast(c, headBuf, c.head) 103 | if err != nil { 104 | return nil, err 105 | } 106 | packSize := ReadHead(c.head, headBuf) 107 | pack := make([]byte, packSize) 108 | _, err = io.ReadAtLeast(c, pack, packSize) 109 | c.SetReadDeadline(time.Time{}) 110 | return pack, err 111 | } 112 | 113 | func (c *ConnNbio) Send(buf []byte) (int, error) { 114 | if c.Conn == nil { 115 | return 0, ErrSocketClosed 116 | } 117 | if c.head == 0 { 118 | n, err := c.Conn.Write(buf) 119 | return n, err 120 | } 121 | head := c.head 122 | size := len(buf) 123 | sendBuf := make([]byte, head, size+head) 124 | WriteSize(sendBuf, head, size) 125 | sendBuf = append(sendBuf, buf...) 126 | n, err := c.Conn.Write(sendBuf) 127 | return n, err 128 | } 129 | 130 | // 这个函数用来发送经过pb打包的数据,可以减少一次分配内存 131 | func (c *ConnNbio) SendBufHead(buf []byte) error { 132 | if c.Conn == nil { 133 | return ErrSocketClosed 134 | } 135 | _, err := c.Conn.Write(buf) 136 | return err 137 | } 138 | 139 | func (c *ConnNbio) SetHead(head int) { 140 | if head != 2 && head != 4 { 141 | kernel.ErrorLog("not support head = %d", head) 142 | return 143 | } 144 | c.head = head 145 | } 146 | func (c *ConnNbio) GetHead() int { 147 | return c.head 148 | } 149 | 150 | // 开始异步接收数据,该函数尽量只调用一次,并且确保没有调用Recv 151 | func (c *ConnNbio) StartReader(dest *kernel.Pid) { 152 | if c.head != 2 && c.head != 4 { 153 | kernel.ErrorLog("not support head = %d", c.head) 154 | return 155 | } 156 | if c.Conn == nil { 157 | kernel.ErrorLog("start reader on close conn") 158 | return 159 | } 160 | c.mux.Lock() 161 | defer c.mux.Unlock() 162 | c.recvState = false 163 | close(c.recvChan) 164 | c.recvChan = nil 165 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&c.recvPid)), unsafe.Pointer(dest)) 166 | // 考虑到可能缓冲区还有数据,这里尝试读取一次数据 167 | c.onRead(c.Conn, dest) 168 | } 169 | 170 | // epoll 触发 171 | func (c *ConnNbio) OnRead(conn *nbio.Conn) { 172 | recvPid := c.getHandler() 173 | if recvPid == nil { 174 | c.mux.Lock() 175 | if c.recvState { 176 | c.recvState = false 177 | c.recvChan <- kernel.Empty{} 178 | } 179 | c.mux.Unlock() 180 | } else { 181 | c.onRead(conn, recvPid) 182 | } 183 | } 184 | 185 | func (c *ConnNbio) onRead(conn *nbio.Conn, recvPid *kernel.Pid) { 186 | if c.buffer == nil { 187 | c.buffer = bpool.New(4 * 1024) 188 | } 189 | buf := c.buffer.ToBytes()[:c.buffer.Cap()] 190 | for { 191 | bSize := c.buffer.Cap() - c.buffer.Size() 192 | n, err := conn.Read(buf[c.buffer.Size():]) 193 | if n > 0 { 194 | c.buffer.SetSize(c.buffer.Size() + n) 195 | buf = c.readBuf(buf, recvPid) 196 | } 197 | if errors.Is(err, syscall.EINTR) { 198 | continue 199 | } 200 | if errors.Is(err, syscall.EAGAIN) { 201 | break 202 | } 203 | if err != nil { 204 | c.CloseWithError(err) 205 | } 206 | if n < bSize { 207 | break 208 | } 209 | } 210 | } 211 | 212 | func (c *ConnNbio) readBuf(buf []byte, recvPid *kernel.Pid) []byte { 213 | for { 214 | if c.totalSize == 0 && c.buffer.Size() >= c.head { 215 | c.totalSize = ReadHead(c.head, buf) + c.head 216 | if c.buffer.Cap() < c.totalSize { 217 | c.buffer = bpool.New(c.totalSize).Append(buf[:c.buffer.Size()]...) 218 | buf = c.buffer.ToBytes()[:c.buffer.Cap()] 219 | } 220 | } 221 | size := c.buffer.Size() 222 | if c.totalSize > 0 && size >= c.totalSize { 223 | if c.totalSize == size { 224 | recvPid.Cast(c.buffer) 225 | c.buffer = bpool.New(4 * 1024) 226 | buf = c.buffer.ToBytes()[:c.buffer.Cap()] 227 | c.totalSize = 0 228 | break 229 | } else { 230 | tmp := c.buffer 231 | c.buffer = bpool.NewBuf(buf[c.totalSize:size]) 232 | buf = c.buffer.ToBytes()[:c.buffer.Cap()] 233 | tmp.SetSize(c.totalSize) 234 | c.totalSize = 0 235 | recvPid.Cast(tmp) 236 | } 237 | } else { 238 | break 239 | } 240 | } 241 | return buf 242 | } 243 | 244 | func (c *ConnNbio) Close() error { 245 | if c.Conn == nil { 246 | return nil 247 | } 248 | err := c.Conn.Close() 249 | c.Conn = nil 250 | c.mux.Lock() 251 | if c.recvState { 252 | c.recvState = false 253 | close(c.recvChan) 254 | } 255 | c.mux.Unlock() 256 | return err 257 | } 258 | -------------------------------------------------------------------------------- /gate/net_conn.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "errors" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "io" 7 | "net" 8 | "sync/atomic" 9 | "time" 10 | "unsafe" 11 | ) 12 | 13 | type ConnNet struct { 14 | net.Conn 15 | 16 | head int 17 | handler *kernel.Pid 18 | headBuf []byte 19 | } 20 | 21 | var ( 22 | ErrSocketClosed = errors.New("socket closed") 23 | ErrSocketReading = errors.New("socket is reading") 24 | ) 25 | 26 | func NewConn(conn net.Conn) Conn { 27 | return &ConnNet{Conn: conn, head: 0} 28 | } 29 | 30 | func (c *ConnNet) Send(buf []byte) (int, error) { 31 | if c.Conn == nil { 32 | return 0, ErrSocketClosed 33 | } 34 | if c.head == 0 { 35 | n, err := c.Conn.Write(buf) 36 | return n, err 37 | } 38 | head := c.head 39 | size := len(buf) 40 | sendBuf := make([]byte, head, size+head) 41 | WriteSize(sendBuf, head, size) 42 | sendBuf = append(sendBuf, buf...) 43 | n, err := c.Conn.Write(sendBuf) 44 | return n, err 45 | } 46 | 47 | // 这个函数用来发送经过pb打包的数据,可以减少一次分配内存 48 | func (c *ConnNet) SendBufHead(buf []byte) error { 49 | if c.Conn == nil { 50 | return ErrSocketClosed 51 | } 52 | _, err := c.Conn.Write(buf) 53 | return err 54 | } 55 | 56 | func (c *ConnNet) SetHead(head int) { 57 | if head != 2 && head != 4 { 58 | kernel.ErrorLog("not support head = %d", head) 59 | return 60 | } 61 | c.head = head 62 | c.headBuf = make([]byte, head, head) 63 | } 64 | func (c *ConnNet) GetHead() int { 65 | return c.head 66 | } 67 | 68 | func (c *ConnNet) Recv(len int, timeOutMS int) ([]byte, error) { 69 | if c.handler != nil { 70 | return nil, ErrSocketReading 71 | } 72 | var err error 73 | if timeOutMS > 0 { 74 | err = c.Conn.SetReadDeadline(time.Now().Add(time.Duration(timeOutMS) * time.Millisecond)) 75 | if err != nil { 76 | return nil, err 77 | } 78 | } 79 | if c.head == 0 { 80 | buf := make([]byte, len) 81 | _, err = io.ReadAtLeast(c.Conn, buf, len) 82 | return buf, err 83 | } 84 | _, err = io.ReadAtLeast(c.Conn, c.headBuf, c.head) 85 | if err != nil { 86 | return nil, err 87 | } 88 | packSize := ReadHead(c.head, c.headBuf) 89 | pack := make([]byte, packSize) 90 | _, err = io.ReadAtLeast(c.Conn, pack, packSize) 91 | return pack, err 92 | } 93 | 94 | // 开始异步接收数据,该函数尽量只调用一次 95 | // 没有考虑并发的情况,使用者自行规避 96 | func (c *ConnNet) StartReader(dest *kernel.Pid) { 97 | if c.head != 2 && c.head != 4 { 98 | kernel.ErrorLog("start reader error: head = %d", c.head) 99 | return 100 | } 101 | if c.handler !=nil { 102 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&c.handler)),unsafe.Pointer(dest)) 103 | } else { 104 | c.handler = dest 105 | go startReader(c) 106 | } 107 | } 108 | 109 | func (c *ConnNet) Close() error { 110 | if c.Conn == nil { 111 | return nil 112 | } 113 | return c.Conn.Close() 114 | } 115 | 116 | func (c *ConnNet)getHandler() *kernel.Pid { 117 | return (*kernel.Pid)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&c.handler)))) 118 | } -------------------------------------------------------------------------------- /gate/pb/type.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type Coder struct { 9 | def map[reflect.Type]*inDef 10 | id2def map[int]*inDef 11 | enMap []fieldEncodeFunc 12 | } 13 | 14 | type inDef struct { 15 | id int 16 | minBufSize int 17 | objType reflect.Type 18 | ens []*encode 19 | des []*decode 20 | } 21 | 22 | type encode struct { 23 | enc fieldEncodeFunc 24 | child reflect.Type 25 | } 26 | 27 | type decode struct { 28 | dec fieldDecodeFunc 29 | child reflect.Type 30 | } 31 | 32 | type fieldEncodeFunc func(buf []byte, ptr unsafe.Pointer,vType reflect.Type) []byte 33 | 34 | type fieldDecodeFunc func(buf []byte, index int, vType reflect.Type) (int, reflect.Value) 35 | -------------------------------------------------------------------------------- /gate/reader.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/bpool" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "io" 7 | "runtime/debug" 8 | "time" 9 | ) 10 | 11 | func startReader(conn *ConnNet) { 12 | defer func() { 13 | p := recover() 14 | if p != nil { 15 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 16 | } 17 | }() // catch the error 18 | c := conn.Conn 19 | if err := c.SetReadDeadline(time.Time{}); err != nil { // 规避超时 20 | kernel.Cast(conn.handler, &TcpError{ErrType: ErrDeadLine, Err: err}) 21 | return 22 | } 23 | head := conn.head 24 | headBuf := make([]byte, head) 25 | pack := bpool.New(100) // 先默认申请一个小的 26 | var packSize int 27 | for { 28 | _, err := io.ReadAtLeast(c, headBuf, head) 29 | if err != nil { 30 | kernel.Cast(conn.getHandler(), &TcpError{ErrType: ErrReadErr, Err: err}) 31 | break 32 | } 33 | packSize = ReadHead(head, headBuf) 34 | if packSize > 0 { 35 | pack = bpool.New(packSize) 36 | _, err = pack.Read(c, packSize) 37 | handler := conn.getHandler() 38 | if err != nil { 39 | kernel.Cast(handler, &TcpError{ErrType: ErrReadErr, Err: err}) 40 | goto end 41 | } 42 | kernel.Cast(handler, pack) 43 | } else { 44 | kernel.Cast(conn.getHandler(), []byte{}) 45 | } 46 | } 47 | end: 48 | } 49 | 50 | func ReadHead(head int, buf []byte) (packSize int) { 51 | switch head { 52 | case 2: 53 | packSize = (int(buf[0]) << 8) + int(buf[1]) 54 | case 4: 55 | packSize = (int(buf[0]) << 24) + (int(buf[1]) << 16) + (int(buf[2]) << 8) + int(buf[3]) 56 | } 57 | return 58 | } 59 | -------------------------------------------------------------------------------- /gate/type.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "net" 6 | ) 7 | 8 | const ( 9 | ErrDeadLine = iota + 1 10 | ErrReadErr 11 | ErrClosed 12 | ) 13 | 14 | type TcpError struct { 15 | ErrType int 16 | Err error 17 | } 18 | 19 | type Pack struct { 20 | ProtoID int 21 | Proto interface{} 22 | } 23 | 24 | type app struct { 25 | name string 26 | port int 27 | handler *kernel.Actor 28 | opt []optFun 29 | } 30 | 31 | type Conn interface { 32 | net.Conn 33 | Send(buf []byte) (int, error) 34 | SendBufHead(buf []byte) error 35 | SetHead(head int) 36 | GetHead() int 37 | Recv(len int, timeOutMS int) ([]byte,error) 38 | StartReader(dest *kernel.Pid) 39 | } 40 | 41 | type optFun func(*optStruct) -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/liangmanlin/gootp 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.6.0 7 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3 8 | github.com/liangmanlin/readline v0.0.0-20220106124050-231e336f3d3d 9 | ) 10 | 11 | require ( 12 | github.com/chzyer/logex v1.2.0 // indirect 13 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23 // indirect 14 | github.com/liangmanlin/routine v1.1.0 // indirect 15 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 2 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 3 | github.com/chzyer/logex v1.2.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM= 4 | github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= 5 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ= 6 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 7 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 8 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 9 | github.com/lesismal/llib v1.1.4/go.mod h1:3vmCrIMrpkaoA3bDu/sI+J7EyEUMPbOvmAxb7PlzilM= 10 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3 h1:670ZAWj/DNCclpVRZSYZUXImKlSLLj87MpENDT7C4ZA= 11 | github.com/lesismal/nbio v1.2.14-0.20220301150822-22a5357345f3/go.mod h1:X2ILVmTUGwZ10337Vat4MLs7FuSZ93gWg7S9sPpN8RY= 12 | github.com/liangmanlin/readline v0.0.0-20220106124050-231e336f3d3d h1:wvgLGd8HRsOaB9VFClM7F541deYH8m3UWP13B90/ckQ= 13 | github.com/liangmanlin/readline v0.0.0-20220106124050-231e336f3d3d/go.mod h1:reZL5xuyygWwVtSJLetMCTzfFCeS6fA3RRijnf1Q3W4= 14 | github.com/liangmanlin/routine v1.1.0 h1:VmIH+rRIIYzNlXFX3N8HvG8K/TDVevcGIseSHNfGLa0= 15 | github.com/liangmanlin/routine v1.1.0/go.mod h1:QfTm8ko1ZoIXSJ0rzS2RpveIiCwQIb5UVOs6weD7dU0= 16 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 17 | golang.org/x/crypto v0.0.0-20210513122933-cd7d49e622d5/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 18 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 19 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 20 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= 23 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 24 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 25 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 26 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 27 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 28 | -------------------------------------------------------------------------------- /gutil/sort.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/pfun" 5 | "reflect" 6 | "sort" 7 | "unsafe" 8 | ) 9 | 10 | type s struct { 11 | cmp cmpFunc 12 | swap swapFunc 13 | ptr unsafe.Pointer 14 | } 15 | 16 | func (s *s) Len() int { 17 | return pfun.SliceSize(s.ptr) 18 | } 19 | 20 | func (s *s) Less(i, j int) bool { 21 | return s.cmp(s.ptr, i, j) 22 | } 23 | 24 | func (s *s) Swap(i, j int) { 25 | s.swap(s.ptr, i, j) 26 | } 27 | 28 | /* 29 | 排序 30 | 31 | 只是一个sort.Sort函数的封装,使得不需要使用接口 32 | 33 | 由于包装了一层,所以效率会比sort.Sort慢2.5倍左右 34 | */ 35 | func Sort(list interface{}, cmp cmpFunc, swap swapFunc) { 36 | if reflect.ValueOf(list).Kind() != reflect.Slice{ 37 | return 38 | } 39 | ptr := pfun.Ptr(list) 40 | sort.Sort(&s{cmp: cmp, swap: swap, ptr: ptr}) 41 | } 42 | -------------------------------------------------------------------------------- /gutil/type.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import "unsafe" 4 | 5 | type cmpFunc = func(data unsafe.Pointer,i,j int) bool 6 | type swapFunc = func(data unsafe.Pointer,i,j int) -------------------------------------------------------------------------------- /gutil/util.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/pfun" 5 | "math" 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | func MaxInt32(a, b int32) int32 { 11 | if a > b { 12 | return a 13 | } 14 | return b 15 | } 16 | 17 | func MinInt32(a, b int32) int32 { 18 | if a < b { 19 | return a 20 | } 21 | return b 22 | } 23 | 24 | func MaxInt64(a, b int64) int64 { 25 | if a > b { 26 | return a 27 | } 28 | return b 29 | } 30 | 31 | func MinInt64(a, b int64) int64 { 32 | if a < b { 33 | return a 34 | } 35 | return b 36 | } 37 | 38 | func MaxFloat32(a, b float32) float32 { 39 | if a > b { 40 | return a 41 | } 42 | return b 43 | } 44 | 45 | func MinFloat32(a, b float32) float32 { 46 | if a < b { 47 | return a 48 | } 49 | return b 50 | } 51 | 52 | func Ceil(v float32) int32 { 53 | return int32(math.Ceil(float64(v))) 54 | } 55 | 56 | func Round(v float32) int32 { 57 | return int32(math.Round(float64(v))) 58 | } 59 | 60 | func Trunc(v float32) int32 { 61 | return int32(v) 62 | } 63 | 64 | func Abs(v int32) int32 { 65 | if v >=0 { 66 | return v 67 | } 68 | return -v 69 | } 70 | 71 | func SliceDelInt32(arr []int32, value int32) []int32 { 72 | for i, v := range arr { 73 | if value == v { 74 | return append(arr[:i], arr[i+1:]...) 75 | } 76 | } 77 | return arr 78 | } 79 | 80 | func SliceDelInt64(arr []int64, value int64) []int64 { 81 | for i, v := range arr { 82 | if value == v { 83 | return append(arr[:i], arr[i+1:]...) 84 | } 85 | } 86 | return arr 87 | } 88 | 89 | func SliceInt32Member(value int32,arr []int32)bool{ 90 | for _,v := range arr{ 91 | if v == value{ 92 | return true 93 | } 94 | } 95 | return false 96 | } 97 | 98 | // 根据一个范围,获取到值 99 | // list := []struct{{int32,int32,interface{}}} 100 | func FindRangeValue(list interface{}, value int32) interface{} { 101 | vf := reflect.ValueOf(list) 102 | if vf.Kind() != reflect.Slice { 103 | return nil 104 | } 105 | rsf := vf.Type().Elem() 106 | inc := rsf.Size() 107 | getPtrF := pfun.KindFun(&rsf) 108 | if !(rsf.NumField() == 3 && rsf.Field(0).Type.Kind() == reflect.Int32 && rsf.Field(1).Type.Kind() == reflect.Int32) { 109 | return nil 110 | } 111 | ptr := pfun.Ptr(list) 112 | sliceDataPtr := *(*unsafe.Pointer)(ptr) 113 | size := pfun.SliceSize(ptr) 114 | for i := 0; i < size; i++ { 115 | p := getPtrF(sliceDataPtr, inc*uintptr(i)) 116 | min := *(*int32)(p) 117 | max := pfun.GetInt32(p, 4) 118 | if min <= value && max >= value { 119 | return vf.Index(i).Field(2).Interface() 120 | } 121 | } 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /gutil/util_test.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | ) 7 | 8 | type rangeConfig struct { 9 | Min, Max int32 10 | V int32 11 | } 12 | 13 | type k struct { 14 | I int32 15 | } 16 | 17 | func TestFindRangeValue(t *testing.T) { 18 | l := []rangeConfig{{1, 2, 1}, {3, 4, 2}, {5, 6, 3}, {7, 199, 4}} 19 | t.Logf("result:%#v", FindRangeValue(l, 100)) 20 | } 21 | 22 | func BenchmarkFindRangeValue(b *testing.B) { 23 | l := []rangeConfig{{1, 2, 1}, {3, 4, 2}, {5, 6, 3}, {7, 199, 4}} 24 | b.ResetTimer() 25 | for i := 0; i < b.N; i++ { 26 | FindRangeValue(l, 100) 27 | } 28 | } 29 | 30 | func BenchmarkSliceDelInt32(b *testing.B) { 31 | b.ResetTimer() 32 | for i := 0; i < b.N; i++ { 33 | l := []int32{1, 2, 3, 4, 5, 6, 7, 8} 34 | SliceDelInt32(l, 3) 35 | } 36 | } 37 | 38 | func BenchmarkSortInt32(b *testing.B) { 39 | l := []int32{5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 40 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 41 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 42 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 43 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 44 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 45 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 46 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 47 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 48 | 5, 4, 2, 1, 7, 8, 9, 10, 11, 100, 3, 49 | } 50 | f := func(i,j int) bool { 51 | return l[i] 0 { 20 | paramStr := linkParam(param, urlEncode) 21 | if strings.Index(url, "?") >= 0 { 22 | url += "&" + paramStr 23 | } else { 24 | url += "?" + paramStr 25 | } 26 | } 27 | return Request(server, "GET", url, args...) 28 | } 29 | 30 | func Post(url, data, contentType string,args ...interface{}) (body []byte, ok bool) { 31 | return post(url, data, contentType,args...) 32 | } 33 | 34 | func PostSSL(url, data, contentType string,args ...interface{}) (body []byte, ok bool) { 35 | args = append(args,WithSSL()) 36 | return post(url, data, contentType,args...) 37 | } 38 | 39 | func post(url, data, contentType string,args ...interface{}) (body []byte, ok bool) { 40 | args = append(args,WithBody(data),WithContentType(contentType)) 41 | return Request(server, "POST", url, args...) 42 | } 43 | 44 | func linkParam(param map[string]interface{}, urlEncode bool) string { 45 | var list []string 46 | for k, v := range param { 47 | switch d := v.(type) { 48 | case string: 49 | list = append(list, k+"="+encode(d, urlEncode)) 50 | case float32, float64: 51 | list = append(list, fmt.Sprintf("%s=%f", k, d)) 52 | default: 53 | list = append(list, fmt.Sprintf("%s=%d", k, d)) 54 | } 55 | } 56 | return strings.Join(list, "&") 57 | } 58 | 59 | func encode(str string, need bool) string { 60 | if need { 61 | return url.QueryEscape(str) 62 | } 63 | return str 64 | } 65 | 66 | func WithSSL() useSSL { 67 | return useSSL{} 68 | } 69 | 70 | func WithBody(bodyStr string) bodyType { 71 | return bodyType(bodyStr) 72 | } 73 | 74 | func WithContentType(contentT string) contentType { 75 | return contentType(contentT) 76 | } 77 | 78 | // timeout sec,default is 3s 79 | func WithTimeOut(t int32) timeOut { 80 | return timeOut(t) 81 | } 82 | 83 | func WithHeader(key,value string) header { 84 | return header{key: key,value: value} 85 | } -------------------------------------------------------------------------------- /httpc/httpc_manager.go: -------------------------------------------------------------------------------- 1 | package httpc 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "github.com/liangmanlin/gootp/ringbuffer" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | var server *kernel.Pid 11 | 12 | var started = false 13 | 14 | var mangerActor = &kernel.Actor{ 15 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 16 | server = pid 17 | state := manager{queue: ringbuffer.NewSingleRingBuffer(10, 100)} 18 | workerPid := StartWorker(pid) 19 | state.idle = []*kernel.Pid{workerPid} 20 | state.poolSize = 1 21 | state.maxPoolSize = 5 22 | kernel.SendAfterForever(pid, 5*1000, kernel.Loop{}) 23 | kernel.ErrorLog("httpc_manager started :%s", pid) 24 | return &state 25 | }, 26 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 27 | state := ctx.State.(*manager) 28 | switch m := msg.(type) { 29 | case *requestData: 30 | size := len(state.idle) 31 | if size > 0 { 32 | workerPid := state.idle[size-1] 33 | state.idle = state.idle[:size-1] 34 | kernel.Cast(workerPid, m) 35 | } else { 36 | state.queue.Put(m) 37 | } 38 | case kernel.Loop: 39 | if state.queue.Size() > 0 && state.poolSize < state.maxPoolSize { 40 | workerPid := StartWorker(ctx.Self()) 41 | ctx.CastSelf(workerPid) 42 | } 43 | case *kernel.Pid: 44 | if req := state.queue.Pop();req !=nil { 45 | kernel.Cast(m, req) 46 | } else { 47 | state.idle = append(state.idle, m) 48 | } 49 | case *kernel.PidExit: 50 | state.poolSize-- 51 | state.idle = delPid(state.idle, m.Pid) 52 | } 53 | }, 54 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 55 | switch r := request.(type) { 56 | case maxPoolSize: 57 | ctx.State.(*manager).maxPoolSize = int32(r) 58 | } 59 | return nil 60 | }, 61 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 62 | 63 | }, 64 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 65 | return true 66 | }, 67 | } 68 | 69 | func SetMaxPoolSize(num int32) { 70 | confirmStart() 71 | kernel.Call(server, maxPoolSize(num)) 72 | } 73 | 74 | func confirmStart() { 75 | if started { 76 | return 77 | } 78 | kernel.SupStartChild("kernel", &kernel.SupChild{ 79 | ChildType: kernel.SupChildTypeSup, 80 | ReStart: true, 81 | Name: "httpc_sup", 82 | }) 83 | kernel.SupStartChild("httpc_sup", &kernel.SupChild{ 84 | ChildType: kernel.SupChildTypeWorker, 85 | ReStart: true, 86 | Name: "httpc_manager", 87 | Svr: mangerActor, 88 | }) 89 | started = true 90 | } 91 | 92 | func Request(pid *kernel.Pid, method, url string, param ...interface{}) ([]byte, bool) { 93 | confirmStart() 94 | if pid == nil { 95 | pid = server 96 | } 97 | recv := make(chan response, 1) 98 | req := &requestData{method: method, url: url, ch: recv, close: 0} 99 | timeout := transParam(req, param) 100 | kernel.Cast(pid, req) 101 | t := time.After(time.Duration(timeout)*time.Second + 100*time.Millisecond) 102 | defer close(recv) 103 | select { 104 | case r := <-recv: 105 | return r.rsp, true 106 | case <-t: 107 | atomic.AddInt32(&req.close, 1) // 给与worker判断是否丢弃 108 | return nil, false 109 | } 110 | } 111 | 112 | func transParam(req *requestData, param []interface{}) (timeout int32) { 113 | timeout = 3 114 | for _, v := range param { 115 | switch t := v.(type) { 116 | case useSSL: 117 | req.ssl = true 118 | case contentType: 119 | req.contentType = string(t) 120 | case bodyType: 121 | req.body = string(t) 122 | case timeOut: 123 | timeout = int32(t) 124 | req.timeOut = timeout 125 | case header: 126 | req.headers = append(req.headers, t) 127 | } 128 | } 129 | return 130 | } 131 | 132 | func delPid(list []*kernel.Pid, pid *kernel.Pid) []*kernel.Pid { 133 | size := len(list) 134 | for i := 0; i < size; i++ { 135 | if list[i].GetID() == pid.GetID() { 136 | list[i] = list[size-1] 137 | list[size-1] = nil // GC 138 | list = list[:size-1] 139 | break 140 | } 141 | } 142 | return list 143 | } 144 | -------------------------------------------------------------------------------- /httpc/httpc_test.go: -------------------------------------------------------------------------------- 1 | package httpc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestGet(t *testing.T) { 11 | go func() { 12 | time.Sleep(3 * time.Second) 13 | kernel.ErrorLog("test init stop now") 14 | kernel.InitStop() 15 | }() 16 | kernel.Env.LogPath = "" 17 | kernel.KernelStart(func() { 18 | if body,ok := Get("http://daohang.4399om.com/",nil,false,WithTimeOut(3));ok{ 19 | fmt.Println(string(body)) 20 | }else{ 21 | t.Errorf("error") 22 | } 23 | if body,ok := GetSSL("https://daohang.4399om.com/",nil,false,WithTimeOut(3),WithHeader("cookie","afd"));ok{ 24 | fmt.Println(string(body)) 25 | }else{ 26 | t.Errorf("error") 27 | } 28 | },nil) 29 | } -------------------------------------------------------------------------------- /httpc/httpc_worker.go: -------------------------------------------------------------------------------- 1 | package httpc 2 | 3 | import ( 4 | "crypto/tls" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "io/ioutil" 7 | "net/http" 8 | "sync/atomic" 9 | "time" 10 | ) 11 | 12 | func StartWorker(father *kernel.Pid) *kernel.Pid { 13 | pid, _ := kernel.Start(workerActor, father) 14 | return pid 15 | } 16 | 17 | var workerActor = &kernel.Actor{ 18 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 19 | tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 20 | var father *kernel.Pid 21 | if args[0] != nil { 22 | father = args[0].(*kernel.Pid) 23 | ctx.Link(father) 24 | } 25 | state := worker{ 26 | father: father, 27 | client: &http.Client{}, 28 | sslClient: &http.Client{Transport: tr}, 29 | } 30 | return &state 31 | }, 32 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 33 | state := ctx.State.(*worker) 34 | switch m := msg.(type) { 35 | case *requestData: 36 | if atomic.LoadInt32(&m.close) == 0 { 37 | client := state.client 38 | if m.ssl { 39 | client = state.sslClient 40 | } 41 | client.Timeout = time.Duration(m.timeOut) * time.Second 42 | req, err := http.NewRequest(m.method, m.url, nil) 43 | if err != nil { 44 | kernel.ErrorLog("new request error:%s",err) 45 | responseFunc(nil, err, m) 46 | return 47 | } 48 | if m.method == "POST"{ 49 | if m.contentType == "" { 50 | m.contentType = "application/ejson" 51 | } 52 | req.Header.Set("Content-Type",m.contentType) 53 | } 54 | addHeader(req,m) 55 | rsp, err := client.Do(req) 56 | responseFunc(rsp, err, m) 57 | } 58 | if state.father != nil { 59 | kernel.Cast(state.father, ctx.Self()) 60 | } 61 | } 62 | }, 63 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 64 | return nil 65 | }, 66 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 67 | 68 | }, 69 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 70 | return true 71 | }, 72 | } 73 | 74 | func responseFunc(rsp *http.Response, err error, m *requestData) { 75 | if err != nil { 76 | kernel.ErrorLog("httpc %s err:%#v", m.url, err) 77 | m.ch <- response{ok: false, rsp: nil} 78 | } else { 79 | defer rsp.Body.Close() 80 | if rsp.StatusCode == 200 { 81 | body, _ := ioutil.ReadAll(rsp.Body) 82 | m.ch <- response{ok: true, rsp: body} 83 | } else { 84 | kernel.ErrorLog("httpc %s rsp code:%d",m.url,rsp.StatusCode) 85 | m.ch <- response{ok: false, rsp: nil} 86 | } 87 | } 88 | } 89 | 90 | func addHeader(req *http.Request,m *requestData) { 91 | for _,v := range m.headers{ 92 | req.Header.Set(v.key,v.value) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /httpc/type.go: -------------------------------------------------------------------------------- 1 | package httpc 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "github.com/liangmanlin/gootp/ringbuffer" 6 | "net/http" 7 | ) 8 | 9 | type manager struct { 10 | idle []*kernel.Pid 11 | queue *ringbuffer.SingleRingBuffer 12 | poolSize int32 13 | maxPoolSize int32 14 | } 15 | 16 | type worker struct { 17 | father *kernel.Pid 18 | client *http.Client 19 | sslClient *http.Client 20 | } 21 | 22 | type response struct { 23 | ok bool 24 | rsp []byte 25 | } 26 | 27 | type requestData struct { 28 | method string 29 | url string 30 | body string 31 | contentType string 32 | ssl bool 33 | close int32 34 | ch chan response 35 | timeOut int32 36 | headers []header 37 | } 38 | 39 | type maxPoolSize int32 40 | 41 | type useSSL struct { 42 | } 43 | 44 | type contentType string 45 | 46 | type bodyType string 47 | 48 | type timeOut int32 49 | 50 | type header struct { 51 | key, value string 52 | } 53 | -------------------------------------------------------------------------------- /httpd/README.md: -------------------------------------------------------------------------------- 1 | # 基于 [nbio](https://github.com/lesismal/nbio) 的webserver 2 | 3 | ## 系统构建于`kernel`之上 4 | 5 | - 基于 [nbio](https://github.com/lesismal/nbio) 支持百万长链接 6 | 7 | - 你可以通过配置,控制固定的处理线程 8 | 9 | - 高性能的路由器 10 | 11 | - 支持`websocket` 12 | 13 | - 支持拦截器 14 | 15 | - 如果使用了`websocket`,不建议在拦截器中执行阻塞代码,这样会使响应效率降低 16 | - 可以单独启动一个`httpd`作为websocket使用 17 | 18 | - 由于基于`kernel`,你可以使用所有相关技术 19 | 20 | - 仅仅支持http,如果有https需求,建议通过nginx做反向代理 21 | 22 | ## websocket可以作为一个单独库使用 23 | 24 | ## 使用方法参考 [example](example) 25 | 26 | ## parser,websocket 均修改自 [nbio](https://github.com/lesismal/nbio) 的http部分 27 | 28 | -------------------------------------------------------------------------------- /httpd/body.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/bpool" 5 | "io" 6 | "sync" 7 | ) 8 | 9 | var ( 10 | bodyReaderPool = sync.Pool{ 11 | New: func() interface{} { 12 | return &BodyReader{} 13 | }, 14 | } 15 | ) 16 | 17 | // BodyReader . 18 | type BodyReader struct { 19 | index int 20 | buffer *bpool.Buff 21 | } 22 | 23 | // Read implements io.Reader. 24 | func (br *BodyReader) Read(p []byte) (int, error) { 25 | need := len(p) 26 | buf := br.buffer.ToBytes() 27 | available := len(buf) - br.index 28 | if available <= 0 { 29 | return 0, io.EOF 30 | } 31 | if available >= need { 32 | copy(p, buf[br.index:br.index+need]) 33 | br.index += need 34 | // if available == need { 35 | // br.Close() 36 | // } 37 | return need, nil 38 | } 39 | copy(p[:available], buf[br.index:]) 40 | br.index += available 41 | return available, io.EOF 42 | } 43 | 44 | // Append . 45 | func (br *BodyReader) Append(data []byte) { 46 | if len(data) > 0 { 47 | if br.buffer == nil { 48 | br.buffer = bpool.NewBuf(data) 49 | } else { 50 | br.buffer = br.buffer.Append(data...) 51 | } 52 | } 53 | } 54 | 55 | func (br *BodyReader) RawBody() []byte { 56 | return br.buffer.ToBytes() 57 | } 58 | 59 | func (br *BodyReader) TakeOver() *bpool.Buff { 60 | b := br.buffer 61 | br.buffer = nil 62 | br.index = 0 63 | return b 64 | } 65 | 66 | // Close implements io. Closer. 67 | func (br *BodyReader) Close() error { 68 | br.close() 69 | return nil 70 | } 71 | 72 | func (br *BodyReader) close() { 73 | if br.buffer != nil { 74 | br.buffer.Free() 75 | br.buffer = nil 76 | br.index = 0 77 | } 78 | } 79 | 80 | // NewBodyReader creates a BodyReader. 81 | func NewBodyReader(buffer *bpool.Buff) *BodyReader { 82 | br := bodyReaderPool.Get().(*BodyReader) 83 | br.buffer = buffer 84 | br.index = 0 85 | return br 86 | } 87 | -------------------------------------------------------------------------------- /httpd/config.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import "github.com/liangmanlin/gootp/httpd/websocket" 4 | 5 | func WithManagerNum(num int) config { 6 | return func(engine *Engine) { 7 | engine.managerNum = num 8 | } 9 | } 10 | 11 | func WithMaxWorkerNum(num int) config { 12 | return func(engine *Engine) { 13 | engine.maxWorkerNum = num 14 | } 15 | } 16 | 17 | func WithReadLimit(num int) config { 18 | return func(engine *Engine) { 19 | engine.readLimit = num 20 | } 21 | } 22 | 23 | func WithTcpBuff(num int) config { 24 | return func(engine *Engine) { 25 | engine.tcpReadBuff = num 26 | } 27 | } 28 | 29 | // 修改负载均衡为随机,默认根据fd取模负载 30 | func WithBalancingRand() config { 31 | return func(engine *Engine) { 32 | engine.balancing = 1 33 | } 34 | } 35 | 36 | // 如果你仅仅想监听内网ip,或者多个端口,使用这个 37 | // WithAddr("127.0.0.1:8080") 38 | // WithAddr(":8081") 39 | func WithAddr(addr string) config { 40 | return func(engine *Engine) { 41 | engine.addr = append(engine.addr,addr) 42 | } 43 | } 44 | 45 | func WithWsConfig(cfg websocket.Config) config { 46 | return func(engine *Engine) { 47 | engine.wsConfig = &cfg 48 | } 49 | } -------------------------------------------------------------------------------- /httpd/ejson/json.go: -------------------------------------------------------------------------------- 1 | package ejson 2 | 3 | /* 4 | easy json 5 | */ 6 | 7 | import ( 8 | "encoding/json" 9 | "unsafe" 10 | ) 11 | 12 | type Json map[string]interface{} 13 | 14 | func Decode(buf []byte) Json { 15 | var m Json 16 | if json.Unmarshal(buf, &m) != nil { 17 | return nil 18 | } 19 | return m 20 | } 21 | 22 | func DecodeString(jsonStr string) Json { 23 | buf := *(*[]byte)(unsafe.Pointer(&jsonStr)) 24 | return Decode(buf) 25 | } 26 | 27 | func (j Json) Int(key string) int { 28 | if j == nil { 29 | return 0 30 | } 31 | if v, ok := j[key]; ok { 32 | return intValue(v) 33 | } 34 | return 0 35 | } 36 | 37 | func (j Json) Float(key string) float64 { 38 | if j == nil { 39 | return 0 40 | } 41 | if v, ok := j[key]; ok { 42 | return floatValue(v) 43 | } 44 | return 0 45 | } 46 | 47 | func (j Json) String(key string) string { 48 | if j == nil { 49 | return "" 50 | } 51 | if v, ok := j[key]; ok { 52 | return stringValue(v) 53 | } 54 | return "" 55 | } 56 | 57 | func (j Json) Json(key string) Json { 58 | if j == nil { 59 | return nil 60 | } 61 | if v, ok := j[key]; ok { 62 | if m, ok := v.(Json); ok { 63 | return m 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | func (j Json) List(key string) []Json { 70 | if j == nil { 71 | return nil 72 | } 73 | if v, ok := j[key]; ok { 74 | if m, ok := v.([]Json); ok { 75 | return m 76 | } 77 | } 78 | return nil 79 | } 80 | 81 | func (j Json) Encode() []byte { 82 | bin, _ := json.Marshal(j) 83 | return bin 84 | } 85 | 86 | func intValue(value interface{}) int { 87 | switch v := value.(type) { 88 | case int: 89 | return v 90 | case float64: 91 | return int(v) 92 | } 93 | return 0 94 | } 95 | 96 | func floatValue(value interface{}) float64 { 97 | if v, ok := value.(float64); ok { 98 | return v 99 | } 100 | return 0 101 | } 102 | 103 | func stringValue(value interface{}) string { 104 | if v, ok := value.(string); ok { 105 | return v 106 | } 107 | return "" 108 | } 109 | -------------------------------------------------------------------------------- /httpd/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 lesismal. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package httpd 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | var ( 12 | // ErrClosed . 13 | ErrClosed = errors.New("closed") 14 | 15 | // ErrInvalidCRLF . 16 | ErrInvalidCRLF = errors.New("invalid cr/lf at the end of line") 17 | 18 | // ErrInvalidHTTPVersion . 19 | ErrInvalidHTTPVersion = errors.New("invalid HTTP version") 20 | 21 | // ErrInvalidHTTPStatusCode . 22 | ErrInvalidHTTPStatusCode = errors.New("invalid HTTP status code") 23 | // ErrInvalidHTTPStatus . 24 | ErrInvalidHTTPStatus = errors.New("invalid HTTP status") 25 | 26 | // ErrInvalidMethod . 27 | ErrInvalidMethod = errors.New("invalid HTTP method") 28 | 29 | // ErrInvalidRequestURI . 30 | ErrInvalidRequestURI = errors.New("invalid URL") 31 | 32 | // ErrInvalidHost . 33 | ErrInvalidHost = errors.New("invalid host") 34 | 35 | // ErrInvalidPort . 36 | ErrInvalidPort = errors.New("invalid port") 37 | 38 | // ErrInvalidPath . 39 | ErrInvalidPath = errors.New("invalid path") 40 | 41 | // ErrInvalidQueryString . 42 | ErrInvalidQueryString = errors.New("invalid query string") 43 | 44 | // ErrInvalidFragment . 45 | ErrInvalidFragment = errors.New("invalid fragment") 46 | 47 | // ErrCRExpected . 48 | ErrCRExpected = errors.New("CR character expected") 49 | 50 | // ErrLFExpected . 51 | ErrLFExpected = errors.New("LF character expected") 52 | 53 | // ErrInvalidCharInHeader . 54 | ErrInvalidCharInHeader = errors.New("invalid character in header") 55 | 56 | // ErrUnexpectedContentLength . 57 | ErrUnexpectedContentLength = errors.New("unexpected content-length header") 58 | 59 | // ErrInvalidContentLength . 60 | ErrInvalidContentLength = errors.New("invalid ContentLength") 61 | 62 | // ErrInvalidChunkSize . 63 | ErrInvalidChunkSize = errors.New("invalid chunk size") 64 | 65 | // ErrTrailerExpected . 66 | ErrTrailerExpected = errors.New("trailer expected") 67 | 68 | // ErrTooLong . 69 | ErrTooLong = errors.New("invalid http message: too long") 70 | ) 71 | 72 | var ( 73 | // ErrInvalidH2SM . 74 | ErrInvalidH2SM = errors.New("invalid http2 SM characters") 75 | 76 | // ErrInvalidH2HeaderR . 77 | ErrInvalidH2HeaderR = errors.New("invalid http2 SM characters") 78 | ) 79 | 80 | var ( 81 | // ErrNilConn . 82 | ErrNilConn = errors.New("nil Conn") 83 | ) 84 | 85 | var ( 86 | // ErrClientUnsupportedSchema . 87 | ErrClientUnsupportedSchema = errors.New("unsupported schema") 88 | 89 | // ErrClientTimeout . 90 | ErrClientTimeout = errors.New("timeout") 91 | 92 | // ErrClientClosed . 93 | ErrClientClosed = errors.New("http client closed") 94 | ) 95 | -------------------------------------------------------------------------------- /httpd/example/http.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/args" 5 | "github.com/liangmanlin/gootp/httpd" 6 | "github.com/liangmanlin/gootp/kernel" 7 | ) 8 | 9 | func main() { 10 | kernel.Env.LogPath = "" 11 | kernel.KernelStart(func() { 12 | port ,_:= args.GetInt("port") 13 | if port == 0 { 14 | port = 8080 15 | } 16 | const managerNum = 8 17 | eg := httpd.New("test", port, 18 | httpd.WithManagerNum(managerNum), 19 | httpd.WithMaxWorkerNum(managerNum*2048), 20 | httpd.WithBalancingRand(), 21 | ) 22 | g := eg.GetGroup("/2") 23 | g2 := g.Group("/e") 24 | { 25 | g2.Get("/e", func(ctx *kernel.Context,request *httpd.Request) { 26 | request.AddBody([]byte("hello e")) 27 | }) 28 | } 29 | g = g.Group("/2") 30 | { 31 | g.Get("/2", func(ctx *kernel.Context,request *httpd.Request) { 32 | request.AddBody([]byte("hello "+request.Lookup("name"))) 33 | }) 34 | g.Get("/1", func(ctx *kernel.Context,request *httpd.Request) { 35 | request.AddBody([]byte("hello "+request.Lookup("name"))) 36 | }) 37 | } 38 | eg.Run() 39 | }, nil) 40 | } -------------------------------------------------------------------------------- /httpd/example/websocket.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/args" 5 | "github.com/liangmanlin/gootp/bpool" 6 | "github.com/liangmanlin/gootp/httpd" 7 | "github.com/liangmanlin/gootp/httpd/websocket" 8 | "github.com/liangmanlin/gootp/kernel" 9 | ) 10 | 11 | type wsState struct { 12 | conn *websocket.Conn 13 | room *kernel.Pid 14 | } 15 | 16 | var chat = &kernel.Actor{ 17 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 18 | state := wsState{} 19 | state.conn = args[0].(*websocket.Conn) 20 | state.room = args[2].(*kernel.Pid) 21 | state.room.Cast(pid) 22 | ctx.Link(state.room) 23 | return &state 24 | }, 25 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 26 | state := ctx.State.(*wsState) 27 | switch m := msg.(type) { 28 | case *bpool.Buff: 29 | kernel.ErrorLog("%s recv: %s",state.conn.RemoteAddr(), string(m.ToBytes())) 30 | state.room.Cast(m) 31 | case []byte: 32 | state.conn.WriteMessage(websocket.TextMessage, m) 33 | case *websocket.WsError: 34 | // 基本上都是close 35 | kernel.ErrorLog("close :%s",ctx.Self()) 36 | ctx.Exit(kernel.ExitReasonNormal) 37 | } 38 | }, 39 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 40 | return nil 41 | }, 42 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 43 | kernel.DebugLog("terminate %s", reason.Reason) 44 | }, 45 | } 46 | 47 | type roomState struct { 48 | m map[int64]*kernel.Pid 49 | } 50 | 51 | var room = &kernel.Actor{ 52 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 53 | state := roomState{ 54 | m: make(map[int64]*kernel.Pid), 55 | } 56 | return &state 57 | }, 58 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 59 | return nil 60 | }, 61 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 62 | state := ctx.State.(*roomState) 63 | switch m := msg.(type) { 64 | case *bpool.Buff: 65 | buf := m.Copy() 66 | m.Free() 67 | for _, pid := range state.m { 68 | pid.Cast(buf) 69 | } 70 | case *kernel.Pid: 71 | state.m[m.GetID()] = m 72 | case *kernel.PidExit: 73 | kernel.DebugLog("pid:%s exit", m.Pid) 74 | delete(state.m, m.Pid.GetID()) 75 | } 76 | }, 77 | } 78 | 79 | func main() { 80 | kernel.Env.LogPath = "" 81 | kernel.SetLogLevel(1) 82 | kernel.KernelStart(func() { 83 | port, _ := args.GetInt("port") 84 | if port == 0 { 85 | port = 8080 86 | } 87 | const managerNum = 8 88 | eg := httpd.New("test", port, 89 | httpd.WithManagerNum(managerNum), 90 | httpd.WithMaxWorkerNum(managerNum*2048), 91 | httpd.WithWsConfig(websocket.Config{ 92 | EnableCompression: true, 93 | EnableWriteCompression: true, 94 | CompressionLevel: 1, 95 | Origin: false, 96 | }), 97 | ) 98 | roomPid,_ := kernel.Start(room) 99 | // websocket uri 100 | eg.GetWebsocket("/ws/chat", chat,roomPid) 101 | 102 | g := eg.GetGroup("/2") 103 | g2 := g.Group("/e") 104 | { 105 | g2.Get("/e", func(ctx *kernel.Context, request *httpd.Request) { 106 | request.AddBody([]byte("hello e")) 107 | }) 108 | } 109 | g = g.Group("/2") 110 | { 111 | g.Get("/2", func(ctx *kernel.Context, request *httpd.Request) { 112 | request.AddBody([]byte("hello " + request.Lookup("name"))) 113 | }) 114 | g.Get("/1", func(ctx *kernel.Context, request *httpd.Request) { 115 | request.AddBody([]byte("hello " + request.Lookup("name"))) 116 | }) 117 | } 118 | eg.Run() 119 | }, nil) 120 | } 121 | -------------------------------------------------------------------------------- /httpd/example/websocket.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Websockets Demo 5 | 6 | 7 |

websocket demo

8 | 9 |
10 | 11 |   State: 12 |
13 |
Protip: open your javascript error console, just in case..
14 |
15 |
16 |
17 | 18 | 20 |
21 |
22 |
23 |
24 | 25 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /httpd/httpd.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/lesismal/nbio" 5 | "github.com/liangmanlin/gootp/gutil" 6 | "github.com/liangmanlin/gootp/httpd/websocket" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "log" 9 | "math/rand" 10 | "net/http" 11 | "runtime" 12 | "runtime/debug" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | var ( 18 | DefaultHTTPReadLimit = 20 * 1024 * 1024 19 | ) 20 | 21 | /* 22 | 快速的http server 23 | 仅仅支持http协议,如果需要https,可以通过nginx做反向代理 24 | */ 25 | func New(name string, port int, c ...config) *Engine { 26 | cpuNum := runtime.NumCPU() 27 | d := &Engine{ 28 | name: name, 29 | port: port, 30 | readLimit: DefaultHTTPReadLimit, 31 | tcpReadBuff: 16 * 1024, // 缺省16k 32 | managerNum: cpuNum, 33 | maxWorkerNum: cpuNum * 1024, 34 | getRouter: &router{rt: map[string]*handler{}}, 35 | postRouter: &router{rt: map[string]*handler{}}, 36 | } 37 | for _, f := range c { 38 | f(d) 39 | } 40 | return d 41 | } 42 | 43 | func (e *Engine) Run() error { 44 | return kernel.AppStart(&eapp{e}) 45 | } 46 | 47 | func (e *Engine) start(sup *kernel.Pid) { 48 | // 先启动必要的进程,再进行网关启动 49 | dev := int(gutil.Ceil(float32(e.maxWorkerNum) / float32(e.managerNum))) 50 | for i := 0; i < e.managerNum; i++ { 51 | _, managerPid := kernel.SupStartChild(sup, &kernel.SupChild{Svr: manager, InitArgs: kernel.MakeArgs(dev, e)}) 52 | e.manager = append(e.manager, managerPid) 53 | } 54 | addrs := e.buildAddr() 55 | g := nbio.NewGopher(nbio.Config{ 56 | Name: e.name, 57 | Network: "tcp", 58 | Addrs: addrs, 59 | ReadBufferSize: e.tcpReadBuff, 60 | EpollMod: nbio.EPOLLET, 61 | }) 62 | h := e.handler 63 | if e.hasWebSocket { 64 | if e.wsConfig == nil { 65 | e.wsConfig = websocket.DefaultConfig() 66 | } 67 | e.wsConfig.Confirm() 68 | h = e.handlerWebSocket 69 | g.OnClose(func(c *nbio.Conn, err error) { 70 | if s, ok := c.Session().(*websocket.Conn); ok { 71 | kernel.DebugLog("websocket closed: %s", c.RemoteAddr()) 72 | s.OnClose() 73 | } 74 | }) 75 | } else { 76 | if e.balancing == 1 { 77 | h = e.handlerRand 78 | } 79 | } 80 | g.OnOpen(func(c *nbio.Conn) { 81 | parser := NewParser(h, c, e.readLimit) 82 | c.SetSession(parser) 83 | kernel.DebugLog("new c:%d", c.Hash()) 84 | }) 85 | g.OnData(func(c *nbio.Conn, data []byte) { 86 | var err error 87 | switch p := c.Session().(type) { 88 | case *Parser: 89 | err = p.Read(data) 90 | case *websocket.Conn: 91 | err = p.Read(data) 92 | } 93 | if err != nil { 94 | kernel.ErrorLog("handle error:%s", err) 95 | c.CloseWithError(err) 96 | } 97 | }) 98 | err := g.Start() 99 | if err != nil { 100 | log.Panic(err) 101 | } 102 | e.engine = g 103 | for _, addr := range addrs { 104 | kernel.ErrorLog("httpd [%s] start on [%s]", e.name, addr) 105 | } 106 | } 107 | 108 | func (e *Engine) handler(conn *nbio.Conn, req *http.Request) { 109 | // 移交到队列处理 110 | // 根据fd hash 111 | r := newRequest(req, conn) 112 | e.manager[conn.Hash()%e.managerNum].Cast(r) 113 | } 114 | 115 | func (e *Engine) handlerRand(conn *nbio.Conn, req *http.Request) { 116 | // 移交到队列处理 117 | r := newRequest(req, conn) 118 | e.manager[rand.Intn(e.managerNum)].Cast(r) 119 | } 120 | 121 | // 性能会有所损失 122 | // 为了提升性能,尽量用最短的uri 123 | func (e *Engine) handlerWebSocket(conn *nbio.Conn, req *http.Request) { 124 | r := newRequest(req, conn) 125 | var ok bool 126 | defer func() { 127 | if !ok { 128 | p := recover() 129 | if p != nil { 130 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 131 | } 132 | conn.Close() 133 | } 134 | }() 135 | if h, err := routerHandler(e, r); err == nil { 136 | if h.isWs { 137 | reqCopy := *req 138 | if c, err := websocket.Upgrade(e.wsConfig, r.ResponseWriter(), conn, req); err != nil { 139 | kernel.ErrorLog("upgrade error: %s", err) 140 | // 再一次关闭,防止内部逻辑没有close 141 | conn.Close() 142 | } else { 143 | args := append(kernel.MakeArgs(c, &reqCopy), h.actorArgs...) 144 | if pid, err := kernel.Start(h.actor, args...); err == nil { 145 | c.SetHandler(pid) 146 | conn.SetSession(c) 147 | } else { 148 | kernel.ErrorLog("start handler error:%s", err) 149 | conn.Close() 150 | } 151 | } 152 | } else { 153 | r.f = h.f 154 | if e.balancing == 1 { 155 | e.manager[rand.Intn(e.managerNum)].Cast(r) 156 | } else { 157 | e.manager[conn.Hash()%e.managerNum].Cast(r) 158 | } 159 | } 160 | } else { 161 | kernel.ErrorLog("router error:%s %s", err, req.RequestURI) 162 | r.reply404() 163 | } 164 | ok = true 165 | } 166 | 167 | func (e *Engine) buildAddr() []string { 168 | if e.port == 0 { 169 | e.port = 8080 170 | } 171 | if len(e.addr) == 0 { 172 | return []string{":" + strconv.Itoa(e.port)} 173 | } 174 | rs := make([]string, 0, len(e.addr)) 175 | for _, addr := range e.addr { 176 | if strings.Index(addr, ":") >= 0 { 177 | rs = append(rs, addr) 178 | } else { 179 | rs = append(rs, addr+":"+strconv.Itoa(e.port)) 180 | } 181 | } 182 | return rs 183 | } 184 | 185 | // 单纯是代码好看一点 186 | func none(ctx *kernel.Context, request *Request) { 187 | 188 | } 189 | -------------------------------------------------------------------------------- /httpd/httpd_app.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | ) 6 | 7 | type eapp struct { 8 | *Engine 9 | } 10 | 11 | var _ = kernel.Application(&eapp{}) 12 | // application 13 | func (e *eapp)Name() string { 14 | return e.name 15 | } 16 | func (e *eapp) Start(bootType kernel.AppBootType) *kernel.Pid { 17 | sup := kernel.SupStart(e.name + "_sup") 18 | e.start(sup) 19 | return sup 20 | } 21 | 22 | func (e *eapp)Stop(stopType kernel.AppStopType) { 23 | kernel.ErrorLog("httpd [%s] stop",e.name) 24 | e.engine.Stop() 25 | } 26 | 27 | func (e *eapp)SetEnv(Key string, value interface{}) { 28 | 29 | } 30 | 31 | func (e *eapp)GetEnv(key string) interface{} { 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /httpd/manager.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/gutil" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "github.com/liangmanlin/gootp/ringbuffer" 7 | "math/bits" 8 | ) 9 | 10 | type managerState struct { 11 | workerNum int 12 | maxWorkerNum int 13 | engine *Engine 14 | worker *ringbuffer.SingleRingBuffer 15 | wait *ringbuffer.SingleRingBuffer 16 | //requestCount uint64 17 | } 18 | 19 | var manager = &kernel.Actor{ 20 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 21 | state := managerState{} 22 | state.maxWorkerNum = args[0].(int) 23 | state.engine = args[1].(*Engine) 24 | const minW = 128 25 | max := 1 << bits.Len(uint(state.maxWorkerNum-1)) 26 | max = int(gutil.MaxInt32(minW, int32(max))) 27 | min := int(gutil.MinInt32(minW, int32(max))) 28 | kernel.ErrorLog("manager [%s] max worker num : %d", pid, max) 29 | state.worker = ringbuffer.NewSingleRingBuffer(min, max) 30 | state.wait = ringbuffer.NewSingleRingBuffer(minW, 2048) 31 | return &state 32 | }, 33 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 34 | state := ctx.State.(*managerState) 35 | switch m := msg.(type) { 36 | case *Request: 37 | state.newRequest(m, ctx) 38 | case *kernel.Pid: 39 | if req := state.wait.Pop(); req != nil { 40 | m.Cast(req) 41 | } else { 42 | state.worker.Put(m) 43 | } 44 | } 45 | }, 46 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 47 | return nil 48 | }, 49 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 50 | return true 51 | }, 52 | Terminate: func(ctx *kernel.Context, reason *kernel.Terminate) { 53 | kernel.ErrorLog("httpd manager [%s] terminate", ctx.State.(*managerState).engine.name) 54 | }, 55 | } 56 | 57 | func (m *managerState) newRequest(req *Request, ctx *kernel.Context) { 58 | if m.worker.Size() > 0 { 59 | m.worker.Pop().(*kernel.Pid).Cast(req) 60 | } else if m.workerNum < m.maxWorkerNum { 61 | m.newWorker(ctx.Self()).Cast(req) 62 | } else { 63 | m.wait.Put(req) 64 | } 65 | } 66 | 67 | func (m *managerState) newWorker(self *kernel.Pid) *kernel.Pid { 68 | worker, _ := kernel.Start(workerActor, self, m.engine) 69 | m.workerNum++ 70 | return worker 71 | } 72 | -------------------------------------------------------------------------------- /httpd/request.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/liangmanlin/gootp/bpool" 6 | "github.com/liangmanlin/gootp/httpd/ejson" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "net" 9 | "net/http" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | var ( 17 | requestPool = sync.Pool{ 18 | New: func() interface{} { 19 | return &http.Request{} 20 | }, 21 | } 22 | 23 | responsePool = sync.Pool{ 24 | New: func() interface{} { 25 | return &http.Response{} 26 | }, 27 | } 28 | 29 | rPool = sync.Pool{ 30 | New: func() interface{} { 31 | return &Request{} 32 | }, 33 | } 34 | ) 35 | 36 | func (r *Request) AddCookie(name, value string) { 37 | cookie := http.Cookie{ 38 | Name: name, 39 | Value: value, 40 | } 41 | r.response.Header.Add("Set-Cookie", cookie.String()) 42 | } 43 | 44 | func (r *Request) AddCookieExpire(name, value string, Second int) { 45 | cookie := http.Cookie{ 46 | Name: name, 47 | Value: value, 48 | Expires: time.Now().Add(time.Duration(Second) * time.Second), 49 | } 50 | r.response.Header.Add("Set-Cookie", cookie.String()) 51 | } 52 | 53 | func (r *Request) SetCookie(name, value string) { 54 | cookie := http.Cookie{ 55 | Name: name, 56 | Value: value, 57 | } 58 | r.response.Header.Set("Set-Cookie", cookie.String()) 59 | } 60 | 61 | func (r *Request) SetCookieExpire(name, value string, Second int) { 62 | cookie := http.Cookie{ 63 | Name: name, 64 | Value: value, 65 | Expires: time.Now().Add(time.Duration(Second) * time.Second), 66 | } 67 | r.response.Header.Set("Set-Cookie", cookie.String()) 68 | } 69 | 70 | func (r *Request) AddHead(name, value string) { 71 | r.response.Header.Add(name, value) 72 | } 73 | 74 | func (r *Request) SetHead(name, value string) { 75 | r.response.Header.Set(name, value) 76 | } 77 | 78 | func (r *Request) AddBody(body []byte) { 79 | if len(body) == 0 { 80 | return 81 | } 82 | if r.responseBody == nil { 83 | r.responseBody = bpool.NewBuf(body) 84 | } else { 85 | r.responseBody = r.responseBody.Append(body...) 86 | } 87 | } 88 | 89 | func (r *Request) AddJsonBody(rs interface{}) { 90 | buf, err := json.Marshal(rs) 91 | if err != nil { 92 | kernel.ErrorLog("ejson %#v error:%s", rs, err) 93 | return 94 | } 95 | r.AddBody(buf) 96 | } 97 | 98 | func (r *Request) RemoteIP() string { 99 | if ip := r.Header.Get("X-Forwarded-For"); ip != "" { 100 | return ip 101 | } 102 | addr := r.Conn.RemoteAddr().String() 103 | return strings.Split(addr, ":")[0] 104 | } 105 | 106 | // 返回查询字符串的值 107 | func (r *Request) Lookup(key string) (value string) { 108 | if r.Form == nil { 109 | r.Form = r.URL.Query() 110 | } 111 | return r.Form.Get(key) 112 | } 113 | 114 | func (r *Request) addHeader(key, value string) { 115 | r.Header.Set(key, value) 116 | } 117 | 118 | // 返回表单多个values 119 | func (r *Request) FormValues(key string) (values []string) { 120 | if r.Method == "POST" { 121 | if r.PostForm == nil { 122 | if r.ParseForm() != nil { 123 | return 124 | } 125 | } 126 | values = r.PostForm[key] 127 | } 128 | return 129 | } 130 | 131 | // 返回表单单个value 132 | func (r *Request) FormValue(key string) (value string) { 133 | if vl := r.FormValues(key); len(vl) > 0 { 134 | value = vl[0] 135 | } 136 | return 137 | } 138 | 139 | // 把整个body解析为json 140 | func (r *Request) Json() ejson.Json { 141 | if r.Body == nil { 142 | return nil 143 | } 144 | if t, ok := r.Body.(*BodyReader); ok { 145 | return ejson.Decode(t.RawBody()) 146 | } 147 | size := int(r.ContentLength) 148 | if size <= 0 { 149 | size = 4 * 1024 150 | } 151 | bp, _ := bpool.ReadAll(r.Body, size) 152 | return ejson.Decode(bp.ToBytes()) 153 | } 154 | 155 | func (r *Request) CacheTime(time int) { 156 | r.SetHead("Cache-Control", "max-age="+strconv.Itoa(time)) 157 | } 158 | 159 | func (r *Request) ResponseWriter() http.ResponseWriter { 160 | return &responseWriter{r} 161 | } 162 | 163 | // 可以提前根据自己的需要,提前返回 164 | func (r *Request) Reply(statusCode int, body []byte) error { 165 | r.has() 166 | r.response.StatusCode = statusCode 167 | r.AddBody(body) 168 | return r.replyNormal() 169 | } 170 | 171 | func (r *Request) Reply304(ETag string) { 172 | r.has() 173 | r.response.StatusCode = http.StatusNotModified 174 | r.SetHead("ETag", ETag) 175 | r.replyNormal() 176 | } 177 | 178 | func (r *Request) replyNormal() error { 179 | if r.isReply { 180 | return nil 181 | } 182 | r.has() 183 | err := r.reply() 184 | if r.Header.Get("Connection") == "close" { 185 | r.Conn.Close() 186 | } else if r.ProtoMajor == 1 && r.ProtoMinor == 0 { 187 | r.Conn.Close() 188 | } 189 | r.release() 190 | return err 191 | } 192 | 193 | func (r *Request) reply404() { 194 | r.has() 195 | r.SetHead("Content-Type", "text/plan") 196 | body := []byte("404 page not found") 197 | r.AddBody(body) 198 | r.response.StatusCode = http.StatusNotFound 199 | r.reply() 200 | r.Conn.Close() 201 | r.release() 202 | } 203 | 204 | func (r *Request) reply() error { 205 | response := r.response 206 | response.Header.Set("Server", "gootp-web") 207 | if r.responseBody != nil { 208 | if response.Header.Get("Content-Type") == "" { 209 | r.SetHead("Content-Type", "text/html") 210 | } 211 | response.ContentLength = int64(r.responseBody.Size()) 212 | response.Body = NewBodyReader(r.responseBody) 213 | } 214 | w := bpool.NewWriter(r.Conn) 215 | err := response.Write(w) 216 | if err == nil { 217 | err = w.Flush() 218 | } 219 | w.Free() 220 | r.isReply = true 221 | return err 222 | } 223 | 224 | func (r *Request) release() { 225 | // 释放资源 226 | if r.Body != nil { 227 | r.Body.Close() 228 | r.Body = nil 229 | } 230 | requestPool.Put(r.Request) 231 | if r.response != nil { 232 | responsePool.Put(r.response) 233 | } 234 | r.Conn = nil 235 | r.response = nil 236 | r.Request = nil 237 | r.responseBody = nil 238 | rPool.Put(r) 239 | } 240 | 241 | func (r *Request) has() { 242 | if r.response == nil { 243 | r.response = responsePool.Get().(*http.Response) 244 | *r.response = http.Response{ 245 | StatusCode: http.StatusOK, 246 | Proto: r.Proto, 247 | ProtoMajor: r.ProtoMajor, 248 | ProtoMinor: r.ProtoMinor, 249 | Header: http.Header{}, 250 | Request: r.Request, 251 | } 252 | } 253 | } 254 | 255 | func newRequest(req *http.Request, conn net.Conn) *Request { 256 | r := rPool.Get().(*Request) 257 | r.Request = req 258 | r.Conn = conn 259 | r.isReply = false 260 | r.f = nil 261 | return r 262 | } 263 | 264 | type responseWriter struct { 265 | *Request 266 | } 267 | 268 | func (r *responseWriter) Header() http.Header { 269 | r.has() 270 | return r.response.Header 271 | } 272 | 273 | func (r *responseWriter) Write(b []byte) (int, error) { 274 | n := len(b) 275 | err := r.Reply(r.response.StatusCode, b) 276 | return n, err 277 | } 278 | 279 | func (r *responseWriter) WriteHeader(statusCode int) { 280 | r.has() 281 | r.response.StatusCode = statusCode 282 | } 283 | -------------------------------------------------------------------------------- /httpd/state.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 lesismal. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package httpd 6 | 7 | const ( 8 | // state: RequestLine 9 | stateClose int8 = iota 10 | stateMethodBefore 11 | stateMethod 12 | 13 | statePathBefore 14 | statePath 15 | stateProtoBefore 16 | stateProto 17 | stateProtoLF 18 | stateClientProtoBefore 19 | stateClientProto 20 | stateStatusCodeBefore 21 | stateStatusCode 22 | stateStatusBefore 23 | stateStatus 24 | stateStatusLF 25 | 26 | // state: Header 27 | stateHeaderKeyBefore 28 | stateHeaderValueLF 29 | stateHeaderKey 30 | 31 | stateHeaderValueBefore 32 | stateHeaderValue 33 | 34 | // state: Body ContentLength 35 | stateBodyContentLength 36 | 37 | // state: Body Chunk 38 | stateHeaderOverLF 39 | stateBodyChunkSizeBefore 40 | stateBodyChunkSize 41 | stateBodyChunkSizeLF 42 | stateBodyChunkData 43 | stateBodyChunkDataCR 44 | stateBodyChunkDataLF 45 | 46 | // state: Body Trailer 47 | stateBodyTrailerHeaderValueLF 48 | stateBodyTrailerHeaderKeyBefore 49 | stateBodyTrailerHeaderKey 50 | stateBodyTrailerHeaderValueBefore 51 | stateBodyTrailerHeaderValue 52 | 53 | // state: Body CRLF 54 | stateTailCR 55 | stateTailLF 56 | ) 57 | -------------------------------------------------------------------------------- /httpd/table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 lesismal. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package httpd 6 | 7 | import "strings" 8 | 9 | var ( 10 | validMethods = map[string]bool{ 11 | "OPTIONS": true, 12 | "GET": true, 13 | "HEAD": true, 14 | "POST": true, 15 | "PUT": true, 16 | "DELETE": true, 17 | "TRACE": true, 18 | "CONNECT": true, 19 | 20 | // http 2.0 21 | "PRI": true, 22 | } 23 | 24 | tokenCharMap = [256]bool{ 25 | '!': true, 26 | '#': true, 27 | '$': true, 28 | '%': true, 29 | '&': true, 30 | '\'': true, 31 | '*': true, 32 | '+': true, 33 | '-': true, 34 | '.': true, 35 | '0': true, 36 | '1': true, 37 | '2': true, 38 | '3': true, 39 | '4': true, 40 | '5': true, 41 | '6': true, 42 | '7': true, 43 | '8': true, 44 | '9': true, 45 | 'A': true, 46 | 'B': true, 47 | 'C': true, 48 | 'D': true, 49 | 'E': true, 50 | 'F': true, 51 | 'G': true, 52 | 'H': true, 53 | 'I': true, 54 | 'J': true, 55 | 'K': true, 56 | 'L': true, 57 | 'M': true, 58 | 'N': true, 59 | 'O': true, 60 | 'P': true, 61 | 'Q': true, 62 | 'R': true, 63 | 'S': true, 64 | 'T': true, 65 | 'U': true, 66 | 'W': true, 67 | 'V': true, 68 | 'X': true, 69 | 'Y': true, 70 | 'Z': true, 71 | '^': true, 72 | '_': true, 73 | '`': true, 74 | 'a': true, 75 | 'b': true, 76 | 'c': true, 77 | 'd': true, 78 | 'e': true, 79 | 'f': true, 80 | 'g': true, 81 | 'h': true, 82 | 'i': true, 83 | 'j': true, 84 | 'k': true, 85 | 'l': true, 86 | 'm': true, 87 | 'n': true, 88 | 'o': true, 89 | 'p': true, 90 | 'q': true, 91 | 'r': true, 92 | 's': true, 93 | 't': true, 94 | 'u': true, 95 | 'v': true, 96 | 'w': true, 97 | 'x': true, 98 | 'y': true, 99 | 'z': true, 100 | '|': true, 101 | '~': true, 102 | } 103 | 104 | // headerCharMap = [256]bool{}. 105 | 106 | numCharMap = [256]bool{} 107 | hexCharMap = [256]bool{} 108 | alphaCharMap = [256]bool{} 109 | alphaNumCharMap = [256]bool{} 110 | 111 | validMethodCharMap = [256]bool{} 112 | ) 113 | 114 | func init() { 115 | var dis byte = 'a' - 'A' 116 | 117 | for m := range validMethods { 118 | for _, c := range m { 119 | validMethodCharMap[c] = true 120 | validMethodCharMap[byte(c)+dis] = true 121 | } 122 | } 123 | 124 | for i := byte(0); i < 10; i++ { 125 | numCharMap['0'+i] = true 126 | alphaNumCharMap['0'+i] = true 127 | hexCharMap['0'+i] = true 128 | } 129 | for i := byte(0); i < 6; i++ { 130 | hexCharMap['A'+i] = true 131 | hexCharMap['a'+i] = true 132 | } 133 | 134 | for i := byte(0); i < 26; i++ { 135 | alphaCharMap['A'+i] = true 136 | alphaCharMap['A'+i+dis] = true 137 | alphaNumCharMap['A'+i] = true 138 | alphaNumCharMap['A'+i+dis] = true 139 | } 140 | 141 | // for i := 0; i < len(tokenCharMap); i++ { 142 | // headerCharMap[i] = tokenCharMap[i] 143 | // } 144 | // headerCharMap[':'] = true 145 | // headerCharMap['?'] = true 146 | } 147 | 148 | func isAlpha(c byte) bool { 149 | return alphaCharMap[c] 150 | } 151 | 152 | func isNum(c byte) bool { 153 | return numCharMap[c] 154 | } 155 | 156 | func isHex(c byte) bool { 157 | return hexCharMap[c] 158 | } 159 | 160 | // func isAlphaNum(c byte) bool { 161 | // return alphaNumCharMap[c] 162 | // } 163 | 164 | func isToken(c byte) bool { 165 | return tokenCharMap[c] 166 | } 167 | 168 | func isValidMethod(m string) bool { 169 | return validMethods[strings.ToUpper(m)] 170 | } 171 | 172 | func isValidMethodChar(c byte) bool { 173 | return validMethodCharMap[c] 174 | } 175 | -------------------------------------------------------------------------------- /httpd/type.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/lesismal/nbio" 5 | "github.com/liangmanlin/gootp/bpool" 6 | "github.com/liangmanlin/gootp/httpd/websocket" 7 | "github.com/liangmanlin/gootp/kernel" 8 | "net" 9 | "net/http" 10 | ) 11 | 12 | type Engine struct { 13 | name string 14 | port int 15 | readLimit int 16 | tcpReadBuff int 17 | maxWorkerNum int 18 | managerNum int 19 | manager []*kernel.Pid 20 | balancing int 21 | engine *nbio.Gopher 22 | addr []string 23 | getRouter *router 24 | postRouter *router 25 | hasWebSocket bool 26 | wsConfig *websocket.Config 27 | } 28 | 29 | type config func(*Engine) 30 | 31 | type Request struct { 32 | *http.Request 33 | Conn net.Conn 34 | response *http.Response 35 | responseBody *bpool.Buff 36 | f handlerFunc 37 | isReply bool 38 | } 39 | 40 | type router struct { 41 | rType byte 42 | rName string 43 | h *handler 44 | rt map[string]*handler 45 | } 46 | 47 | type handler struct { 48 | isWs bool 49 | actor *kernel.Actor 50 | actorArgs []interface{} 51 | interceptor func(request *Request) bool // 拦截器 52 | f handlerFunc 53 | r *router 54 | } 55 | 56 | type handlerFunc func(ctx *kernel.Context, request *Request) 57 | -------------------------------------------------------------------------------- /httpd/websocket/compression.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "bytes" 5 | "compress/flate" 6 | "errors" 7 | "github.com/liangmanlin/gootp/bpool" 8 | "io" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | const ( 14 | minCompressionLevel = -2 15 | maxCompressionLevel = flate.BestCompression 16 | defaultCompressionLevel = 1 17 | 18 | flateReaderTail = "\x00\x00\xff\xff" + "\x01\x00\x00\xff\xff" 19 | ) 20 | 21 | var ( 22 | flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool 23 | flateReaderPool = sync.Pool{New: func() interface{} { 24 | return flate.NewReader(nil) 25 | }} 26 | ) 27 | 28 | func isValidCompressionLevel(level int) bool { 29 | return minCompressionLevel <= level && level <= maxCompressionLevel 30 | } 31 | 32 | func Decompress(buf []byte) (*bpool.Buff, error) { 33 | rc := decompressReader(io.MultiReader(bytes.NewBuffer(buf), strings.NewReader(flateReaderTail))) 34 | b, err := bpool.ReadAll(rc, len(buf)*2) 35 | rc.Close() 36 | return b, err 37 | } 38 | 39 | func decompressReader(r io.Reader) io.ReadCloser { 40 | fr, _ := flateReaderPool.Get().(io.ReadCloser) 41 | fr.(flate.Resetter).Reset(r, nil) 42 | return &flateReadWrapper{fr} 43 | } 44 | 45 | type flateReadWrapper struct { 46 | fr io.ReadCloser 47 | } 48 | 49 | func (r *flateReadWrapper) Read(p []byte) (int, error) { 50 | if r.fr == nil { 51 | return 0, io.ErrClosedPipe 52 | } 53 | n, err := r.fr.Read(p) 54 | if errors.Is(err, io.EOF) { 55 | // Preemptively place the reader back in the pool. This helps with 56 | // scenarios where the application does not call NextReader() soon after 57 | // this final read. 58 | r.Close() 59 | } 60 | return n, err 61 | } 62 | 63 | func (r *flateReadWrapper) Close() error { 64 | if r.fr == nil { 65 | return io.ErrClosedPipe 66 | } 67 | err := r.fr.Close() 68 | flateReaderPool.Put(r.fr) 69 | r.fr = nil 70 | return err 71 | } 72 | 73 | func compressWriter(w io.WriteCloser, level int) io.WriteCloser { 74 | p := &flateWriterPools[level-minCompressionLevel] 75 | fw, _ := p.Get().(*flate.Writer) 76 | tw := &truncWriter{w: w} 77 | if fw == nil { 78 | fw, _ = flate.NewWriter(tw, level) 79 | } else { 80 | fw.Reset(tw) 81 | } 82 | return &flateWriteWrapper{fw: fw, p: p} 83 | } 84 | 85 | type truncWriter struct { 86 | w io.WriteCloser 87 | n int 88 | p [4]byte 89 | } 90 | 91 | func (w *truncWriter) Write(p []byte) (int, error) { 92 | n := 0 93 | 94 | if w.n < len(w.p) { 95 | n = copy(w.p[w.n:], p) 96 | p = p[n:] 97 | w.n += n 98 | if len(p) == 0 { 99 | return n, nil 100 | } 101 | } 102 | 103 | m := len(p) 104 | if m > len(w.p) { 105 | m = len(w.p) 106 | } 107 | 108 | if nn, err := w.w.Write(w.p[:m]); err != nil { 109 | return n + nn, err 110 | } 111 | 112 | copy(w.p[:], w.p[m:]) 113 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 114 | nn, err := w.w.Write(p[:len(p)-m]) 115 | return n + nn, err 116 | } 117 | 118 | type flateWriteWrapper struct { 119 | fw *flate.Writer 120 | p *sync.Pool 121 | } 122 | 123 | func (w *flateWriteWrapper) Write(p []byte) (int, error) { 124 | return w.fw.Write(p) 125 | } 126 | 127 | func (w *flateWriteWrapper) Close() error { 128 | err := w.fw.Flush() 129 | w.p.Put(w.fw) 130 | w.fw = nil 131 | return err 132 | } 133 | -------------------------------------------------------------------------------- /httpd/websocket/config.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import "time" 4 | 5 | type Config struct { 6 | ReadLimit int 7 | MaxMessageSize int 8 | CompressionLevel int 9 | HandshakeTimeout time.Duration 10 | EnableCompression bool // 是否开启压缩,并且告知客户端 11 | EnableWriteCompression bool // 是否对写数据进行压缩,通常不需要压缩 12 | Origin bool // 是否检查域名 13 | } 14 | 15 | func DefaultConfig() *Config { 16 | return &Config{ 17 | ReadLimit: 1024 * 1024, 18 | MaxMessageSize: 1025 * 1024, 19 | } 20 | } 21 | 22 | func (c *Config) isMessageTooLarge(size int) bool { 23 | return size > c.MaxMessageSize 24 | } 25 | 26 | func (c *Config) Confirm() { 27 | if c.ReadLimit <= 0 { 28 | c.ReadLimit = 1024 * 1024 29 | } 30 | if c.MaxMessageSize <= 0 { 31 | c.MaxMessageSize = 1024 * 1024 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /httpd/websocket/const.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | type WsError struct { 4 | ErrType ErrorType 5 | } 6 | 7 | type ErrorType int 8 | 9 | const ( 10 | ErrTypeClosed ErrorType = iota + 1 11 | ) -------------------------------------------------------------------------------- /httpd/websocket/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 lesismal. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | var ( 12 | // ErrUpgradeTokenNotFound . 13 | ErrUpgradeTokenNotFound = errors.New("websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header") 14 | 15 | // ErrUpgradeMethodIsGet . 16 | ErrUpgradeMethodIsGet = errors.New("websocket: the client is not using the websocket protocol: request method is not GET") 17 | 18 | // ErrUpgradeInvalidWebsocketVersion . 19 | ErrUpgradeInvalidWebsocketVersion = errors.New("websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") 20 | 21 | // ErrUpgradeUnsupportedExtensions . 22 | ErrUpgradeUnsupportedExtensions = errors.New("websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") 23 | 24 | // ErrUpgradeOriginNotAllowed . 25 | ErrUpgradeOriginNotAllowed = errors.New("websocket: request origin not allowed by upgrader.CheckOrigin") 26 | 27 | // ErrUpgradeMissingWebsocketKey . 28 | ErrUpgradeMissingWebsocketKey = errors.New("websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") 29 | 30 | // ErrUpgradeNotHijacker . 31 | ErrUpgradeNotHijacker = errors.New("websocket: response does not implement http.Hijacker") 32 | 33 | // ErrInvalidControlFrame . 34 | ErrInvalidControlFrame = errors.New("websocket: invalid control frame") 35 | 36 | // ErrInvalidWriteCalling . 37 | ErrInvalidWriteCalling = errors.New("websocket: invalid write calling, should call WriteMessage instead") 38 | 39 | // ErrReserveBitSet . 40 | ErrReserveBitSet = errors.New("websocket: reserved bit set it frame") 41 | 42 | // ErrReservedOpcodeSet . 43 | ErrReservedOpcodeSet = errors.New("websocket: reserved opcode received") 44 | 45 | // ErrControlMessageFragmented . 46 | ErrControlMessageFragmented = errors.New("websocket: control messages must not be fragmented") 47 | 48 | // ErrFragmentsShouldNotHaveBinaryOrTextOpcode . 49 | ErrFragmentsShouldNotHaveBinaryOrTextOpcode = errors.New("websocket: fragments should not have opcode of text or binary") 50 | 51 | // ErrInvalidCloseCode . 52 | ErrInvalidCloseCode = errors.New("websocket: invalid close code") 53 | 54 | // ErrBadHandshake . 55 | ErrBadHandshake = errors.New("websocket: bad handshake") 56 | 57 | // ErrInvalidCompression . 58 | ErrInvalidCompression = errors.New("websocket: invalid compression negotiation") 59 | 60 | // ErrMalformedURL . 61 | ErrMalformedURL = errors.New("malformed ws or wss URL") 62 | 63 | // ErrMessageTooLarge. 64 | ErrMessageTooLarge = errors.New("message exceeds the configured limit") 65 | ) 66 | -------------------------------------------------------------------------------- /httpd/worker.go: -------------------------------------------------------------------------------- 1 | package httpd 2 | 3 | import ( 4 | "github.com/lesismal/nbio" 5 | "github.com/liangmanlin/gootp/kernel" 6 | "runtime/debug" 7 | ) 8 | 9 | type workerState struct { 10 | manager *kernel.Pid 11 | engine *Engine 12 | } 13 | 14 | var workerActor = &kernel.Actor{ 15 | Init: func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 16 | state := workerState{} 17 | state.manager = args[0].(*kernel.Pid) 18 | state.engine = args[1].(*Engine) 19 | return &state 20 | }, 21 | HandleCast: func(ctx *kernel.Context, msg interface{}) { 22 | switch r := msg.(type) { 23 | case *Request: 24 | state := ctx.State.(*workerState) 25 | defer kernel.Cast(state.manager, ctx.Self()) 26 | var ok bool 27 | defer func() { 28 | if !ok { 29 | p := recover() 30 | if p != nil { 31 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 32 | } 33 | r.Conn.Close() 34 | } 35 | }() 36 | if r.f != nil { 37 | r.f(ctx, r) 38 | r.replyNormal() 39 | } else { 40 | if c, _ := r.Conn.(*nbio.Conn).IsClosed(); c { 41 | ok = true 42 | kernel.ErrorLog("connect closed: %s uri: %s", r.RemoteIP(), r.RequestURI) 43 | return 44 | } 45 | //kernel.DebugLog("worker:%s %s",ctx.Self(),r.RequestURI) 46 | if h,err := routerHandler(state.engine, r); err == nil { 47 | h.f(ctx,r) 48 | r.replyNormal() 49 | } else { 50 | kernel.ErrorLog("err: %s uri: %s", err.Error(), r.RequestURI) 51 | r.reply404() 52 | } 53 | } 54 | ok = true 55 | } 56 | 57 | }, 58 | HandleCall: func(ctx *kernel.Context, request interface{}) interface{} { 59 | return nil 60 | }, 61 | ErrorHandler: func(ctx *kernel.Context, err interface{}) bool { 62 | return true 63 | }, 64 | } 65 | -------------------------------------------------------------------------------- /kernel/README.md: -------------------------------------------------------------------------------- 1 | # Kernel 2 | 3 | go 版本otp,提供一个简单的supervisor && gen_server的模型 -------------------------------------------------------------------------------- /kernel/application.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "container/list" 5 | "errors" 6 | "runtime/debug" 7 | "sync" 8 | ) 9 | 10 | var _appMaps sync.Map 11 | var _appPid2Name sync.Map 12 | 13 | var appPid *Pid 14 | 15 | var ErrAppAlreadyStarted = errors.New("app already started ") 16 | var ErrAppStart = errors.New("app start catch error ") 17 | var ErrAppNotStart = errors.New("app not Start ") 18 | 19 | type app struct { 20 | l *list.List 21 | } 22 | 23 | func AppStart(app Application) (err error) { 24 | var ok bool 25 | defer func() { 26 | if !ok { 27 | err = ErrAppStart 28 | p := recover() 29 | ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 30 | } 31 | }() 32 | if AppInfo(app.Name()) != nil { 33 | return ErrAppAlreadyStarted 34 | } 35 | pid := app.Start(APP_BOOT_TYPE_START) 36 | ai := &appInfo{app: app, pid: pid} 37 | _appMaps.Store(app.Name(), ai) 38 | _appPid2Name.Store(pid.id, app.Name()) 39 | ok = true 40 | Cast(appPid, ai) 41 | return 42 | } 43 | 44 | func AppStop(name string) { 45 | CallTimeOut(appPid, name, 20) 46 | } 47 | 48 | func AppRestart(name string) (err error) { 49 | app := AppInfo(name) 50 | if app == nil { 51 | return ErrAppNotStart 52 | } 53 | var ok bool 54 | defer func() { 55 | if !ok { 56 | err = ErrAppStart 57 | p := recover() 58 | ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 59 | } 60 | }() 61 | app.Stop(APP_STOP_TYPE_RESTART) 62 | app.Start(APP_BOOT_TYPE_RESTART) 63 | return 64 | } 65 | 66 | func AppInfo(name string) Application { 67 | if app, ok := _appMaps.Load(name); ok { 68 | return app.(*appInfo).app 69 | } 70 | return nil 71 | } 72 | 73 | var appSvr = &Actor{ 74 | Init: func(ctx *Context, pid *Pid, args ...interface{}) interface{} { 75 | ErrorLog("application %s started", pid) 76 | addToKernelMap(pid) 77 | appPid = pid 78 | state := app{ 79 | l: list.New(), 80 | } 81 | return &state 82 | }, 83 | HandleCast: func(ctx *Context, msg interface{}) { 84 | switch m := msg.(type) { 85 | case *appInfo: 86 | m.e =ctx.State.(*app).l.PushFront(m.app.Name()) 87 | Link(ctx.self, m.pid) 88 | case *PidExit: 89 | if appName, ok := _appPid2Name.Load(m.Pid.id); ok { 90 | _appPid2Name.Delete(m.Pid.id) 91 | if ai, ok := _appMaps.Load(appName); ok { 92 | ctx.State.(*app).l.Remove(ai.(*appInfo).e) 93 | _appMaps.Delete(appName) 94 | } 95 | } 96 | } 97 | 98 | }, 99 | HandleCall: func(ctx *Context, request interface{}) (rs interface{}) { 100 | switch r := request.(type) { 101 | case string: // stop 102 | if ai, ok := _appMaps.Load(r); ok { 103 | aInf := ai.(*appInfo) 104 | app_stop(aInf) 105 | } 106 | case *initStop: 107 | l := ctx.State.(*app).l 108 | for e := l.Front(); e != nil; e = e.Next() { 109 | if ai, ok := _appMaps.Load(e.Value); ok { 110 | aInf := ai.(*appInfo) 111 | if aInf.pid.IsAlive() { 112 | app_stop(aInf) 113 | } 114 | } 115 | } 116 | } 117 | return 118 | }, 119 | Terminate: func(ctx *Context, reason *Terminate) { 120 | 121 | }, 122 | ErrorHandler: func(ctx *Context, err interface{}) bool { 123 | return true 124 | }, 125 | } 126 | 127 | func app_stop(ai *appInfo) { 128 | ai.app.Stop(APP_STOP_TYPE_NORMAL) 129 | SupStop(ai.pid) // 这里不删除数据,等待PidExit消息处理 130 | } 131 | -------------------------------------------------------------------------------- /kernel/context.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/ringbuffer" 5 | "time" 6 | ) 7 | 8 | type Context struct { 9 | self *Pid 10 | actor *Actor 11 | name string 12 | links []*Pid 13 | terminateReason *Terminate 14 | msgQ *ringbuffer.SingleRingBuffer 15 | callMode callMode 16 | State interface{} 17 | } 18 | 19 | func (c *Context) Self() *Pid { 20 | return c.self 21 | } 22 | 23 | // 如果自身注册了名字,返回 24 | func (c *Context) Name() string { 25 | return c.name 26 | } 27 | 28 | func (c *Context) CastName(name string, msg interface{}) { 29 | CastName(name, msg) 30 | } 31 | 32 | func (c *Context) CastNameNode(name string, node interface{}, msg interface{}) { 33 | CastNameNode(name, node, msg) 34 | } 35 | 36 | func (c *Context) Cast(pid *Pid, msg interface{}) { 37 | Cast(pid, msg) 38 | } 39 | 40 | func (c *Context) CallName(name string, request interface{}) (bool, interface{}) { 41 | if pid := WhereIs(name); pid != nil { 42 | return c.Call(pid, request) 43 | } 44 | return false, nil 45 | } 46 | 47 | func (c *Context) Call(pid *Pid, request interface{}) (bool, interface{}) { 48 | return callTimeOut(pid, request, 5, c.self.callResult, false, c.recResult) 49 | } 50 | 51 | func (c *Context) CallNameNode(name string, node interface{}, request interface{}) (bool, interface{}) { 52 | return callNameNode(name, node, request, c.self.callResult, false, c.recResult) 53 | } 54 | 55 | func (c *Context) StartLink(newActor *Actor, args ...interface{}) (*Pid, interface{}) { 56 | pid, err := c.StartLinkOpt(newActor, nil, args...) 57 | return pid, err 58 | } 59 | func (c *Context) StartNameLink(name string, newActor *Actor, args ...interface{}) (*Pid, interface{}) { 60 | opt := []interface{}{regName(name)} 61 | pid, err := c.StartLinkOpt(newActor, opt, args...) 62 | return pid, err 63 | } 64 | 65 | func (c *Context) StartLinkOpt(newActor *Actor, opt []interface{}, args ...interface{}) (*Pid, interface{}) { 66 | opt = append(opt, &link{pid: c.self}) 67 | pid, err := StartOpt(newActor, opt, args...) 68 | return pid, err 69 | } 70 | 71 | func (c *Context) Exit(reason string) { 72 | m := &actorOP{&Terminate{Reason: reason}} 73 | c.CastSelf(m) 74 | } 75 | 76 | func (c *Context) Link(pid *Pid) { 77 | c.links = append(c.links, pid) 78 | } 79 | 80 | func (c *Context) CastSelf(msg interface{}) { 81 | if len(c.self.c) == 0 { 82 | c.recMsg(msg) 83 | } else { 84 | Cast(selfSenderPid, &routerMsg{to: c.Self(), msg: msg}) 85 | } 86 | } 87 | 88 | func (c *Context)ChangeCallMode() { 89 | c.callMode = call_mode_no_reply 90 | } 91 | 92 | func (c *Context) handleOP(op interface{}) (int, *Terminate) { 93 | switch m := op.(type) { 94 | case regName: 95 | c.name = string(m) 96 | case *link: 97 | c.links = append(c.links, m.pid) 98 | case *Terminate: 99 | return actorCodeExit, m 100 | case *initStop: 101 | return actorCodeInitStop, &Terminate{Reason: ExitReasonNormal} 102 | } 103 | return actorCodeNone, nil 104 | } 105 | 106 | func (c *Context) handleCall(ci *CallInfo) { 107 | if c.callMode == call_mode_normal { 108 | result := c.actor.HandleCall(c, ci.Request) 109 | Reply(ci.RecCh, ci.CallID, result) 110 | }else{ 111 | c.actor.HandleCall(c, ci) 112 | } 113 | } 114 | 115 | func (c *Context) parseOP(opt []interface{}) { 116 | if len(opt) > 0 { 117 | for _, op := range opt { 118 | switch o := op.(type) { 119 | case *link: 120 | c.links = append(c.links, o.pid) 121 | case regName: 122 | c.name = string(o) 123 | registerNotExist(c.name, c.self) 124 | } 125 | } 126 | } 127 | } 128 | 129 | // 这个函数是对应parseOP的,用来回滚一些处理 130 | func (c *Context) initExit(opt []interface{}) { 131 | if len(opt) > 0 { 132 | for _, op := range opt { 133 | switch o := op.(type) { 134 | case *link: 135 | c.links = nil 136 | case regName: 137 | c.name = string(o) 138 | UnRegister(c.name) 139 | c.name = "" 140 | } 141 | } 142 | } 143 | msg := &PidExit{Pid: c.self, Reason: &Terminate{Reason: ExitReasonError}} 144 | Cast(initServerPid, msg) 145 | } 146 | 147 | // 由于chan会阻塞,为了消除在call的时候,对方阻塞,或者是,对方正在发送消息给自己导致阻塞 148 | func (c *Context) recResult(callID int64, rec chan interface{}, timeOut time.Duration) (bool, interface{}) { 149 | t := time.NewTimer(timeOut * time.Second) 150 | rec: 151 | select { 152 | case result := <-rec: 153 | r := result.(*CallResult) 154 | if r.ID == callID { 155 | t.Stop() 156 | return true, r.Result 157 | } 158 | ErrorLog("not match callResult ID,%d,%d", r.ID, callID) 159 | goto rec 160 | case msg := <-c.self.c: // 这里修改为:如果是一个Actor进程,那么在call的时候,也会接收消息,放在队列里面 161 | c.recMsg(msg) 162 | goto rec 163 | case <-t.C: 164 | ErrorLog("rec callResult timeout") 165 | return false, &CallError{CallErrorTypeTimeOut, nil} 166 | } 167 | } 168 | 169 | func (c *Context) recMsg(msg interface{}) { 170 | if c.msgQ == nil { 171 | c.msgQ = ringbuffer.NewSingleRingBuffer(4, 32) 172 | } 173 | c.msgQ.Put(msg) 174 | } 175 | -------------------------------------------------------------------------------- /kernel/deep_copy.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import "reflect" 4 | 5 | func DeepCopy(src interface{}) interface{} { 6 | rt := reflect.TypeOf(src) 7 | vf := reflect.ValueOf(src) 8 | if rt.Kind() == reflect.Ptr { 9 | rt = rt.Elem() 10 | vf = vf.Elem() 11 | } 12 | destPtr := reflect.New(rt) 13 | dest := destPtr.Elem() 14 | fNum := vf.NumField() 15 | for i := 0; i < fNum; i++ { 16 | f := vf.Field(i) 17 | df := dest.Field(i) 18 | dv := copyValue(&f) 19 | df.Set(dv) 20 | } 21 | return destPtr.Interface() 22 | } 23 | 24 | func copySlice(src *reflect.Value) reflect.Value { 25 | lens := src.Len() 26 | sl := reflect.MakeSlice(src.Type(), lens, lens) 27 | for i := 0; i < lens; i++ { 28 | v := src.Index(i) 29 | v = copyValue(&v) 30 | sl.Index(i).Set(v) 31 | } 32 | return sl 33 | } 34 | 35 | func copyMap(src *reflect.Value) reflect.Value { 36 | m := reflect.MakeMapWithSize(src.Type(), src.Len()) 37 | keys := src.MapKeys() 38 | for _, k := range keys { 39 | v := src.MapIndex(k) 40 | v = copyValue(&v) 41 | m.SetMapIndex(k, v) 42 | } 43 | return m 44 | } 45 | 46 | func copyValue(src *reflect.Value) reflect.Value { 47 | switch src.Kind() { 48 | case reflect.Ptr: 49 | if src.IsNil() { 50 | return *src 51 | } 52 | return reflect.ValueOf(DeepCopy(src.Interface())) 53 | case reflect.Struct: 54 | return reflect.ValueOf(DeepCopy(src.Interface())).Elem() 55 | case reflect.Map: 56 | if src.IsNil() { 57 | return *src 58 | } 59 | return copyMap(src) 60 | case reflect.Slice: 61 | if src.IsNil() { 62 | return *src 63 | } 64 | return copySlice(src) 65 | default: 66 | return *src 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kernel/env.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | var Env = &env{ 8 | WriteLogStd: true, 9 | LogPath: "", //如果为空,则不会输出到文件,默认不输出日志 10 | ActorChanCacheSize: 100, 11 | timerMinTick: 100, 12 | TimerProcNum: 3, 13 | } 14 | 15 | type env struct { 16 | timerMinTick int64 `command:"timer_min_tick"` 17 | TimerProcNum int `command:"timer_proc_num"` 18 | ActorChanCacheSize int `command:"actor_chan_cache_size"` 19 | LogPath string `command:"log_path"` 20 | WriteLogStd bool `command:"write_log_std"` 21 | } 22 | 23 | func (e *env) SetTimerMinTick(tick int64) { 24 | if Millisecond%tick != 0 { 25 | log.Panicf("TimerMinTick error,value:%d ", tick) 26 | } 27 | e.timerMinTick = tick 28 | } 29 | func (e *env) TimerMinTick() int64 { 30 | return e.timerMinTick 31 | } 32 | -------------------------------------------------------------------------------- /kernel/init.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel/kct" 5 | ) 6 | 7 | const initServerName = "init" 8 | 9 | var initServerPid *Pid = nil 10 | 11 | var isStop = false 12 | 13 | var stopCB func() = nil 14 | 15 | // 停止整个服务 16 | func InitStop() { 17 | ErrorLog("system going to init stop") 18 | if !isStop && stopCB != nil { 19 | stopCB() 20 | } 21 | // 先停止各种app 22 | CallTimeOut(appPid, &initStop{}, 600) 23 | CallTimeOut(initServerPid, &initStop{}, 600) 24 | } 25 | 26 | type initStop struct { 27 | reply bool 28 | callID int64 29 | recCh chan interface{} 30 | } 31 | 32 | type initState struct { 33 | started *kct.BMap 34 | } 35 | 36 | var initActor = &Actor{ 37 | Init: func(context *Context, pid *Pid, args ...interface{}) interface{} { 38 | ErrorLog("%s %s started", initServerName, pid) 39 | initServerPid = pid 40 | addToKernelMap(pid) 41 | return &initState{started: kct.NewBMap()} 42 | }, 43 | HandleCast: func(context *Context, msg interface{}) { 44 | state := context.State.(*initState) 45 | switch m := msg.(type) { 46 | case *Pid: 47 | state.started.Insert(m.id, m) 48 | case *PidExit: 49 | state.started.Delete(m.Pid.id) 50 | } 51 | }, 52 | HandleCall: func(context *Context, request interface{}) interface{} { 53 | state := context.State.(*initState) 54 | switch r := request.(type) { 55 | case *initStop: 56 | if !isStop { 57 | isStop = true 58 | initStopF(state, context) 59 | } 60 | return nil 61 | case stopFunc: 62 | stopCB = r 63 | } 64 | return nil 65 | }, 66 | Terminate: func(context *Context, reason *Terminate) { 67 | }, 68 | ErrorHandler: func(context *Context, err interface{}) bool { 69 | return true 70 | }, 71 | } 72 | 73 | func initStopF(state *initState, context *Context) { 74 | f := func(e interface{}) { 75 | pid := e.(*Pid) 76 | if _, ok := kernelPid[pid.id]; !ok && pid.IsAlive() { 77 | callID := makeCallID() 78 | iStop := &initStop{callID: callID, recCh: context.self.callResult} 79 | Cast(pid, &actorOP{iStop}) 80 | for rl := true; rl; { 81 | succ, rs := context.recResult(callID, context.self.callResult, 3) 82 | if succ { 83 | rl = false 84 | } else { 85 | switch r := rs.(type) { 86 | case *CallError: 87 | // 应该只有数据库持久进程会超时 88 | if r.ErrType == CallErrorTypeTimeOut && pid.IsAlive() { 89 | if name := TryGetName(pid); name != "" { 90 | ErrorLog("stop %s timeout", name) 91 | } 92 | } else { 93 | rl = false 94 | } 95 | default: 96 | rl = false 97 | } 98 | } 99 | } 100 | } 101 | } 102 | state.started.Foreach(f) 103 | ErrorLog("kernel going to stop") 104 | kernelStop() 105 | } 106 | 107 | func initRegister(pid *Pid) *Pid { 108 | Cast(initServerPid, pid) 109 | return initServerPid 110 | } 111 | -------------------------------------------------------------------------------- /kernel/kct/bmap.go: -------------------------------------------------------------------------------- 1 | package kct 2 | 3 | import "container/list" 4 | 5 | type BMap struct { 6 | l *list.List 7 | m map[int64]*list.Element 8 | } 9 | 10 | func NewBMap() *BMap { 11 | return &BMap{l: list.New(), m: make(map[int64]*list.Element)} 12 | } 13 | 14 | func (b *BMap) Insert(key int64, value interface{}) { 15 | e := b.l.PushFront(value) 16 | b.m[key] = e 17 | } 18 | 19 | func (b *BMap) Lookup(key int64) interface{} { 20 | if e, ok := b.m[key]; ok { 21 | return e.Value 22 | } 23 | return nil 24 | } 25 | 26 | func (b *BMap) Delete(key int64) { 27 | if e, ok := b.m[key]; ok { 28 | b.l.Remove(e) 29 | delete(b.m, key) 30 | } 31 | } 32 | 33 | func (b *BMap) Foreach(f func(interface{})) { 34 | for e := b.l.Front(); e != nil; e = e.Next() { 35 | f(e.Value) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kernel/kct/list.go: -------------------------------------------------------------------------------- 1 | package kct 2 | 3 | // 提供一种类似erlang:list的数据结构,但是从尾部插入 4 | // 创建的时候需要传入一个比较函数 5 | 6 | type equalFun func(interface{}, interface{}) bool 7 | 8 | type foldlFun func(e, acc interface{}) interface{} 9 | 10 | type Klist struct { 11 | arr []interface{} 12 | eqFun equalFun 13 | } 14 | 15 | func NewList(f equalFun) *Klist { 16 | return &Klist{eqFun: f} 17 | } 18 | 19 | func (l *Klist) Append(e interface{}) int { 20 | l.arr = append(l.arr, e) 21 | return len(l.arr) - 1 22 | } 23 | 24 | func (l *Klist) Len() int { 25 | return len(l.arr) 26 | } 27 | 28 | func (l *Klist) Delete(e interface{}) { 29 | size := len(l.arr) 30 | for i := 0; i < size; i++ { 31 | if l.eqFun(e, l.arr[i]) { 32 | l.arr = append(l.arr[:i], l.arr[i+1:]...) 33 | break 34 | } 35 | } 36 | } 37 | 38 | func (l *Klist) Nth(idx int) interface{} { 39 | if idx < 0 || idx >= len(l.arr) { 40 | return nil 41 | } 42 | return l.arr[idx] 43 | } 44 | 45 | func (l *Klist) Reverse() { 46 | for i, j := 0, len(l.arr)-1; i < j; i, j = i+1, j-1 { 47 | l.arr[i], l.arr[j] = l.arr[j], l.arr[i] 48 | } 49 | } 50 | 51 | func (l *Klist) Foreach(f func(e interface{})) { 52 | for _, v := range l.arr { 53 | f(v) 54 | } 55 | } 56 | 57 | func (l *Klist) ForeachReverse(f func(e interface{})) { 58 | for i := len(l.arr) - 1; i >= 0; i-- { 59 | f(l.arr[i]) 60 | } 61 | } 62 | 63 | func (l *Klist) Fold(f foldlFun, acc interface{}) interface{} { 64 | for _, v := range l.arr { 65 | acc = f(v, acc) 66 | } 67 | return acc 68 | } 69 | 70 | // 删除指定索引,并返回 71 | func (l *Klist) Take(idx int) interface{} { 72 | if idx < 0 || idx >= len(l.arr) { 73 | return nil 74 | } 75 | tmp := l.arr[idx] 76 | l.arr = append(l.arr[:idx], l.arr[idx+1:]...) 77 | return tmp 78 | } 79 | 80 | func (l *Klist) All() []interface{} { 81 | return l.arr 82 | } 83 | -------------------------------------------------------------------------------- /kernel/kct/set.go: -------------------------------------------------------------------------------- 1 | package kct 2 | 3 | /* 4 | 等1.18泛型重写 5 | */ 6 | 7 | type empty struct {} 8 | 9 | type Set map[interface{}]empty 10 | 11 | func NewSet(size int) Set { 12 | if size <= 0 { 13 | size = 1 14 | } 15 | return make(map[interface{}]empty,size) 16 | } 17 | 18 | func (s Set)Insert(val interface{}) { 19 | s[val] = empty{} 20 | } 21 | 22 | func (s Set)Erase(val interface{}) { 23 | delete(s,val) 24 | } 25 | 26 | func (s Set)Size() int { 27 | return len(s) 28 | } 29 | 30 | func (s Set)Has(v interface{}) bool { 31 | _,ok := s[v] 32 | return ok 33 | } 34 | 35 | func (s Set)Foreach(f func(v interface{})){ 36 | for k,_ := range s{ 37 | f(k) 38 | } 39 | } -------------------------------------------------------------------------------- /kernel/kct/string.go: -------------------------------------------------------------------------------- 1 | package kct 2 | 3 | // 使用给定的符号进行字符串切割,和strings.Split()不同,如果连续2个符号,那么视为一个 4 | // CutWith("adsf ff f",' ')->{"adsf","ff","f"} 5 | func CutWith(str string,cut byte) []string { 6 | var rs []string 7 | startIdx := -1 8 | for i := 0; i < len(str); i++ { 9 | if str[i] == cut && startIdx >= 0 { 10 | rs = append(rs, str[startIdx:i]) 11 | startIdx = -1 12 | } else if str[i] != cut && startIdx < 0 { 13 | startIdx = i 14 | } 15 | } 16 | if startIdx >= 0 { 17 | rs = append(rs, str[startIdx:]) 18 | } 19 | return rs 20 | } 21 | -------------------------------------------------------------------------------- /kernel/kernel.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/args" 5 | "os" 6 | "path/filepath" 7 | "runtime/debug" 8 | "sync" 9 | ) 10 | 11 | const ( 12 | ExitReasonNormal = "normal" 13 | ExitReasonError = "error" 14 | ) 15 | 16 | var mainCh = make(chan int, 1) 17 | 18 | var kernelPid = make(map[int64]*Pid) 19 | 20 | var kernelAliveMap sync.Map 21 | 22 | func KernelStart(start func(), stop func()) { 23 | StartNameOpt(initServerName, initActor,ActorOpt(ActorChanCacheSize(10000))) 24 | kernelChild := []*SupChild{ 25 | {Name: selfServerName, Svr: selfSenderActor, ReStart: true,InitOpt: ActorOpt(ActorChanCacheSize(10000))}, 26 | {Name: "application", Svr: appSvr, ReStart: true}, 27 | } 28 | kernel := SupStart("kernel", kernelChild...) 29 | addToKernelMap(kernel) 30 | initTimer() 31 | startLogger() 32 | if stop != nil { 33 | Call(initServerPid, stopFunc(stop)) 34 | } 35 | ErrorLog("kernel Start OTP complete") 36 | start() 37 | // block the main goroutine 38 | <-mainCh 39 | ErrorLog("kernel stopped") 40 | } 41 | 42 | func kernelStop() { 43 | mainCh <- 1 44 | } 45 | 46 | func addToKernelMap(pid *Pid) { 47 | kernelPid[pid.id] = pid 48 | } 49 | 50 | func addAliveMap(pid *Pid) () { 51 | kernelAliveMap.Store(pid.id, pid) 52 | } 53 | 54 | func removeAliveMap(pid *Pid) { 55 | kernelAliveMap.Delete(pid.id) 56 | } 57 | 58 | // return args in slice 59 | func MakeArgs(args ...interface{}) []interface{} { 60 | return args 61 | } 62 | 63 | func CatchFun(f func()) { 64 | defer func() { 65 | p := recover() 66 | if p != nil { 67 | ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 68 | } 69 | }() 70 | f() 71 | } 72 | 73 | func init() { 74 | // 支持命令行参数 75 | args.FillEvn(Env) 76 | if v, ok := args.GetInt("timer_min_tick"); ok { 77 | Env.SetTimerMinTick(int64(v)) 78 | } 79 | if v, ok := args.GetInt("log_level"); ok { 80 | logLevel = LogLevel(v) 81 | } 82 | DebugLog("env :%#v",Env) 83 | } 84 | 85 | var mainRoot string 86 | 87 | // 获取二进制执行文件所在目录 88 | func GetMainRoot() string { 89 | if mainRoot != "" { 90 | return mainRoot 91 | } 92 | m := os.Args[0] 93 | // 获取main的目录 94 | mainRoot = filepath.Dir(m) 95 | return mainRoot 96 | } 97 | 98 | // 纯粹方便测试用,不用每次编写一堆相同代码 99 | func DefaultActor() *Actor { 100 | actor := &Actor{ 101 | Init: func(context *Context, pid *Pid, args ...interface{}) interface{} { 102 | return nil 103 | }, 104 | HandleCast: func(context *Context, msg interface{}) { 105 | }, 106 | HandleCall: func(context *Context, request interface{}) interface{} { 107 | return nil 108 | }, 109 | Terminate: func(context *Context, reason *Terminate) { 110 | }, 111 | ErrorHandler: func(context *Context, err interface{}) bool { 112 | return true 113 | }, 114 | } 115 | return actor 116 | } 117 | 118 | func Link(srcPid, destPid *Pid) { 119 | Cast(destPid, &actorOP{op: &link{pid: srcPid}}) 120 | } 121 | 122 | // 减少代码规模,但是不应该用在需要高性能的地方 123 | func NewActor(funList ...interface{}) *Actor { 124 | actor := DefaultActor() 125 | for _, f := range funList { 126 | switch fun := f.(type) { 127 | case InitFunc: 128 | actor.Init = fun 129 | case HandleCastFunc: 130 | actor.HandleCast = fun 131 | case HandleCallFunc: 132 | actor.HandleCall = fun 133 | case TerminateFunc: 134 | actor.Terminate = fun 135 | case ErrorHandleFunc: 136 | actor.ErrorHandler = fun 137 | } 138 | } 139 | return actor 140 | } 141 | -------------------------------------------------------------------------------- /kernel/kernel_test.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestKernelStart(t *testing.T) { 9 | go func() { 10 | time.Sleep(3 * time.Second) 11 | ErrorLog("test init stop now") 12 | InitStop() 13 | }() 14 | Env.LogPath = "" 15 | KernelStart(func() { 16 | for i := 0; i < 10; i++ { 17 | A := DefaultActor() 18 | A.Init = func(context *Context, pid *Pid, args ...interface{}) interface{} { 19 | SendAfter(TimerTypeForever, pid, 100, 1) 20 | ErrorLog("Start :%s", pid) 21 | return nil 22 | } 23 | A.Terminate = func(context *Context, reason *Terminate) { 24 | ErrorLog("stop :%s", Ctx().Self()) 25 | } 26 | Start(A) 27 | } 28 | }, nil) 29 | } 30 | -------------------------------------------------------------------------------- /kernel/logger.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/bpool" 6 | "io" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "runtime" 11 | "time" 12 | ) 13 | 14 | const loggerName = "logger" 15 | 16 | var loggerServerPid *Pid = nil 17 | 18 | type makeFile struct{} 19 | 20 | type loggerState struct { 21 | out io.Writer 22 | redirect bool 23 | } 24 | 25 | type LogLevel int 26 | 27 | var logLevel LogLevel = 2 28 | 29 | type logData struct { 30 | pkg string 31 | module string 32 | line int 33 | log string 34 | } 35 | 36 | func SetLogLevel(lv LogLevel) { 37 | logLevel = lv 38 | } 39 | 40 | func SetLoggerOut(writer io.Writer) { 41 | Cast(loggerServerPid, writer) 42 | } 43 | 44 | func DebugLog(format string, args ...interface{}) { 45 | if logLevel < 2 { 46 | var pkg string 47 | _, file, line, ok := runtime.Caller(1) 48 | if !ok { 49 | file = "???" 50 | pkg = "???" 51 | line = 0 52 | } else { 53 | pkg, file = filepath.Split(file) 54 | pkg = filepath.Base(pkg) 55 | } 56 | sendLog(pkg, file, line, format, args...) 57 | } 58 | } 59 | 60 | func ErrorLog(format string, args ...interface{}) { 61 | _, file, line, ok := runtime.Caller(1) 62 | var pkg string 63 | if !ok { 64 | file = "???" 65 | pkg = "???" 66 | line = 0 67 | } else { 68 | pkg, file = filepath.Split(file) 69 | pkg = filepath.Base(pkg) 70 | } 71 | sendLog(pkg, file, line, format, args...) 72 | } 73 | 74 | func UnHandleMsg(msg interface{}) { 75 | var pkg string 76 | _, file, line, ok := runtime.Caller(1) 77 | if !ok { 78 | file = "???" 79 | pkg = "???" 80 | line = 0 81 | } else { 82 | pkg, file = filepath.Split(file) 83 | pkg = filepath.Base(pkg) 84 | } 85 | sendLog(pkg, file, line, "un handle msg : %#v", msg) 86 | } 87 | 88 | func startLogger() { 89 | StartNameOpt(loggerName, loggerActor, ActorOpt(ActorChanCacheSize(10000))) 90 | } 91 | 92 | type logWrite struct { 93 | logger *Pid 94 | } 95 | 96 | func (l *logWrite) Write(p []byte) (n int, err error) { 97 | n = len(p) 98 | // 考虑到上层逻辑可能会重用,这里copy 99 | Cast(l.logger, bpool.NewBuf(p)) 100 | return 101 | } 102 | 103 | func LoggerWriter() io.Writer { 104 | return &logWrite{logger: loggerServerPid} 105 | } 106 | 107 | var loggerActor = &Actor{ 108 | Init: func(context *Context, pid *Pid, args ...interface{}) interface{} { 109 | ErrorLog("%s %s started", loggerName, pid) 110 | addToKernelMap(pid) 111 | startLogTimer(pid) 112 | // 捕获go内置log信息 113 | log.SetOutput(&logWrite{logger: pid}) 114 | state := initFile(&loggerState{out: nil}) 115 | if Env.LogPath != "" && (*loggerState)(state).out == nil { 116 | os.Exit(789) 117 | } 118 | loggerServerPid = pid 119 | return state 120 | }, 121 | HandleCast: func(context *Context, msg interface{}) { 122 | state := context.State.(*loggerState) 123 | switch m := msg.(type) { 124 | case *logData: 125 | if state.out != nil { 126 | writeLog(state.out, m) 127 | } 128 | case *bpool.Buff: 129 | if state.out != nil { 130 | _, _ = state.out.Write(m.ToBytes()) 131 | } 132 | if Env.WriteLogStd { 133 | _, _ = os.Stdout.Write(m.ToBytes()) 134 | } 135 | m.Free() 136 | case io.Writer: 137 | state.out = m 138 | state.redirect = true 139 | case makeFile: 140 | if !state.redirect { 141 | context.State = initFile(state) 142 | startLogTimer(context.Self()) 143 | } 144 | } 145 | }, 146 | HandleCall: func(context *Context, request interface{}) interface{} { 147 | return nil 148 | }, 149 | Terminate: func(context *Context, reason *Terminate) { 150 | 151 | }, 152 | ErrorHandler: func(context *Context, err interface{}) bool { 153 | return true 154 | }, 155 | } 156 | 157 | func initFile(logger *loggerState) *loggerState { 158 | if logger.out != nil { 159 | logger.out.(*os.File).Close() 160 | } 161 | logger.out = defaultWriter() 162 | return logger 163 | } 164 | 165 | func startLogTimer(self *Pid) { 166 | // 计算整点时间,用来更改文件名 167 | t := time.Now() 168 | _, min, sec := t.Clock() 169 | less := hourMillisecond - (int64(min)*minMillisecond + int64(sec)*Millisecond) 170 | SendAfter(TimerTypeOnce, self, less, makeFile{}) 171 | } 172 | 173 | func sendLog(pkg, module string, line int, format string, args ...interface{}) () { 174 | msg := &logData{pkg: pkg, module: module, line: line, log: fmt.Sprintf(format, args...)} 175 | if loggerServerPid != nil { 176 | Cast(loggerServerPid, msg) 177 | } else if Env.LogPath != "" { 178 | // if the logger not Start yet,write to the file native 179 | logger := defaultWriter() 180 | defer logger.Close() 181 | writeLog(logger, msg) 182 | } 183 | if Env.WriteLogStd { 184 | writeLog(os.Stdout, msg) 185 | } 186 | } 187 | 188 | func writeLog(w io.Writer, data *logData) { 189 | t := time.Now() 190 | year, month, day := t.Date() 191 | hour, min, sec := t.Clock() 192 | _, _ = fmt.Fprintf(w, "\n%d-%d-%d %d:%02d:%02d [%s.%s:%d] %s\n", 193 | year, month, day, hour, min, sec, data.pkg, data.module, data.line, data.log) 194 | } 195 | 196 | func defaultWriter() *os.File { 197 | if Env.LogPath == "" { 198 | return nil 199 | } 200 | t := time.Now() 201 | year, month, day := t.Date() 202 | hour, _, _ := t.Clock() 203 | path := Env.LogPath + fmt.Sprintf("/%d_%d_%d", year, month, day) 204 | file := path + fmt.Sprintf("/sy_%d_%d_%d___%02d.log", year, month, day, hour) 205 | if _, err := os.Stat(path); err != nil { 206 | _ = os.MkdirAll(path, os.ModeDir) 207 | } 208 | ioFile, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 209 | if err != nil { 210 | ErrorLog("cannot open file:%s", err.Error()) 211 | return nil 212 | } 213 | return ioFile 214 | } 215 | -------------------------------------------------------------------------------- /kernel/name_map.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "sync" 7 | ) 8 | 9 | //保全全局名字 10 | var ( 11 | nameMap sync.Map 12 | id2Name sync.Map 13 | ) 14 | 15 | func Register(name string, pid *Pid) { 16 | nameMap.Store(name, pid) 17 | id2Name.Store(pid.id,name) 18 | pid.c <- regName(name) 19 | } 20 | 21 | func RegisterNotExist(name string, pid *Pid) { 22 | registerNotExist(name, pid) 23 | pid.c <- regName(name) 24 | } 25 | 26 | func UnRegister(name string) { 27 | if pid, ok := nameMap.Load(name); ok { 28 | nameMap.Delete(name) 29 | id2Name.Delete(pid.(*Pid).id) 30 | } 31 | } 32 | 33 | func WhereIs(name string) *Pid { 34 | if pid, ok := nameMap.Load(name); ok { 35 | return pid.(*Pid) 36 | } 37 | return nil 38 | } 39 | 40 | func registerNotExist(name string, pid *Pid) { 41 | if p, ok := nameMap.Load(name);ok && p.(*Pid).IsAlive() { 42 | log.Panic(fmt.Errorf("Name :[%s] is register ", name)) 43 | } 44 | nameMap.Store(name, pid) 45 | id2Name.Store(pid.id,name) 46 | } 47 | 48 | func TryGetName(pid *Pid) string { 49 | if n,ok := id2Name.Load(pid.id);ok{ 50 | return n.(string) 51 | } 52 | return "" 53 | } 54 | -------------------------------------------------------------------------------- /kernel/node.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | var node = &Node{id: 0} 9 | 10 | var nodeMap sync.Map 11 | 12 | var nodeId2Node sync.Map 13 | 14 | var nodeID int32 15 | 16 | var nodeNetWork sync.Map 17 | 18 | func SetSelfNodeName(name string) { 19 | node.name = name 20 | nodeMap.Store(name, node) 21 | } 22 | 23 | func SelfNode() *Node { 24 | return node 25 | } 26 | 27 | func GetNode(nodeName string) *Node { 28 | if n, ok := nodeMap.Load(nodeName); ok { 29 | return n.(*Node) 30 | } 31 | id := atomic.AddInt32(&nodeID,1) 32 | n := &Node{id: id, name: nodeName} 33 | nodeMap.Store(nodeName, n) 34 | nodeId2Node.Store(id, n) 35 | return n 36 | } 37 | 38 | func SetNodeNetWork(node *Node, pid *Pid) { 39 | nodeNetWork.Store(node.id, pid) 40 | } 41 | 42 | func NodeDisconnect(node *Node) { 43 | nodeNetWork.Delete(node.id) 44 | } 45 | 46 | func GetNodeNetWork(node *Node) (*Pid, bool) { 47 | if p, ok := nodeNetWork.Load(node.id); ok { 48 | return p.(*Pid), true 49 | } 50 | return nil, false 51 | } 52 | 53 | func IsNodeConnect(nodeName string) bool { 54 | n, ok := nodeMap.Load(nodeName) 55 | if !ok { 56 | return false 57 | } 58 | id := n.(*Node).id 59 | if p, ok := nodeNetWork.Load(id); ok { 60 | return p.(*Pid).IsAlive() 61 | } 62 | return false 63 | } 64 | 65 | func Nodes() []*Node { 66 | var rs []*Node 67 | nodeNetWork.Range(func(key, value interface{}) bool { 68 | if n, ok := nodeId2Node.Load(key); ok { 69 | rs = append(rs, n.(*Node)) 70 | } 71 | return true 72 | }) 73 | return rs 74 | } 75 | 76 | func (n *Node) Name() string { 77 | return n.name 78 | } 79 | 80 | // 判断节点相等 81 | func (n *Node) Equal(node *Node) bool { 82 | return n.id == node.id 83 | } 84 | -------------------------------------------------------------------------------- /kernel/pid.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "strconv" 5 | "sync/atomic" 6 | ) 7 | 8 | var deadPid = &Pid{isAlive: 0} 9 | 10 | type Pid struct { 11 | isAlive int32 // 放在第一个位置,有利于cpu快速定位 12 | id int64 13 | c chan interface{} 14 | callResult chan interface{} 15 | node *Node // 添加分布式支持 16 | } 17 | 18 | func (p *Pid) GetChannel() chan interface{} { 19 | return p.c 20 | } 21 | 22 | func (p *Pid) GetID() int64 { 23 | return p.id 24 | } 25 | 26 | func (p *Pid) String() string { 27 | if p.node == nil { 28 | return "" 29 | } 30 | return "" 31 | } 32 | 33 | func (p *Pid) IsAlive() bool { 34 | if p == nil || p.node != nil { 35 | return false 36 | } 37 | return atomic.LoadInt32(&p.isAlive) == 1 38 | } 39 | 40 | // 在一些阻塞的进程里,可以设置为消亡,这样停服就不会卡主 41 | func (p *Pid) SetDie() { 42 | atomic.StoreInt32(&p.isAlive,0) 43 | } 44 | 45 | func (p *Pid) Node() *Node { 46 | if p.node == nil { 47 | return node 48 | } 49 | return p.node 50 | } 51 | 52 | func (p *Pid)Cast(msg interface{}) { 53 | Cast(p,msg) 54 | } 55 | 56 | func (p *Pid) ToBytes(buf []byte) []byte { 57 | v := p.id 58 | buf = append(buf, uint8(v>>56), uint8(v>>48), uint8(v>>40), uint8(v>>32), uint8(v>>24), uint8(v>>16), uint8(v>>8), uint8(v)) 59 | var nodeName string 60 | if p.node != nil { 61 | nodeName = p.node.name 62 | } else { 63 | nodeName = node.name 64 | } 65 | lens := len(nodeName) 66 | buf = append(buf, uint8(lens>>8), uint8(lens)) 67 | buf = append(buf, nodeName...) 68 | return buf 69 | } 70 | 71 | func DecodePid(buf []byte, index int) (int, *Pid) { 72 | id := int64(buf[index])<<56 + int64(buf[index+1])<<48 + int64(buf[index+2])<<40 + int64(buf[index+3])<<32 + 73 | int64(buf[index+4])<<24 + int64(buf[index+5])<<16 + int64(buf[index+6])<<8 + int64(buf[index+7]) 74 | index += 8 75 | var size int 76 | size = int(buf[index]) << 8 77 | size += int(buf[index+1]) 78 | index += 2 79 | end := index + size 80 | nodeName := string(buf[index:end]) 81 | // 判断是否本地pid 82 | if node.name == nodeName { 83 | // 判断pid存活 84 | if pid, ok := kernelAliveMap.Load(id); ok { 85 | return end, pid.(*Pid) 86 | } 87 | return end, deadPid 88 | } 89 | n := GetNode(nodeName) 90 | return end, &Pid{id: id, c: nil, node: n} 91 | } 92 | 93 | func LocalPid(id int64) *Pid { 94 | if pid, ok := kernelAliveMap.Load(id); ok { 95 | return pid.(*Pid) 96 | } 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /kernel/self_sender.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | const selfServerName = "selfSender" 4 | 5 | var selfSenderPid *Pid 6 | 7 | var selfSenderActor = &Actor{ 8 | Init: func(context *Context,pid *Pid, args ...interface{}) interface{} { 9 | ErrorLog("%s %s started", selfServerName, pid) 10 | selfSenderPid = pid 11 | addToKernelMap(pid) 12 | return nil 13 | }, 14 | HandleCast: func(context *Context, msg interface{}) { 15 | switch m := msg.(type) { 16 | case *routerMsg: 17 | Cast(m.to, m.msg) 18 | } 19 | }, 20 | HandleCall: func(context *Context, request interface{}) interface{} { 21 | return nil 22 | }, 23 | Terminate: func(context *Context, reason *Terminate) { 24 | 25 | }, 26 | ErrorHandler: func(context *Context, err interface{}) bool { 27 | return true 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /kernel/supervisor.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/kernel/kct" 6 | ) 7 | 8 | type op uint 9 | 10 | const ( 11 | opWhichChild op = 1 << iota 12 | ) 13 | 14 | const ( 15 | SupChildTypeWorker = iota 16 | SupChildTypeSup 17 | ) 18 | 19 | type SupChild struct { 20 | ChildType int 21 | ReStart bool 22 | Name string 23 | Svr *Actor 24 | InitOpt []interface{} 25 | InitArgs []interface{} 26 | } 27 | 28 | type SupChildInfo struct { 29 | *SupChild 30 | Pid *Pid 31 | } 32 | 33 | func SupStartChild(sup interface{}, child *SupChild) (interface{}, *Pid) { 34 | switch s := sup.(type) { 35 | case string: 36 | name := sup.(string) 37 | pid := WhereIs(name) 38 | if pid != nil { 39 | return startChild(pid, child) 40 | } 41 | return fmt.Sprintf("sup Name:%s not founded\n", name), nil 42 | case *Pid: 43 | return startChild(s, child) 44 | } 45 | return fmt.Errorf("unknow arg:%#v", sup), nil 46 | } 47 | 48 | func SupStart(name string, initChild ...*SupChild) *Pid { 49 | pid, _ := StartName(name, supervisorActor, name, initChild) 50 | return pid 51 | } 52 | 53 | func SupStop(sup interface{}) { 54 | switch s := sup.(type) { 55 | case string: 56 | name := sup.(string) 57 | pid := WhereIs(name) 58 | if pid != nil { 59 | _, _ = CallTimeOut(pid, stop(ExitReasonNormal), 86400) 60 | } 61 | case *Pid: 62 | _, _ = CallTimeOut(s, stop(ExitReasonNormal), 86400) 63 | } 64 | } 65 | 66 | func SupWhichChild(sup interface{}) []*SupChildInfo { 67 | var pid *Pid 68 | switch t := sup.(type) { 69 | case string: 70 | pid = WhereIs(t) 71 | case *Pid: 72 | pid = t 73 | } 74 | if pid == nil { 75 | return nil 76 | } 77 | _, rs := Call(pid, opWhichChild) 78 | if t, ok := rs.([]*SupChildInfo); ok { 79 | return t 80 | } 81 | return nil 82 | } 83 | 84 | func startChild(supPid *Pid, child *SupChild) (interface{}, *Pid) { 85 | ok, rs := CallTimeOut(supPid, child, 86400) 86 | if ok { 87 | rs2 := rs.(*initResult) 88 | if rs2.ok { 89 | return nil, rs2.err.(*Pid) 90 | } 91 | return rs2.err, nil 92 | } 93 | return rs, nil 94 | } 95 | 96 | var supervisorActor *Actor 97 | 98 | func init() { 99 | supervisorActor = &Actor{ 100 | Init: supInit, 101 | HandleCast: supHandleCast, 102 | HandleCall: supHandleCall, 103 | Terminate: func(context *Context, reason *Terminate) { 104 | context.State.(*supervisor).stopAllChild(context) 105 | }, 106 | ErrorHandler: func(context *Context, err interface{}) bool { 107 | return true 108 | }, 109 | } 110 | } 111 | 112 | func supInit(context *Context, pid *Pid, args ...interface{}) interface{} { 113 | s := supervisor{} 114 | s.name = args[0].(string) 115 | ErrorLog("%s sup %s started", s.name, pid) 116 | s.initChild = args[1].([]*SupChild) 117 | s.child = kct.NewBMap() 118 | s.cache = make(map[int64]*SupChildInfo, 1000) 119 | for _, child := range s.initChild { 120 | opt := buildOpt(child, pid) 121 | childPid, _ := StartOpt(child.Svr, opt, child.InitArgs...) 122 | s.addChild(&SupChildInfo{child, childPid}) 123 | } 124 | return &s 125 | } 126 | 127 | func supHandleCast(context *Context, msg interface{}) { 128 | s := context.State.(*supervisor) 129 | switch m := msg.(type) { 130 | case *PidExit: 131 | if m.Reason.Reason != ExitReasonNormal { 132 | child := s.cache[m.Pid.id].SupChild 133 | ErrorLog("sup [%s] child %s%s exit,Reason:%s", s.name, child.Name, m.Pid, m.Reason.Reason) 134 | if child.ReStart { 135 | // restart 136 | ErrorLog("%s restart %#v", s.name, child) 137 | s.startChild(context, child) 138 | } 139 | } 140 | s.childExit(m.Pid) 141 | } 142 | } 143 | 144 | func supHandleCall(context *Context, request interface{}) interface{} { 145 | s := context.State.(*supervisor) 146 | switch r := request.(type) { 147 | case *SupChild: //StartOpt a new service 148 | return s.startChild(context, r) 149 | case op: 150 | return s.callOP(r) 151 | case stop: 152 | ErrorLog("%s stop", context.name) 153 | reason := string(r) 154 | s.stopAllChild(context) 155 | context.Exit(reason) 156 | return nil 157 | } 158 | return fmt.Errorf("no case match") 159 | } 160 | 161 | type supervisor struct { 162 | name string 163 | initChild []*SupChild 164 | child *kct.BMap 165 | cache map[int64]*SupChildInfo 166 | } 167 | 168 | func (s *supervisor) startChild(context *Context, child *SupChild) interface{} { 169 | var pid *Pid 170 | var err interface{} 171 | if child.ChildType == SupChildTypeWorker { 172 | if child.Name != "" { 173 | pid, err = context.StartLinkOpt(child.Svr, append(child.InitOpt, regName(child.Name)), child.InitArgs...) 174 | } else { 175 | pid, err = context.StartLinkOpt(child.Svr, child.InitOpt, child.InitArgs...) 176 | } 177 | } else if child.ChildType == SupChildTypeSup { 178 | var sc []*SupChild 179 | for _, c := range child.InitArgs { 180 | sc = append(sc, c.(*SupChild)) 181 | } 182 | pid = SupStart(child.Name, sc...) 183 | } 184 | s.addChild(&SupChildInfo{child, pid}) 185 | if err != nil { 186 | return &initResult{ok: false, err: err} 187 | } 188 | return &initResult{ok: true, err: pid} 189 | } 190 | 191 | func (s *supervisor) addChild(child *SupChildInfo) { 192 | id := child.Pid.id 193 | s.child.Insert(id, id) 194 | s.cache[id] = child 195 | } 196 | func (s *supervisor) childExit(pid *Pid) { 197 | id := pid.id 198 | s.child.Delete(id) 199 | delete(s.cache, id) 200 | } 201 | 202 | func (s *supervisor) stopAllChild(context *Context) { 203 | f := func(e interface{}) { 204 | child := s.cache[e.(int64)].Pid 205 | if child.IsAlive() { 206 | callID := makeCallID() 207 | iStop := &initStop{callID: callID, recCh: context.self.callResult} 208 | Cast(child, &actorOP{iStop}) 209 | context.recResult(callID, context.self.callResult, 100) 210 | } 211 | } 212 | s.child.Foreach(f) 213 | } 214 | 215 | func (s *supervisor) callOP(op op) interface{} { 216 | switch op { 217 | case opWhichChild: 218 | var rs []*SupChildInfo 219 | f := func(e interface{}) { 220 | child := s.cache[e.(int64)] 221 | rs = append(rs, child) 222 | } 223 | s.child.Foreach(f) 224 | return rs 225 | } 226 | return nil 227 | } 228 | 229 | func buildOpt(child *SupChild, father *Pid) []interface{} { 230 | opt := append(child.InitOpt, &link{pid: father}) 231 | if child.Name != "" { 232 | opt = append(opt, regName(child.Name)) 233 | } 234 | return opt 235 | } 236 | -------------------------------------------------------------------------------- /kernel/time.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import "time" 4 | 5 | const ( 6 | minSec = 60 7 | hourSec = 60 * minSec 8 | daySec = 24 * hourSec 9 | ) 10 | 11 | // unix时间戳 12 | func Now() int64 { 13 | return time.Now().Unix() 14 | } 15 | 16 | // 毫秒unix时间戳 17 | func Now2() int64 { 18 | return time.Now().UnixNano() / 1e6 19 | } 20 | 21 | // 0点的时间戳 22 | func Midnight() int64 { 23 | t := time.Now() 24 | now := t.Unix() 25 | return now - int64(t.Hour()*hourSec+t.Minute()*minSec+t.Second()) 26 | } 27 | 28 | func WeekDay() int64 { 29 | return weekDay(time.Now()) 30 | } 31 | 32 | func WeekDayFromUnix(now int64) int64 { 33 | return weekDay(time.Unix(now, 0)) 34 | } 35 | 36 | func weekDay(t time.Time) int64 { 37 | week := time.Now().Weekday() 38 | if week == time.Sunday { 39 | return 7 40 | } 41 | return int64(week) 42 | } 43 | 44 | // 本周1的0点时间戳 45 | func WeekOneMidnight() int64 { 46 | t := time.Now() 47 | now := t.Unix() 48 | week := t.Weekday() 49 | var dc int 50 | if week == time.Sunday { 51 | dc = 6 52 | } else { 53 | dc = int(week - time.Monday) 54 | } 55 | return now - int64(t.Hour()*hourSec+t.Minute()*minSec+t.Second()+dc*daySec) 56 | } 57 | 58 | // 根据时间戳,获取距离1970.1.1 的天数 59 | func DayNumFromUnix(now int64) int64 { 60 | return now-time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local).Unix() / daySec 61 | } 62 | 63 | // 获取当前时间距离目标时间的天数 64 | func DayNum(t time.Time) int64 { 65 | return DayNumFromUnix(Now()) - DayNumFromUnix(t.Unix()) 66 | } 67 | 68 | func NowToTime(now int64) time.Time { 69 | return time.Unix(now,0) 70 | } 71 | 72 | func NowToTimeString(now int64) string { 73 | return TimeString(NowToTime(now)) 74 | } 75 | 76 | func TimeString(t time.Time) string { 77 | return t.Format("2006-01-02 15:04:05") 78 | } 79 | -------------------------------------------------------------------------------- /kernel/timer.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type timerType int 9 | 10 | const ( 11 | TimerTypeForever timerType = 1 << iota 12 | TimerTypeOnce 13 | ) 14 | 15 | const ( 16 | Millisecond int64 = 1000 17 | tenMillisecond = 10 * Millisecond 18 | minMillisecond = 60 * Millisecond 19 | hourMillisecond = 60 * minMillisecond 20 | ) 21 | 22 | var defaultRC = make(TimerChan, 1000) 23 | 24 | type TimerChan chan *actorTimer 25 | 26 | func initTimer() { 27 | timerStartGroup("system",defaultRC,Env.TimerProcNum,Env.timerMinTick) 28 | } 29 | 30 | func timerStartGroup(GName string,rc TimerChan,procNum int,minTick int64) { 31 | for i := 1; i <= procNum; i++ { 32 | name := fmt.Sprintf(GName+"_"+"timer_%d", i) 33 | _, pid := SupStartChild("kernel", &SupChild{Name: name, Svr: timerActor, InitArgs: []interface{}{i,rc,minTick}}) 34 | addToKernelMap(pid) 35 | } 36 | ErrorLog("timer [%s] service started,min tick: %dms",GName, minTick) 37 | } 38 | 39 | type timerList struct { 40 | pre *timerList 41 | next *timerList 42 | data *actorTimer 43 | } 44 | 45 | type actorTimer struct { 46 | timerType timerType 47 | d int64 //毫秒 48 | inv int64 49 | pid *Pid 50 | msg interface{} 51 | } 52 | 53 | func SendAfterForever(pid *Pid, inv int64, msg interface{}) { 54 | SendAfter(TimerTypeForever,pid,inv,msg) 55 | } 56 | 57 | func SendAfter(timerType timerType, pid *Pid, inv int64, msg interface{}) { 58 | defaultRC.SendAfter(timerType,pid,inv,msg) 59 | } 60 | 61 | func (r TimerChan)SendAfter(timerType timerType, pid *Pid, inv int64, msg interface{}) { 62 | ti := &actorTimer{timerType: timerType, inv: inv, pid: pid, msg: msg, d: Now2() + inv} 63 | r <- ti 64 | } 65 | 66 | type aTimer struct { 67 | secM, tenM, minM, hourM int64 68 | tick int64 69 | t0 *timerList // 少于1秒 70 | t1 *timerList // 少于10秒 71 | t2 *timerList // 超过10秒的时间 72 | t3 *timerList // 超过1分钟的时间 73 | t4 *timerList // 超过1小时的时间 74 | } 75 | 76 | var timerActor = &Actor{ 77 | Init: func(context *Context,self *Pid, args ...interface{}) interface{} { 78 | t := aTimer{} 79 | rc := args[1].(TimerChan) 80 | timerMinTick := args[2].(int64) 81 | t.secM = Millisecond / timerMinTick 82 | t.tenM = tenMillisecond / timerMinTick 83 | t.minM = minMillisecond / timerMinTick 84 | t.hourM = hourMillisecond / timerMinTick 85 | t.tick = 0 86 | t.t0 = nil 87 | t.t1 = nil 88 | t.t2 = nil 89 | t.t3 = nil 90 | t.t4 = nil 91 | go startReceiver(self, rc) 92 | go tLoop(self,args[0].(int)) 93 | return &t 94 | }, 95 | HandleCast: func(context *Context, msg interface{}) { 96 | t := context.State.(*aTimer) 97 | switch tm := msg.(type) { 98 | case *actorTimer: 99 | t.insertTimer(&timerList{pre: nil, next: nil, data: tm}, tm.inv) 100 | case Loop: 101 | t.loopTimer() 102 | } 103 | }, 104 | HandleCall: func(context *Context, request interface{}) interface{} { 105 | return nil 106 | }, 107 | Terminate: func(context *Context, reason *Terminate) { 108 | 109 | }, 110 | ErrorHandler: func(context *Context, err interface{}) bool { 111 | return true 112 | }, 113 | } 114 | 115 | // 遍历所有定时器 116 | func (t *aTimer) loopTimer() { 117 | now := Now2() 118 | t.t0 = t.loopTimerCheck(t.t0, now) 119 | t.tick++ 120 | if t.tick%t.secM == 0 { 121 | t.t1 = t.actTimer(t.t1, now, Millisecond) 122 | } 123 | if t.tick%t.tenM == 0 { 124 | t.t2 = t.actTimer(t.t2, now, tenMillisecond) 125 | } 126 | if t.tick%t.minM == 0 { 127 | t.t3 = t.actTimer(t.t3, now, minMillisecond) 128 | } 129 | if t.tick == t.hourM { 130 | t.t4 = t.actTimer(t.t4, now, hourMillisecond) 131 | t.tick = 0 132 | } 133 | } 134 | 135 | func (t *aTimer) actTimer(list *timerList, now int64, inv int64) *timerList { 136 | for e := list; e != nil; { 137 | v := e 138 | e = e.next 139 | dif := v.data.d - now 140 | if dif <= inv { 141 | removeTimer(&list, v) 142 | t.insertTimer(v, dif) 143 | } 144 | } 145 | return list 146 | } 147 | 148 | func (t *aTimer) loopTimerCheck(list *timerList, now int64) *timerList { 149 | for e := list; e != nil; { 150 | v := e 151 | e = e.next 152 | if now >= v.data.d { 153 | if v.data.pid.IsAlive() { 154 | Cast(v.data.pid, v.data.msg) 155 | if v.data.timerType == TimerTypeOnce || v.data.inv > Millisecond { 156 | removeTimer(&list, v) 157 | } 158 | if v.data.timerType == TimerTypeForever && v.data.inv > Millisecond { 159 | v.data.d += v.data.inv 160 | t.insertTimer(v, v.data.inv) 161 | } else if v.data.timerType == TimerTypeForever { 162 | v.data.d = v.data.d + v.data.inv 163 | } 164 | } else { 165 | removeTimer(&list, v) 166 | } 167 | } 168 | } 169 | return list 170 | } 171 | 172 | func (t *aTimer) insertTimer(ti *timerList, inv int64) { 173 | if inv <= Millisecond { 174 | t.t0 = insertTimer(t.t0, ti) 175 | } else if inv <= tenMillisecond { 176 | t.t1 = insertTimer(t.t1, ti) 177 | } else if inv <= minMillisecond { 178 | t.t2 = insertTimer(t.t2, ti) 179 | } else if inv <= hourMillisecond { 180 | t.t3 = insertTimer(t.t3, ti) 181 | } else { 182 | t.t4 = insertTimer(t.t4, ti) 183 | } 184 | } 185 | 186 | func insertTimer(list *timerList, ti *timerList) *timerList { 187 | ti.next = list 188 | ti.pre = nil 189 | if list != nil { 190 | list.pre = ti 191 | } 192 | return ti 193 | } 194 | 195 | func startReceiver(father *Pid, rc chan *actorTimer) { 196 | for { 197 | ti := <-rc 198 | father.c <- ti 199 | } 200 | } 201 | 202 | func tLoop(father *Pid,i int) { 203 | time.Sleep(time.Duration(i)*10*time.Millisecond) 204 | c := time.Tick(time.Duration(Env.timerMinTick) * time.Millisecond) 205 | for { 206 | <-c 207 | Cast(father, Loop{}) 208 | } 209 | } 210 | 211 | func removeTimer(list **timerList, e *timerList) { 212 | if e.next == nil { 213 | if *list == e { 214 | *list = nil 215 | } else { 216 | e.pre.next = nil 217 | } 218 | } else { 219 | if *list == e { 220 | *list = e.next 221 | if *list != nil { 222 | (*list).pre = nil 223 | } 224 | } else { 225 | e.pre.next = e.next 226 | e.next.pre = e.pre 227 | } 228 | } 229 | } 230 | 231 | // 单独开启一个timer组 232 | func TimerStartHandler(GName string,minInv int64,handlerProcNum int,chanSize int) TimerChan { 233 | c := make(TimerChan, chanSize) 234 | timerStartGroup(GName,c,handlerProcNum,minInv) 235 | return c 236 | } -------------------------------------------------------------------------------- /kernel/type.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | type actorOP struct { 8 | op interface{} 9 | } 10 | 11 | type regName string 12 | 13 | type link struct { 14 | pid *Pid 15 | } 16 | 17 | type ActorChanCacheSize int 18 | 19 | type PidExit struct { 20 | Pid *Pid 21 | Reason *Terminate 22 | } 23 | 24 | type routerMsg struct { 25 | to *Pid 26 | msg interface{} 27 | } 28 | 29 | type Terminate struct { 30 | Reason string 31 | } 32 | 33 | type msgQueue struct { 34 | next *msgQueue 35 | msg interface{} 36 | } 37 | 38 | const ( 39 | actorCodeNone = iota + 1 40 | actorCodeExit 41 | actorCodeInitStop 42 | ) 43 | 44 | type callErrorType int 45 | 46 | type CallError struct { 47 | ErrType callErrorType 48 | err interface{} 49 | } 50 | 51 | const ( 52 | CallErrorTypeTimeOut callErrorType = iota + 1 53 | CallErrorTypeNoProc 54 | CallErrorTypeNodeNotConnect 55 | ) 56 | 57 | type stop string 58 | 59 | type stopFunc func() 60 | 61 | type InitFunc func(ctx *Context, pid *Pid, args ...interface{}) interface{} 62 | type HandleCastFunc func(ctx *Context, msg interface{}) 63 | type HandleCallFunc func(ctx *Context, request interface{}) interface{} 64 | type TerminateFunc func(ctx *Context, reason *Terminate) 65 | type ErrorHandleFunc func(ctx *Context, err interface{}) bool 66 | 67 | type Actor struct { 68 | // 初始化回调 69 | Init InitFunc 70 | // 接收消息 71 | HandleCast HandleCastFunc 72 | // 接受同步调用 73 | HandleCall HandleCallFunc 74 | // actor退出回调 75 | // 非必要实现函数 76 | Terminate TerminateFunc 77 | // 当发生catch错误时调用,如果返回false,那么进程将会退出 78 | // 非必要实现函数 79 | ErrorHandler ErrorHandleFunc 80 | } 81 | 82 | type Node struct { 83 | id int32 84 | name string 85 | } 86 | 87 | type NodeMsg struct { 88 | Dest *Pid 89 | Msg interface{} 90 | } 91 | 92 | type NodeMsgName struct { 93 | Dest string 94 | Msg interface{} 95 | } 96 | 97 | type NodeCall struct { 98 | Dest *Pid 99 | Req interface{} 100 | CallID int64 101 | Ch chan interface{} 102 | } 103 | 104 | type NodeCallName struct { 105 | Dest string 106 | Req interface{} 107 | CallID int64 108 | Ch chan interface{} 109 | } 110 | 111 | type KMsg struct { 112 | ModID int32 113 | Msg interface{} 114 | } 115 | 116 | type Application interface { 117 | Name() string 118 | Start(bootType AppBootType) *Pid // return the supervisor pid 119 | Stop(stopType AppStopType) 120 | SetEnv(Key string, value interface{}) 121 | GetEnv(key string) interface{} 122 | } 123 | 124 | type appInfo struct { 125 | app Application 126 | pid *Pid 127 | e *list.Element 128 | } 129 | 130 | type AppBootType int 131 | type AppStopType int 132 | 133 | const ( 134 | APP_BOOT_TYPE_START = iota + 1 135 | APP_BOOT_TYPE_RESTART 136 | ) 137 | 138 | const ( 139 | APP_STOP_TYPE_NORMAL = iota + 1 140 | APP_STOP_TYPE_RESTART 141 | ) 142 | 143 | type ConsoleCommand struct { 144 | RecvPid *Pid 145 | CType int32 146 | Command string 147 | } 148 | 149 | type Loop struct{} 150 | 151 | type callMode int 152 | 153 | const ( 154 | call_mode_normal callMode = iota + 1 155 | call_mode_no_reply // 特殊模式,会向内部传递CallInfo 156 | ) 157 | 158 | type Empty struct {} -------------------------------------------------------------------------------- /node/env.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import "github.com/liangmanlin/gootp/args" 4 | 5 | var Env = &NodeEnv{ 6 | PingTick: 60000, 7 | } 8 | 9 | const gmpdPort = 3333 10 | 11 | func init() { 12 | args.FillEvn(Env) 13 | } -------------------------------------------------------------------------------- /node/gmpd/bin/gmpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR=$(cd "$(dirname "$0")"; pwd) 4 | gmpd=gmpd.x64 5 | os=$(uname -i) 6 | if [[ os = i386 ]];then 7 | gmpd=gmpd.x32 8 | fi 9 | $BASEDIR/$gmpd $1 & 10 | -------------------------------------------------------------------------------- /node/gmpd/build.sh: -------------------------------------------------------------------------------- 1 | GOARCH=amd64 go build -o bin/gmpd.x64 main.go 2 | GOARCH=386 go build -o bin/gmpd.x32 main.go -------------------------------------------------------------------------------- /node/gmpd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/gate" 6 | "net" 7 | "os" 8 | "regexp" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | var nodeMap sync.Map 14 | 15 | func main() { 16 | port, _ := strconv.Atoi(os.Args[1]) 17 | ls,err := net.Listen("tcp",fmt.Sprintf("0.0.0.0:%d",port)) 18 | if err != nil { 19 | os.Exit(1) 20 | } 21 | fmt.Printf("start on port :%d\n",port) 22 | for{ 23 | conn,err := ls.Accept() 24 | if err != nil { 25 | return 26 | } 27 | go handle(conn) 28 | } 29 | } 30 | 31 | func handle(conn net.Conn) { 32 | defer func() {recover()}() 33 | c := gate.NewConn(conn) 34 | c.SetHead(2) 35 | defer c.Close() 36 | buf,err := c.Recv(0,0) 37 | if err != nil { 38 | return 39 | } 40 | switch buf[0] { 41 | case '1':// "1" 注册本地端口 42 | exp := regexp.MustCompile(`(\w+@[^:]+):(\d+)`) 43 | sl := exp.FindStringSubmatch(string(buf[2:])) 44 | nodeName := sl[1] 45 | port,_ := strconv.Atoi(sl[2]) 46 | fmt.Printf("node register:%s,port:%d\n",nodeName,port) 47 | nodeMap.Store(nodeName,port) 48 | case '2':// "2" 查询端口 49 | nodeName := string(buf[2:]) 50 | fmt.Printf("query node:%s\n",nodeName) 51 | if p,ok := nodeMap.Load(nodeName);ok{ 52 | port := p.(int) 53 | buf = []byte{uint8(port >> 8),uint8(port)} 54 | c.Send(buf) 55 | }else{ 56 | buf = []byte{0,0} 57 | c.Send(buf) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /node/monitor.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | ) 6 | 7 | type empty struct {} 8 | 9 | var monitorPid *kernel.Pid 10 | 11 | func Monitor(nodeName string,pid *kernel.Pid) { 12 | kernel.Cast(monitorPid,&monitorNode{pid: pid,node: nodeName}) 13 | } 14 | 15 | func DeMonitor(nodeName string,pid *kernel.Pid) { 16 | kernel.Cast(monitorPid,&deMonitorNode{pid: pid,node: nodeName}) 17 | } 18 | 19 | type monitor struct { 20 | node2pid map[string]map[int64]*kernel.Pid 21 | id2node map[int64]map[string]empty 22 | } 23 | 24 | var monitorActor = &kernel.Actor{ 25 | Init: func(context *kernel.Context,pid *kernel.Pid, args ...interface{}) interface{} { 26 | monitorPid = pid 27 | state := monitor{ 28 | node2pid: make(map[string]map[int64]*kernel.Pid), 29 | id2node: make(map[int64]map[string]empty), 30 | } 31 | kernel.ErrorLog("NodeMonitor %s started",pid) 32 | return &state 33 | }, 34 | HandleCast: func(context *kernel.Context, msg interface{}) { 35 | state := context.State.(*monitor) 36 | switch m := msg.(type) { 37 | case *kernel.PidExit: 38 | id := m.Pid.GetID() 39 | if s, ok := state.id2node[id]; ok { 40 | delete(state.id2node, id) 41 | for sv, _ := range s { 42 | if mm, ok := state.node2pid[sv]; ok { 43 | delete(mm, id) 44 | } 45 | } 46 | } 47 | case *monitorNode: 48 | id := m.pid.GetID() 49 | if sl, ok := state.id2node[id]; ok { 50 | sl[m.node] = empty{} 51 | } else { 52 | sl := make(map[string]empty) 53 | state.id2node[id] = sl 54 | sl[m.node] = empty{} 55 | } 56 | if mm, ok := state.node2pid[m.node]; ok { 57 | mm[id] = m.pid 58 | } else { 59 | mm := make(map[int64]*kernel.Pid) 60 | state.node2pid[m.node] = mm 61 | mm[id] = m.pid 62 | } 63 | case *NodeOP: 64 | if mm, ok := state.node2pid[m.Name]; ok { 65 | for _, pid := range mm { 66 | kernel.Cast(pid,m) 67 | } 68 | } 69 | case *deMonitorNode: 70 | if mm, ok := state.node2pid[m.node]; ok { 71 | id := m.pid.GetID() 72 | if _,ok:= mm[id];ok{ 73 | delete(mm,id) 74 | if sl,ok := state.id2node[id];ok{ 75 | delete(sl,m.node) 76 | if len(sl) == 0 { 77 | delete(state.id2node,id) 78 | } 79 | } 80 | } 81 | } 82 | } 83 | }, 84 | HandleCall: func(context *kernel.Context, request interface{}) interface{} { 85 | return nil 86 | }, 87 | Terminate: func(context *kernel.Context, reason *kernel.Terminate) { 88 | 89 | }, 90 | ErrorHandler: func(context *kernel.Context, err interface{}) bool { 91 | return true 92 | }, 93 | } 94 | -------------------------------------------------------------------------------- /node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "fmt" 5 | "github.com/liangmanlin/gootp/args" 6 | "github.com/liangmanlin/gootp/gate" 7 | "github.com/liangmanlin/gootp/gate/pb" 8 | "github.com/liangmanlin/gootp/kernel" 9 | "log" 10 | "net" 11 | "os/exec" 12 | "reflect" 13 | "regexp" 14 | "runtime" 15 | "strconv" 16 | "time" 17 | ) 18 | 19 | var started bool 20 | var coder *pb.Coder 21 | 22 | /* 23 | 需要引入gate模块以及pb模块, 24 | */ 25 | 26 | func Start(nodeName string, cookie string, defs []interface{}) { 27 | nodeStart(nodeName, cookie, defs, true) 28 | } 29 | 30 | func StartHidden(nodeName string, cookie string, defs []interface{}) { 31 | nodeStart(nodeName, cookie, defs, false) 32 | } 33 | 34 | func StartFromCommandLine(defs []interface{}) { 35 | var nodeName,cookie string 36 | var ok bool 37 | if nodeName,ok = args.GetString("name");!ok{ 38 | log.Panic("node miss -name arg") 39 | } 40 | if cookie,ok = args.GetString("cookie");!ok{ 41 | log.Panic("node miss -cookie arg") 42 | } 43 | nodeStart(nodeName, cookie, defs, true) 44 | } 45 | 46 | func nodeStart(nodeName string, cookie string, defs []interface{}, register bool) { 47 | if runtime.GOOS == "windows" { 48 | log.Panicf("not support node on %s", runtime.GOOS) 49 | } 50 | // 先启动本地服务 51 | cmd := exec.Command("gmpd.sh", fmt.Sprintf("%d", gmpdPort)) 52 | err := cmd.Run() 53 | if err != nil { 54 | log.Panic(err.Error()) 55 | } 56 | time.Sleep(100 * time.Millisecond) 57 | checkNodeName(nodeName) 58 | Env.cookie = cookie 59 | // 加载pb 60 | def := []interface{}{ 61 | &Connect{}, 62 | &ConnectSucc{}, 63 | &RpcCallArgs{}, 64 | &kernel.ConsoleCommand{}, 65 | } 66 | def = append(def, defs...) 67 | coder = pb.ParseSlice(def, -1) 68 | Env.nodeName = nodeName 69 | if err = kernel.AppStart(&app{register: register}); err != nil { 70 | panic(err) 71 | } 72 | } 73 | 74 | func start(nodeName string, register bool) { 75 | // 首先验证当前节点是否启动 76 | if ok, c := tryConnect(nodeName, true); ok { 77 | c.Close() 78 | log.Panicf("node:[%s] aready started", nodeName) 79 | } 80 | gate.Start("Node", nodeClient, Env.Port, gate.WithAcceptNum(5)) 81 | addr := gate.GetAddr("Node") 82 | kernel.ErrorLog("Node: [%s] listen on: %s", nodeName, addr) 83 | if !register { 84 | started = true 85 | return 86 | } 87 | // 分析出使用的端口 88 | exp := regexp.MustCompile(`.+:(\d+)`) 89 | m := exp.FindStringSubmatch(addr) 90 | port, _ := strconv.Atoi(m[1]) 91 | var c gate.Conn 92 | var ok bool 93 | if ok, c = connectGmpd("127.0.0.1", true); !ok { 94 | return 95 | } 96 | defer c.Close() 97 | c.SetHead(2) 98 | buf := []byte(fmt.Sprintf("1:%s:%d", nodeName, port)) 99 | if _,err := c.Send(buf);err != nil { 100 | log.Panicf("can not connect to gmpd port: %d", gmpdPort) 101 | } 102 | started = true 103 | } 104 | 105 | func checkNodeName(nodeName string) { 106 | exp := regexp.MustCompile(`\w+@[\w.]+`) 107 | if exp.MatchString(nodeName) { 108 | return 109 | } 110 | log.Panicf("Node name [%s] not allow", nodeName) 111 | } 112 | 113 | func ConnectNode(destNode string) bool { 114 | if !started { 115 | log.Panicf("node is not start") 116 | } 117 | // 判断是否已经连接上 118 | if kernel.IsNodeConnect(destNode) { 119 | return true 120 | } 121 | ok, c := tryConnect(destNode, false) 122 | if !ok { 123 | return false 124 | } 125 | pid, _ := kernel.StartName(destNode, nodeClient, c) 126 | rs, e := kernel.Call(pid, false) 127 | if !rs { 128 | kernel.ErrorLog("connect to Node error:%#v", e) 129 | } 130 | kernel.ErrorLog("connect Node [%s] succ:%v", destNode, e) 131 | return e.(bool) 132 | } 133 | 134 | func tryConnect(destNode string, abort bool) (ok bool,cn gate.Conn) { 135 | exp := regexp.MustCompile(`\w+@([\w.]+)`) 136 | if !exp.MatchString(destNode) { 137 | kernel.ErrorLog("Node name [%s] not allow", destNode) 138 | return false, nil 139 | } 140 | fl := exp.FindStringSubmatch(destNode) 141 | ip := fl[1] 142 | var c gate.Conn 143 | if ok, c = connectGmpd(ip, abort); !ok { 144 | return false, nil 145 | } 146 | c.SetHead(2) 147 | defer c.Close() 148 | _,err := c.Send([]byte(fmt.Sprintf("2:%s", destNode))) 149 | if err != nil { 150 | if abort { 151 | log.Panicf("query port error: %s", err.Error()) 152 | } else { 153 | return false, nil 154 | } 155 | } 156 | var buf []byte 157 | buf,err = c.Recv(0, 0) 158 | if err != nil { 159 | if abort { 160 | log.Panicf("query port error: %s", err.Error()) 161 | } else { 162 | return false, nil 163 | } 164 | } 165 | // 获取真实的端口 166 | port := int(buf[0])<<8 + int(buf[1]) 167 | if port == 0 { 168 | return false, nil 169 | } 170 | conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, port)) 171 | if err != nil { 172 | if !abort { 173 | kernel.ErrorLog("can not connect to Node: %s", destNode) 174 | } 175 | return false, nil 176 | } 177 | return true, gate.NewConn(conn) 178 | } 179 | 180 | func connectGmpd(ip string, abort bool) (bool, gate.Conn) { 181 | failCount := 0 182 | reg: 183 | // 向本地注册名字 184 | conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, gmpdPort)) 185 | if err != nil { 186 | failCount++ 187 | if failCount >= 10 { 188 | if abort { 189 | log.Panicf("can not connect to gmpd %s port: %d", ip,gmpdPort) 190 | } else { 191 | return false, nil 192 | } 193 | } else { 194 | time.Sleep(100 * time.Millisecond) 195 | goto reg 196 | } 197 | } 198 | return true, gate.NewConn(conn) 199 | } 200 | 201 | func GetCookie() string { 202 | return Env.cookie 203 | } 204 | 205 | func IsProtoDef(rType reflect.Type) bool { 206 | return coder.IsDef(rType) 207 | } 208 | -------------------------------------------------------------------------------- /node/node_app.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import "github.com/liangmanlin/gootp/kernel" 4 | 5 | func (a *app) Name() string { 6 | return "NodeApp" 7 | } 8 | 9 | func (a *app) Start(bootType kernel.AppBootType) *kernel.Pid { 10 | kernel.SetSelfNodeName(Env.nodeName) 11 | // 启动监控数 12 | _, supPid := kernel.SupStartChild("kernel", &kernel.SupChild{ChildType: kernel.SupChildTypeSup, Name: "NodeSup"}) 13 | kernel.SupStartChild(supPid, &kernel.SupChild{ChildType: kernel.SupChildTypeWorker, Name: "NodeMonitor", Svr: monitorActor, ReStart: true}) 14 | kernel.SupStartChild(supPid, &kernel.SupChild{ChildType: kernel.SupChildTypeWorker, Name: "RPC", Svr: rpcSvr, ReStart: true}) 15 | start(Env.nodeName,a.register) 16 | return supPid 17 | } 18 | 19 | func (a *app)Stop(stopType kernel.AppStopType){ 20 | 21 | } 22 | 23 | func (a *app)SetEnv(key string,value interface{}){ 24 | 25 | } 26 | 27 | func (a *app)GetEnv(key string)interface{}{ 28 | return nil 29 | } -------------------------------------------------------------------------------- /node/node_client_worker.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/gate" 5 | "github.com/liangmanlin/gootp/gate/pb" 6 | "github.com/liangmanlin/gootp/kernel" 7 | "io" 8 | "log" 9 | "runtime/debug" 10 | "time" 11 | ) 12 | 13 | func recPacket(conn gate.Conn, father *kernel.Pid) { 14 | if err := conn.SetReadDeadline(time.Time{}); err != nil { // 规避超时 15 | kernel.Cast(father, &gate.TcpError{ErrType:gate.ErrDeadLine,Err: err}) 16 | return 17 | } 18 | // 节点连接毕竟少,这里考虑只使用原生库实现 19 | var connNet *gate.ConnNet 20 | var ok bool 21 | if connNet,ok = conn.(*gate.ConnNet);!ok { 22 | log.Panic("node not support other conn") 23 | } 24 | recv := true 25 | for { 26 | rec(connNet,father,&recv) 27 | if !recv { 28 | break // close 29 | } 30 | } 31 | } 32 | 33 | func rec(conn *gate.ConnNet,father *kernel.Pid,recv *bool) { 34 | defer func() { 35 | p := recover() 36 | if p != nil { 37 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 38 | } 39 | }() 40 | c := conn.Conn 41 | head := conn.GetHead() 42 | headBuf := make([]byte, head) 43 | var packSize int 44 | var pack []byte 45 | for { 46 | _, err := io.ReadAtLeast(c, headBuf, head) 47 | if err != nil { 48 | kernel.Cast(father, &gate.TcpError{ErrType:gate.ErrReadErr,Err: err}) 49 | *recv = false 50 | return 51 | } 52 | packSize = gate.ReadHead(head, headBuf) 53 | if packSize > 0 { 54 | if packSize > cap(pack) { 55 | pack = make([]byte, packSize, packSize) // TODO 使用一个单次分配的缓冲区,后续应该慢慢缩小 56 | } else { 57 | pack = pack[:packSize] 58 | } 59 | _, err = io.ReadAtLeast(c, pack, packSize) 60 | if err != nil { 61 | kernel.Cast(father, &gate.TcpError{ErrType:gate.ErrReadErr,Err: err}) 62 | kernel.ErrorLog("%#v",err) 63 | *recv = false 64 | return 65 | } 66 | router(pack, father) 67 | } 68 | } 69 | } 70 | 71 | func router(pack []byte, father *kernel.Pid) { 72 | switch pack[0] { 73 | case M_TYPE_PING: // ping 74 | kernel.Cast(father, ping{}) 75 | case M_TYPE_CAST: //目标消息 76 | index, pid := kernel.DecodePid(pack, 1) 77 | if pid != nil { 78 | _, msg := coder.Decode(pack[index:]) 79 | kernel.Cast(pid, msg) 80 | } 81 | case M_TYPE_CALL: // call 82 | _, callID := pb.DecodeInt64(pack, 1) 83 | index, pid := kernel.DecodePid(pack, 9) 84 | if pid != nil { 85 | _, msg := coder.Decode(pack[index:]) 86 | ch := father.GetChannel() 87 | ci := &kernel.CallInfo{RecCh: ch, CallID: callID, Request: msg} 88 | kernel.Cast(pid, ci) 89 | }else{ 90 | replyNil(callID,father) 91 | } 92 | case M_TYPE_CALL_RESULT: // call result 93 | index, callID := pb.DecodeInt64(pack, 1) 94 | var reply interface{} 95 | if len(pack) > 9 { 96 | _, reply = coder.Decode(pack[index:]) 97 | }else{ 98 | reply = nil 99 | } 100 | r := &call{callID: callID, reply: reply} 101 | kernel.Cast(father, r) 102 | case M_TYPE_CAST_NAME: // cast name 103 | index, name := pb.DecodeString(pack, 1) 104 | if pid := kernel.WhereIs(name); pid != nil { 105 | _, msg := coder.Decode(pack[index:]) 106 | kernel.Cast(pid, msg) 107 | } 108 | case M_TYPE_CALL_NAME: // call name 109 | _, callID := pb.DecodeInt64(pack, 1) 110 | index, name := pb.DecodeString(pack, 9) 111 | if pid := kernel.WhereIs(name); pid != nil { 112 | _, msg := coder.Decode(pack[index:]) 113 | ch := father.GetChannel() 114 | ci := &kernel.CallInfo{RecCh: ch, CallID: callID, Request: msg} 115 | kernel.Cast(pid, ci) 116 | }else{ 117 | replyNil(callID,father) 118 | } 119 | case M_TYPE_CAST_KMSG: 120 | index, pid := kernel.DecodePid(pack, 1) 121 | if pid != nil { 122 | var modID int32 123 | index,modID = pb.DecodeInt32(pack,index) 124 | _, msg := coder.Decode(pack[index:]) 125 | kernel.Cast(pid, &kernel.KMsg{ModID: modID,Msg: msg}) 126 | } 127 | case M_TYPE_CAST_NAME_KMSG: 128 | index, name := pb.DecodeString(pack, 5) 129 | if pid := kernel.WhereIs(name); pid != nil { 130 | _,modID := pb.DecodeInt32(pack,1) 131 | _, msg := coder.Decode(pack[index:]) 132 | kernel.Cast(pid, &kernel.KMsg{ModID: modID,Msg: msg}) 133 | } 134 | case M_TYPE_PONG: 135 | kernel.Cast(father, pong{}) 136 | } 137 | } 138 | 139 | func replyNil(callID int64,father *kernel.Pid) { 140 | buf := []byte{0,0,0,9,3,0,0,0,0,0,0,0,0} 141 | pb.WriteInt64(buf, callID, 5) 142 | kernel.Cast(father,buf) 143 | } 144 | -------------------------------------------------------------------------------- /node/proto_def.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import "github.com/liangmanlin/gootp/kernel" 4 | 5 | type Connect struct { 6 | Time int64 7 | Sign string 8 | Name string 9 | DestName string 10 | } 11 | 12 | type ConnectSucc struct { 13 | Self *kernel.Pid 14 | Name string 15 | } 16 | 17 | type RpcCallArgs struct { 18 | Fun string 19 | Args []byte 20 | } -------------------------------------------------------------------------------- /node/rpc.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "runtime/debug" 6 | ) 7 | 8 | var callBack = make(map[string]*func(interface{})interface{}) 9 | 10 | 11 | // args 是一个协议struct,对应的函数会接收到这个参数 12 | func RpcCall(node interface{}, fun string, argStruct interface{}) (succ bool, result interface{}) { 13 | var dn *kernel.Node 14 | switch n := node.(type) { 15 | case string: 16 | dn = kernel.GetNode(n) 17 | case *kernel.Node: 18 | dn = n 19 | default: 20 | return 21 | } 22 | if dn.Equal(kernel.SelfNode()) { 23 | succ = true 24 | return succ, callFunc(fun, argStruct) 25 | } 26 | var buf []byte 27 | if argStruct != nil { 28 | buf = coder.Encode(argStruct,0) 29 | } 30 | a := &RpcCallArgs{Fun: fun,Args: buf} 31 | return kernel.CallNameNode("RPC",dn,a) 32 | } 33 | 34 | // 注册rpc回调函数 35 | // 不建议运行时动态注册,因为没有加锁,建议项目启动前注册函数 36 | func RpcRegister(fun string,function RpcFunc) { 37 | callBack[fun] = function 38 | } 39 | 40 | func callFunc(fun string, argStruct interface{}) interface{} { 41 | defer func() { 42 | p := recover() 43 | if p != nil { 44 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 45 | } 46 | }() 47 | if f,ok:= callBack[fun];ok{ 48 | return (*f)(argStruct) 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /node/rpc_server.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | ) 6 | 7 | var rpcSvr = kernel.NewActor( 8 | kernel.InitFunc(func(ctx *kernel.Context, pid *kernel.Pid, args ...interface{}) interface{} { 9 | ctx.ChangeCallMode() 10 | return nil 11 | }), 12 | kernel.HandleCallFunc(func(context *kernel.Context, request interface{}) interface{} { 13 | go rpcHandleCall(request.(*kernel.CallInfo)) 14 | return nil 15 | })) 16 | 17 | func rpcHandleCall(call *kernel.CallInfo) { 18 | var result interface{} 19 | switch r := call.Request.(type) { 20 | case *RpcCallArgs: 21 | var args interface{} 22 | if len(r.Args) > 0 { 23 | _, args = coder.Decode(r.Args) 24 | } else { 25 | args = nil 26 | } 27 | result = callFunc(r.Fun, args) 28 | } 29 | kernel.Reply(call.RecCh,call.CallID,result) 30 | } -------------------------------------------------------------------------------- /node/type.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | ) 6 | 7 | type NodeEnv struct { 8 | cookie string 9 | nodeName string 10 | PingTick int64 `command:"ping_tick"`// 毫秒 11 | Port int `command:"node_port"` //可以指定一个端口,而不是随机 12 | } 13 | 14 | type ping struct {} 15 | type pong struct {} 16 | 17 | type call struct { 18 | callID int64 19 | reply interface{} 20 | } 21 | 22 | type monitorNode struct { 23 | pid *kernel.Pid 24 | node string 25 | } 26 | 27 | type deMonitorNode struct { 28 | pid *kernel.Pid 29 | node string 30 | } 31 | 32 | type NodeOP struct { 33 | Name string 34 | OP OPType 35 | } 36 | 37 | type OPType = int 38 | 39 | type RpcFunc *func(interface{})interface{} 40 | 41 | const ( 42 | OPConnect OPType = 1 << iota 43 | OPDisConnect 44 | ) 45 | 46 | const ( 47 | M_TYPE_PING byte = iota 48 | M_TYPE_CAST 49 | M_TYPE_CALL 50 | M_TYPE_CALL_RESULT 51 | M_TYPE_CAST_NAME 52 | M_TYPE_CALL_NAME 53 | M_TYPE_CAST_KMSG 54 | M_TYPE_CAST_NAME_KMSG 55 | M_TYPE_PONG 56 | ) 57 | 58 | type app struct { 59 | register bool 60 | } 61 | -------------------------------------------------------------------------------- /pfun/ptr.go: -------------------------------------------------------------------------------- 1 | package pfun 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | func KindFun(v *reflect.Type) PtrFun { 9 | if (*v).Kind() == reflect.Ptr { 10 | *v = (*v).Elem() 11 | return getPtrPtr 12 | } 13 | return getPtr 14 | } 15 | 16 | func getPtr(pointer unsafe.Pointer, offset uintptr) unsafe.Pointer { 17 | return unsafe.Pointer(uintptr(pointer) + offset) 18 | } 19 | 20 | func getPtrPtr(pointer unsafe.Pointer, offset uintptr) unsafe.Pointer { 21 | return *(*unsafe.Pointer)(unsafe.Pointer(uintptr(pointer) + offset)) 22 | } 23 | 24 | var _i = int(0) 25 | 26 | const CpuSize = unsafe.Sizeof(&_i) // 预先计算出int指针的大小 27 | 28 | func Ptr(i interface{}) unsafe.Pointer { 29 | return *(*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(&i)) + CpuSize)) // TODO 这里是根据interface 的内部实现写死的地址 30 | } 31 | 32 | func SliceSize(ptr unsafe.Pointer) int { 33 | return *(*int)(unsafe.Pointer(uintptr(ptr) + CpuSize)) // slice的size是第二个字段 34 | } 35 | 36 | func GetInt32(ptr unsafe.Pointer,offset uintptr) int32 { 37 | return *(*int32)(unsafe.Pointer(uintptr(ptr)+offset)) 38 | } 39 | 40 | func GetInt64(ptr unsafe.Pointer,offset uintptr) int64 { 41 | return *(*int64)(unsafe.Pointer(uintptr(ptr)+offset)) 42 | } -------------------------------------------------------------------------------- /pfun/type.go: -------------------------------------------------------------------------------- 1 | package pfun 2 | 3 | import "unsafe" 4 | 5 | type PtrFun = func(pointer unsafe.Pointer, offset uintptr) unsafe.Pointer 6 | -------------------------------------------------------------------------------- /rand/rand.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/pfun" 5 | "math/rand" 6 | "reflect" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | var defaultR = Rand{rand.New(rand.NewSource(time.Now().UnixNano()))} 12 | 13 | func Random(min, max int32) int32 { 14 | return defaultR.Random(min,max) 15 | } 16 | 17 | func Int32(n int32) int32 { 18 | return defaultR.Int32(n) 19 | } 20 | 21 | func Int64(n int64) int64 { 22 | return defaultR.Int64(n) 23 | } 24 | 25 | // 给定一个范围,随机若干个数 26 | func RandomNum(min, max, num int32) []int32 { 27 | return defaultR.RandomNum(min,max,num) 28 | } 29 | 30 | /* 31 | 从一个slice里面,根据权重,随机出value 32 | 33 | 可以重复的参数比不可重复效率略高 34 | 35 | list := []struct{int32,interface{}} 36 | 37 | 切记不要修改返回值里面的内容 38 | */ 39 | func RandomQSlice(list interface{}, num int32, canRepeated bool) interface{} { 40 | return defaultR.RandomQSlice(list,num,canRepeated) 41 | } 42 | 43 | func New() Rand { 44 | return Rand{rand.New(rand.NewSource(time.Now().UnixNano()))} 45 | } 46 | 47 | func (r Rand) Random(min, max int32) int32 { 48 | return r.rand.Int31n(max-min+1) + min 49 | } 50 | 51 | func (r Rand) Int32(n int32) int32 { 52 | return r.rand.Int31n(n) 53 | } 54 | 55 | func (r Rand) Int64(n int64) int64 { 56 | return r.rand.Int63n(n) 57 | } 58 | 59 | // 给定一个范围,随机若干个数 60 | func (r Rand) RandomNum(min, max, num int32) []int32 { 61 | dc := max - min + 1 62 | if dc < num { 63 | num = dc 64 | } 65 | rs := make([]int32, num) 66 | m := make(map[int32]mv, num+1) 67 | var k, l int32 68 | m[k] = mv{min, max} 69 | for i := int32(0); i < num; i++ { 70 | l = int32(len(m)) 71 | k = r.rand.Int31n(l) 72 | tmp := m[k] 73 | if tmp.min == tmp.max { 74 | rs[i] = tmp.min 75 | l-- 76 | m[k] = m[l] 77 | delete(m, l) 78 | } else { 79 | v := r.rand.Int31n(tmp.max-tmp.min+1) + tmp.min 80 | rs[i] = v 81 | if v == tmp.min { 82 | tmp.min++ 83 | m[k] = tmp 84 | } else if v == tmp.max { 85 | tmp.max-- 86 | m[k] = tmp 87 | } else { 88 | m[l] = mv{v + 1, tmp.max} 89 | tmp.max = v - 1 90 | m[k] = tmp 91 | } 92 | } 93 | } 94 | return rs 95 | } 96 | 97 | /* 98 | 从一个slice里面,根据权重,随机出value 99 | 100 | 可以重复的参数比不可重复效率略高 101 | 102 | list := []struct{int32,interface{}} 103 | 104 | 切记不要修改返回值里面的内容 105 | */ 106 | func (r Rand) RandomQSlice(list interface{}, num int32, canRepeated bool) interface{} { 107 | num2 := int(num) 108 | vf := reflect.ValueOf(list) 109 | if vf.Kind() != reflect.Slice { 110 | return nil 111 | } 112 | ptr := pfun.Ptr(list) 113 | size := pfun.SliceSize(ptr) 114 | rsf := vf.Type().Elem() 115 | inc := rsf.Size() 116 | sliceDataPtr := *(*unsafe.Pointer)(ptr) 117 | getPtrF := pfun.KindFun(&rsf) 118 | var total int32 = 1 119 | // 复制一个数组,规避远数组被修改 120 | ql := make([]qi, size) 121 | for i := 0; i < size; i++ { 122 | p := getPtrF(sliceDataPtr, inc*uintptr(i)) 123 | q := *(*int32)(p) 124 | ql[i] = qi{q, i} 125 | total += q 126 | } 127 | vvt := rsf.Field(1).Type 128 | rs := reflect.MakeSlice(reflect.SliceOf(vvt), num2, num2) 129 | for i := 0; i < num2; i++ { 130 | random := r.rand.Int31n(total) 131 | for j := 0; j < size; j++ { 132 | tmp := ql[j] 133 | q := tmp.q 134 | if q >= random { 135 | f := vf.Index(tmp.idx) 136 | if f.Kind() == reflect.Ptr { 137 | f = f.Elem() 138 | } 139 | rs.Index(i).Set(f.Field(1)) 140 | if !canRepeated { 141 | total -= q 142 | size-- 143 | ql[j] = ql[size] 144 | } 145 | break 146 | } 147 | random -= q 148 | } 149 | } 150 | return rs.Interface() 151 | } 152 | 153 | -------------------------------------------------------------------------------- /rand/rand_test.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type qv struct { 9 | Q int32 10 | V *data 11 | } 12 | 13 | type data struct { 14 | I int32 15 | } 16 | 17 | func TestRand(t *testing.T) { 18 | l := []*qv{{10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 19 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 20 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 21 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 22 | } 23 | r := New() 24 | fmt.Println(r.RandomQSlice(l, 2, true).([]*data)[1]) 25 | fmt.Println(r.RandomQSlice(l, 4, false)) 26 | fmt.Println(r.RandomNum(1, 100, 10)) 27 | } 28 | 29 | func BenchmarkRand_RandomQSlice_Single(b *testing.B) { 30 | l := []*qv{{10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 31 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 32 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 33 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 34 | } 35 | r := New() 36 | b.ResetTimer() 37 | for i := 0; i < b.N; i++ { 38 | r.RandomQSlice(l, 4, false) 39 | } 40 | } 41 | 42 | func BenchmarkRand_RandomQSlice_Repeated(b *testing.B) { 43 | l := []*qv{{10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 44 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 45 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 46 | {10, &data{1}}, {20, &data{2}}, {10, &data{3}},{20,&data{4}}, 47 | } 48 | r := New() 49 | b.ResetTimer() 50 | for i := 0; i < b.N; i++ { 51 | r.RandomQSlice(l, 4, true) 52 | } 53 | } -------------------------------------------------------------------------------- /rand/type.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import "math/rand" 4 | 5 | type Rand struct { 6 | rand *rand.Rand 7 | } 8 | 9 | type mv struct { 10 | min, max int32 11 | } 12 | 13 | type qi struct { 14 | q int32 15 | idx int 16 | } 17 | -------------------------------------------------------------------------------- /ringbuffer/ring_buffer.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | import ( 4 | "runtime" 5 | "sync/atomic" 6 | ) 7 | 8 | const is_expand = 1 << 31 9 | 10 | /* 11 | 暂时没有实现,不要使用 12 | 13 | 通用ring buffer,这里是实现无锁队列 14 | 因为每个队列成员都多了一个uint32,所以内存会比单线程的大 15 | 和channel不同,会实现动态扩容,不会通知消费者有消息(TODO 后续可以研究实现) 16 | */ 17 | 18 | type wait struct{} 19 | 20 | type bufCache struct { 21 | count uint32 // 这里使用一个标记,告诉消费者,这里是否开始设置值 22 | value interface{} // TODO 这里可以换成unsafe.Pointer 23 | } 24 | 25 | type RingBuffer struct { 26 | tail uint32 27 | head uint32 28 | cap uint32 29 | max uint32 30 | cache []bufCache 31 | } 32 | 33 | // 参数必须是2的指数倍 34 | func NewRingBuffer(size, maxSize int) *RingBuffer { 35 | if !isPower(size) { 36 | return nil 37 | } 38 | s := &RingBuffer{ 39 | cap: uint32(size), 40 | cache: make([]bufCache, size, size), 41 | max: uint32(maxSize), 42 | } 43 | return s 44 | } 45 | 46 | func (r *RingBuffer) Put(v interface{}) { 47 | for { 48 | tail := atomic.LoadUint32(&r.tail) 49 | // 借用了tail的最高位,标记当前是否正在扩容 50 | if (tail & is_expand) > 0 { 51 | runtime.Gosched() // TODO 可以考虑不让出cpu 52 | continue 53 | } 54 | head := atomic.LoadUint32(&r.head) 55 | if (head & is_expand) > 0 { 56 | runtime.Gosched() // TODO 可以考虑不让出cpu 57 | continue 58 | } 59 | cap := atomic.LoadUint32(&r.cap) 60 | next := (tail + 1) & (cap - 1) 61 | if next == head { 62 | // 满了,扩容 63 | //r.expand(tail, next, cap) 64 | continue 65 | } 66 | tmp := &r.cache[tail] 67 | if !atomic.CompareAndSwapUint32(&r.tail, tail, next) { 68 | runtime.Gosched() // TODO 可以考虑不让出cpu 69 | continue 70 | } 71 | for { 72 | if atomic.LoadUint32(&tmp.count) == 0 { 73 | tmp.value = v 74 | atomic.StoreUint32(&tmp.count, 1) 75 | break 76 | } 77 | } 78 | break 79 | } 80 | } 81 | 82 | func (r *RingBuffer) Pop() (rs interface{}) { 83 | for { 84 | tail := atomic.LoadUint32(&r.tail) // 消费者应该只有一个,所以这里先读取tail索引是可以的 85 | if (tail & is_expand) > 0 { 86 | tail = tail - is_expand 87 | } 88 | head := atomic.LoadUint32(&r.head) 89 | // 判断是否扩容,通常应该很快,所以可能下一刻就好了 90 | if (head & is_expand) > 0 { 91 | continue 92 | } 93 | if head == tail { 94 | continue 95 | } 96 | // 这里只考虑单一消费者的情况 97 | cap := atomic.LoadUint32(&r.cap) 98 | newHead := (head + 1) & (cap - 1) 99 | tmp := &r.cache[head] 100 | if !atomic.CompareAndSwapUint32(&r.head, head, newHead) { 101 | continue 102 | } 103 | for { 104 | // 判断是否已经写入 105 | if atomic.LoadUint32(&tmp.count) == 1 { 106 | rs = tmp.value 107 | tmp.value = nil // gc 108 | atomic.StoreUint32(&tmp.count, 0) // TODO 这里是不是可以不需要使用 atomic 109 | break 110 | } 111 | } 112 | return rs 113 | } 114 | } 115 | 116 | func (r *RingBuffer) Cache() []bufCache { 117 | return r.cache 118 | } 119 | 120 | func (r *RingBuffer) checkComplete(start, end uint32) { 121 | var tmp *bufCache 122 | check: 123 | for i := start; i < end; i++ { 124 | tmp = &r.cache[i] 125 | // 判断是否已经写入 126 | if atomic.LoadUint32(&tmp.count) == 0 { 127 | runtime.Gosched() 128 | start = i 129 | goto check 130 | } 131 | } 132 | } 133 | 134 | func (r *RingBuffer) expand(tail, next, cap uint32) { 135 | // 设定一个最大值,通常都不应该这么大,如果无限增大,可能会使得系统oom 136 | if cap >= r.max { 137 | runtime.Gosched() 138 | return 139 | } 140 | newTail := tail | is_expand 141 | if atomic.CompareAndSwapUint32(&r.tail, tail, newTail) { 142 | var head uint32 143 | // 需要锁住读取 144 | for { 145 | head = atomic.LoadUint32(&r.head) 146 | // 如果消费者没有阻塞,可能还在消费 147 | if next != head { 148 | atomic.StoreUint32(&r.tail, tail) 149 | goto finish 150 | } 151 | newHead := head | is_expand 152 | if atomic.CompareAndSwapUint32(&r.head, head, newHead) { 153 | break 154 | } 155 | } 156 | // 判断当前位置是否已经写入完成 157 | if head != tail { 158 | if tail > head { 159 | r.checkComplete(head, tail) 160 | } else { 161 | r.checkComplete(0, tail) 162 | r.checkComplete(head, r.cap) 163 | } 164 | } 165 | // 开始扩容 166 | cap := r.cap 167 | newCap := cap << 1 // 2倍扩容 168 | cache := make([]bufCache, newCap, newCap) 169 | // 复制数据 170 | if head > tail { 171 | idx := cap - head 172 | newTail = idx + tail 173 | copy(cache, r.cache[head:]) 174 | copy(cache[idx:], r.cache[0:tail]) 175 | } else { 176 | newTail = tail - head 177 | copy(cache, r.cache[head:tail]) 178 | } 179 | r.cache = cache 180 | atomic.StoreUint32(&r.cap, newCap) 181 | atomic.StoreUint32(&r.head, 0) 182 | atomic.StoreUint32(&r.tail, newTail) 183 | } 184 | finish: 185 | } 186 | -------------------------------------------------------------------------------- /ringbuffer/single_ring_buffer.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | /* 4 | 通用ring buffer,主要是想解决链表带来的垃圾产生,以及减少go的gc对象 5 | */ 6 | 7 | // 这里是单线程使用的,无锁 8 | type SingleRingBuffer struct { 9 | head int 10 | tail int 11 | cap int 12 | maxCap int // 当队列空闲的时候,会判断是否超过最大值 13 | cache []interface{} // TODO 这里可以考虑使用unsafe 14 | } 15 | 16 | // 参数必须是2的指数倍 17 | func NewSingleRingBuffer(size, maxSize int) *SingleRingBuffer { 18 | if !isPower(size) || !isPower(maxSize) { 19 | return nil 20 | } 21 | s := &SingleRingBuffer{ 22 | cap: size, 23 | maxCap: maxSize, 24 | cache: make([]interface{}, size, size), 25 | } 26 | return s 27 | } 28 | 29 | func (s *SingleRingBuffer) Size() int { 30 | if s.head > s.tail { 31 | return s.cap - s.head + s.tail 32 | } 33 | return s.tail - s.head 34 | } 35 | 36 | // 放入队列,如果满了,会扩容 37 | func (s *SingleRingBuffer) Put(value interface{}) { 38 | next := (s.tail + 1) & (s.cap-1) 39 | if next == s.head { 40 | // 满了,扩容 41 | s.expand() 42 | // 需要从新计算 43 | next = (s.tail + 1) & (s.cap-1) 44 | } 45 | s.cache[s.tail] = value 46 | s.tail = next 47 | } 48 | 49 | func (s *SingleRingBuffer) Pop() interface{} { 50 | if s == nil || s.tail == s.head { 51 | return nil 52 | } 53 | rs := s.cache[s.head] 54 | s.cache[s.head] = nil // gc 55 | s.head = (s.head + 1) & (s.cap-1) 56 | if s.tail == s.head && s.cap > s.maxCap { 57 | // 空了,缩小 58 | s.narrow() 59 | } 60 | return rs 61 | } 62 | 63 | func (s *SingleRingBuffer) expand() { 64 | newCap := s.cap * 2 65 | cache := make([]interface{}, newCap, newCap) 66 | var size int 67 | // 拷贝到新cache 68 | if s.head > s.tail { 69 | idx := s.cap - s.head 70 | size = idx + s.tail 71 | copy(cache, s.cache[s.head:]) 72 | copy(cache[idx:], s.cache[0:s.tail]) 73 | } else { 74 | size = s.tail - s.head 75 | copy(cache, s.cache[s.head:s.tail]) 76 | } 77 | s.head = 0 78 | s.tail = size 79 | s.cache = cache 80 | s.cap = newCap 81 | } 82 | 83 | func (s *SingleRingBuffer) narrow() { 84 | s.cache = make([]interface{}, s.maxCap, s.maxCap) 85 | s.head = 0 86 | s.tail = 0 87 | } 88 | 89 | func isPower(v int) bool { 90 | return v > 0 && v&(v-1) == 0 91 | } 92 | -------------------------------------------------------------------------------- /shell/README.md: -------------------------------------------------------------------------------- 1 | # 一个用于`kernel`的交互式命令行工具 2 | 3 | 你只需要拷贝`bin`目录下的文件到你的`path`目录,或者当前目录 4 | 5 | ```bash 6 | gshell -node game@127.0.0.1 -cookie 6d27544c07937e4a7fab8123291cc4df 7 | ``` 8 | 9 | 即可启动一个交互式命令行窗口 10 | 11 | `kernel`原有的web控制台已经被这个替代。 12 | 13 | 如过你只想执行命令 14 | 15 | ```bash 16 | ## 停止服务 17 | gshell -node game@127.0.0.1 -cookie 6d27544c07937e4a7fab8123291cc4df -cmd stop 18 | ``` -------------------------------------------------------------------------------- /shell/build.sh: -------------------------------------------------------------------------------- 1 | GOARCH=amd64 go build -o bin/gshell gshell.go 2 | GOARCH=386 go build -o bin/gshellx32 gshell.go -------------------------------------------------------------------------------- /shell/gshell.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "github.com/liangmanlin/readline" 8 | "github.com/liangmanlin/gootp/gutil" 9 | "github.com/liangmanlin/gootp/kernel" 10 | "github.com/liangmanlin/gootp/kernel/kct" 11 | "github.com/liangmanlin/gootp/node" 12 | "io" 13 | "log" 14 | "os" 15 | "sort" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | func main() { 21 | kernel.Env.WriteLogStd = false 22 | kernel.Env.LogPath = "" 23 | var nodeName, cookie, cmd string 24 | flag.StringVar(&nodeName, "node", "", "请输入节点名") 25 | flag.StringVar(&cookie, "cookie", "", "请输入cookie") 26 | flag.StringVar(&cmd, "cmd", "", "可选,脱离交互界面直接执行") 27 | flag.Parse() 28 | if nodeName == "" || cookie == "" { 29 | flag.Usage() 30 | os.Exit(1) 31 | } 32 | var live = true 33 | if cmd != "" { 34 | live = false 35 | } 36 | kernel.KernelStart(func() { 37 | node.StartHidden("debug-"+strconv.Itoa(os.Getpid())+"-"+nodeName, cookie, nil) 38 | if !node.ConnectNode(nodeName) { 39 | println("cannot connect " + nodeName + " with cookie " + cookie) 40 | os.Exit(1) 41 | } 42 | var l *readline.Instance 43 | // 启动一个专门用来显示的进程 44 | echoActor := kernel.NewActor( 45 | kernel.HandleCastFunc(func(ctx *kernel.Context, msg interface{}) { 46 | switch m := msg.(type) { 47 | case *kernel.ConsoleCommand: 48 | println(m.Command) 49 | case *node.NodeOP: 50 | if m.OP == node.OPDisConnect { 51 | println("node: " + nodeName + " disconnect") 52 | l.Close() 53 | } 54 | } 55 | }), 56 | ) 57 | recPid, _ := kernel.Start(echoActor) 58 | if !live { 59 | _, rs := kernel.CallNameNode("console", nodeName, &kernel.ConsoleCommand{RecvPid: recPid, Command: cmd}) 60 | switch r := rs.(type) { 61 | case *kernel.ConsoleCommand: 62 | fmt.Print(r.Command) 63 | } 64 | os.Exit(0) 65 | } 66 | node.Monitor(nodeName, recPid) 67 | // 获取所有命令 68 | _, rs := kernel.CallNameNode("console", nodeName, &kernel.ConsoleCommand{CType: 3}) 69 | help, pl, needConfirm := buildHelp(rs.(*kernel.ConsoleCommand).Command) 70 | go func() { 71 | defer kernel.InitStop() 72 | var completer = readline.NewPrefixCompleter(pl...) 73 | var err error 74 | l, err = readline.NewEx(&readline.Config{ 75 | Prompt: "(" + nodeName + ")\033[31m>\033[0m ", 76 | AutoComplete: completer, 77 | InterruptPrompt: "^C", 78 | EOFPrompt: "exit", 79 | HistorySearchFold: true, 80 | FuncFilterInputRune: filterInput, 81 | }) 82 | if err != nil { 83 | panic(err) 84 | } 85 | defer l.Close() 86 | log.SetOutput(l.Stderr()) 87 | println("\nwelcome to use gshell\n") 88 | println("command: help for more information\n") 89 | println("To exit this debug: \u001B[31mCtrl-C\u001B[0m\n") 90 | for { 91 | line, err := l.Readline() 92 | if err == readline.ErrInterrupt { 93 | if len(line) == 0 { 94 | break 95 | } else { 96 | continue 97 | } 98 | } else if err == io.EOF { 99 | break 100 | } 101 | start: 102 | line = strings.TrimSpace(line) 103 | switch line { 104 | case "": 105 | case "help": 106 | println("commands:\n" + strings.Join(help, "\n")) 107 | default: 108 | C := kct.CutWith(line,' ') 109 | if confirmCommit, ok := needConfirm[C[0]]; ok { 110 | println("ensure to " + confirmCommit + ": " + line + "? [y/n]") 111 | read, err := l.ReadlineWithDefault("n") 112 | if err != nil || read != "y" { 113 | println("cancel " + confirmCommit) 114 | break 115 | } 116 | } 117 | if ok, result := kernel.CallNameNode("console", nodeName, &kernel.ConsoleCommand{RecvPid: recPid, Command: line}); ok { 118 | switch c := result.(type) { 119 | case *kernel.ConsoleCommand: 120 | if c.CType == 1 { 121 | line = c.Command 122 | goto start 123 | } else { 124 | println(c.Command) 125 | } 126 | default: 127 | fmt.Fprintf(l.Stderr(), "%#v", result) 128 | } 129 | } else { 130 | fmt.Fprintf(l.Stderr(), "%#v", result) 131 | } 132 | } 133 | } 134 | }() 135 | }, nil) 136 | } 137 | 138 | func filterInput(r rune) (rune, bool) { 139 | switch r { 140 | // block CtrlZ feature 141 | case readline.CharCtrlZ: 142 | return r, false 143 | } 144 | return r, true 145 | } 146 | 147 | func buildHelp(jsonStr string) ([]string, []readline.PrefixCompleterInterface, map[string]string) { 148 | var commands map[string]map[string]string 149 | _ = json.Unmarshal([]byte(jsonStr), &commands) 150 | // 生成 151 | pl := []readline.PrefixCompleterInterface{ 152 | readline.PcItem("help"), 153 | } 154 | var help []string 155 | var list []string 156 | var maxSize, maxArgs int32 157 | for k, v := range commands { 158 | list = append(list, k) 159 | maxSize = gutil.MaxInt32(maxSize, int32(len(k))) 160 | maxArgs = gutil.MaxInt32(maxArgs, int32(len(v["args"]))) 161 | } 162 | sort.Strings(list) 163 | var needConfirm = make(map[string]string) 164 | for _, k := range list { 165 | pl = append(pl, readline.PcItem(k)) 166 | cm := commands[k] 167 | help = append(help, fmt.Sprintf(" %-"+strconv.Itoa(int(maxSize))+"s %-"+strconv.Itoa(int(maxArgs))+"s \u001B[35m# %s\033[0m", 168 | k, cm["args"], cm["commit"])) 169 | if confirm, ok := cm["confirm"]; ok { 170 | needConfirm[k] = confirm 171 | } 172 | } 173 | return help, pl, needConfirm 174 | } 175 | -------------------------------------------------------------------------------- /timer/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "github.com/liangmanlin/gootp/kernel" 5 | "reflect" 6 | "runtime/debug" 7 | "unsafe" 8 | ) 9 | 10 | func NewTimerPointer() unsafe.Pointer { 11 | t := NewTimer() 12 | return unsafe.Pointer(t) 13 | } 14 | 15 | func NewTimer() *Timer { 16 | return &Timer{m: make(map[TimerKey]*timer)} 17 | } 18 | 19 | func Start(timer unsafe.Pointer, key TimerKey, inv, times int32, f interface{}, args ...interface{}) { 20 | t := (*Timer)(timer) 21 | t.Add(key, inv, times, f, args...) 22 | } 23 | 24 | func Loop(timer unsafe.Pointer, state interface{}, now2 int64) { 25 | (*Timer)(timer).Loop(state, now2) 26 | } 27 | 28 | // inv <= 0 表示永久定时器 29 | func (t *Timer) Add(key TimerKey, inv, times int32, f interface{}, args ...interface{}) { 30 | now2 := kernel.Now2() 31 | tm := &timer{ 32 | time: now2 + int64(inv), 33 | times: times, 34 | f: f, 35 | arg: args, 36 | } 37 | if t.isLooping { 38 | t.tmp[key] = tm 39 | } else { 40 | t.m[key] = tm 41 | } 42 | } 43 | 44 | func (t *Timer)Del(key TimerKey) { 45 | delete(t.m,key) 46 | } 47 | 48 | // TODO 后续考虑增加多级时间分类,减少遍历长度 49 | func (t *Timer) Loop(state interface{}, now2 int64) { 50 | t.isLooping = true 51 | for k, tm := range t.m { 52 | if now2 >= tm.time { 53 | tm.time += int64(tm.inv) 54 | // 激活回调函数 55 | if tm.times > 0 { 56 | tm.times-- 57 | if tm.times == 0 { 58 | // 先删除 59 | delete(t.m, k) 60 | } 61 | } 62 | actFun(state, tm.f, tm.arg) 63 | } 64 | } 65 | if len(t.tmp) > 0 { 66 | for k, tm := range t.tmp { 67 | t.m[k] = tm 68 | delete(t.tmp, k) 69 | } 70 | } 71 | t.isLooping = false 72 | } 73 | 74 | func actFun(state interface{}, f interface{}, arg []interface{}) { 75 | defer func() { 76 | p := recover() 77 | if p != nil { 78 | kernel.ErrorLog("catch error:%s,Stack:%s", p, debug.Stack()) 79 | } 80 | }() 81 | // 浪费一些性能,使用反射执行 82 | vf := reflect.ValueOf(f) 83 | if vf.Kind() == reflect.Ptr{ 84 | vf = vf.Elem() 85 | } 86 | size := len(arg) + 1 87 | in := make([]reflect.Value, size) 88 | in[0] = reflect.ValueOf(state) 89 | for i := 0; i < len(arg); i++ { 90 | in[i+1] = reflect.ValueOf(arg[i]) 91 | } 92 | vf.Call(in) 93 | } 94 | -------------------------------------------------------------------------------- /timer/type.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | type TimerKey struct { 4 | Key int32 5 | ID int32 6 | } 7 | 8 | type Timer struct { 9 | m map[TimerKey]*timer 10 | tmp map[TimerKey]*timer 11 | isLooping bool 12 | } 13 | 14 | type timer struct { 15 | time int64 16 | times int32 17 | inv int32 18 | f interface{} 19 | arg []interface{} 20 | } 21 | --------------------------------------------------------------------------------