├── .gitignore
├── LICENSE
├── README.md
├── cham
├── addr.go
├── const.go
├── master.go
├── mq.go
├── service.go
├── service_test.go
├── smain.go
├── timer.go
└── timer_test.go
├── etc
└── config.json
├── example
└── question
│ ├── db
│ └── db.go
│ ├── lobby
│ └── lobby.go
│ ├── main.go
│ ├── model
│ └── model.go
│ ├── protocol
│ ├── protocol.go
│ └── protocol_test.go
│ ├── room
│ ├── room.go
│ └── roommanger.go
│ ├── test.html
│ ├── test
│ └── test.html
│ ├── usermanager
│ ├── user
│ │ └── user.go
│ └── userManager.go
│ └── util
│ └── util.go
├── lib
├── database
│ ├── db.go
│ └── db_test.go
├── filter
│ ├── wordfilter.go
│ └── wordfilter_test.go
├── helper
│ ├── helper.go
│ └── helper_test.go
├── lru
│ ├── lru.go
│ └── lru_test.go
└── zset
│ ├── skiplist.c
│ ├── skiplist.h
│ ├── zset.go
│ └── zset_test.go
└── service
├── debug
├── debug.go
└── debug_test.go
├── gate
├── gate.go
├── gate_test.go
├── test.html
└── websocket.go
├── log
├── log.go
├── log_test.go
└── logservice.go
└── multicast
├── channel.go
├── multicast.go
└── mutlticase_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 skycrab
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cham
2 | Golang lightweight online game framework
3 |
4 | skynet(https://github.com/cloudwu/skynet) 的Golang版本
5 |
--------------------------------------------------------------------------------
/cham/addr.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "sync/atomic"
5 | )
6 |
7 | type Address uint32
8 |
9 | var START_ADDR uint32 = 1
10 |
11 | func GenAddr() Address {
12 | return Address(atomic.AddUint32(&START_ADDR, 1))
13 | }
14 |
15 | func (addr Address) GetService() *Service {
16 | return master.GetService(addr)
17 | }
18 |
--------------------------------------------------------------------------------
/cham/const.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | type NULL struct{}
4 |
5 | const (
6 | PTYPE_GO uint8 = iota //service -> service
7 | PTYPE_MULTICAST //multicast -> service
8 | PTYPE_CLIENT //client -> gate
9 | PTYPE_RESPONSE //gate -> client
10 | )
11 |
12 | var (
13 | NULLVALUE = NULL{}
14 | NORET []interface{}
15 | )
16 |
--------------------------------------------------------------------------------
/cham/master.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | // "runtime"
5 | "strings"
6 | "sync"
7 | )
8 |
9 | const (
10 | DEFAULT_SERVICE_SIZE = 64
11 | )
12 |
13 | var master *Master
14 |
15 | type Master struct {
16 | sync.RWMutex
17 | services map[Address]*Service
18 | nameservices map[string][]*Service
19 | }
20 |
21 | func NewMaster() *Master {
22 | master := new(Master)
23 | master.services = make(map[Address]*Service, DEFAULT_SERVICE_SIZE)
24 | master.nameservices = make(map[string][]*Service, DEFAULT_SERVICE_SIZE)
25 | return master
26 | }
27 |
28 | func (m *Master) Register(s *Service) bool {
29 | m.Lock()
30 | defer m.Unlock()
31 | m.services[s.Addr] = s
32 | if ns, ok := m.nameservices[s.Name]; ok {
33 | ns = append(ns, s)
34 | return false
35 | } else {
36 | ss := []*Service{s}
37 | m.nameservices[s.Name] = ss
38 | return true
39 | }
40 | }
41 |
42 | func (m *Master) Unregister(s *Service) bool {
43 | m.Lock()
44 | defer m.Unlock()
45 | delete(m.services, s.Addr)
46 | nss := m.nameservices[s.Name]
47 | var idx int = -1
48 | for i, ns := range nss {
49 | if ns.Addr == s.Addr {
50 | idx = i
51 | break
52 | }
53 | }
54 | if idx == -1 {
55 | return false
56 | } else {
57 | m.nameservices[s.Name] = append(nss[:idx], nss[idx+1:]...)
58 | return true
59 | }
60 | }
61 |
62 | func (m *Master) GetService(query interface{}) *Service {
63 | switch v := query.(type) {
64 | case Address:
65 | return m.services[v]
66 | case string:
67 | ss := m.nameservices[v]
68 | if len(ss) > 0 {
69 | return ss[0]
70 | }
71 | return nil
72 | case *Service:
73 | return v
74 | default:
75 | panic("not reachable")
76 | }
77 | }
78 |
79 | func (m *Master) UniqueService(name string) *Service {
80 | m.RLock()
81 | ns := m.nameservices[name]
82 | m.RUnlock()
83 | if len(ns) > 1 {
84 | panic("unique service duplicate")
85 | } else if len(ns) == 1 {
86 | return ns[0]
87 | } else {
88 | return nil
89 | }
90 | }
91 |
92 | func (m *Master) AllService() map[Address]*Service {
93 | m.RLock()
94 | defer m.RUnlock()
95 | return master.services
96 | }
97 |
98 | func DumpService() string {
99 | services := master.AllService()
100 | info := make([]string, 0, len(services))
101 | for _, s := range services {
102 | info = append(info, s.String())
103 | }
104 | return strings.Join(info, "\r\n")
105 | }
106 |
107 | func init() {
108 | master = NewMaster()
109 | }
110 |
--------------------------------------------------------------------------------
/cham/mq.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | const (
9 | DEFAULT_QUEUE_SIZE = 64
10 | )
11 |
12 | type Queue struct {
13 | sync.Mutex
14 | head int
15 | tail int
16 | buf []*Msg
17 | }
18 |
19 | func NewQueue() *Queue {
20 | q := new(Queue)
21 | q.buf = make([]*Msg, DEFAULT_QUEUE_SIZE)
22 | return q
23 | }
24 |
25 | func (q *Queue) String() string {
26 | return fmt.Sprintf("QUEUE[%d-%d]", q.head, q.tail)
27 | }
28 |
29 | func (q *Queue) Length() int {
30 | q.Lock()
31 | head := q.head
32 | tail := q.tail
33 | size := cap(q.buf)
34 | q.Unlock()
35 |
36 | if head <= tail {
37 | return tail - head
38 | }
39 | return tail + size - head
40 | }
41 |
42 | func (q *Queue) Push(msg *Msg) {
43 | q.Lock()
44 | q.buf[q.tail] = msg
45 | q.tail++
46 |
47 | if q.tail >= cap(q.buf) {
48 | q.tail = 0
49 | }
50 |
51 | if q.head == q.tail {
52 | q.expand()
53 | }
54 | q.Unlock()
55 | }
56 |
57 | func (q *Queue) Pop() (msg *Msg) {
58 | q.Lock()
59 | if q.head != q.tail {
60 | msg = q.buf[q.head]
61 | q.head++
62 | if q.head >= cap(q.buf) {
63 | q.head = 0
64 | }
65 | }
66 | q.Unlock()
67 | return
68 | }
69 |
70 | func (q *Queue) expand() {
71 | newbuf := make([]*Msg, cap(q.buf)*2)
72 | copy(newbuf, q.buf[q.head:cap(q.buf)])
73 | q.head = 0
74 | q.tail = cap(q.buf)
75 | q.buf = newbuf
76 | }
77 |
--------------------------------------------------------------------------------
/cham/service.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "sync"
7 | "sync/atomic"
8 | )
9 |
10 | const (
11 | DEFAULT_SERVICE_WORKER int = 1
12 | )
13 |
14 | var (
15 | serviceMutex *sync.Mutex
16 | )
17 |
18 | type Dispatch func(session int32, source Address, ptype uint8, args ...interface{}) []interface{}
19 | type Start func(service *Service, args ...interface{}) Dispatch
20 |
21 | type Msg struct {
22 | source Address
23 | session int32
24 | ptype uint8
25 | args interface{}
26 | }
27 |
28 | type Service struct {
29 | session int32
30 | Name string
31 | Addr Address
32 | queue *Queue
33 | closed bool
34 | quit chan struct{}
35 | rlock *sync.Mutex
36 | rcond *sync.Cond
37 | pending map[int32]chan *Msg
38 | dispatchs map[uint8]Dispatch
39 | }
40 |
41 | func Ret(args ...interface{}) []interface{} {
42 | return args
43 | }
44 |
45 | func NewMsg(source Address, session int32, ptype uint8, args interface{}) *Msg {
46 | return &Msg{source, session, ptype, args}
47 | }
48 |
49 | //args[0] is worker number, args[1:] will pass to start
50 | func NewService(name string, start Start, args ...interface{}) *Service {
51 | service := new(Service)
52 | service.session = 0
53 | service.Name = name
54 | service.Addr = GenAddr()
55 | service.queue = NewQueue()
56 | service.closed = false
57 | service.quit = make(chan struct{})
58 | service.rlock = new(sync.Mutex)
59 | service.rcond = sync.NewCond(service.rlock)
60 | service.pending = make(map[int32]chan *Msg)
61 |
62 | var n int = 1
63 | if len(args) > 0 {
64 | n = args[0].(int)
65 | if n <= 0 {
66 | n = DEFAULT_SERVICE_WORKER
67 | }
68 | args = args[1:]
69 | }
70 |
71 | service.dispatchs = map[uint8]Dispatch{PTYPE_GO: start(service, args...)}
72 |
73 | // start may failed, user can invoke service.Stop(), so check service.closed flag
74 | if service.closed {
75 | return nil
76 | }
77 | master.Register(service)
78 |
79 | for i := 0; i < n; i++ {
80 | go service.Start(i)
81 | }
82 |
83 | return service
84 | }
85 |
86 | //create or return already name
87 | func UniqueService(name string, start Start, args ...interface{}) *Service {
88 | serviceMutex.Lock()
89 | defer serviceMutex.Unlock()
90 |
91 | s := master.UniqueService(name)
92 | if s == nil {
93 | s = NewService(name, start, args...)
94 | }
95 | return s
96 | }
97 |
98 | func (s *Service) Start(i int) {
99 | seq := strconv.Itoa(i)
100 | fmt.Println(s.String(), "-- ", seq, " --", "running")
101 | for {
102 | select {
103 | case <-s.quit:
104 | fmt.Println(s.String(), "-- ", seq, " --", "stop")
105 | return
106 | default:
107 | msg := s.queue.Pop()
108 | if msg == nil {
109 | s.rlock.Lock()
110 | s.rcond.Wait()
111 | s.rlock.Unlock()
112 | } else {
113 | s.dispatchMsg(msg)
114 | }
115 | }
116 | }
117 | }
118 |
119 | func (s *Service) dispatchMsg(msg *Msg) {
120 | if msg.session == 0 {
121 | s.dispatchs[msg.ptype](msg.session, msg.source, msg.ptype, msg.args.([]interface{})...)
122 | } else if msg.session > 0 {
123 | result := s.dispatchs[msg.ptype](msg.session, msg.source, msg.ptype, msg.args.([]interface{})...)
124 | resp := &Msg{s.Addr, -msg.session, msg.ptype, result}
125 | dest := msg.source.GetService()
126 | dest.Push(resp)
127 | } else {
128 | session := -msg.session
129 | done := s.pending[session]
130 | delete(s.pending, session)
131 | done <- msg
132 | }
133 | }
134 |
135 | func (s *Service) RegisterProtocol(ptype uint8, start Start, args ...interface{}) {
136 | if _, ok := s.dispatchs[ptype]; ok {
137 | panic(s.String() + "duplicate register protocol")
138 | }
139 | s.dispatchs[ptype] = start(s, args...)
140 | }
141 |
142 | func (s *Service) send(query interface{}, ptype uint8, session int32, args ...interface{}) chan *Msg {
143 | if session != 0 {
144 | session = atomic.AddInt32(&s.session, 1)
145 | }
146 | msg := &Msg{s.Addr, session, ptype, args}
147 | dest := master.GetService(query)
148 | dest.Push(msg)
149 | var done chan *Msg
150 | if session != 0 { // need reply
151 | done = make(chan *Msg, 1)
152 | s.pending[session] = done
153 | }
154 |
155 | return done
156 | }
157 |
158 | // wait response, query can service name/addr/service
159 | func (s *Service) Call(query interface{}, ptype uint8, args ...interface{}) []interface{} {
160 | m := <-s.send(query, ptype, 1, args...)
161 | return m.args.([]interface{})
162 | }
163 |
164 | // no reply
165 | func (s *Service) Notify(query interface{}, ptype uint8, args ...interface{}) {
166 | s.send(query, ptype, 0, args...)
167 | }
168 |
169 | func (s *Service) NotifySelf(ptype uint8, args ...interface{}) {
170 | s.send(s, ptype, 0, args...)
171 | }
172 |
173 | // no wait response
174 | func (s *Service) Send(query interface{}, ptype uint8, args ...interface{}) chan *Msg {
175 | return s.send(query, ptype, 1, args...)
176 | }
177 |
178 | func (s *Service) Push(msg *Msg) {
179 | s.queue.Push(msg)
180 | s.rcond.Signal()
181 | }
182 |
183 | func (s *Service) Stop() bool {
184 | if !s.closed {
185 | s.closed = true
186 | close(s.quit)
187 | s.rcond.Signal()
188 | master.Unregister(s)
189 | return true
190 | }
191 | return false
192 | }
193 |
194 | func (s *Service) String() string {
195 | return fmt.Sprintf("SERVICE [addr->%d, name->%s] ", s.Addr, s.Name)
196 | }
197 |
198 | func (s *Service) Status() int {
199 | return s.queue.Length()
200 | }
201 |
202 | func Redirect(source Address, msg *Msg) {
203 | dest := source.GetService()
204 | dest.Push(msg)
205 | }
206 |
207 | func init() {
208 | serviceMutex = new(sync.Mutex)
209 | }
210 |
--------------------------------------------------------------------------------
/cham/service_test.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "fmt"
5 | // "runtime"
6 | // "sync"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func helloStart(service *Service, args ...interface{}) Dispatch {
12 | return func(session int32, source Address, ptype uint8, args ...interface{}) []interface{} {
13 | fmt.Println(session, source, args)
14 | time.Sleep(time.Second * 4)
15 | cmd := args[0].(string)
16 | if cmd == "Hello" {
17 | return Ret("World")
18 | } else if cmd == "Notify" {
19 | fmt.Println("no reply")
20 | return Ret(nil)
21 | } else {
22 | return Ret("Error")
23 | }
24 | }
25 | }
26 |
27 | func init() {
28 | // runtime.GOMAXPROCS(4)
29 | }
30 |
31 | func worldStart(service *Service, args ...interface{}) Dispatch {
32 | other := args[0].(string)
33 | fmt.Println("worldStart:", other)
34 | return func(session int32, source Address, ptypt uint8, args ...interface{}) []interface{} {
35 | return Ret("999")
36 | }
37 | }
38 |
39 | func TestService(t *testing.T) {
40 | hello := NewService("Hello", helloStart, 4) // 4 worker of goroutine
41 | world := NewService("World", worldStart, 1, "other args") // 1 worker of goroutine, "other args" will pass to worldStart
42 | for i := 0; i < 5; i++ {
43 | // world.Call("Hello", "Hello")
44 | go func() {
45 | fmt.Println(world.Call(hello, PTYPE_GO, "Hello")) // worker < 5 : there is a request will wait
46 | }()
47 | // world.Send(hello, PTYPE_GO, "send")
48 | }
49 | time.Sleep(time.Second * 100)
50 | }
51 |
--------------------------------------------------------------------------------
/cham/smain.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | const (
8 | CHAM_STOP uint8 = iota
9 | )
10 |
11 | var Main *Service
12 |
13 | var DTimer *Timer
14 |
15 | var stop chan NULL
16 |
17 | func mainStart(service *Service, args ...interface{}) Dispatch {
18 | return func(session int32, source Address, ptype uint8, args ...interface{}) []interface{} {
19 | switch ptype {
20 | case CHAM_STOP:
21 | stop <- NULLVALUE
22 | }
23 | return NORET
24 | }
25 | }
26 |
27 | func Run() {
28 | <-stop
29 | }
30 |
31 | func init() {
32 | Main = NewService("main", mainStart)
33 | DTimer = NewWheelTimer(time.Millisecond * 10)
34 | go func() { DTimer.Start() }()
35 | }
36 |
--------------------------------------------------------------------------------
/cham/timer.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "container/list"
5 | "fmt"
6 | "sync"
7 | "time"
8 | )
9 |
10 | //referer https://github.com/cloudwu/skynet/blob/master/skynet-src/skynet_timer.c
11 |
12 | const (
13 | TIME_NEAR_SHIFT = 8
14 | TIME_NEAR = 1 << TIME_NEAR_SHIFT
15 | TIME_LEVEL_SHIFT = 6
16 | TIME_LEVEL = 1 << TIME_LEVEL_SHIFT
17 | TIME_NEAR_MASK = TIME_NEAR - 1
18 | TIME_LEVEL_MASK = TIME_LEVEL - 1
19 | )
20 |
21 | type Timer struct {
22 | near [TIME_NEAR]*list.List
23 | t [4][TIME_LEVEL]*list.List
24 | sync.Mutex
25 | time uint32
26 | tick time.Duration
27 | quit chan struct{}
28 | }
29 |
30 | type Node struct {
31 | expire uint32
32 | period time.Duration
33 | C chan time.Time
34 | }
35 |
36 | func (n *Node) String() string {
37 | return fmt.Sprintf("Node:expire,%d", n.expire)
38 | }
39 |
40 | func NewWheelTimer(d time.Duration) *Timer {
41 | t := new(Timer)
42 | t.time = 0
43 | t.tick = d
44 | t.quit = make(chan struct{})
45 |
46 | var i, j int
47 | for i = 0; i < TIME_NEAR; i++ {
48 | t.near[i] = list.New()
49 | }
50 |
51 | for i = 0; i < 4; i++ {
52 | for j = 0; j < TIME_LEVEL; j++ {
53 | t.t[i][j] = list.New()
54 | }
55 | }
56 |
57 | return t
58 | }
59 |
60 | func (t *Timer) addNode(n *Node) {
61 | expire := n.expire
62 | current := t.time
63 | if (expire | TIME_NEAR_MASK) == (current | TIME_NEAR_MASK) {
64 | t.near[expire&TIME_NEAR_MASK].PushBack(n)
65 | } else {
66 | var i uint32
67 | var mask uint32 = TIME_NEAR << TIME_LEVEL_SHIFT
68 | for i = 0; i < 3; i++ {
69 | if (expire | (mask - 1)) == (current | (mask - 1)) {
70 | break
71 | }
72 | mask <<= TIME_LEVEL_SHIFT
73 | }
74 |
75 | t.t[i][(expire>>(TIME_NEAR_SHIFT+i*TIME_LEVEL_SHIFT))&TIME_LEVEL_MASK].PushBack(n)
76 | }
77 |
78 | }
79 |
80 | func (t *Timer) NewTicker(d time.Duration) *Node {
81 | n := new(Node)
82 | n.C = make(chan time.Time, 1)
83 | n.period = d
84 | t.Lock()
85 | n.expire = uint32(d/t.tick) + t.time
86 | t.addNode(n)
87 | t.Unlock()
88 | return n
89 | }
90 |
91 | func (t *Timer) NewTimer(d time.Duration) *Node {
92 | n := new(Node)
93 | n.C = make(chan time.Time, 1)
94 | n.period = 0
95 | t.Lock()
96 | n.expire = uint32(d/t.tick) + t.time
97 | t.addNode(n)
98 | t.Unlock()
99 | return n
100 | }
101 |
102 | func (t *Timer) String() string {
103 | return fmt.Sprintf("Timer:time:%d, tick:%s", t.time, t.tick)
104 | }
105 |
106 | func (t *Timer) dispatchList(front *list.Element) {
107 | now := time.Now()
108 | for e := front; e != nil; e = e.Next() {
109 | node := e.Value.(*Node)
110 | select {
111 | case node.C <- now:
112 | default:
113 | }
114 | if node.period > 0 {
115 | t.Lock()
116 | node.expire = uint32(node.period/t.tick) + t.time
117 | t.addNode(node)
118 | t.Unlock()
119 | }
120 | }
121 | }
122 |
123 | func (t *Timer) moveList(level, idx int) {
124 | vec := t.t[level][idx]
125 | front := vec.Front()
126 | vec.Init()
127 | for e := front; e != nil; e = e.Next() {
128 | node := e.Value.(*Node)
129 | t.addNode(node)
130 | }
131 | }
132 |
133 | func (t *Timer) shift() {
134 | t.Lock()
135 | var mask uint32 = TIME_NEAR
136 | t.time++
137 | ct := t.time
138 | if ct == 0 {
139 | t.moveList(3, 0)
140 | } else {
141 | time := ct >> TIME_NEAR_SHIFT
142 | var i int = 0
143 | for (ct & (mask - 1)) == 0 {
144 | idx := int(time & TIME_LEVEL_MASK)
145 | if idx != 0 {
146 | t.moveList(i, idx)
147 | break
148 | }
149 | mask <<= TIME_LEVEL_SHIFT
150 | time >>= TIME_LEVEL_SHIFT
151 | i++
152 | }
153 | }
154 | t.Unlock()
155 | }
156 |
157 | func (t *Timer) execute() {
158 | t.Lock()
159 | idx := t.time & TIME_NEAR_MASK
160 | vec := t.near[idx]
161 | if vec.Len() > 0 {
162 | front := vec.Front()
163 | vec.Init()
164 | t.Unlock()
165 | // dispatch_list don't need lock
166 | t.dispatchList(front)
167 | return
168 | }
169 |
170 | t.Unlock()
171 | }
172 |
173 | func (t *Timer) update() {
174 | // try to dispatch timeout 0 (rare condition)
175 | t.execute()
176 |
177 | // shift time first, and then dispatch timer message
178 | t.shift()
179 |
180 | t.execute()
181 |
182 | }
183 |
184 | func (t *Timer) Start() {
185 | tick := time.NewTicker(t.tick)
186 | defer tick.Stop()
187 | for {
188 | select {
189 | case <-tick.C:
190 | t.update()
191 | case <-t.quit:
192 | return
193 | }
194 | }
195 | }
196 |
197 | func (t *Timer) Stop() {
198 | close(t.quit)
199 | }
200 |
--------------------------------------------------------------------------------
/cham/timer_test.go:
--------------------------------------------------------------------------------
1 | package cham
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestTimer(t *testing.T) {
10 | timer := NewWheelTimer(time.Millisecond * 10)
11 | t1 := timer.NewTimer(time.Second * 10)
12 | t2 := timer.NewTicker(time.Second * 2)
13 | go timer.Start()
14 | // var now time.Time
15 | fmt.Println(time.Now())
16 | for {
17 | select {
18 | case <-t1.C:
19 | fmt.Println("t1,", time.Now())
20 | case <-t2.C:
21 | fmt.Println("t2", time.Now())
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/etc/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "GOMAXPROCS":8
3 | }
--------------------------------------------------------------------------------
/example/question/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "cham/lib/database"
5 | "cham/lib/lru"
6 | "database/sql"
7 | // "fmt"
8 | _ "github.com/go-sql-driver/mysql"
9 | )
10 |
11 | var DbCache *QueryCache
12 |
13 | type QueryCache struct {
14 | conn *sql.DB
15 | Db *database.Database
16 | queryset map[string]*lru.Cache
17 | expires chan lru.Value
18 | updateModel chan database.Model
19 | }
20 |
21 | func New(conn *sql.DB) *QueryCache {
22 | q := &QueryCache{
23 | conn: conn,
24 | Db: database.New(conn),
25 | queryset: make(map[string]*lru.Cache),
26 | expires: make(chan lru.Value, 1000),
27 | updateModel: make(chan database.Model, 1000),
28 | }
29 | go q.Run()
30 | return q
31 | }
32 |
33 | func (q *QueryCache) Run() {
34 | for {
35 | select {
36 | case m := <-q.updateModel:
37 | q.Db.UpdatePk(m)
38 | case v := <-q.expires:
39 | q.Db.UpdatePk(v.(database.Model))
40 | }
41 | }
42 | }
43 |
44 | func (q *QueryCache) UpdateModel(m database.Model) {
45 | q.updateModel <- m
46 | }
47 |
48 | func (q *QueryCache) Register(m database.Model, maxEntries int) {
49 | name := m.TableName()
50 | if _, ok := q.queryset[name]; ok {
51 | panic("duplicate queryset table:" + name)
52 | }
53 | q.queryset[name] = lru.New(maxEntries, func(key lru.Key, value lru.Value) {
54 | q.expires <- value
55 | })
56 | }
57 |
58 | func (q *QueryCache) GetPk(m database.Model) (database.Model, interface{}, error) {
59 | name := m.TableName()
60 | if _, ok := q.queryset[name]; !ok {
61 | panic("please Register first table:" + name)
62 | }
63 | _, value := database.GetPkValue(m)
64 | cache := q.queryset[name]
65 | v, ok := cache.Get(value)
66 | if !ok {
67 | err := q.Db.GetPk(m)
68 | if err != nil {
69 | return nil, nil, err
70 | }
71 | cache.Add(value, m)
72 | return m, value, nil
73 | }
74 | vv := v.(database.Model)
75 | return vv, value, nil
76 | }
77 |
78 | func init() {
79 | conn, err := sql.Open("mysql", "root:dajidan2bu2@tcp(10.9.28.162:3306)/brain")
80 | if err != nil {
81 | panic("mysql connect error," + err.Error())
82 | }
83 | DbCache = New(conn)
84 | }
85 |
--------------------------------------------------------------------------------
/example/question/lobby/lobby.go:
--------------------------------------------------------------------------------
1 | package lobby
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | "fmt"
7 | "question/room"
8 | "sync"
9 | )
10 |
11 | type Lobby struct {
12 | roomManager room.RoomManager
13 | rooms map[room.Roomid]*cham.Service
14 | }
15 |
16 | func newLobby() *Lobby {
17 | lobby := &Lobby{
18 | roomManager: room.NewRoomManager(),
19 | rooms: make(map[room.Roomid]*cham.Service),
20 | }
21 | return lobby
22 | }
23 |
24 | func (lobby *Lobby) Allocate() {
25 | roomId := lobby.roomManager.NextRoom()
26 | roomName := fmt.Sprintf("room-%d", int(roomId))
27 | lobby.rooms[roomId] = cham.NewService(roomName, room.Start, 1, roomId)
28 | }
29 |
30 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
31 | log.Infoln("New Service ", lobby.String())
32 | lobby := newLobby()
33 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/question/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/debug"
6 | "cham/service/gate"
7 | "cham/service/log"
8 | "fmt"
9 | // "question/lobby"
10 | "question/protocol"
11 | "question/usermanager"
12 | "question/usermanager/user"
13 | )
14 |
15 | func brokerStart(service *cham.Service, args ...interface{}) cham.Dispatch {
16 | log.Infoln("New Service ", service.String())
17 | um := args[0].(*usermanager.UserManager)
18 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
19 | cmd := args[0].(uint8)
20 | switch cmd {
21 | case user.DELETE_USER:
22 | openid := args[1].(string)
23 | um.Delete(openid)
24 | }
25 | return cham.NORET
26 | }
27 | }
28 |
29 | func brokerDispatch(service *cham.Service, args ...interface{}) cham.Dispatch {
30 | um := args[0].(*usermanager.UserManager)
31 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
32 | sessionid := args[0].(uint32)
33 | gt := args[1].(uint8)
34 | switch gt {
35 | // case gate.OnOpen:
36 | // fmt.Println("OnOpen ", sessionid)
37 | // case gate.OnClose:
38 | // fmt.Println("OnClose ", sessionid, args[2:])
39 | // case gate.OnPong:
40 | // fmt.Println("OnPong ", sessionid, args[2])
41 | case gate.OnMessage:
42 | data := args[2].([]byte)
43 | fmt.Println("OnMessage", sessionid, string(data))
44 | name, request := protocol.Decode(data)
45 | um.Handle(sessionid, name, request)
46 | }
47 | return cham.NORET
48 | }
49 | }
50 |
51 | func main() {
52 | gs := cham.NewService("gate", gate.Start, 8)
53 | um := usermanager.New()
54 | bs := cham.NewService("broker", brokerStart, 8, um)
55 | bs.RegisterProtocol(cham.PTYPE_CLIENT, brokerDispatch, um)
56 | bs.Call(gs, cham.PTYPE_GO, gate.OPEN, gate.NewConf("127.0.0.1:9998", 0, "/ws"))
57 | cham.NewService("debug", debug.Start, 1, "127.0.0.1:8888")
58 | // lobby := cham.NewService("lobby", lobby.Start)
59 | cham.Run()
60 | }
61 |
--------------------------------------------------------------------------------
/example/question/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "question/db"
5 | "time"
6 | )
7 |
8 | //UserModel
9 | type UserModel struct {
10 | ID int `json:"id"`
11 | Openid string `json:"openid" pk:"true"`
12 | Name string `json:"name"`
13 | Headimgurl string `json:"headimgurl"`
14 | Sex int `json:"sex"`
15 | LastLogin time.Time `json:"lastlogin" field:"last_login"`
16 | }
17 |
18 | func (u *UserModel) TableName() string {
19 | return "question_user"
20 | }
21 |
22 | //register all model
23 | func init() {
24 | dc := db.DbCache
25 | dc.Register(&UserModel{}, 1)
26 | }
27 |
--------------------------------------------------------------------------------
/example/question/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "cham/service/log"
5 | "encoding/json"
6 | "reflect"
7 | "strings"
8 | )
9 |
10 | var protocols = make(map[string]reflect.Type)
11 |
12 | //request head
13 | type Request struct {
14 | Service string `json:"service"`
15 | Data json.RawMessage `json:"data"`
16 | }
17 |
18 | //response head
19 | type Response struct {
20 | Code int `json:"code"` // 0 correct
21 | Service string `json:"service"`
22 | Result interface{} `json:"result"`
23 | }
24 |
25 | //
26 | //
27 | //concrete protocol
28 |
29 | type Login struct {
30 | Openid string `json:"openid"`
31 | }
32 |
33 | //to common, websocket and tcp use the same
34 | type HeartBeat struct {
35 | }
36 |
37 | func Decode(data []byte) (string, interface{}) {
38 | request := &Request{}
39 | err := json.Unmarshal(data, request)
40 | if err != nil {
41 | log.Errorln("Request Decode Header error:", err.Error())
42 | return "", nil
43 | }
44 | if p, ok := protocols[request.Service]; ok {
45 | np := reflect.New(p).Interface()
46 | err := json.Unmarshal(request.Data, np)
47 | if err != nil {
48 | log.Errorln("Request Decode ", request.Service, "error:", err.Error())
49 | } else {
50 | return request.Service, np
51 | }
52 | }
53 | return "", nil
54 |
55 | }
56 |
57 | //result can ptr or value
58 | func Encode(code int, result interface{}) []byte {
59 | t := reflect.TypeOf(result)
60 | if t.Kind() == reflect.Ptr {
61 | t = t.Elem()
62 | }
63 | response := Response{code, strings.ToLower(t.Name()), result}
64 | b, e := json.Marshal(response)
65 | if e != nil {
66 | log.Errorln("Response Encode error, ", e.Error())
67 | return nil
68 | }
69 | return b
70 | }
71 |
72 | func register(p interface{}) {
73 | v := reflect.ValueOf(p)
74 | name := strings.ToLower(v.Elem().Type().Name())
75 | protocols[name] = v.Type().Elem()
76 | }
77 |
78 | func init() {
79 | protos := []interface{}{&Login{}}
80 | for _, p := range protos {
81 | register(p)
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/example/question/protocol/protocol_test.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestProto(t *testing.T) {
9 | d := []byte(`{"service":"user","data":{"openid":"lwy"}}`)
10 | fmt.Println(Decode(d))
11 | }
12 |
--------------------------------------------------------------------------------
/example/question/room/room.go:
--------------------------------------------------------------------------------
1 | package room
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | "question/model"
7 | )
8 |
9 | type Room struct {
10 | rid Roomid
11 | owner *user.User
12 | players []*user.User
13 | }
14 |
15 | func newRoom(rid Roomid) *Room {
16 | return &Room{
17 | rid: rid,
18 | owner: nil,
19 | players: make([]*user.User, 0, 2),
20 | }
21 | }
22 |
23 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
24 | log.Infoln("New Service ", room.String())
25 | room := newRoom(args[0].(Roomid))
26 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/question/room/roommanger.go:
--------------------------------------------------------------------------------
1 | package room
2 |
3 | import (
4 | "sync/atomic"
5 | )
6 |
7 | type Roomid uint32
8 |
9 | type RoomManager struct {
10 | current Roomid
11 | }
12 |
13 | func NewRoomManager() *RoomManager {
14 | return &RoomManager{0}
15 | }
16 |
17 | func (rm *RoomManager) NextRoom() Roomid {
18 | return Roomid(atomic.AddUint32(&rm.current, 1))
19 | }
20 |
--------------------------------------------------------------------------------
/example/question/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebSocket example
5 |
6 |
7 |
29 |
30 |
--------------------------------------------------------------------------------
/example/question/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebSocket example
5 |
6 |
7 |
27 |
28 |
--------------------------------------------------------------------------------
/example/question/usermanager/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | // "fmt"
7 | "question/db"
8 | "question/model"
9 | "question/protocol"
10 | "time"
11 | )
12 |
13 | const (
14 | CHANGE_SESSION uint8 = iota
15 | DELETE_USER
16 | )
17 |
18 | //user
19 | type User struct {
20 | *model.UserModel
21 | service *cham.Service
22 | session uint32
23 | activeTime time.Time
24 | }
25 |
26 | func newUser(service *cham.Service, u *model.UserModel, session uint32) (*User, error) {
27 | m, _, err := db.DbCache.GetPk(u)
28 | if err != nil {
29 | return nil, err
30 | }
31 | user := &User{m.(*model.UserModel), service, session, time.Now()}
32 | go user.Run()
33 | return user, nil
34 | }
35 |
36 | func (user *User) Save() {
37 | db.DbCache.UpdateModel(user.UserModel)
38 | }
39 |
40 | func (user *User) Response(data []byte) error {
41 | result := user.service.Call("gate", cham.PTYPE_RESPONSE, user.session, data)
42 | err := result[0]
43 | if err != nil {
44 | return err.(error)
45 | }
46 | return nil
47 | }
48 |
49 | func (user *User) Kill() {
50 | user.service.Call("broker", cham.PTYPE_GO, DELETE_USER, user.Openid)
51 | user.service.Stop()
52 | user.Save()
53 | }
54 |
55 | func (user *User) Run() {
56 | t := cham.DTimer.NewTicker(time.Minute * 5)
57 | t2 := cham.DTimer.NewTicker(time.Second * 5)
58 | heart := protocol.HeartBeat{}
59 | lost := 0
60 | for {
61 | select {
62 | case t := <-t.C:
63 | log.Infoln("every 5 Minute check start, openid:", user.Openid, " time:", t)
64 | user.Save()
65 | case t2 := <-t2.C:
66 | log.Infoln("every 5 Second heart beat, time:", t2)
67 | err := user.Response(protocol.Encode(0, heart))
68 | if err != nil {
69 | lost++
70 | log.Infoln("user heart beat lost,openid:", user.Openid)
71 | } else {
72 | lost = 0
73 | }
74 | // fmt.Println("---------", lost)
75 | if lost >= 5 {
76 | log.Infoln("heart beat lost more 5 time, close user service, openid:", user.Openid)
77 | user.Kill()
78 | return
79 | }
80 | }
81 | }
82 | }
83 |
84 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
85 | log.Infoln("New Service ", service.String())
86 | openid, session := args[0].(string), args[1].(uint32)
87 | user, err := newUser(service, &model.UserModel{Openid: openid}, session)
88 | if err != nil {
89 | log.Errorln("Service ", service.String(), "init error,", err.Error())
90 | service.Stop()
91 | }
92 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
93 | cmd := args[0].(uint8)
94 | switch cmd {
95 | case CHANGE_SESSION:
96 | user.session = args[1].(uint32)
97 | }
98 |
99 | return cham.NORET
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/example/question/usermanager/userManager.go:
--------------------------------------------------------------------------------
1 | package usermanager
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | "fmt"
7 | "question/protocol"
8 | "question/usermanager/user"
9 | "sync"
10 | )
11 |
12 | //manager
13 | type UserManager struct {
14 | sync.RWMutex
15 | users map[string]*cham.Service
16 | }
17 |
18 | func New() *UserManager {
19 | return &UserManager{users: make(map[string]*cham.Service)}
20 | }
21 |
22 | func (um *UserManager) Add(request *protocol.Login, session uint32) {
23 | openid := request.Openid
24 | um.RLock()
25 | if us, ok := um.users[openid]; ok {
26 | um.RUnlock()
27 | us.NotifySelf(cham.PTYPE_GO, user.CHANGE_SESSION, session)
28 | return
29 | }
30 | um.RUnlock()
31 | log.Infoln("new user,openid:", openid)
32 | us := cham.NewService(fmt.Sprintf("user-%s", openid), user.Start, 1, openid, session)
33 | if us == nil {
34 | log.Errorln("new user failed, openid:", openid)
35 | return
36 | }
37 | um.Lock()
38 | um.users[openid] = us
39 | um.Unlock()
40 | }
41 |
42 | func (um *UserManager) Delete(openid string) {
43 | um.Lock()
44 | delete(um.users, openid)
45 | um.Unlock()
46 | }
47 |
48 | func (um *UserManager) Handle(session uint32, protocolName string, request interface{}) {
49 | fmt.Printf("protocol: %#v\n", request)
50 | if protocolName == "login" {
51 | um.Add(request.(*protocol.Login), session)
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/example/question/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | const (
8 | TIME_FORMATER = "2006-01-02 15:04:05"
9 | )
10 |
11 | func ParseTime(t string) time.Time {
12 | tt, err := time.Parse(TIME_FORMATER, t)
13 | if err != nil {
14 | tt = time.Unix(0, 0)
15 | }
16 | return tt
17 | }
18 |
19 | func FormatTime(t time.Time) string {
20 | return t.Format(TIME_FORMATER)
21 | }
22 |
--------------------------------------------------------------------------------
/lib/database/db.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "bytes"
5 | "database/sql"
6 | "fmt"
7 | "reflect"
8 | "strings"
9 | "time"
10 | )
11 |
12 | const (
13 | tablenameFunc = "TableName"
14 | fieldTag = "field"
15 | pkTag = "pk" // primary key
16 | attrTag = "attr"
17 | autoAttr = "auto" //auto increamemt,e.g. id
18 | )
19 |
20 | const (
21 | TIME_FORMATER = "2006-01-02 15:04:05"
22 | )
23 |
24 | var (
25 | TIME_PTR_TYPE = reflect.TypeOf(&time.Time{})
26 | )
27 |
28 | type Model interface {
29 | TableName() string
30 | }
31 |
32 | type Database struct {
33 | db *sql.DB
34 | Debug bool
35 | }
36 |
37 | type Scanner interface {
38 | Scan(dest ...interface{}) error
39 | }
40 |
41 | func New(db *sql.DB) *Database {
42 | return &Database{db, false}
43 | }
44 |
45 | func (d *Database) Close() {
46 | d.db.Close()
47 | }
48 |
49 | //sql provide row and rows
50 | func scan(row Scanner, v reflect.Value, args []interface{}) error {
51 | //need support time.Time,save time.Time index and value
52 | keys := make([]int, 0)
53 | values := make([]*string, 0)
54 | for i := 0; i < len(args); i++ {
55 | vv := v.Field(i).Addr()
56 | t := vv.Type()
57 | if t == TIME_PTR_TYPE {
58 | var tmp string
59 | args[i] = &tmp
60 | keys = append(keys, i)
61 | values = append(values, &tmp)
62 | } else {
63 | args[i] = vv.Interface()
64 | }
65 |
66 | }
67 | err := row.Scan(args...)
68 | if err == nil {
69 | for i := 0; i < len(keys); i++ {
70 | t, err := time.Parse(TIME_FORMATER, *values[i])
71 | if err == nil {
72 | v.Field(keys[i]).Set(reflect.ValueOf(t))
73 | }
74 | }
75 | }
76 | return err
77 | }
78 |
79 | //need value to reduce once reflect expense
80 | // d.Get(&User{}, "openid", "123456")
81 | func (d *Database) Get(m Model, field string, value interface{}) error {
82 | q, n := query(m, field, "", 0)
83 | if d.Debug {
84 | fmt.Println(q, " [", value, "]")
85 | }
86 | row := d.db.QueryRow(q, value)
87 | v := reflect.ValueOf(m).Elem()
88 | args := make([]interface{}, n)
89 | // for i := 0; i < n; i++ {
90 | // args[i] = v.Field(i).Addr().Interface()
91 | // }
92 | // return row.Scan(args...)
93 | return scan(row, v, args)
94 | }
95 |
96 | //primary key get
97 | func (d *Database) GetPk(m Model) error {
98 | field, value := GetPkValue(m)
99 | return d.Get(m, field, value)
100 | }
101 |
102 | func (d *Database) Select(m Model, field string, condition interface{}, value ...interface{}) (ms []Model, err error) {
103 | q, n := query(m, field, condition, len(value))
104 | if d.Debug {
105 | fmt.Println(q, " ", value)
106 | }
107 | rows, err := d.db.Query(q, value...)
108 | if err != nil {
109 | return
110 | }
111 | defer rows.Close()
112 | args := make([]interface{}, n)
113 | t := reflect.TypeOf(m).Elem()
114 | for rows.Next() {
115 | v := reflect.New(t).Elem()
116 | // for i := 0; i < n; i++ {
117 | // args[i] = v.Field(i).Addr().Interface()
118 | // }
119 | // if err = rows.Scan(args...); err != nil {
120 | // return
121 | // }
122 | if err = scan(rows, v, args); err != nil {
123 | return
124 | }
125 | ms = append(ms, (v.Addr().Interface()).(Model))
126 | }
127 | err = rows.Err()
128 | return
129 | }
130 |
131 | // d.GetCondition(&User{}, "where level >10 and money limit ?, 10", 100, 5)
132 | func (d *Database) GetCondition(m Model, condition interface{}, value ...interface{}) ([]Model, error) {
133 | return d.Select(m, "", condition, value...)
134 | }
135 |
136 | //mysql in usage
137 | // ms := d.GetMultiIn(&User{}, "openid", "12", "34")
138 | // ms[0].(*User) [return interface{} need type change]
139 | func (d *Database) GetMultiIn(m Model, field string, value ...interface{}) ([]Model, error) {
140 | return d.Select(m, field, "", value...)
141 | }
142 |
143 | func (d *Database) GetMultiPkIn(m Model, value ...interface{}) ([]Model, error) {
144 | field, _ := GetPkValue(m)
145 | return d.GetMultiIn(m, field, value...)
146 | }
147 |
148 | func (d *Database) Del(m Model, field string, value interface{}) (affect int64, err error) {
149 | q := delQuery(m, field)
150 | if d.Debug {
151 | fmt.Println(q, " [", value, "]")
152 | }
153 | stmt, err := d.db.Prepare(q)
154 | if err != nil {
155 | return
156 | }
157 | res, err := stmt.Exec(value)
158 | if err != nil {
159 | return
160 | }
161 | return res.RowsAffected()
162 | }
163 |
164 | func (d *Database) DelPk(m Model) (int64, error) {
165 | field, value := GetPkValue(m)
166 | return d.Del(m, field, value)
167 | }
168 |
169 | func (d *Database) Update(m Model, field string, value interface{}) (affect int64, err error) {
170 | q, n, auto := updateQuery(m, field)
171 | stmt, err := d.db.Prepare(q)
172 | if err != nil {
173 | return
174 | }
175 | values := structValues(m, n, auto)
176 | vv := make([]interface{}, len(values)+1)
177 | copy(vv, values)
178 | vv[len(values)] = value
179 | if d.Debug {
180 | fmt.Println(q, " ", values)
181 | }
182 | res, err := stmt.Exec(vv...)
183 | if err != nil {
184 | return
185 | }
186 |
187 | return res.RowsAffected()
188 | }
189 |
190 | func (d *Database) UpdatePk(m Model) (int64, error) {
191 | field, value := GetPkValue(m)
192 | return d.Update(m, field, value)
193 | }
194 |
195 | func (d *Database) Insert(m Model) (last int64, err error) {
196 | q, n, auto := insertquery(m)
197 | stmt, err := d.db.Prepare(q)
198 | if err != nil {
199 | return
200 | }
201 | values := structValues(m, n, auto)
202 | if d.Debug {
203 | fmt.Println(q, " ", values)
204 | }
205 | res, err := stmt.Exec(values...)
206 | if err != nil {
207 | return
208 | }
209 | lastId, err := res.LastInsertId()
210 | _, _, autoIndex, _ := structKeys(m, true)
211 | if autoIndex != -1 {
212 | reflect.ValueOf(m).Elem().Field(autoIndex).SetInt(lastId)
213 | }
214 |
215 | return lastId, err
216 | }
217 |
218 | func GetPkValue(m Model) (string, interface{}) {
219 | keys, _, _, pkIndex := structKeys(m, true)
220 | if pkIndex == -1 {
221 | panic("no pk tag, table: " + m.TableName())
222 | }
223 | value := reflect.ValueOf(m).Elem().Field(pkIndex).Interface()
224 | return keys[pkIndex], value
225 | }
226 |
227 | //reflect is expensive,so cache model's field
228 | var fieldCache = make(map[string][]interface{})
229 |
230 | //delete autoIndex
231 | func autoDelete(keys []string, autoIndex int) []string {
232 | keysNew := make([]string, len(keys)-1)
233 | copy(keysNew, keys[0:autoIndex])
234 | copy(keysNew, keys[autoIndex+1:])
235 | return keysNew
236 | }
237 |
238 | // return {struct keys, NumField, auto field index, pk field index}
239 | func structKeys(m Model, autoNeed bool) ([]string, int, int, int) {
240 | //cache check start
241 | table := m.TableName()
242 | if cache, ok := fieldCache[table]; ok {
243 | keys, numField, autoIndex, pkIndex := cache[0].([]string), cache[1].(int), cache[2].(int), cache[3].(int)
244 | if !autoNeed && autoIndex != -1 {
245 | return autoDelete(keys, autoIndex), numField, autoIndex, pkIndex
246 | }
247 | return keys, numField, autoIndex, pkIndex
248 | }
249 | //cache check end
250 |
251 | t := reflect.TypeOf(m).Elem()
252 | n := t.NumField()
253 | keys := make([]string, 0, n)
254 | autoIndex, pkIndex := -1, -1
255 | for i := 0; i < n; i++ {
256 | f := t.Field(i)
257 | if f.Tag.Get(attrTag) == autoAttr {
258 | if autoIndex != -1 {
259 | panic("table duplicate auto :" + m.TableName())
260 | }
261 | autoIndex = i
262 | }
263 | tag := f.Tag.Get(fieldTag)
264 | if tag == "" {
265 | tag = strings.ToLower(f.Name)
266 | }
267 | pk := f.Tag.Get(pkTag)
268 | if pk == "true" {
269 | pkIndex = i
270 | }
271 | keys = append(keys, tag)
272 | }
273 |
274 | //cache
275 | fieldCache[table] = []interface{}{keys, n, autoIndex, pkIndex}
276 | if !autoNeed && autoIndex != -1 {
277 | return autoDelete(keys, autoIndex), n, autoIndex, pkIndex
278 | }
279 |
280 | return keys, n, autoIndex, pkIndex
281 | }
282 |
283 | // m must reflect.Ptr
284 | //get all struct value except auto
285 | func structValues(m Model, n int, auto int) []interface{} {
286 | N := n
287 | if auto != -1 {
288 | n--
289 | }
290 | vv := make([]interface{}, 0, n)
291 | v := reflect.ValueOf(m).Elem()
292 | for i := 0; i < N; i++ {
293 | if i == auto {
294 | continue
295 | }
296 | vt := v.Field(i).Addr().Type()
297 | var tmp interface{}
298 | if vt == TIME_PTR_TYPE {
299 | tmp = v.Field(i).Interface().(time.Time).Format(TIME_FORMATER)
300 | } else {
301 | tmp = v.Field(i).Interface()
302 | }
303 | vv = append(vv, tmp)
304 | }
305 | return vv
306 | }
307 |
308 | // if you don't want provide TableName, you can combine DeafultModel
309 | type DeafultModel struct{}
310 |
311 | func (m *DeafultModel) TableName() string {
312 | v := reflect.ValueOf(m)
313 | return strings.ToLower(v.Elem().Type().Name())
314 | }
315 |
316 | // m must reflect.Ptr
317 | func tableName(m interface{}) string {
318 | v := reflect.ValueOf(m)
319 | if method := v.MethodByName(tablenameFunc); method.IsValid() {
320 | return method.Call([]reflect.Value{})[0].String()
321 | } else {
322 | return strings.ToLower(v.Elem().Type().Name())
323 | }
324 | }
325 |
326 | // m must reflect.Ptr
327 | // inlen : multiIN number
328 | // condition nil -> select all, string -> condition
329 | func query(m Model, field string, condition interface{}, inlen int) (string, int) {
330 | buf := bytes.NewBufferString("SELECT ")
331 | keys, n, _, _ := structKeys(m, true)
332 | buf.WriteString(strings.Join(keys, ", "))
333 | buf.WriteString(" FROM ")
334 | buf.WriteString(m.TableName())
335 |
336 | if condition != nil {
337 | if c, ok := condition.(string); ok && c != "" {
338 | buf.WriteByte(' ')
339 | buf.WriteString(c)
340 | } else {
341 | buf.WriteString(" Where ")
342 | buf.WriteString(field)
343 | if inlen <= 0 {
344 | buf.WriteString("=?")
345 | } else {
346 | buf.WriteString(" IN(")
347 | var tmp2 []string
348 | // try reuse keys
349 | if cap(keys) >= inlen {
350 | tmp2 = keys[0:0]
351 | } else {
352 | tmp2 = make([]string, 0, inlen)
353 | }
354 | for i := 0; i < inlen; i++ {
355 | tmp2 = append(tmp2, "?")
356 | }
357 | buf.WriteString(strings.Join(tmp2, ", "))
358 | buf.WriteString(")")
359 | }
360 | }
361 | }
362 | return buf.String(), n
363 | }
364 |
365 | func delQuery(m Model, field string) string {
366 | buf := bytes.NewBufferString("DELETE FROM ")
367 | buf.WriteString(m.TableName())
368 | buf.WriteString(" WHERE ")
369 | buf.WriteString(field)
370 | buf.WriteString("=?")
371 | return buf.String()
372 | }
373 |
374 | func updateQuery(m Model, field string) (string, int, int) {
375 | buf := bytes.NewBufferString("UPDATE ")
376 | buf.WriteString(m.TableName())
377 | buf.WriteString(" SET ")
378 | keys, n, auto, _ := structKeys(m, false)
379 | for i, k := range keys {
380 | buf.WriteString(k)
381 | buf.WriteString("=?")
382 | if n != 2 && i != n-1 { // one key || last
383 | buf.WriteString(" ,")
384 | }
385 | }
386 | buf.WriteString(" WHERE ")
387 | buf.WriteString(field)
388 | buf.WriteString("=?")
389 |
390 | return buf.String(), n, auto
391 | }
392 |
393 | // return {insert string, struct NumField, auto field index}
394 | func insertquery(m Model) (string, int, int) {
395 | buf := bytes.NewBufferString("INSERT INTO ")
396 | buf.WriteString(m.TableName())
397 | buf.WriteString("(")
398 | keys, n, auto, _ := structKeys(m, false)
399 | buf.WriteString(strings.Join(keys, ", "))
400 | buf.WriteString(")")
401 | buf.WriteString(" VALUES(")
402 | for i := 0; i < len(keys); i++ {
403 | keys[i] = "?"
404 | }
405 | buf.WriteString(strings.Join(keys, ", "))
406 | buf.WriteString(")")
407 | return buf.String(), n, auto
408 | }
409 |
--------------------------------------------------------------------------------
/lib/database/db_test.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | _ "github.com/go-sql-driver/mysql"
7 | "os"
8 | "testing"
9 | )
10 |
11 | var db *sql.DB
12 |
13 | type User struct {
14 | Id int `attr:"auto" pk:"true"`
15 | Name string
16 | }
17 |
18 | func (u *User) TableName() string {
19 | return "users"
20 | }
21 |
22 | type UserWithName struct {
23 | Id int
24 | Name string `field:"username"`
25 | }
26 |
27 | func (u *UserWithName) TableName() string {
28 | return "usertable"
29 | }
30 |
31 | type DefaultUser struct {
32 | DeafultModel
33 | }
34 |
35 | func TestTableName(t *testing.T) {
36 | u := &User{}
37 | if tableName(u) != "users" {
38 | t.Error("User tableName error")
39 | }
40 | uu := &UserWithName{}
41 | if tableName(uu) != "usertable" {
42 | t.Error("UserWithName tableName error")
43 | }
44 | fmt.Println("end")
45 | }
46 |
47 | func TestDefaultModel(t *testing.T) {
48 | u := &DefaultUser{}
49 | if u.TableName() != "deafultmodel" {
50 | t.Error("TestDefaultModel error")
51 | }
52 | }
53 |
54 | func TestQuery(t *testing.T) {
55 | uu := &UserWithName{}
56 | fmt.Println(query(uu, "name", "", 0))
57 | }
58 |
59 | func TestGet(t *testing.T) {
60 | u := &User{}
61 | d := &Database{db, true}
62 | fmt.Println(d.Get(u, "id", 3))
63 | fmt.Println(u)
64 |
65 | fmt.Println("test GetPk")
66 | u.Id = 4
67 | d.GetPk(u)
68 | fmt.Println(u)
69 |
70 | fmt.Println("------------------")
71 | // v, e := d.GetMultiIn(u, "id", 3, 4)
72 | v, e := d.GetCondition(&User{}, "where id >3 and id and name=?", 10, "kehan")
73 | // v, e := d.GetCondition(&User{}, nil) // select all
74 | if e != nil {
75 | t.Error("GetMultiIn error," + e.Error())
76 | } else {
77 | for _, m := range v {
78 | fmt.Println(m.(*User))
79 | }
80 | }
81 |
82 | }
83 |
84 | func TestDel(t *testing.T) {
85 | u := &User{}
86 | d := &Database{db, true}
87 | u.Id = 10
88 | d.DelPk(u)
89 | }
90 |
91 | func TestInsert(t *testing.T) {
92 | u := &User{Name: "kehan"}
93 | d := &Database{db, true}
94 | fmt.Println(d.Insert(u))
95 | fmt.Println(u)
96 | }
97 |
98 | func TestUpdate(t *testing.T) {
99 | u := &User{}
100 | d := &Database{db, true}
101 | d.Get(u, "id", 3)
102 | fmt.Println(u)
103 | u.Name = "3333"
104 | fmt.Println(d.Update(u, "id", u.Id))
105 | fmt.Println(u)
106 | }
107 | func init() {
108 | db = new(sql.DB)
109 | d, err := sql.Open("mysql", "root@tcp(localhost:3306)/test")
110 | if err != nil {
111 | fmt.Println("mysql open error, please check:" + err.Error())
112 | os.Exit(1)
113 | } else {
114 | db = d
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/lib/filter/wordfilter.go:
--------------------------------------------------------------------------------
1 | package filter
2 |
3 | //referer http://blog.csdn.net/chenssy/article/details/26961957
4 |
5 | type TrieNode struct {
6 | words map[rune]*TrieNode
7 | full bool
8 | }
9 |
10 | func New() *TrieNode {
11 | t := new(TrieNode)
12 | t.words = make(map[rune]*TrieNode, 64)
13 | t.full = false
14 | return t
15 | }
16 |
17 | func (t *TrieNode) Add(world string) {
18 | node := t
19 | for _, w := range world {
20 | _, ok := node.words[w]
21 | if !ok {
22 | node.words[w] = &TrieNode{make(map[rune]*TrieNode, 0), false}
23 | }
24 | node = node.words[w]
25 | }
26 | node.full = true
27 | }
28 |
29 | func (t *TrieNode) Filter(world string) string {
30 | filter := []rune(world)
31 | length := len(filter)
32 | matches := make([]int, 0, 4)
33 | for i := 0; i < length; {
34 | node := t
35 | matches = matches[0:0]
36 | for j := i; j < length; j++ {
37 | w := filter[j]
38 | node = node.words[w]
39 | if node == nil {
40 | break
41 | } else {
42 | matches = append(matches, j)
43 | if node.full {
44 | for _, v := range matches {
45 | filter[v] = '*'
46 | }
47 | break
48 | }
49 | }
50 | }
51 | //skip **
52 | if len(matches) > 0 {
53 | i += len(matches)
54 | } else {
55 | i++
56 | }
57 | }
58 | return string(filter)
59 | }
60 |
--------------------------------------------------------------------------------
/lib/filter/wordfilter_test.go:
--------------------------------------------------------------------------------
1 | package filter
2 |
3 | import (
4 | // "fmt"
5 | "testing"
6 | )
7 |
8 | func TestAscFilter(t *testing.T) {
9 | r := New()
10 | r.Add("12")
11 | r.Add("13")
12 | r.Add("14")
13 | r.Add("15")
14 | r.Add("16")
15 | r.Add("34")
16 | r.Add("23")
17 | if r.Filter("1234121567") != "********67" {
18 | t.Error("filter ascii error")
19 | }
20 | }
21 |
22 | func TestRuneFilter(t *testing.T) {
23 | r := New()
24 | r.Add("毛片")
25 | r.Add("毛毛")
26 | if r.Filter("我是老毛") != "我是老毛" {
27 | t.Error("rune error")
28 | }
29 | if r.Filter("我是毛毛") != "我是**" {
30 | t.Error("rune 2 error")
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/lib/helper/helper.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "os"
5 | "runtime"
6 | "syscall"
7 | )
8 |
9 | func Fork() (int, syscall.Errno) {
10 | r1, r2, err := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
11 | if err != 0 {
12 | return 0, err
13 | }
14 | if runtime.GOOS == "darwin" && r2 == 1 {
15 | r1 = 1
16 | }
17 | return int(r1), 0
18 | }
19 |
20 | // referer http://ikarishinjieva.github.io/blog/blog/2014/03/20/go-file-lock/
21 | func LockFile(name string, truncate bool) (*os.File, error) {
22 | flag := os.O_RDWR | os.O_CREATE
23 | if truncate {
24 | flag |= os.O_TRUNC
25 | }
26 | f, err := os.OpenFile(name, flag, 0666)
27 | if err != nil {
28 | return nil, err
29 | }
30 | if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
31 | f.Close()
32 | return nil, err
33 | }
34 | return f, nil
35 | }
36 |
--------------------------------------------------------------------------------
/lib/helper/helper_test.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestFork(t *testing.T) {
11 | fmt.Println("pid", os.Getpid())
12 | pid, err := fork()
13 | if err != 0 {
14 | t.Error("fork error,errno:", err)
15 | }
16 | if pid == 0 {
17 | fmt.Println("child:", os.Getpid(), "parent:", os.Getppid())
18 | time.Sleep(time.Second * 20)
19 | fmt.Println("child exit")
20 | os.Exit(0)
21 | } else {
22 | fmt.Println("parent:", os.Getpid(), "child:", pid)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/lru/lru.go:
--------------------------------------------------------------------------------
1 | package lru
2 |
3 | import (
4 | "container/list"
5 | "sync"
6 | )
7 |
8 | type ExpireHandler func(Key, Value)
9 | type Key interface{}
10 | type Value interface{}
11 |
12 | type entry struct {
13 | key Key
14 | value Value
15 | }
16 |
17 | type Cache struct {
18 | sync.Mutex
19 | MaxEntries int // 0 -> no limit
20 | OnExpire ExpireHandler // invoke when expire delete
21 | ll *list.List
22 | cache map[Key]*list.Element
23 | }
24 |
25 | func New(maxEntries int, handler ExpireHandler) *Cache {
26 | return &Cache{
27 | MaxEntries: maxEntries,
28 | OnExpire: handler,
29 | ll: list.New(),
30 | cache: make(map[Key]*list.Element),
31 | }
32 | }
33 |
34 | func (c *Cache) Add(key Key, value Value) (ok bool) {
35 | c.Lock()
36 | defer c.Unlock()
37 | if e, ok := c.cache[key]; ok {
38 | c.ll.MoveToFront(e)
39 | e.Value.(*entry).value = value
40 | } else {
41 | e = c.ll.PushFront(&entry{key, value})
42 | c.cache[key] = e
43 | }
44 | if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
45 | c.RemoveOldest()
46 | }
47 | return
48 | }
49 |
50 | func (c *Cache) Get(key Key) (Value, bool) {
51 | c.Lock()
52 | defer c.Unlock()
53 | if e, ok := c.cache[key]; ok {
54 | c.ll.MoveToFront(e)
55 | return e.Value.(*entry).value, true
56 | } else {
57 | return nil, false
58 | }
59 | }
60 |
61 | func (c *Cache) Len() int {
62 | c.Lock()
63 | defer c.Unlock()
64 | return c.ll.Len()
65 | }
66 |
67 | func (c *Cache) RemoveOldest() {
68 | e := c.ll.Back()
69 | c.ll.Remove(e)
70 | kv := e.Value.(*entry)
71 | delete(c.cache, kv.key)
72 | if c.OnExpire != nil {
73 | c.OnExpire(kv.key, kv.value)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/lru/lru_test.go:
--------------------------------------------------------------------------------
1 | package lru
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestLru(t *testing.T) {
9 | c := New(2, nil)
10 | c.Add(1, "hello")
11 | c.Add(2, "world")
12 | if d, ok := c.Get(1); ok {
13 | if d.(string) != "hello" {
14 | t.Error("error")
15 | }
16 | } else {
17 | t.Error("error")
18 | }
19 | c.Add(3, "overload")
20 | if e, ok := c.Get(2); ok || e != nil {
21 | t.Error("remove oldest error")
22 | }
23 | if c.Len() != 2 {
24 | t.Error("len error")
25 | }
26 | fmt.Println("end")
27 | }
28 |
--------------------------------------------------------------------------------
/lib/zset/skiplist.c:
--------------------------------------------------------------------------------
1 |
2 | // skiplist similar with the version in redis
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "skiplist.h"
9 |
10 |
11 | skiplistNode *slCreateNode(int level, double score, slobj *obj) {
12 | skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel));
13 | n->score = score;
14 | n->obj = obj;
15 | return n;
16 | }
17 |
18 | skiplist *slCreate(void) {
19 | int j;
20 | skiplist *sl;
21 |
22 | sl = malloc(sizeof(*sl));
23 | sl->level = 1;
24 | sl->length = 0;
25 | sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, NULL);
26 | for (j=0; j < SKIPLIST_MAXLEVEL; j++) {
27 | sl->header->level[j].forward = NULL;
28 | sl->header->level[j].span = 0;
29 | }
30 | sl->header->backward = NULL;
31 | sl->tail = NULL;
32 | return sl;
33 | }
34 |
35 | slobj* slCreateObj(const char* ptr, size_t length) {
36 | slobj *obj = malloc(sizeof(*obj));
37 | obj->ptr = malloc(length + 1);
38 |
39 | if(ptr) {
40 | memcpy(obj->ptr, ptr, length);
41 | }
42 | obj->ptr[length] = '\0';
43 |
44 | obj->length = length;
45 | return obj;
46 | }
47 |
48 | void slFreeObj(slobj *obj) {
49 | free(obj->ptr);
50 | free(obj);
51 | }
52 |
53 | void slFreeNode(skiplistNode *node) {
54 | slFreeObj(node->obj);
55 | free(node);
56 | }
57 |
58 | void slFree(skiplist *sl) {
59 | skiplistNode *node = sl->header->level[0].forward, *next;
60 |
61 | free(sl->header);
62 | while(node) {
63 | next = node->level[0].forward;
64 | slFreeNode(node);
65 | node = next;
66 | }
67 | free(sl);
68 | }
69 |
70 | int slRandomLevel(void) {
71 | int level = 1;
72 | while((random() & 0xffff) < (SKIPLIST_P * 0xffff))
73 | level += 1;
74 | return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
75 | }
76 |
77 | int compareslObj(slobj *a, slobj *b) {
78 | int cmp = memcmp(a->ptr, b->ptr, a->length <= b->length ? a->length : b->length);
79 | if(cmp == 0) return a->length - b->length;
80 | return cmp;
81 | }
82 |
83 | int equalslObj(slobj *a, slobj *b) {
84 | return compareslObj(a, b) == 0;
85 | }
86 |
87 | void slInsert(skiplist *sl, double score, slobj *obj) {
88 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
89 | unsigned int rank[SKIPLIST_MAXLEVEL];
90 | int i, level;
91 |
92 | x = sl->header;
93 | for (i = sl->level-1; i >= 0; i--) {
94 | /* store rank that is crossed to reach the insert position */
95 | rank[i] = i == (sl->level-1) ? 0 : rank[i+1];
96 | while (x->level[i].forward &&
97 | (x->level[i].forward->score < score ||
98 | (x->level[i].forward->score == score &&
99 | compareslObj(x->level[i].forward->obj,obj) < 0))) {
100 | rank[i] += x->level[i].span;
101 | x = x->level[i].forward;
102 | }
103 | update[i] = x;
104 | }
105 | /* we assume the key is not already inside, since we allow duplicated
106 | * scores, and the re-insertion of score and redis object should never
107 | * happen since the caller of slInsert() should test in the hash table
108 | * if the element is already inside or not. */
109 | level = slRandomLevel();
110 | if (level > sl->level) {
111 | for (i = sl->level; i < level; i++) {
112 | rank[i] = 0;
113 | update[i] = sl->header;
114 | update[i]->level[i].span = sl->length;
115 | }
116 | sl->level = level;
117 | }
118 | x = slCreateNode(level,score,obj);
119 | for (i = 0; i < level; i++) {
120 | x->level[i].forward = update[i]->level[i].forward;
121 | update[i]->level[i].forward = x;
122 |
123 | /* update span covered by update[i] as x is inserted here */
124 | x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
125 | update[i]->level[i].span = (rank[0] - rank[i]) + 1;
126 | }
127 |
128 | /* increment span for untouched levels */
129 | for (i = level; i < sl->level; i++) {
130 | update[i]->level[i].span++;
131 | }
132 |
133 | x->backward = (update[0] == sl->header) ? NULL : update[0];
134 | if (x->level[0].forward)
135 | x->level[0].forward->backward = x;
136 | else
137 | sl->tail = x;
138 | sl->length++;
139 | }
140 |
141 | /* Internal function used by slDelete, slDeleteByScore */
142 | void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) {
143 | int i;
144 | for (i = 0; i < sl->level; i++) {
145 | if (update[i]->level[i].forward == x) {
146 | update[i]->level[i].span += x->level[i].span - 1;
147 | update[i]->level[i].forward = x->level[i].forward;
148 | } else {
149 | update[i]->level[i].span -= 1;
150 | }
151 | }
152 | if (x->level[0].forward) {
153 | x->level[0].forward->backward = x->backward;
154 | } else {
155 | sl->tail = x->backward;
156 | }
157 | while(sl->level > 1 && sl->header->level[sl->level-1].forward == NULL)
158 | sl->level--;
159 | sl->length--;
160 | }
161 |
162 | /* Delete an element with matching score/object from the skiplist. */
163 | int slDelete(skiplist *sl, double score, slobj *obj) {
164 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
165 | int i;
166 |
167 | x = sl->header;
168 | for (i = sl->level-1; i >= 0; i--) {
169 | while (x->level[i].forward &&
170 | (x->level[i].forward->score < score ||
171 | (x->level[i].forward->score == score &&
172 | compareslObj(x->level[i].forward->obj,obj) < 0)))
173 | x = x->level[i].forward;
174 | update[i] = x;
175 | }
176 | /* We may have multiple elements with the same score, what we need
177 | * is to find the element with both the right score and object. */
178 | x = x->level[0].forward;
179 | if (x && score == x->score && equalslObj(x->obj,obj)) {
180 | slDeleteNode(sl, x, update);
181 | slFreeNode(x);
182 | return 1;
183 | } else {
184 | return 0; /* not found */
185 | }
186 | return 0; /* not found */
187 | }
188 |
189 | void slDeleteCb(void* ud, slobj *obj) {
190 | delCb(ud, obj);
191 | }
192 |
193 | /* Delete all the elements with rank between start and end from the skiplist.
194 | * Start and end are inclusive. Note that start and end need to be 1-based */
195 | unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, void* ud) {
196 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
197 | unsigned long traversed = 0, removed = 0;
198 | int i;
199 |
200 | x = sl->header;
201 | for (i = sl->level-1; i >= 0; i--) {
202 | while (x->level[i].forward && (traversed + x->level[i].span) < start) {
203 | traversed += x->level[i].span;
204 | x = x->level[i].forward;
205 | }
206 | update[i] = x;
207 | }
208 |
209 | traversed++;
210 | x = x->level[0].forward;
211 | while (x && traversed <= end) {
212 | skiplistNode *next = x->level[0].forward;
213 | slDeleteNode(sl,x,update);
214 | slDeleteCb(ud, x->obj);
215 | slFreeNode(x);
216 | removed++;
217 | traversed++;
218 | x = next;
219 | }
220 | return removed;
221 | }
222 |
223 | /* Find the rank for an element by both score and key.
224 | * Returns 0 when the element cannot be found, rank otherwise.
225 | * Note that the rank is 1-based due to the span of sl->header to the
226 | * first element. */
227 | unsigned long slGetRank(skiplist *sl, double score, slobj *o) {
228 | skiplistNode *x;
229 | unsigned long rank = 0;
230 | int i;
231 |
232 | x = sl->header;
233 | for (i = sl->level-1; i >= 0; i--) {
234 | while (x->level[i].forward &&
235 | (x->level[i].forward->score < score ||
236 | (x->level[i].forward->score == score &&
237 | compareslObj(x->level[i].forward->obj,o) <= 0))) {
238 | rank += x->level[i].span;
239 | x = x->level[i].forward;
240 | }
241 |
242 | /* x might be equal to sl->header, so test if obj is non-NULL */
243 | if (x->obj && equalslObj(x->obj, o)) {
244 | return rank;
245 | }
246 | }
247 | return 0;
248 | }
249 |
250 | /* Finds an element by its rank. The rank argument needs to be 1-based. */
251 | skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) {
252 | if(rank == 0 || rank > sl->length) {
253 | return NULL;
254 | }
255 |
256 | skiplistNode *x;
257 | unsigned long traversed = 0;
258 | int i;
259 |
260 | x = sl->header;
261 | for (i = sl->level-1; i >= 0; i--) {
262 | while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
263 | {
264 | traversed += x->level[i].span;
265 | x = x->level[i].forward;
266 | }
267 | if (traversed == rank) {
268 | return x;
269 | }
270 | }
271 |
272 | return NULL;
273 | }
274 |
275 | /* range [min, max], left & right both include */
276 | /* Returns if there is a part of the zset is in range. */
277 | int slIsInRange(skiplist *sl, double min, double max) {
278 | skiplistNode *x;
279 |
280 | /* Test for ranges that will always be empty. */
281 | if(min > max) {
282 | return 0;
283 | }
284 | x = sl->tail;
285 | if (x == NULL || x->score < min)
286 | return 0;
287 |
288 | x = sl->header->level[0].forward;
289 | if (x == NULL || x->score > max)
290 | return 0;
291 | return 1;
292 | }
293 |
294 | /* Find the first node that is contained in the specified range.
295 | * Returns NULL when no element is contained in the range. */
296 | skiplistNode *slFirstInRange(skiplist *sl, double min, double max) {
297 | skiplistNode *x;
298 | int i;
299 |
300 | /* If everything is out of range, return early. */
301 | if (!slIsInRange(sl,min, max)) return NULL;
302 |
303 | x = sl->header;
304 | for (i = sl->level-1; i >= 0; i--) {
305 | /* Go forward while *OUT* of range. */
306 | while (x->level[i].forward && x->level[i].forward->score < min)
307 | x = x->level[i].forward;
308 | }
309 |
310 | /* This is an inner range, so the next node cannot be NULL. */
311 | x = x->level[0].forward;
312 | return x;
313 | }
314 |
315 | /* Find the last node that is contained in the specified range.
316 | * Returns NULL when no element is contained in the range. */
317 | skiplistNode *slLastInRange(skiplist *sl, double min, double max) {
318 | skiplistNode *x;
319 | int i;
320 |
321 | /* If everything is out of range, return early. */
322 | if (!slIsInRange(sl, min, max)) return NULL;
323 |
324 | x = sl->header;
325 | for (i = sl->level-1; i >= 0; i--) {
326 | /* Go forward while *IN* range. */
327 | while (x->level[i].forward &&
328 | x->level[i].forward->score <= max)
329 | x = x->level[i].forward;
330 | }
331 |
332 | /* This is an inner range, so this node cannot be NULL. */
333 | return x;
334 | }
335 |
336 | skiplistNode *getNextNode(skiplistNode *node, int reverse) {
337 | return reverse? node->backward : node->level[0].forward;
338 | }
339 |
340 | void slDump(skiplist *sl) {
341 | skiplistNode *x;
342 | int i;
343 |
344 | x = sl->header;
345 | i = 0;
346 | while(x->level[0].forward) {
347 | x = x->level[0].forward;
348 | i++;
349 | printf("node %d: score:%f, member:%s\n", i, x->score, x->obj->ptr);
350 | }
351 | }
352 |
353 |
354 |
355 |
--------------------------------------------------------------------------------
/lib/zset/skiplist.h:
--------------------------------------------------------------------------------
1 | //
2 | #include
3 |
4 | #define SKIPLIST_MAXLEVEL 32
5 | #define SKIPLIST_P 0.25
6 |
7 | typedef struct slobj {
8 | char *ptr;
9 | size_t length;
10 | } slobj;
11 |
12 | typedef struct skiplistNode {
13 | slobj* obj;
14 | double score;
15 | struct skiplistNode *backward;
16 | struct skiplistLevel {
17 | struct skiplistNode *forward;
18 | unsigned int span;
19 | }level[];
20 | } skiplistNode;
21 |
22 | typedef struct skiplist {
23 | struct skiplistNode *header, *tail;
24 | unsigned long length;
25 | int level;
26 | } skiplist;
27 |
28 | slobj* slCreateObj(const char* ptr, size_t length);
29 | void slFreeObj(slobj *obj);
30 |
31 | skiplist *slCreate(void);
32 | void slFree(skiplist *sl);
33 | void slDump(skiplist *sl);
34 |
35 | void slInsert(skiplist *sl, double score, slobj *obj);
36 | int slDelete(skiplist *sl, double score, slobj *obj);
37 | unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, void* ud);
38 |
39 | unsigned long slGetRank(skiplist *sl, double score, slobj *o);
40 | skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank);
41 |
42 | skiplistNode *slFirstInRange(skiplist *sl, double min, double max);
43 | skiplistNode *slLastInRange(skiplist *sl, double min, double max);
44 |
45 | skiplistNode *getNextNode(skiplistNode *node, int reverse);
46 |
47 | extern void delCb(void* ud, slobj *o);
48 |
49 |
--------------------------------------------------------------------------------
/lib/zset/zset.go:
--------------------------------------------------------------------------------
1 | package zset
2 |
3 | //#include "skiplist.h"
4 | import "C"
5 |
6 | import (
7 | "bufio"
8 | "cham/lib/helper"
9 | "encoding/json"
10 | "errors"
11 | "os"
12 | "reflect"
13 | "runtime"
14 | "unsafe"
15 | )
16 |
17 | const DEFAULT_TBL_LEN = 16
18 |
19 | type zset struct {
20 | sl *C.skiplist
21 | tbl map[string]float64
22 | }
23 |
24 | func tocstring(s string) (*C.char, C.size_t) {
25 | v := (*reflect.StringHeader)(unsafe.Pointer(&s))
26 | return (*C.char)(unsafe.Pointer(v.Data)), C.size_t(v.Len)
27 | }
28 |
29 | func newslobj(s string) *C.slobj {
30 | p, l := tocstring(s)
31 | return C.slCreateObj(p, l)
32 | }
33 |
34 | func New() *zset {
35 | z := &zset{C.slCreate(), make(map[string]float64, DEFAULT_TBL_LEN)}
36 | runtime.SetFinalizer(z, func(z *zset) {
37 | C.slFree(z.sl)
38 | z.tbl = nil
39 | })
40 | return z
41 | }
42 |
43 | func (z *zset) Add(score float64, member string) {
44 | if old, ok := z.tbl[member]; ok {
45 | if old == score {
46 | return
47 | }
48 | var obj C.slobj
49 | obj.ptr, obj.length = tocstring(member)
50 | C.slDelete(z.sl, C.double(old), &obj)
51 | }
52 | C.slInsert(z.sl, C.double(score), newslobj(member))
53 | z.tbl[member] = score
54 | }
55 |
56 | func (z *zset) Rem(member string) {
57 | if score, ok := z.tbl[member]; ok {
58 | var obj C.slobj
59 | obj.ptr, obj.length = tocstring(member)
60 | C.slDelete(z.sl, C.double(score), &obj)
61 | delete(z.tbl, member)
62 | }
63 | }
64 |
65 | func (z *zset) Count() int {
66 | return int(z.sl.length)
67 | }
68 |
69 | func (z *zset) Score(member string) (float64, bool) {
70 | score, ex := z.tbl[member]
71 | return score, ex
72 | }
73 |
74 | func (z *zset) Range(r1, r2 int) []string {
75 | if r1 < 1 {
76 | r1 = 1
77 | }
78 | if r2 < 1 {
79 | r2 = 1
80 | }
81 | var reverse, rangelen int
82 | if r1 <= r2 {
83 | reverse = 0
84 | rangelen = r2 - r1 + 1
85 | } else {
86 | reverse = 1
87 | rangelen = r1 - r2 + 1
88 | }
89 | node := C.slGetNodeByRank(z.sl, C.ulong(r1))
90 | result := make([]string, 0, rangelen)
91 | rr := C.int(reverse)
92 | for n := 0; node != nil && n < rangelen; {
93 | result = append(result, C.GoStringN(node.obj.ptr, C.int(node.obj.length)))
94 | node = C.getNextNode(node, rr)
95 | n++
96 | }
97 | return result
98 | }
99 |
100 | func (z *zset) reverseRank(r int) int {
101 | return z.Count() - r + 1
102 | }
103 |
104 | func (z *zset) RevRange(r1, r2 int) []string {
105 | return z.Range(z.reverseRank(r1), z.reverseRank(r2))
106 | }
107 |
108 | func (z *zset) RangeByScore(s1, s2 float64) []string {
109 | var reverse int
110 | var node *C.skiplistNode
111 | cs1, cs2 := C.double(s1), C.double(s2)
112 | if s1 <= s2 {
113 | reverse = 0
114 | node = C.slFirstInRange(z.sl, cs1, cs2)
115 | } else {
116 | reverse = 1
117 | node = C.slLastInRange(z.sl, cs2, cs1)
118 | }
119 |
120 | result := make([]string, 0)
121 | rr := C.int(reverse)
122 | for node != nil {
123 | if reverse == 1 {
124 | if node.score < cs2 {
125 | break
126 | }
127 | } else {
128 | if node.score > cs2 {
129 | break
130 | }
131 | }
132 | result = append(result, C.GoStringN(node.obj.ptr, C.int(node.obj.length)))
133 | node = C.getNextNode(node, rr)
134 | }
135 | return result
136 | }
137 |
138 | // rank is 1-based, 0 not found
139 | func (z *zset) Rank(member string) int {
140 | score, ex := z.tbl[member]
141 | if !ex {
142 | return 0
143 | }
144 | var obj C.slobj
145 | obj.ptr, obj.length = tocstring(member)
146 | rank := C.slGetRank(z.sl, C.double(score), &obj)
147 | return int(rank)
148 | }
149 |
150 | func (z *zset) RevRank(member string) int {
151 | rank := z.Rank(member)
152 | if rank != 0 {
153 | rank = z.reverseRank(rank)
154 | }
155 | return rank
156 | }
157 |
158 | func (z *zset) deleteByRank(from, to int) int {
159 | if from > to {
160 | from, to = to, from
161 | }
162 | return int(C.slDeleteByRank(z.sl, C.uint(from), C.uint(to), unsafe.Pointer(z)))
163 | }
164 |
165 | //export delCb
166 | func delCb(p unsafe.Pointer, obj *C.slobj) {
167 | z := (*zset)(p)
168 | member := C.GoStringN(obj.ptr, C.int(obj.length))
169 | delete(z.tbl, member)
170 | }
171 |
172 | func (z *zset) Limit(count int) int {
173 | total := z.Count()
174 | if total <= count {
175 | return 0
176 | }
177 | return z.deleteByRank(count+1, total)
178 | }
179 |
180 | func (z *zset) RevLimit(count int) int {
181 | total := z.Count()
182 | if total <= count {
183 | return 0
184 | }
185 | from := z.reverseRank(count + 1)
186 | to := z.reverseRank(total)
187 | return z.deleteByRank(from, to)
188 | }
189 |
190 | func (z *zset) Dump() {
191 | C.slDump(z.sl)
192 | }
193 |
194 | //use fork snapshot to save
195 | func (z *zset) BgStore(name string) error {
196 | f, err := helper.LockFile(name, true)
197 | if err != nil {
198 | return err
199 | }
200 | pid, errno := helper.Fork()
201 | if errno != 0 {
202 | return errors.New("fork error," + errno.Error())
203 | }
204 | //child
205 | if pid == 0 {
206 | buf := bufio.NewWriter(f)
207 | encoder := json.NewEncoder(buf)
208 | encoder.Encode(z.tbl)
209 | buf.Flush()
210 | f.Close()
211 | os.Exit(0)
212 | }
213 |
214 | return nil
215 |
216 | }
217 |
218 | func (z *zset) ReStore(name string) error {
219 | f, err := helper.LockFile(name, false)
220 | if err != nil {
221 | return err
222 | }
223 | tbl := make(map[string]float64, DEFAULT_TBL_LEN)
224 | buf := bufio.NewReader(f)
225 | decoder := json.NewDecoder(buf)
226 | decoder.Decode(&tbl)
227 | for k, v := range tbl {
228 | z.Add(v, k)
229 | }
230 | f.Close()
231 | return nil
232 | }
233 |
--------------------------------------------------------------------------------
/lib/zset/zset_test.go:
--------------------------------------------------------------------------------
1 | package zset
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func equal(a []string, b []string) bool {
10 | if len(a) != len(b) {
11 | return false
12 | }
13 | for i := 0; i < len(a); i++ {
14 | if a[i] != b[i] {
15 | return false
16 | }
17 | }
18 | return true
19 | }
20 |
21 | func assert(t *testing.T, ok bool, s string) {
22 | if !ok {
23 | t.Error(s)
24 | }
25 | }
26 |
27 | func TestBase(t *testing.T) {
28 | z := New()
29 | assert(t, z.Count() == 0, "empty Count error")
30 | z.Add(1, "12")
31 | z.Add(1, "32")
32 | assert(t, z.Count() == 2, "not empty Count error")
33 | var score float64
34 | var ex bool
35 | score, ex = z.Score("12")
36 | assert(t, score == 1, "Score error")
37 | z.Add(2, "12")
38 | assert(t, z.Count() == 2, "after add duplicate Count error")
39 | score, ex = z.Score("12")
40 | assert(t, score == 2, "after add Score error")
41 | z.Rem("12")
42 | assert(t, z.Count() == 1, "after rem Count error")
43 | score, ex = z.Score("12")
44 | assert(t, ex == false, "not exist Score error")
45 | fmt.Println("")
46 | }
47 |
48 | func TestRangeByScore(t *testing.T) {
49 | z := New()
50 | z.Add(2, "22")
51 | z.Add(1, "11")
52 | z.Add(3, "33")
53 | s := "TestRangeByScore error"
54 | assert(t, equal(z.RangeByScore(2, 3), []string{"22", "33"}), s)
55 | assert(t, equal(z.RangeByScore(0, 5), []string{"11", "22", "33"}), s)
56 | assert(t, equal(z.RangeByScore(10, 5), []string{}), s)
57 | assert(t, equal(z.RangeByScore(10, 0), []string{"33", "22", "11"}), s)
58 | }
59 |
60 | func TestRange(t *testing.T) {
61 | z := New()
62 | z.Add(100.1, "1")
63 | z.Add(100.9, "9")
64 | z.Add(100.5, "5")
65 | assert(t, equal(z.Range(1, 3), []string{"1", "5", "9"}), "Range1 error")
66 | assert(t, equal(z.Range(3, 1), []string{"9", "5", "1"}), "Range2 error")
67 | assert(t, equal(z.RevRange(1, 2), []string{"9", "5"}), "RevRange1 error")
68 | assert(t, equal(z.RevRange(3, 2), []string{"1", "5"}), "RevRange2 error")
69 |
70 | }
71 |
72 | func TestRank(t *testing.T) {
73 | z := New()
74 | assert(t, z.Rank("kehan") == 0, "Rank empty error")
75 | z.Add(1111.1111, "kehan")
76 | assert(t, z.Rank("kehan") == 1, "Rank error")
77 | z.Add(222.2222, "lwy")
78 | assert(t, z.Rank("kehan") == 2, "Rank 2 error")
79 | assert(t, z.RevRank("kehan") == 1, "RevRank error")
80 | }
81 |
82 | func TestLimit(t *testing.T) {
83 | z := New()
84 | z.Add(1, "1")
85 | z.Add(2, "2")
86 | z.Add(3, "3")
87 | z.Limit(1)
88 | assert(t, z.Count() == 1, "Limit error")
89 | assert(t, z.Rank("3") == 0, "Limit Rank error")
90 | z.Add(4.4, "4")
91 | z.Add(5.5, "5")
92 | z.Add(0.5, "0.5")
93 | z.Dump()
94 | assert(t, z.RevLimit(4) == 0, "RevLimit error")
95 | assert(t, z.RevLimit(0) == 4, "RevLimit2 error")
96 | }
97 |
98 | func TestStore(t *testing.T) {
99 | z := New()
100 | z.Add(1, "1")
101 | z.Add(2, "2")
102 | z.Add(3, "3")
103 | z.BgStore("zdb.json")
104 | go func() {
105 | time.Sleep(time.Second * 2)
106 | fmt.Println("continue add")
107 | z.Add(4, "4")
108 | z.Add(5, "5")
109 | }()
110 |
111 | }
112 |
113 | func TestReStore(t *testing.T) {
114 | z := New()
115 | assert(t, len(z.tbl) == 0, "restore error")
116 | z.ReStore("zdb.json")
117 | fmt.Println(z.tbl)
118 | assert(t, len(z.tbl) != 0, "restore2 error")
119 | assert(t, z.Count() == len(z.tbl), "restore3 error")
120 | }
121 |
--------------------------------------------------------------------------------
/service/debug/debug.go:
--------------------------------------------------------------------------------
1 | package debug
2 |
3 | import (
4 | "bufio"
5 | "cham/cham"
6 | "cham/service/log"
7 | "fmt"
8 | "net"
9 | "reflect"
10 | "strconv"
11 | "strings"
12 | )
13 |
14 | type buffer struct {
15 | conn net.Conn
16 | }
17 |
18 | func (s buffer) WriteString(str string) {
19 | s.conn.Write([]byte(str))
20 | }
21 |
22 | type debug struct {
23 | addr string
24 | funcs map[string]reflect.Value
25 | }
26 |
27 | func (d *debug) serve(conn net.Conn) {
28 | defer conn.Close()
29 | buf := buffer{conn}
30 | br := bufio.NewReader(conn)
31 | buf.WriteString("Welcome to cham console\r\n")
32 |
33 | for {
34 | line, _, err := br.ReadLine()
35 | if err != nil {
36 | return
37 | }
38 | if len(line) < 1 {
39 | continue
40 | }
41 | cmds := strings.Fields(string(line))
42 | result := d.handle(cmds)
43 | buf.WriteString(result)
44 | buf.WriteString("\r\n")
45 | }
46 | }
47 |
48 | func (d *debug) handle(cmds []string) string {
49 | defer func() {
50 | if err := recover(); err != nil {
51 | log.Infoln("debug cmd", cmds, " error:", err)
52 | }
53 | }()
54 |
55 | funName := strings.Title(cmds[0])
56 | if f, ok := d.funcs[funName]; !ok {
57 | return "Invalid command, type help for command list"
58 | } else {
59 | var in []reflect.Value
60 | n := len(cmds) - 1
61 | if n > 0 {
62 | args := cmds[1:]
63 | in = make([]reflect.Value, n)
64 | for i := 0; i < n; i++ {
65 | in[i] = reflect.ValueOf(args[i])
66 | }
67 | } else {
68 | in = []reflect.Value{}
69 | }
70 | result := f.Call(in)
71 | return result[0].String()
72 | }
73 | }
74 |
75 | func (d *debug) getfuncs() {
76 | v := reflect.ValueOf(d)
77 | t := v.Type()
78 | for i := 0; i < t.NumMethod(); i++ {
79 | name := t.Method(i).Name
80 | if strings.HasPrefix(name, "Cmd") {
81 | d.funcs[name[3:]] = v.Method(i)
82 | }
83 | }
84 | }
85 |
86 | func (d *debug) start() {
87 | listener, err := net.Listen("tcp", d.addr)
88 | if err != nil {
89 | panic("debug listen addr:" + d.addr + " ,error:" + err.Error())
90 | }
91 | log.Infoln("debug start, listen ", d.addr)
92 | defer listener.Close()
93 | d.getfuncs()
94 | for {
95 | conn, err := listener.Accept()
96 | if err != nil {
97 | continue
98 | }
99 | go d.serve(conn)
100 | }
101 | }
102 |
103 | func (d *debug) CmdList() string {
104 | return cham.DumpService()
105 | }
106 |
107 | func (d *debug) CmdHelp() string {
108 | return `
109 | <-------------------->
110 | help help message
111 | list list all service
112 | send send message to service(send addr message)
113 | <-------------------->
114 |
115 | `
116 | }
117 |
118 | func (d *debug) CmdSend(args ...string) string {
119 | if len(args) < 2 {
120 | return "FAILED, args not enough"
121 | }
122 | vv := make([]interface{}, len(args)-1)
123 | addr, err := strconv.Atoi(args[0])
124 | if err != nil {
125 | return "FAILED, addr error"
126 | }
127 | pt, err := strconv.Atoi(args[1])
128 | if err != nil {
129 | return "FAILED, ptype error"
130 | }
131 | vv[0] = uint8(pt)
132 | for i := 1; i < len(vv); i++ {
133 | vv[i] = args[i+2]
134 | }
135 |
136 | msg := cham.Main.Send(cham.Address(addr), cham.PTYPE_GO, vv...)
137 | return "SUCCESS," + fmt.Sprint(msg)
138 | }
139 |
140 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
141 | d := &debug{args[0].(string), make(map[string]reflect.Value)}
142 | go d.start()
143 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
144 | return cham.NORET
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/service/debug/debug_test.go:
--------------------------------------------------------------------------------
1 | package debug
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestDebug(t *testing.T) {
11 | ll := log.New("log.txt", log.LDEFAULT, log.LDEBUG)
12 | ll.Infoln("hello")
13 | cham.NewService("debug", Start, 1, "127.0.0.1:8888")
14 | time.Sleep(time.Minute * 30)
15 | }
16 |
--------------------------------------------------------------------------------
/service/gate/gate.go:
--------------------------------------------------------------------------------
1 | package gate
2 |
3 | import (
4 | "bufio"
5 | "cham/cham"
6 | "cham/service/log"
7 | "encoding/binary"
8 | "io"
9 | "net"
10 | "net/http"
11 | "sync"
12 | "sync/atomic"
13 | )
14 |
15 | const (
16 | OPEN uint8 = iota
17 | KICK
18 | )
19 |
20 | const (
21 | OnOpen uint8 = iota
22 | OnMessage
23 | OnClose
24 | OnPong
25 | )
26 |
27 | var (
28 | bufioReaderPool sync.Pool
29 | bufioWriterPool sync.Pool
30 | )
31 |
32 | type Conf struct {
33 | address string //127.0.0.1:8000
34 | maxclient uint32 // 0 -> no limit
35 | path string // "/ws" websocket, null is tcp
36 | }
37 |
38 | func NewConf(address string, maxclient uint32, path string) *Conf {
39 | return &Conf{address, maxclient, path}
40 | }
41 |
42 | type Gate struct {
43 | rwmutex *sync.RWMutex
44 | Source cham.Address
45 | service *cham.Service
46 | session uint32
47 | clinetnum uint32
48 | maxclient uint32
49 | sessions map[uint32]Backend
50 | }
51 |
52 | type Backend interface {
53 | Write(data []byte) error
54 | Close()
55 | }
56 |
57 | type TcpBackend struct {
58 | session uint32
59 | conn net.Conn
60 | brw *bufio.ReadWriter
61 | }
62 |
63 | // tcp readbuf start
64 | func newBufioReader(r io.Reader) *bufio.Reader {
65 | if v := bufioReaderPool.Get(); v != nil {
66 | br := v.(*bufio.Reader)
67 | br.Reset(r)
68 | return br
69 | }
70 | return bufio.NewReader(r)
71 | }
72 |
73 | func putBufioReader(r *bufio.Reader) {
74 | r.Reset(nil)
75 | bufioReaderPool.Put(r)
76 | }
77 |
78 | func newBufioWriter(w io.Writer) *bufio.Writer {
79 | if v := bufioWriterPool.Get(); v != nil {
80 | bw := v.(*bufio.Writer)
81 | bw.Reset(w)
82 | return bw
83 | }
84 | return bufio.NewWriter(w)
85 | }
86 |
87 | func putBufioWriter(w *bufio.Writer) {
88 | w.Reset(nil)
89 | bufioWriterPool.Put(w)
90 | }
91 |
92 | func newTcpBackend(session uint32, conn net.Conn) *TcpBackend {
93 | br := newBufioReader(conn)
94 | bw := newBufioWriter(conn)
95 | return &TcpBackend{session, conn, bufio.NewReadWriter(br, bw)}
96 | }
97 |
98 | // tcp readbuf end
99 |
100 | func (t *TcpBackend) Close() {
101 | putBufioReader(t.brw.Reader)
102 | putBufioWriter(t.brw.Writer)
103 | t.conn.Close()
104 | }
105 |
106 | func (t *TcpBackend) Write(data []byte) (err error) {
107 | head := make([]byte, 2)
108 | binary.BigEndian.PutUint16(head, uint16(len(data)))
109 | _, err = t.brw.Write(head)
110 | if err == nil {
111 | _, err = t.brw.Write(data)
112 | if err == nil {
113 | err = t.brw.Flush()
114 | }
115 | }
116 | return
117 | }
118 |
119 | func (t *TcpBackend) readFull(buf []byte) error {
120 | if _, err := io.ReadFull(t.brw, buf); err != nil {
121 | if e, ok := err.(net.Error); ok && !e.Temporary() {
122 | return err
123 | }
124 | }
125 | return nil
126 | }
127 |
128 | // bigendian 2byte length+data
129 | func (t *TcpBackend) serve(g *Gate) {
130 | head := make([]byte, 2)
131 | for {
132 | if err := t.readFull(head); err != nil {
133 | g.kick(t.session)
134 | return
135 | }
136 |
137 | length := binary.BigEndian.Uint16(head)
138 | data := make([]byte, length, length)
139 |
140 | if err := t.readFull(data); err != nil {
141 | g.kick(t.session)
142 | return
143 | }
144 | g.service.Notify(g.Source, cham.PTYPE_CLIENT, t.session, OnMessage, data)
145 | }
146 | }
147 |
148 | type WebsocketBackend struct {
149 | *Websocket
150 | }
151 |
152 | func (w *WebsocketBackend) Close() {
153 | w.Websocket.Close(0, []byte(""))
154 | }
155 |
156 | func (w *WebsocketBackend) Write(data []byte) error {
157 | return w.SendText(data)
158 | }
159 |
160 | func newWebsocket(w http.ResponseWriter, r *http.Request, opt *Option, session uint32, gate *Gate) (*WebsocketBackend, error) {
161 | ws, err := NewWebsocket(w, r, opt, session, gate)
162 | if err != nil {
163 | return nil, err
164 | }
165 | return &WebsocketBackend{ws}, nil
166 | }
167 |
168 | //websocket start
169 | type wsHandler struct {
170 | }
171 |
172 | func (wd wsHandler) CheckOrigin(origin, host string) bool {
173 | return true
174 | }
175 |
176 | func (wd wsHandler) OnOpen(ws *Websocket) {
177 | ws.gate.service.Notify(ws.gate.Source, cham.PTYPE_CLIENT, ws.session, OnOpen)
178 | }
179 |
180 | func (wd wsHandler) OnMessage(ws *Websocket, message []byte) {
181 | ws.gate.service.Notify(ws.gate.Source, cham.PTYPE_CLIENT, ws.session, OnMessage, message)
182 | }
183 |
184 | func (wd wsHandler) OnClose(ws *Websocket, code uint16, reason []byte) {
185 | ws.gate.service.Notify(ws.gate.Source, cham.PTYPE_CLIENT, ws.session, OnClose, code, reason)
186 | }
187 |
188 | func (wd wsHandler) OnPong(ws *Websocket, data []byte) {
189 | ws.gate.service.Notify(ws.gate.Source, cham.PTYPE_CLIENT, ws.session, OnPong, data)
190 | }
191 |
192 | //websocket end
193 |
194 | func New(source cham.Address, service *cham.Service) *Gate {
195 | gate := new(Gate)
196 | gate.rwmutex = new(sync.RWMutex)
197 | gate.service = service
198 | gate.Source = source
199 | gate.clinetnum = 0
200 | gate.session = 0
201 | gate.sessions = make(map[uint32]Backend)
202 | return gate
203 | }
204 |
205 | func (g *Gate) nextSession() uint32 {
206 | return atomic.AddUint32(&g.session, 1)
207 | }
208 |
209 | func (g *Gate) addBackend(session uint32, b Backend) {
210 | g.rwmutex.Lock()
211 | g.sessions[session] = b
212 | g.rwmutex.Unlock()
213 | }
214 |
215 | //gate listen
216 | func (g *Gate) open(conf *Conf) {
217 | maxclient := conf.maxclient
218 | g.maxclient = maxclient
219 | if conf.path == "" {
220 | log.Infoln("Tcp Gate start, listen ", conf.address)
221 | listen, err := net.Listen("tcp", conf.address)
222 | if err != nil {
223 | panic("gate http open error:" + err.Error())
224 | }
225 | go func() {
226 | defer listen.Close()
227 | for {
228 | conn, err := listen.Accept()
229 | if err != nil {
230 | continue
231 | }
232 | if maxclient != 0 && g.clinetnum >= maxclient {
233 | conn.Close() //server close socket(!net.Error)
234 | break
235 | }
236 | g.clinetnum++
237 | session := g.nextSession()
238 | backend := newTcpBackend(session, conn)
239 | g.sessions[session] = backend // not need mutex, so not addBackend
240 | go backend.serve(g)
241 | }
242 | }()
243 |
244 | } else {
245 | log.Infoln("Websocket Gate start, listen ", conf.address)
246 | var opt = Option{wsHandler{}, false}
247 | http.HandleFunc(conf.path, func(w http.ResponseWriter, r *http.Request) {
248 |
249 | if maxclient != 0 && g.clinetnum >= maxclient {
250 | return
251 | }
252 | session := g.nextSession()
253 | ws, err := newWebsocket(w, r, &opt, session, g)
254 | if err != nil {
255 | return
256 | }
257 | g.addBackend(session, ws)
258 | g.clinetnum++
259 | ws.Start()
260 | })
261 | go func() { http.ListenAndServe(conf.address, nil) }()
262 | }
263 | }
264 |
265 | func (g *Gate) kick(session uint32) {
266 | var b Backend
267 | var ok bool
268 | g.rwmutex.Lock()
269 | if b, ok = g.sessions[session]; ok {
270 | delete(g.sessions, session)
271 | g.clinetnum--
272 | }
273 | g.rwmutex.Unlock()
274 | if ok {
275 | b.Close()
276 | }
277 | }
278 |
279 | func (g *Gate) Write(session uint32, data []byte) (err error) {
280 | g.rwmutex.RLock()
281 | b, ok := g.sessions[session]
282 | g.rwmutex.RUnlock()
283 | if ok {
284 | err = b.Write(data)
285 | }
286 | return
287 | }
288 |
289 | func ResponseStart(service *cham.Service, args ...interface{}) cham.Dispatch {
290 | gate := args[0].(*Gate)
291 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
292 | sessionid := args[0].(uint32)
293 | data := args[1].([]byte)
294 | err := gate.Write(sessionid, data)
295 | return cham.Ret(err)
296 | }
297 | }
298 |
299 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
300 | log.Infoln("New Service ", service.String())
301 | gate := New(0, service)
302 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
303 | cmd := args[0].(uint8)
304 | result := cham.NORET
305 | switch cmd {
306 | case OPEN:
307 | gate.Source = source
308 | service.RegisterProtocol(cham.PTYPE_RESPONSE, ResponseStart, gate)
309 | gate.open(args[1].(*Conf))
310 | case KICK:
311 | gate.kick(args[1].(uint32))
312 | }
313 |
314 | return result
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/service/gate/gate_test.go:
--------------------------------------------------------------------------------
1 | package gate
2 |
3 | import (
4 | "cham/cham"
5 | "encoding/binary"
6 | "fmt"
7 | "io"
8 | "net"
9 | "strconv"
10 | "testing"
11 | "time"
12 | )
13 |
14 | func watchDogStart(service *cham.Service, args ...interface{}) cham.Dispatch {
15 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
16 | return cham.NORET
17 | }
18 | }
19 |
20 | func clientDispatch(service *cham.Service, args ...interface{}) cham.Dispatch {
21 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
22 | sessionid := args[0].(uint32)
23 | gt := args[1].(uint8)
24 | time.Sleep(time.Second * 5)
25 | switch gt {
26 | case OnOpen:
27 | fmt.Println("OnOpen ", sessionid)
28 | case OnClose:
29 | fmt.Println("OnClose ", sessionid, args[2:])
30 | case OnPong:
31 | fmt.Println("OnPong ", sessionid, args[2])
32 | case OnMessage:
33 | data := string(args[2].([]byte))
34 | fmt.Println("OnMessage", sessionid, data)
35 | if data == "hello" {
36 | service.Notify("gate", cham.PTYPE_RESPONSE, sessionid, []byte("world"+strconv.Itoa(int(sessionid))))
37 | } else if data == "WebSocket" {
38 | service.Notify("gate", cham.PTYPE_RESPONSE, sessionid, []byte("websocket reply"+strconv.Itoa(int(sessionid))))
39 | }
40 | go func() {
41 | time.Sleep(time.Second * 5)
42 | fmt.Println("kick")
43 | // service.Notify("gate", cham.PTYPE_GO, KICK, sessionid)
44 | err := service.Call("gate", cham.PTYPE_RESPONSE, sessionid, []byte("world"))
45 | fmt.Println(err)
46 | }()
47 | }
48 | return cham.NORET
49 | }
50 | }
51 |
52 | func runClient(n int) {
53 | conn, err := net.Dial("tcp", "127.0.0.1:9999")
54 | if err != nil {
55 | fmt.Println("client error:" + err.Error())
56 | return
57 | }
58 | i := string(strconv.Itoa(n))
59 | fmt.Println("client start " + i)
60 | for {
61 | data := []byte("hello")
62 | head := make([]byte, 2)
63 | binary.BigEndian.PutUint16(head, uint16(len(data)))
64 | if _, err := conn.Write(head); err != nil {
65 | fmt.Println("client error:" + err.Error())
66 | break
67 | }
68 | conn.Write(data)
69 | time.Sleep(time.Second)
70 | io.ReadFull(conn, head)
71 | length := binary.BigEndian.Uint16(head)
72 | result := make([]byte, length)
73 | io.ReadFull(conn, result)
74 | fmt.Println("client get:"+i, string(result))
75 | time.Sleep(time.Second * 15)
76 | // break
77 | }
78 | fmt.Println("client end" + i)
79 |
80 | }
81 |
82 | func TestGateService(t *testing.T) {
83 | ws := cham.NewService("watchdog", watchDogStart, 16) // 16 worker to process client data
84 | ws.RegisterProtocol(cham.PTYPE_CLIENT, clientDispatch)
85 | gs := cham.NewService("gate", Start, 16) // 16 worker to send data to client
86 | ws.Call(gs, cham.PTYPE_GO, OPEN, NewConf("127.0.0.1:9999", 100, ""))
87 | for i := 0; i < 20; i++ {
88 | go runClient(i)
89 | }
90 | time.Sleep(time.Minute * 2)
91 | }
92 |
93 | func TestGateWsService(t *testing.T) {
94 | gs := cham.NewService("gate", Start, 16) // 16 worker to send data to clie
95 | ws := cham.NewService("watchdog", watchDogStart, 16) // 16 worker to process client data
96 | ws.RegisterProtocol(cham.PTYPE_CLIENT, clientDispatch)
97 | ws.Call(gs, cham.PTYPE_GO, OPEN, NewConf("127.0.0.1:9998", 100, "/ws"))
98 | time.Sleep(time.Minute * 10)
99 | }
100 |
--------------------------------------------------------------------------------
/service/gate/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebSocket example
5 |
6 |
7 |
29 |
30 |
--------------------------------------------------------------------------------
/service/gate/websocket.go:
--------------------------------------------------------------------------------
1 | package gate
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "crypto/sha1"
7 | "encoding/base64"
8 | "encoding/binary"
9 | "errors"
10 | "io"
11 | "net"
12 | "net/http"
13 | "strings"
14 | )
15 |
16 | var (
17 | ErrUpgrade = errors.New("Can \"Upgrade\" only to \"WebSocket\"")
18 | ErrConnection = errors.New("\"Connection\" must be \"Upgrade\"")
19 | ErrCrossOrigin = errors.New("Cross origin websockets not allowed")
20 | ErrSecVersion = errors.New("HTTP/1.1 Upgrade Required\r\nSec-WebSocket-Version: 13\r\n\r\n")
21 | ErrSecKey = errors.New("\"Sec-WebSocket-Key\" must not be nil")
22 | ErrHijacker = errors.New("Not implement http.Hijacker")
23 | )
24 |
25 | var (
26 | ErrReservedBits = errors.New("Reserved_bits show using undefined extensions")
27 | ErrFrameOverload = errors.New("Control frame payload overload")
28 | ErrFrameFragmented = errors.New("Control frame must not be fragmented")
29 | ErrInvalidOpcode = errors.New("Invalid frame opcode")
30 | )
31 |
32 | var (
33 | crlf = []byte("\r\n")
34 | challengeKey = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
35 | )
36 |
37 | //referer https://github.com/Skycrab/skynet_websocket/blob/master/websocket.lua
38 |
39 | type WsHandler interface {
40 | CheckOrigin(origin, host string) bool
41 | OnOpen(ws *Websocket)
42 | OnMessage(ws *Websocket, message []byte)
43 | OnClose(ws *Websocket, code uint16, reason []byte)
44 | OnPong(ws *Websocket, data []byte)
45 | }
46 |
47 | type WsDefaultHandler struct {
48 | checkOriginOr bool // 是否校验origin, default true
49 | }
50 |
51 | func (wd WsDefaultHandler) CheckOrigin(origin, host string) bool {
52 | return true
53 | }
54 |
55 | func (wd WsDefaultHandler) OnOpen(ws *Websocket) {
56 | }
57 |
58 | func (wd WsDefaultHandler) OnMessage(ws *Websocket, message []byte) {
59 | }
60 |
61 | func (wd WsDefaultHandler) OnClose(ws *Websocket, code uint16, reason []byte) {
62 | }
63 |
64 | func (wd WsDefaultHandler) OnPong(ws *Websocket, data []byte) {
65 |
66 | }
67 |
68 | type Websocket struct {
69 | conn net.Conn
70 | session uint32
71 | gate *Gate
72 | rw *bufio.ReadWriter
73 | handler WsHandler
74 | clientTerminated bool
75 | serverTerminated bool
76 | maskOutgoing bool
77 | }
78 |
79 | type Option struct {
80 | Handler WsHandler // 处理器, default WsDefaultHandler
81 | MaskOutgoing bool //发送frame是否mask, default false
82 | }
83 |
84 | func challengeResponse(key, protocol string) []byte {
85 | sha := sha1.New()
86 | sha.Write([]byte(key))
87 | sha.Write(challengeKey)
88 | accept := base64.StdEncoding.EncodeToString(sha.Sum(nil))
89 | buf := bytes.NewBufferString("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ")
90 | buf.WriteString(accept)
91 | buf.Write(crlf)
92 | if protocol != "" {
93 | buf.WriteString("Sec-WebSocket-Protocol: ")
94 | buf.WriteString(protocol)
95 | buf.Write(crlf)
96 | }
97 | buf.Write(crlf)
98 |
99 | return buf.Bytes()
100 | }
101 |
102 | func acceptConnection(r *http.Request, h WsHandler) (challenge []byte, err error) {
103 | //Upgrade header should be present and should be equal to WebSocket
104 | if strings.ToLower(r.Header.Get("Upgrade")) != "websocket" {
105 | return nil, ErrUpgrade
106 | }
107 |
108 | //Connection header should be upgrade. Some proxy servers/load balancers
109 | // might mess with it.
110 | if !strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade") {
111 | return nil, ErrConnection
112 | }
113 |
114 | // Handle WebSocket Origin naming convention differences
115 | // The difference between version 8 and 13 is that in 8 the
116 | // client sends a "Sec-Websocket-Origin" header and in 13 it's
117 | // simply "Origin".
118 | if r.Header.Get("Sec-Websocket-Version") != "13" {
119 | return nil, ErrSecVersion
120 | }
121 |
122 | origin := r.Header.Get("Origin")
123 | if origin == "" {
124 | origin = r.Header.Get("Sec-Websocket-Origin")
125 | }
126 |
127 | if origin != "" && !h.CheckOrigin(origin, r.Header.Get("Host")) {
128 | return nil, ErrCrossOrigin
129 | }
130 |
131 | key := r.Header.Get("Sec-Websocket-Key")
132 | if key == "" {
133 | return nil, ErrSecKey
134 | }
135 |
136 | protocol := r.Header.Get("Sec-Websocket-Protocol")
137 | if protocol != "" {
138 | idx := strings.IndexByte(protocol, ',')
139 | if idx != -1 {
140 | protocol = protocol[:idx]
141 | }
142 | }
143 |
144 | return challengeResponse(key, protocol), nil
145 |
146 | }
147 |
148 | func websocketMask(mask []byte, data []byte) {
149 | for i := range data {
150 | data[i] ^= mask[i%4]
151 | }
152 | }
153 |
154 | func NewWebsocket(w http.ResponseWriter, r *http.Request, opt *Option, session uint32, gate *Gate) (*Websocket, error) {
155 |
156 | var h WsHandler
157 | var maskOutgoing bool
158 | if opt == nil {
159 | h = WsDefaultHandler{true}
160 | maskOutgoing = false
161 | } else {
162 | h = opt.Handler
163 | maskOutgoing = opt.MaskOutgoing
164 | }
165 |
166 | challenge, err := acceptConnection(r, h)
167 | if err != nil {
168 | var code int
169 | if err == ErrCrossOrigin {
170 | code = 403
171 | } else {
172 | code = 400
173 | }
174 | w.WriteHeader(code)
175 | w.Write([]byte(err.Error()))
176 | return nil, err
177 | }
178 | hj, ok := w.(http.Hijacker)
179 | if !ok {
180 | return nil, ErrHijacker
181 | }
182 |
183 | conn, rw, err := hj.Hijack()
184 |
185 | ws := new(Websocket)
186 | ws.session = session
187 | ws.gate = gate
188 | ws.conn = conn
189 | ws.rw = rw
190 | ws.handler = h
191 | ws.maskOutgoing = maskOutgoing
192 |
193 | if _, err := ws.conn.Write(challenge); err != nil {
194 | ws.conn.Close()
195 | return nil, err
196 | }
197 | ws.handler.OnOpen(ws)
198 | return ws, nil
199 | }
200 |
201 | func (ws *Websocket) read(buf []byte) error {
202 | _, err := io.ReadFull(ws.rw, buf)
203 | return err
204 | }
205 |
206 | func (ws *Websocket) SendFrame(fin bool, opcode byte, data []byte) error {
207 | //max frame header may 14 length
208 | buf := make([]byte, 0, len(data)+14)
209 | var finBit, maskBit byte
210 | if fin {
211 | finBit = 0x80
212 | } else {
213 | finBit = 0
214 | }
215 |
216 | buf = append(buf, finBit|opcode)
217 | length := len(data)
218 | if ws.maskOutgoing {
219 | maskBit = 0x80
220 | } else {
221 | maskBit = 0
222 | }
223 | if length < 126 {
224 | buf = append(buf, byte(length)|maskBit)
225 | } else if length < 0xFFFF {
226 | buf = append(buf, 126|maskBit, 0, 0)
227 | binary.BigEndian.PutUint16(buf[len(buf)-2:], uint16(length))
228 | } else {
229 | buf = append(buf, 127|maskBit, 0, 0, 0, 0, 0, 0, 0, 0)
230 | binary.BigEndian.PutUint64(buf[len(buf)-8:], uint64(length))
231 | }
232 |
233 | if ws.maskOutgoing {
234 |
235 | }
236 |
237 | buf = append(buf, data...)
238 | ws.rw.Write(buf)
239 | return ws.rw.Flush()
240 | }
241 |
242 | func (ws *Websocket) SendText(data []byte) error {
243 | return ws.SendFrame(true, 0x1, data)
244 | }
245 |
246 | func (ws *Websocket) SendBinary(data []byte) error {
247 | return ws.SendFrame(true, 0x2, data)
248 | }
249 |
250 | func (ws *Websocket) SendPing(data []byte) error {
251 | return ws.SendFrame(true, 0x9, data)
252 | }
253 |
254 | func (ws *Websocket) SendPong(data []byte) error {
255 | return ws.SendFrame(true, 0xA, data)
256 | }
257 |
258 | func (ws *Websocket) Close(code uint16, reason []byte) {
259 | if !ws.serverTerminated {
260 | data := make([]byte, 0, len(reason)+2)
261 | if code == 0 && reason != nil {
262 | code = 1000
263 | }
264 | if code != 0 {
265 | data = append(data, 0, 0)
266 | binary.BigEndian.PutUint16(data, code)
267 | }
268 | if reason != nil {
269 | data = append(data, reason...)
270 | }
271 | ws.SendFrame(true, 0x8, data)
272 | ws.serverTerminated = true
273 | }
274 | if ws.clientTerminated {
275 | ws.conn.Close()
276 | }
277 |
278 | }
279 |
280 | func (ws *Websocket) RecvFrame() (final bool, message []byte, err error) { //text 数据报文
281 | buf := make([]byte, 8, 8)
282 | err = ws.read(buf[:2])
283 | if err != nil {
284 | return
285 | }
286 | header, payload := buf[0], buf[1]
287 | final = header&0x80 != 0
288 | reservedBits := header&0x70 != 0
289 | frameOpcode := header & 0xf
290 | frameOpcodeIsControl := frameOpcode&0x8 != 0
291 |
292 | if reservedBits {
293 | // client is using as-yet-undefined extensions
294 | err = ErrReservedBits
295 | return
296 | }
297 |
298 | maskFrame := payload&0x80 != 0
299 | payloadlen := uint64(payload & 0x7f)
300 |
301 | if frameOpcodeIsControl && payloadlen >= 126 {
302 | err = ErrFrameOverload
303 | return
304 | }
305 |
306 | if frameOpcodeIsControl && !final {
307 | err = ErrFrameFragmented
308 | return
309 | }
310 |
311 | //解析frame长度
312 | var frameLength uint64
313 | if payloadlen < 126 {
314 | frameLength = payloadlen
315 | } else if payloadlen == 126 {
316 | err = ws.read(buf[:2])
317 | if err != nil {
318 | return
319 | }
320 | frameLength = uint64(binary.BigEndian.Uint16(buf[:2]))
321 |
322 | } else { //payloadlen == 127
323 | err = ws.read(buf[:8])
324 | if err != nil {
325 | return
326 | }
327 | frameLength = binary.BigEndian.Uint64(buf[:8])
328 | }
329 |
330 | frameMask := make([]byte, 4, 4)
331 | if maskFrame {
332 | err = ws.read(frameMask)
333 | if err != nil {
334 | return
335 | }
336 | }
337 |
338 | // fmt.Println("final_frame:", final, "frame_opcode:", frameOpcode, "mask_frame:", maskFrame, "frame_length:", frameLength)
339 |
340 | message = make([]byte, frameLength, frameLength)
341 | if frameLength > 0 {
342 | err = ws.read(message)
343 | if err != nil {
344 | return
345 | }
346 | }
347 |
348 | if maskFrame && frameLength > 0 {
349 | websocketMask(frameMask, message)
350 | }
351 |
352 | if !final {
353 | return
354 | } else {
355 | switch frameOpcode {
356 | case 0x1: //text
357 | case 0x2: //binary
358 | case 0x8: // close
359 | var code uint16
360 | var reason []byte
361 | if frameLength >= 2 {
362 | code = binary.BigEndian.Uint16(message[:2])
363 | }
364 | if frameLength > 2 {
365 | reason = message[2:]
366 | }
367 | message = nil
368 | ws.clientTerminated = true
369 | ws.Close(0, nil)
370 | ws.handler.OnClose(ws, code, reason)
371 | case 0x9: //ping
372 | message = nil
373 | ws.SendPong(nil)
374 | case 0xA:
375 | ws.handler.OnPong(ws, message)
376 | message = nil
377 | default:
378 | err = ErrInvalidOpcode
379 | }
380 | return
381 | }
382 |
383 | }
384 |
385 | func (ws *Websocket) Recv() ([]byte, error) {
386 | data := make([]byte, 0, 8)
387 | for {
388 | final, message, err := ws.RecvFrame()
389 | if final {
390 | data = append(data, message...)
391 | break
392 | } else {
393 | data = append(data, message...)
394 | }
395 | if err != nil {
396 | return data, err
397 | }
398 | }
399 | if len(data) > 0 {
400 | ws.handler.OnMessage(ws, data)
401 | }
402 | return data, nil
403 | }
404 |
405 | func (ws *Websocket) Start() {
406 | for {
407 | _, err := ws.Recv()
408 | if err != nil {
409 | ws.conn.Close()
410 | }
411 | }
412 |
413 | }
414 |
--------------------------------------------------------------------------------
/service/log/log.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "cham/cham"
5 | "fmt"
6 | "runtime"
7 | "strconv"
8 | "time"
9 | )
10 |
11 | const (
12 | LDEBUG = iota
13 | LINFO
14 | LERROR
15 | )
16 |
17 | const (
18 | LTIME = 1 << iota
19 | LFILE
20 | LLEVEL
21 | LDEFAULT = LTIME | LLEVEL
22 | LALL = LTIME | LFILE | LLEVEL
23 | )
24 |
25 | const TimeFormat = "2006/01/02 15:04:05"
26 |
27 | var Names = [3]string{"Debug", "Info", "Error"}
28 | var logs = cham.NewService("log", Start)
29 |
30 | type Logger struct {
31 | level int
32 | flag int
33 | out string
34 | }
35 |
36 | //if out empty, use os.Stdout
37 | func New(out string, flag int, level int) *Logger {
38 | cham.Main.Call(logs, cham.PTYPE_GO, OPEN, out)
39 | return &Logger{level, flag, out}
40 | }
41 |
42 | var std = New("", LDEFAULT, LINFO)
43 |
44 | func (l *Logger) Output(calldepth int, level int, s string) {
45 | if l.level > level {
46 | return
47 | }
48 | buf := make([]byte, 0, len(s)+28)
49 | if l.flag<IME != 0 {
50 | now := time.Now().Format(TimeFormat)
51 | buf = append(buf, now...)
52 | }
53 | if l.flag&LFILE != 0 {
54 | if _, file, line, ok := runtime.Caller(calldepth); ok {
55 | buf = append(buf, file...)
56 | buf = append(buf, ':')
57 | buf = strconv.AppendInt(buf, int64(line), 10)
58 | }
59 | }
60 | if l.flag&LLEVEL != 0 {
61 | buf = append(buf, " ["...)
62 | buf = append(buf, Names[level]...)
63 | buf = append(buf, "] "...)
64 | }
65 | buf = append(buf, s...)
66 | cham.Main.Notify(logs, cham.PTYPE_GO, WRITE, buf)
67 | }
68 |
69 | func (l *Logger) Debug(v ...interface{}) {
70 | l.Output(2, LDEBUG, fmt.Sprint(v...))
71 | }
72 |
73 | func (l *Logger) Debugln(v ...interface{}) {
74 | l.Output(2, LDEBUG, fmt.Sprintln(v...))
75 | }
76 |
77 | func (l *Logger) Debugf(f string, v ...interface{}) {
78 | l.Output(2, LDEBUG, fmt.Sprintf(f, v...))
79 | }
80 |
81 | func (l *Logger) Info(v ...interface{}) {
82 | l.Output(2, LINFO, fmt.Sprint(v...))
83 | }
84 |
85 | func (l *Logger) Infoln(v ...interface{}) {
86 | l.Output(2, LINFO, fmt.Sprintln(v...))
87 | }
88 |
89 | func (l *Logger) Infof(f string, v ...interface{}) {
90 | l.Output(2, LINFO, fmt.Sprintf(f, v...))
91 | }
92 |
93 | func (l *Logger) Error(v ...interface{}) {
94 | l.Output(2, LERROR, fmt.Sprint(v...))
95 | }
96 |
97 | func (l *Logger) Errorln(v ...interface{}) {
98 | l.Output(2, LERROR, fmt.Sprintln(v...))
99 | }
100 |
101 | func (l *Logger) Errorf(f string, v ...interface{}) {
102 | l.Output(2, LERROR, fmt.Sprintf(f, v...))
103 | }
104 |
105 | // for std
106 | func Debug(v ...interface{}) {
107 | std.Output(2, LDEBUG, fmt.Sprint(v...))
108 | }
109 |
110 | func Debugln(v ...interface{}) {
111 | std.Output(2, LDEBUG, fmt.Sprintln(v...))
112 | }
113 |
114 | func Debugf(f string, v ...interface{}) {
115 | std.Output(2, LDEBUG, fmt.Sprintf(f, v...))
116 | }
117 |
118 | func Info(v ...interface{}) {
119 | std.Output(2, LINFO, fmt.Sprint(v...))
120 | }
121 |
122 | func Infoln(v ...interface{}) {
123 | std.Output(2, LINFO, fmt.Sprintln(v...))
124 | }
125 |
126 | func Infof(f string, v ...interface{}) {
127 | std.Output(2, LINFO, fmt.Sprintf(f, v...))
128 | }
129 |
130 | func Error(v ...interface{}) {
131 | std.Output(2, LERROR, fmt.Sprint(v...))
132 | }
133 |
134 | func Errorln(v ...interface{}) {
135 | std.Output(2, LERROR, fmt.Sprintln(v...))
136 | }
137 |
138 | func Errorf(f string, v ...interface{}) {
139 | std.Output(2, LERROR, fmt.Sprintf(f, v...))
140 | }
141 |
--------------------------------------------------------------------------------
/service/log/log_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "cham/cham"
5 | "testing"
6 | )
7 |
8 | func TestLog(t *testing.T) {
9 | Info("hello")
10 | Debug("world")
11 | }
12 |
13 | func TestFileLog(t *testing.T) {
14 | ll := New("log.txt", LDEFAULT, LDEBUG)
15 | ll.Infoln("hello")
16 | ll.Debugln("world")
17 | cham.Main.Call("log", cham.PTYPE_GO, FLUSH)
18 | }
19 |
20 | func TestAllFlagLog(t *testing.T) {
21 | ll := New("log.txt", LALL, LDEBUG)
22 | ll.Infoln("hello")
23 | ll.Debugln("world")
24 | }
25 |
--------------------------------------------------------------------------------
/service/log/logservice.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bufio"
5 | "cham/cham"
6 | "io"
7 | "os"
8 | )
9 |
10 | const (
11 | OPEN uint8 = iota
12 | WRITE
13 | FLUSH
14 | )
15 |
16 | type Loggers struct {
17 | outs []io.Writer
18 | }
19 |
20 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
21 | loggers := new(Loggers)
22 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
23 | cmd := args[0].(uint8)
24 | switch cmd {
25 | case OPEN:
26 | name := args[1].(string)
27 | if name == "" {
28 | loggers.outs = append(loggers.outs, os.Stdout)
29 | } else {
30 | if f, err := os.Create(name); err != nil {
31 | panic("create log file:" + name + ":" + err.Error())
32 | } else {
33 | loggers.outs = append(loggers.outs, bufio.NewWriter(f))
34 | }
35 | }
36 | case WRITE:
37 | data := args[1].([]byte)
38 | for _, f := range loggers.outs {
39 | f.Write(data)
40 | }
41 | case FLUSH:
42 | for _, f := range loggers.outs {
43 | if w, ok := f.(*bufio.Writer); ok {
44 | w.Flush()
45 | }
46 | }
47 | }
48 | return cham.NORET
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/service/multicast/channel.go:
--------------------------------------------------------------------------------
1 | package multicast
2 |
3 | import (
4 | "cham/cham"
5 | // "fmt"
6 | )
7 |
8 | var multicast *cham.Service
9 |
10 | type Channel struct {
11 | service *cham.Service
12 | Channel uint32
13 | }
14 |
15 | //Channel base on a service, args[0] is N worker to dispatch msg
16 | func New(service *cham.Service, channel uint32, start cham.Start, args ...interface{}) *Channel {
17 | multicast = cham.UniqueService("multicast", Start, args...)
18 | if channel == 0 {
19 | channel = service.Call(multicast, cham.PTYPE_GO, NEW, uint32(0), service.Addr)[0].(uint32)
20 | }
21 | service.RegisterProtocol(cham.PTYPE_MULTICAST, start)
22 | c := &Channel{service, channel}
23 | return c
24 | }
25 |
26 | func (c *Channel) Publish(args ...interface{}) {
27 | v := make([]interface{}, 0, len(args)+3)
28 | v = append(v, PUB, c.Channel, c.service.Addr)
29 | v = append(v, args...)
30 | c.service.Call(multicast, cham.PTYPE_GO, v...)
31 | }
32 |
33 | func (c *Channel) Subscribe() {
34 | c.service.Call(multicast, cham.PTYPE_GO, SUB, c.Channel, c.service.Addr)
35 | }
36 |
37 | func (c *Channel) Unsubscribe() {
38 | c.service.Notify(multicast, cham.PTYPE_GO, UNSUB, c.Channel, c.service.Addr)
39 | }
40 |
41 | func (c *Channel) Delete() {
42 | c.service.Notify(multicast, cham.PTYPE_GO, DEL, c.Channel, cham.Address(0))
43 | }
44 |
--------------------------------------------------------------------------------
/service/multicast/multicast.go:
--------------------------------------------------------------------------------
1 | package multicast
2 |
3 | import (
4 | "cham/cham"
5 | "cham/service/log"
6 | // "fmt"
7 | "sync/atomic"
8 | )
9 |
10 | const (
11 | DEFAULT_GROUP_SIZE = 64
12 | DEFAULT_CHANNEL_SIZE = 16
13 | )
14 |
15 | const (
16 | NEW uint8 = iota
17 | SUB
18 | PUB
19 | UNSUB
20 | DEL
21 | )
22 |
23 | type Multicast struct {
24 | channel uint32
25 | groups map[uint32]map[cham.Address]cham.NULL // channel->set(address)
26 | }
27 |
28 | func (m *Multicast) new(addr cham.Address) uint32 {
29 | ch := atomic.AddUint32(&m.channel, 1)
30 | m.groups[ch] = make(map[cham.Address]cham.NULL, DEFAULT_CHANNEL_SIZE)
31 | return ch
32 | }
33 |
34 | func (m *Multicast) sub(addr cham.Address, ch uint32) {
35 | if _, ok := m.groups[ch]; !ok {
36 | panic("should new a channel before sub a channel")
37 | }
38 | m.groups[ch][addr] = cham.NULLVALUE
39 | }
40 |
41 | func (m *Multicast) unsub(addr cham.Address, ch uint32) {
42 | if _, ok := m.groups[ch]; !ok {
43 | panic("should new a channel before unsub a channel")
44 | }
45 | delete(m.groups[ch], addr)
46 | }
47 |
48 | // multi invode harmless
49 | func (m *Multicast) del(ch uint32) {
50 | delete(m.groups, ch)
51 | }
52 |
53 | func (m *Multicast) pub(addr cham.Address, ch uint32, args ...interface{}) {
54 | peers := m.groups[ch]
55 | if len(peers) > 0 {
56 | data := make([]interface{}, 0, len(args)+1)
57 | data = append(data, ch)
58 | data = append(data, args...)
59 | msg := cham.NewMsg(addr, 0, cham.PTYPE_MULTICAST, data) // args[0] is channel id
60 | for peer := range peers {
61 | cham.Redirect(peer, msg)
62 | }
63 | }
64 | }
65 |
66 | //service self
67 | func Start(service *cham.Service, args ...interface{}) cham.Dispatch {
68 | log.Infoln("New Service ", service.String())
69 | mul := new(Multicast)
70 | mul.channel = 0
71 | mul.groups = make(map[uint32]map[cham.Address]cham.NULL, DEFAULT_GROUP_SIZE)
72 |
73 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
74 | cmd := args[0].(uint8)
75 | channel := args[1].(uint32)
76 | addr := args[2].(cham.Address)
77 |
78 | result := cham.NORET
79 |
80 | switch cmd {
81 | case NEW:
82 | result = cham.Ret(mul.new(addr))
83 | case SUB:
84 | mul.sub(addr, channel)
85 | case PUB:
86 | mul.pub(addr, channel, args[3:]...)
87 | case UNSUB:
88 | mul.unsub(addr, channel)
89 | case DEL:
90 | mul.del(channel)
91 | }
92 | return result
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/service/multicast/mutlticase_test.go:
--------------------------------------------------------------------------------
1 | package multicast
2 |
3 | import (
4 | "cham/cham"
5 | "fmt"
6 | "testing"
7 | )
8 |
9 | func mainStart(service *cham.Service, args ...interface{}) cham.Dispatch {
10 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
11 | fmt.Println(service)
12 | fmt.Println(args)
13 | return cham.NORET
14 | }
15 | }
16 |
17 | // args[0] is channel id
18 | func chatStart(service *cham.Service, args ...interface{}) cham.Dispatch {
19 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
20 | fmt.Println(service)
21 | fmt.Println(args)
22 | return cham.NORET
23 | }
24 | }
25 |
26 | func channelStart(service *cham.Service, args ...interface{}) cham.Dispatch {
27 | return func(session int32, source cham.Address, ptype uint8, args ...interface{}) []interface{} {
28 | fmt.Println(args)
29 | return cham.NORET
30 | }
31 | }
32 |
33 | func TestMulticast(t *testing.T) {
34 | main := cham.NewService("Leader", mainStart)
35 | chat1 := cham.NewService("chat1", chatStart)
36 | chat2 := cham.NewService("chat2", chatStart)
37 | //create a channel
38 | channel := New(main, 0, channelStart)
39 | //bind a channel
40 | chat1Channel := New(chat1, channel.Channel, channelStart)
41 | chat2Channel := New(chat2, channel.Channel, channelStart)
42 | chat1Channel.Subscribe()
43 | chat2Channel.Subscribe()
44 | channel.Publish("hello world")
45 | chat1Channel.Publish("i am chat1")
46 | chat2Channel.Unsubscribe()
47 | chat2.Stop() // test stop services
48 | channel.Publish("last")
49 | channel.Delete()
50 |
51 | }
52 |
--------------------------------------------------------------------------------