github.com/glide-im/glide@v1.6.0/pkg/subscription/subscription_impl/chan.go (about) 1 package subscription_impl 2 3 import ( 4 "errors" 5 "github.com/glide-im/glide/pkg/gate" 6 "github.com/glide-im/glide/pkg/logger" 7 "github.com/glide-im/glide/pkg/messages" 8 "github.com/glide-im/glide/pkg/store" 9 "github.com/glide-im/glide/pkg/subscription" 10 "github.com/glide-im/glide/pkg/timingwheel" 11 errors2 "github.com/pkg/errors" 12 "reflect" 13 "sync" 14 "sync/atomic" 15 "time" 16 ) 17 18 const ( 19 errNotMemberOfChannel = "not member of channel" 20 errPermissionDeniedWrite = "permission denied: write" 21 errChannelMuted = "channel is muted" 22 errChannelBlocked = "channel is blocked" 23 ) 24 25 var tw = timingwheel.NewTimingWheel(time.Second, 3, 20) 26 27 // messageQueueTimeout channel message push queue idle timeout. 28 const messageQueueTimeout = time.Second * 10 29 30 // ChannelSequenceStore . 31 type ChannelSequenceStore interface { 32 33 // NextSegmentSequence return the next segment of specified channel, and segment length. 34 NextSegmentSequence(id subscription.ChanID, info subscription.ChanInfo) (int64, int64, error) 35 } 36 37 // SubscriberOptions is the options for the subscriber 38 type SubscriberOptions struct { 39 Perm Permission 40 } 41 42 // getSubscriberOptions assertion type of `i` is *SubscribeOptions 43 func getSubscriberOptions(i interface{}) (*SubscriberOptions, error) { 44 so, ok1 := i.(*SubscriberOptions) 45 if !ok1 { 46 return nil, errors.New("extra expect type: *SubscriberOptions, actual: " + reflect.TypeOf(i).String()) 47 } 48 return so, nil 49 } 50 51 type SubscriberInfo struct { 52 Perm Permission 53 } 54 55 func (i *SubscriberInfo) canRead() bool { 56 return i.Perm.allows(MaskPermRead) 57 } 58 59 func (i *SubscriberInfo) canWrite() bool { 60 return i.Perm.allows(MaskPermWrite) 61 } 62 63 func (i *SubscriberInfo) isSystem() bool { 64 return i.Perm.allows(MaskPermSystem) 65 } 66 67 func (i *SubscriberInfo) isAdmin() bool { 68 return i.Perm.allows(MaskPermAdmin) 69 } 70 71 func (i *SubscriberInfo) update(options *SubscriberOptions) error { 72 i.Perm = options.Perm 73 return nil 74 } 75 76 func NewSubscriberInfo(so *SubscriberOptions) *SubscriberInfo { 77 return &SubscriberInfo{ 78 Perm: so.Perm, 79 } 80 } 81 82 type Channel struct { 83 id subscription.ChanID 84 85 seq int64 86 seqRemain int64 87 88 messages chan *PublishMessage 89 90 queueRunning int32 91 queued int32 92 93 sleepTimer *timingwheel.Task 94 95 activeAt time.Time 96 mu *sync.RWMutex 97 subscribers map[subscription.SubscriberID]*SubscriberInfo 98 info *subscription.ChanInfo 99 100 store store.SubscriptionStore 101 seqStore ChannelSequenceStore 102 gate gate.DefaultGateway 103 } 104 105 func NewChannel(chanID subscription.ChanID, gate gate.DefaultGateway, 106 store store.SubscriptionStore, seqStore ChannelSequenceStore) (*Channel, error) { 107 108 ret := &Channel{ 109 id: chanID, 110 messages: make(chan *PublishMessage, 100), 111 sleepTimer: tw.After(messageQueueTimeout), 112 mu: &sync.RWMutex{}, 113 subscribers: map[subscription.SubscriberID]*SubscriberInfo{}, 114 info: &subscription.ChanInfo{}, 115 store: store, 116 seqStore: seqStore, 117 gate: gate, 118 } 119 err := ret.loadSeq() 120 if err != nil { 121 return nil, err 122 } 123 return ret, nil 124 } 125 126 func (g *Channel) Update(ci *subscription.ChanInfo) error { 127 g.info.Blocked = ci.Blocked 128 g.info.Muted = ci.Muted 129 return nil 130 } 131 132 func (g *Channel) Subscribe(id subscription.SubscriberID, extra interface{}) error { 133 so, err := getSubscriberOptions(extra) 134 if err != nil { 135 return err 136 } 137 138 if g.info.Closed { 139 return errors.New("channel is closed") 140 } 141 if g.info.Blocked { 142 return errors.New("channel is blocked") 143 } 144 145 g.mu.Lock() 146 defer g.mu.Unlock() 147 148 sb, ok := g.subscribers[id] 149 if ok { 150 return sb.update(so) 151 } 152 g.subscribers[id] = NewSubscriberInfo(so) 153 return nil 154 } 155 156 func (g *Channel) Unsubscribe(id subscription.SubscriberID) error { 157 g.mu.Lock() 158 defer g.mu.Unlock() 159 160 _, ok := g.subscribers[id] 161 if !ok { 162 return errors.New(subscription.ErrNotSubscribed) 163 } 164 delete(g.subscribers, id) 165 return nil 166 } 167 168 func (g *Channel) Publish(msg subscription.Message) error { 169 if g.info.Closed { 170 return errors.New("channel closed") 171 } 172 message, ok := msg.(*PublishMessage) 173 if !ok { 174 return errors.New("unexpected message type, expect: *subscription.PublishMessage, actual:" + reflect.TypeOf(msg).String()) 175 } 176 177 if !isValidMessageType(message.Type) { 178 return errors.New(errUnknownMessageType) 179 } 180 181 g.mu.RLock() 182 s, exist := g.subscribers[message.From] 183 g.mu.RUnlock() 184 185 if !exist { 186 return errors.New(errNotMemberOfChannel) 187 } 188 if !s.canWrite() { 189 return errors.New(errPermissionDeniedWrite) 190 } 191 if g.info.Muted { 192 if !s.isSystem() || !s.isAdmin() { 193 return errors.New(errChannelMuted) 194 } 195 } 196 if g.info.Blocked { 197 if !s.isSystem() { 198 return errors.New(errChannelBlocked) 199 } 200 } 201 202 switch message.Type { 203 case TypeNotify: 204 return g.enqueueNotify(message) 205 case TypeMessage: 206 err := g.enqueue(message) 207 return err 208 default: 209 return errors.New(errUnknownMessageType) 210 } 211 } 212 213 func (g *Channel) Close() error { 214 g.mu.Lock() 215 defer g.mu.Unlock() 216 217 g.info.Closed = true 218 219 close(g.messages) 220 g.subscribers = map[subscription.SubscriberID]*SubscriberInfo{} 221 222 if g.queued > 0 { 223 logger.D("chan %s closed, %d messages dropped", g.id, g.queued) 224 } 225 return nil 226 } 227 228 func (g *Channel) enqueueNotify(msg *PublishMessage) error { 229 select { 230 case g.messages <- msg: 231 atomic.AddInt32(&g.queued, 1) 232 default: 233 return errors.New("notify message queue is full") 234 } 235 return g.checkMsgQueue() 236 } 237 238 func (g *Channel) enqueue(m *PublishMessage) error { 239 240 cm, err := m.GetChatMessage() 241 if err != nil { 242 return errors2.Wrap(err, "enqueue message deserialize body error") 243 } 244 m.Seq, err = g.nextSeq() 245 if err != nil { 246 return err 247 } 248 cm.Seq = m.Seq 249 m.Message.Data = messages.NewData(&cm) 250 251 if m.Type == TypeMessage { 252 err = g.store.StoreChannelMessage(g.id, cm) 253 if err != nil { 254 return errors2.Wrap(err, "store channel message error") 255 } 256 } 257 258 select { 259 case g.messages <- m: 260 atomic.AddInt32(&g.queued, 1) 261 default: 262 return errors.New("too many messages,the group message queue is full") 263 } 264 if err = g.checkMsgQueue(); err != nil { 265 return err 266 } 267 return nil 268 } 269 270 func (g *Channel) checkMsgQueue() error { 271 272 if atomic.LoadInt32(&g.queueRunning) != 0 { 273 return nil 274 } 275 276 atomic.StoreInt32(&g.queueRunning, 1) 277 go func() { 278 defer func() { 279 err := recover() 280 if err != nil { 281 atomic.StoreInt32(&g.queueRunning, 0) 282 logger.E("message queue panic: %v", err) 283 } 284 }() 285 286 g.sleepTimer = tw.After(messageQueueTimeout) 287 for { 288 select { 289 case <-g.sleepTimer.C: 290 g.sleepTimer.Cancel() 291 if g.activeAt.Add(messageQueueTimeout).Before(time.Now()) { 292 goto REST 293 } else { 294 g.sleepTimer = tw.After(messageQueueTimeout) 295 } 296 case m := <-g.messages: 297 if m == nil { 298 goto REST 299 } 300 atomic.AddInt32(&g.queued, -1) 301 g.activeAt = time.Now() 302 g.push(m) 303 } 304 } 305 REST: 306 dropped := 0 307 if atomic.LoadInt32(&g.queued) > 0 { 308 for { 309 _, ok := <-g.messages 310 if !ok { 311 break 312 } 313 dropped++ 314 atomic.StoreInt32(&g.queued, -1) 315 } 316 } 317 if dropped > 0 { 318 logger.W("chan %s message queue stopped, %d message(s) have been dropped", g.id, dropped) 319 } else { 320 logger.D("chan %s message queue stopped", g.id) 321 } 322 atomic.StoreInt32(&g.queued, 0) 323 atomic.StoreInt32(&g.queueRunning, 0) 324 }() 325 return nil 326 } 327 328 func (g *Channel) push(message *PublishMessage) { 329 logger.I("chan %s push message: %v", g.id, message.Message) 330 331 g.mu.RLock() 332 defer g.mu.RUnlock() 333 334 // TODO recycler use 335 var received = map[subscription.SubscriberID]interface{}{} 336 337 if message.To != nil { 338 for _, id := range message.To { 339 received[id] = nil 340 } 341 } 342 343 for subscriberID, sInfo := range g.subscribers { 344 if received != nil && len(received) > 0 { 345 _, contained := received[subscriberID] 346 if !contained { 347 continue 348 } 349 } 350 if !sInfo.canRead() { 351 continue 352 } 353 err := g.gate.EnqueueMessage(gate.ID(subscriberID), message.Message) 354 if err != nil { 355 logger.E("chan %s push message to subscribe %s error: %v", g.id, subscriberID, err) 356 } 357 } 358 } 359 360 func (g *Channel) nextSeq() (int64, error) { 361 if atomic.AddInt64(&g.seqRemain, -1) <= 0 { 362 err := g.loadSeq() 363 if err != nil { 364 return 0, err 365 } 366 atomic.AddInt64(&g.seq, -1) 367 } 368 return atomic.AddInt64(&g.seq, 1), nil 369 } 370 371 func (g *Channel) loadSeq() error { 372 seq, length, err := g.seqStore.NextSegmentSequence(g.id, *g.info) 373 if err != nil { 374 return err 375 } 376 atomic.StoreInt64(&g.seqRemain, length) 377 // because seq increment before set to message 378 atomic.StoreInt64(&g.seq, seq) 379 return nil 380 }