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  }