github.com/polarismesh/polaris@v1.17.8/common/eventhub/topic.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package eventhub 19 20 import ( 21 "context" 22 "sync" 23 24 "github.com/google/uuid" 25 26 "github.com/polarismesh/polaris/common/log" 27 ) 28 29 //go:generate gotests -w -all topic.go 30 31 type topic struct { 32 name string 33 queue chan Event 34 closeCh chan struct{} 35 subs map[string]*subscription 36 mu sync.RWMutex 37 } 38 39 func newTopic(name string) *topic { 40 t := &topic{ 41 name: name, 42 queue: make(chan Event, defaultQueueSize), 43 closeCh: make(chan struct{}), 44 subs: make(map[string]*subscription), 45 } 46 return t 47 } 48 49 // publish publish msg to topic 50 func (t *topic) publish(ctx context.Context, event Event) { 51 if log.DebugEnabled() { 52 log.Debugf("[EventHub] publish topic:%s, event:%v", t.name, event) 53 } 54 t.queue <- event 55 } 56 57 // subscribe subscribe msg from topic 58 func (t *topic) subscribe(ctx context.Context, handler Handler, 59 opts ...SubOption) (*SubscribtionContext, error) { 60 61 subID := uuid.NewString() 62 sub := newSubscription(subID, handler, opts...) 63 64 t.mu.Lock() 65 defer t.mu.Unlock() 66 t.subs[subID] = sub 67 68 newCtx, cancel := context.WithCancel(ctx) 69 subscribtionCtx := &SubscribtionContext{ 70 subID: subID, 71 cancel: func() { 72 cancel() 73 t.unsubscribe(subID) 74 }, 75 } 76 77 go sub.receive(newCtx) 78 return subscribtionCtx, nil 79 } 80 81 // unsubscribe unsubscrib msg from topic 82 func (t *topic) unsubscribe(name string) { 83 sub, ok := t.subs[name] 84 if !ok { 85 return 86 } 87 t.mu.Lock() 88 defer t.mu.Unlock() 89 delete(t.subs, sub.name) 90 } 91 92 // close close topic 93 func (t *topic) close(ctx context.Context) { 94 t.mu.Lock() 95 defer t.mu.Unlock() 96 close(t.closeCh) 97 for _, sub := range t.subs { 98 sub.close() 99 delete(t.subs, sub.name) 100 } 101 } 102 103 // run read msg from topic queue and send to all subscription 104 func (t *topic) run(ctx context.Context) { 105 log.Infof("[EventHub] topic:%s run dispatch", t.name) 106 for { 107 select { 108 case msg := <-t.queue: 109 func() { 110 subs := t.listSubscribers() 111 for i := range subs { 112 sub := subs[i] 113 go sub.send(ctx, msg) 114 } 115 }() 116 case <-t.closeCh: 117 log.Infof("[EventHub] topic:%s run stop", t.name) 118 return 119 case <-ctx.Done(): 120 log.Infof("[EventHub] topic:%s run stop by context cancel", t.name) 121 return 122 } 123 } 124 } 125 126 func (t *topic) listSubscribers() []*subscription { 127 t.mu.RLock() 128 defer t.mu.RUnlock() 129 ret := make([]*subscription, 0, len(t.subs)) 130 for _, sub := range t.subs { 131 ret = append(ret, sub) 132 } 133 return ret 134 }