github.com/turingchain2020/turingchain@v1.1.21/queue/queue.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package queue turingchain底层消息队列模块
     6  package queue
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"os/signal"
    13  	"sync"
    14  	"sync/atomic"
    15  	"syscall"
    16  	"time"
    17  
    18  	"github.com/turingchain2020/turingchain/types"
    19  
    20  	log "github.com/turingchain2020/turingchain/common/log/log15"
    21  )
    22  
    23  //消息队列:
    24  //多对多消息队列
    25  //消息:topic
    26  
    27  //1. 队列特点:
    28  //1.1 一个topic 只有一个订阅者(以后会变成多个)目前基本够用,模块都只有一个实例.
    29  //1.2 消息的回复直接通过消息自带的channel 回复
    30  var qlog = log.New("module", "queue")
    31  
    32  const (
    33  	defaultChanBuffer    = 64
    34  	defaultLowChanBuffer = 40960
    35  )
    36  
    37  //消息队列的错误
    38  var (
    39  	ErrIsQueueClosed    = errors.New("ErrIsQueueClosed")
    40  	ErrQueueTimeout     = errors.New("ErrQueueTimeout")
    41  	ErrQueueChannelFull = errors.New("ErrQueueChannelFull")
    42  )
    43  
    44  // DisableLog disable log
    45  func DisableLog() {
    46  	qlog.SetHandler(log.DiscardHandler())
    47  }
    48  
    49  type chanSub struct {
    50  	high    chan *Message
    51  	low     chan *Message
    52  	isClose int32
    53  }
    54  
    55  // Queue only one obj in project
    56  // Queue only generate Client and start、Close operate,
    57  // if you send massage or receive massage on Queue, please use Client.
    58  type Queue interface {
    59  	Close()
    60  	Start()
    61  	Client() Client
    62  	Name() string
    63  	SetConfig(cfg *types.TuringchainConfig)
    64  	GetConfig() *types.TuringchainConfig
    65  }
    66  
    67  type queue struct {
    68  	chanSubs  map[string]*chanSub
    69  	mu        sync.Mutex
    70  	done      chan struct{}
    71  	interrupt chan struct{}
    72  	callback  chan *Message
    73  	isClose   int32
    74  	name      string
    75  	cfg       *types.TuringchainConfig
    76  	msgPool   *sync.Pool
    77  }
    78  
    79  // New new queue struct
    80  func New(name string) Queue {
    81  	q := &queue{
    82  		chanSubs:  make(map[string]*chanSub),
    83  		name:      name,
    84  		done:      make(chan struct{}, 1),
    85  		interrupt: make(chan struct{}, 1),
    86  		callback:  make(chan *Message, 1024),
    87  	}
    88  	q.msgPool = &sync.Pool{
    89  		New: func() interface{} {
    90  			return &Message{
    91  				chReply: make(chan *Message, 1),
    92  			}
    93  		},
    94  	}
    95  	go func() {
    96  		for {
    97  			select {
    98  			case <-q.done:
    99  				qlog.Info("closing turingchain callback")
   100  				return
   101  			case msg := <-q.callback:
   102  				if msg.callback != nil {
   103  					msg.callback(msg)
   104  				}
   105  			}
   106  		}
   107  	}()
   108  	return q
   109  }
   110  
   111  // GetConfig return the queue TuringchainConfig
   112  func (q *queue) GetConfig() *types.TuringchainConfig {
   113  	return q.cfg
   114  }
   115  
   116  // Name return the queue name
   117  func (q *queue) SetConfig(cfg *types.TuringchainConfig) {
   118  	if cfg == nil {
   119  		panic("set config is nil")
   120  	}
   121  	if q.cfg != nil {
   122  		panic("do not reset queue config")
   123  	}
   124  	q.cfg = cfg
   125  }
   126  
   127  // Name return the queue name
   128  func (q *queue) Name() string {
   129  	return q.name
   130  }
   131  
   132  // Start 开始运行消息队列
   133  func (q *queue) Start() {
   134  	c := make(chan os.Signal, 1)
   135  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   136  	// Block until a signal is received.
   137  	select {
   138  	case <-q.done:
   139  		qlog.Info("closing turingchain done")
   140  		//atomic.StoreInt32(&q.isClose, 1)
   141  		break
   142  	case <-q.interrupt:
   143  		qlog.Info("closing turingchain")
   144  		//atomic.StoreInt32(&q.isClose, 1)
   145  		break
   146  	case s := <-c:
   147  		qlog.Info("Got signal:", s)
   148  		//atomic.StoreInt32(&q.isClose, 1)
   149  		break
   150  	}
   151  }
   152  
   153  func (q *queue) isClosed() bool {
   154  	return atomic.LoadInt32(&q.isClose) == 1
   155  }
   156  
   157  // Close 关闭消息队列
   158  func (q *queue) Close() {
   159  	if q.isClosed() {
   160  		return
   161  	}
   162  	q.mu.Lock()
   163  	for topic, ch := range q.chanSubs {
   164  		if ch.isClose == 0 {
   165  			ch.high <- &Message{}
   166  			ch.low <- &Message{}
   167  			q.chanSubs[topic] = &chanSub{isClose: 1}
   168  		}
   169  	}
   170  	q.mu.Unlock()
   171  	q.done <- struct{}{}
   172  	close(q.done)
   173  	atomic.StoreInt32(&q.isClose, 1)
   174  	qlog.Info("queue module closed")
   175  }
   176  
   177  func (q *queue) chanSub(topic string) *chanSub {
   178  	q.mu.Lock()
   179  	defer q.mu.Unlock()
   180  	_, ok := q.chanSubs[topic]
   181  	if !ok {
   182  		q.chanSubs[topic] = &chanSub{
   183  			high:    make(chan *Message, defaultChanBuffer),
   184  			low:     make(chan *Message, defaultLowChanBuffer),
   185  			isClose: 0,
   186  		}
   187  	}
   188  	return q.chanSubs[topic]
   189  }
   190  
   191  func (q *queue) closeTopic(topic string) {
   192  	q.mu.Lock()
   193  	defer q.mu.Unlock()
   194  	sub, ok := q.chanSubs[topic]
   195  	if !ok {
   196  		return
   197  	}
   198  	if sub.isClose == 0 {
   199  		sub.high <- &Message{}
   200  		sub.low <- &Message{}
   201  	}
   202  	q.chanSubs[topic] = &chanSub{isClose: 1}
   203  }
   204  
   205  func (q *queue) send(msg *Message, timeout time.Duration) (err error) {
   206  	if q.isClosed() {
   207  		return types.ErrChannelClosed
   208  	}
   209  	sub := q.chanSub(msg.Topic)
   210  	if sub.isClose == 1 {
   211  		return types.ErrChannelClosed
   212  	}
   213  	if timeout == -1 {
   214  		sub.high <- msg
   215  		return nil
   216  	}
   217  	defer func() {
   218  		res := recover()
   219  		if res != nil {
   220  			err = res.(error)
   221  		}
   222  	}()
   223  	if timeout == 0 {
   224  		select {
   225  		case sub.high <- msg:
   226  			return nil
   227  		default:
   228  			qlog.Error("send chainfull", "msg", msg, "topic", msg.Topic, "sub", sub)
   229  			return ErrQueueChannelFull
   230  		}
   231  	}
   232  	t := time.NewTimer(timeout)
   233  	defer t.Stop()
   234  	select {
   235  	case sub.high <- msg:
   236  	case <-t.C:
   237  		qlog.Error("send timeout", "msg", msg, "topic", msg.Topic, "sub", sub)
   238  		return ErrQueueTimeout
   239  	}
   240  	return nil
   241  }
   242  
   243  func (q *queue) sendAsyn(msg *Message) error {
   244  	if q.isClosed() {
   245  		return types.ErrChannelClosed
   246  	}
   247  	sub := q.chanSub(msg.Topic)
   248  	if sub.isClose == 1 {
   249  		return types.ErrChannelClosed
   250  	}
   251  	select {
   252  	case sub.low <- msg:
   253  		return nil
   254  	default:
   255  		qlog.Error("send asyn err", "msg", msg, "err", ErrQueueChannelFull)
   256  		return ErrQueueChannelFull
   257  	}
   258  }
   259  
   260  func (q *queue) sendLowTimeout(msg *Message, timeout time.Duration) error {
   261  	if q.isClosed() {
   262  		return types.ErrChannelClosed
   263  	}
   264  	sub := q.chanSub(msg.Topic)
   265  	if sub.isClose == 1 {
   266  		return types.ErrChannelClosed
   267  	}
   268  	if timeout == -1 {
   269  		sub.low <- msg
   270  		return nil
   271  	}
   272  	if timeout == 0 {
   273  		return q.sendAsyn(msg)
   274  	}
   275  	t := time.NewTimer(timeout)
   276  	defer t.Stop()
   277  	select {
   278  	case sub.low <- msg:
   279  		return nil
   280  	case <-t.C:
   281  		qlog.Error("send asyn timeout", "msg", msg)
   282  		return ErrQueueTimeout
   283  	}
   284  }
   285  
   286  // Client new client
   287  func (q *queue) Client() Client {
   288  	return newClient(q)
   289  }
   290  
   291  // Message message struct
   292  type Message struct {
   293  	Topic    string
   294  	Ty       int64
   295  	ID       int64
   296  	Data     interface{}
   297  	chReply  chan *Message
   298  	callback func(msg *Message)
   299  }
   300  
   301  // NewMessage new message
   302  func NewMessage(id int64, topic string, ty int64, data interface{}) (msg *Message) {
   303  	msg = &Message{}
   304  	msg.ID = id
   305  	msg.Ty = ty
   306  	msg.Data = data
   307  	msg.Topic = topic
   308  	msg.chReply = make(chan *Message, 1)
   309  	return msg
   310  }
   311  
   312  // NewMessageCallback reply block
   313  func NewMessageCallback(id int64, topic string, ty int64, data interface{}, callback func(msg *Message)) (msg *Message) {
   314  	msg = &Message{}
   315  	msg.ID = id
   316  	msg.Ty = ty
   317  	msg.Data = data
   318  	msg.Topic = topic
   319  	msg.callback = callback
   320  	return msg
   321  }
   322  
   323  // GetData get message data
   324  func (msg *Message) GetData() interface{} {
   325  	if _, ok := msg.Data.(error); ok {
   326  		return nil
   327  	}
   328  	return msg.Data
   329  }
   330  
   331  // Err if err return error msg, or return nil
   332  func (msg *Message) Err() error {
   333  	if err, ok := msg.Data.(error); ok {
   334  		return err
   335  	}
   336  	return nil
   337  }
   338  
   339  // Reply reply message to reply chan
   340  func (msg *Message) Reply(replyMsg *Message) {
   341  	if msg.chReply == nil {
   342  		qlog.Debug("reply a empty chreply", "msg", msg)
   343  		return
   344  	}
   345  	msg.chReply <- replyMsg
   346  }
   347  
   348  // String print the message information
   349  func (msg *Message) String() string {
   350  	return fmt.Sprintf("{topic:%s, Ty:%s, Id:%d, Err:%v, Ch:%v}", msg.Topic,
   351  		types.GetEventName(int(msg.Ty)), msg.ID, msg.Err(), msg.chReply != nil)
   352  }
   353  
   354  // ReplyErr reply error
   355  func (msg *Message) ReplyErr(title string, err error) {
   356  	var reply types.Reply
   357  	if err != nil {
   358  		qlog.Error(title, "reply.err", err.Error())
   359  		reply.IsOk = false
   360  		reply.Msg = []byte(err.Error())
   361  	} else {
   362  		qlog.Debug(title, "success", "ok")
   363  		reply.IsOk = true
   364  	}
   365  	id := atomic.AddInt64(&gid, 1)
   366  	msg.Reply(NewMessage(id, "", types.EventReply, &reply))
   367  }