github.com/turingchain2020/turingchain@v1.1.21/queue/client.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
     6  
     7  import (
     8  	"errors"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"unsafe"
    14  
    15  	"github.com/turingchain2020/turingchain/types"
    16  )
    17  
    18  //消息队列的主要作用是解耦合,让各个模块相对的独立运行。
    19  //每个模块都会有一个client 对象
    20  //主要的操作大致如下:
    21  // client := queue.Client()
    22  // client.Sub("topicname")
    23  // for msg := range client.Recv() {
    24  //     process(msg)
    25  // }
    26  // process 函数会调用 处理具体的消息逻辑
    27  
    28  var gid int64
    29  
    30  // Client 消息队列的接口,每个模块都需要一个发送接受client
    31  type Client interface {
    32  	Send(msg *Message, waitReply bool) (err error) //同步发送消息
    33  	SendTimeout(msg *Message, waitReply bool, timeout time.Duration) (err error)
    34  	Wait(msg *Message) (*Message, error)                               //等待消息处理完成
    35  	WaitTimeout(msg *Message, timeout time.Duration) (*Message, error) //等待消息处理完成
    36  	Recv() chan *Message
    37  	Reply(msg *Message)
    38  	Sub(topic string) //订阅消息
    39  	Close()
    40  	CloseQueue() (*types.Reply, error)
    41  	NewMessage(topic string, ty int64, data interface{}) (msg *Message)
    42  	FreeMessage(msg ...*Message) //回收msg, 需要注意回收时上下文不再引用
    43  	GetConfig() *types.TuringchainConfig
    44  }
    45  
    46  // Module be used for module interface
    47  type Module interface {
    48  	SetQueueClient(client Client)
    49  	//wait for ready
    50  	Wait()
    51  	Close()
    52  }
    53  
    54  type client struct {
    55  	q          *queue
    56  	recv       chan *Message
    57  	done       chan struct{}
    58  	wg         *sync.WaitGroup
    59  	topic      unsafe.Pointer
    60  	isClosed   int32
    61  	isCloseing int32
    62  }
    63  
    64  func newClient(q *queue) Client {
    65  	client := &client{}
    66  	client.q = q
    67  	client.recv = make(chan *Message, 5)
    68  	client.done = make(chan struct{}, 1)
    69  	client.wg = &sync.WaitGroup{}
    70  	return client
    71  }
    72  
    73  // GetConfig return the queue TuringchainConfig
    74  func (client *client) GetConfig() *types.TuringchainConfig {
    75  	types.AssertConfig(client.q)
    76  	cfg := client.q.GetConfig()
    77  	if cfg == nil {
    78  		panic("TuringchainConfig is nil")
    79  	}
    80  	return cfg
    81  }
    82  
    83  // Send 发送消息,msg 消息 ,waitReply 是否等待回应
    84  //1. 系统保证send出去的消息就是成功了,除非系统崩溃
    85  //2. 系统保证每个消息都有对应的 response 消息
    86  func (client *client) Send(msg *Message, waitReply bool) (err error) {
    87  	timeout := time.Duration(-1)
    88  	err = client.SendTimeout(msg, waitReply, timeout)
    89  	if err == ErrQueueTimeout {
    90  		panic(err)
    91  	}
    92  	return err
    93  }
    94  
    95  // SendTimeout 超时发送, msg 消息 ,waitReply 是否等待回应, timeout 超时时间
    96  func (client *client) SendTimeout(msg *Message, waitReply bool, timeout time.Duration) (err error) {
    97  	if client.isClose() {
    98  		return ErrIsQueueClosed
    99  	}
   100  	if !waitReply {
   101  		//msg.chReply = nil
   102  		return client.q.sendLowTimeout(msg, timeout)
   103  	}
   104  	return client.q.send(msg, timeout)
   105  }
   106  
   107  //系统设计出两种优先级别的消息发送
   108  //1. SendAsyn 低优先级
   109  //2. Send 高优先级别的发送消息
   110  
   111  // NewMessage 新建消息 topic模块名称 ty消息类型 data 数据
   112  func (client *client) NewMessage(topic string, ty int64, data interface{}) (msg *Message) {
   113  	id := atomic.AddInt64(&gid, 1)
   114  	msg = client.q.msgPool.Get().(*Message)
   115  	msg.ID = id
   116  	msg.Ty = ty
   117  	msg.Data = data
   118  	msg.Topic = topic
   119  	return
   120  }
   121  
   122  // FreeMessage 回收msg 到内存池
   123  func (client *client) FreeMessage(msgs ...*Message) {
   124  	for _, msg := range msgs {
   125  		if msg == nil || msg.chReply == nil {
   126  			continue
   127  		}
   128  		msg.Data = nil
   129  		client.q.msgPool.Put(msg)
   130  	}
   131  }
   132  
   133  func (client *client) Reply(msg *Message) {
   134  	if msg.chReply != nil {
   135  		msg.Reply(msg)
   136  		return
   137  	}
   138  	if msg.callback != nil {
   139  		client.q.callback <- msg
   140  	}
   141  }
   142  
   143  // WaitTimeout 等待时间 msg 消息 timeout 超时时间
   144  func (client *client) WaitTimeout(msg *Message, timeout time.Duration) (*Message, error) {
   145  	if msg.chReply == nil {
   146  		return &Message{}, errors.New("empty wait channel")
   147  	}
   148  
   149  	var t <-chan time.Time
   150  	if timeout > 0 {
   151  		timer := time.NewTimer(timeout)
   152  		defer timer.Stop()
   153  		t = timer.C
   154  	}
   155  	select {
   156  	case msg = <-msg.chReply:
   157  		return msg, msg.Err()
   158  	case <-client.done:
   159  		return &Message{}, ErrIsQueueClosed
   160  	case <-t:
   161  		return &Message{}, ErrQueueTimeout
   162  	}
   163  }
   164  
   165  // Wait 等待时间
   166  func (client *client) Wait(msg *Message) (*Message, error) {
   167  	timeout := time.Duration(-1)
   168  	msg, err := client.WaitTimeout(msg, timeout)
   169  	if err == ErrQueueTimeout {
   170  		panic(err)
   171  	}
   172  	return msg, err
   173  }
   174  
   175  // Recv 获取接受消息通道
   176  func (client *client) Recv() chan *Message {
   177  	return client.recv
   178  }
   179  
   180  func (client *client) getTopic() string {
   181  	return *(*string)(atomic.LoadPointer(&client.topic))
   182  }
   183  
   184  func (client *client) setTopic(topic string) {
   185  	// #nosec
   186  	atomic.StorePointer(&client.topic, unsafe.Pointer(&topic))
   187  }
   188  
   189  func (client *client) isClose() bool {
   190  	return atomic.LoadInt32(&client.isClosed) == 1
   191  }
   192  
   193  func (client *client) isInClose() bool {
   194  	return atomic.LoadInt32(&client.isCloseing) == 1
   195  }
   196  
   197  // Close 关闭client
   198  func (client *client) Close() {
   199  	if atomic.LoadInt32(&client.isClosed) == 1 || atomic.LoadPointer(&client.topic) == nil {
   200  		return
   201  	}
   202  	topic := client.getTopic()
   203  	client.q.closeTopic(topic)
   204  	close(client.done)
   205  	atomic.StoreInt32(&client.isCloseing, 1)
   206  	client.wg.Wait()
   207  	atomic.StoreInt32(&client.isClosed, 1)
   208  	close(client.Recv())
   209  	for msg := range client.Recv() {
   210  		msg.Reply(client.NewMessage(msg.Topic, msg.Ty, types.ErrChannelClosed))
   211  	}
   212  }
   213  
   214  // CloseQueue 关闭消息队列
   215  func (client *client) CloseQueue() (*types.Reply, error) {
   216  	//	client.q.Close()
   217  	if client.q.isClosed() {
   218  		return &types.Reply{IsOk: true}, nil
   219  	}
   220  	qlog.Debug("queue", "msg", "closing turingchain")
   221  	client.q.interrupt <- struct{}{}
   222  	//	close(client.q.interupt)
   223  	return &types.Reply{IsOk: true}, nil
   224  }
   225  
   226  func (client *client) isEnd(data *Message, ok bool) bool {
   227  	if !ok {
   228  		return true
   229  	}
   230  	if data.Data == nil && data.ID == 0 && data.Ty == 0 {
   231  		return true
   232  	}
   233  	if atomic.LoadInt32(&client.isClosed) == 1 {
   234  		return true
   235  	}
   236  	return false
   237  }
   238  
   239  // Sub 订阅消息类型
   240  func (client *client) Sub(topic string) {
   241  	//正在关闭或者已经关闭
   242  	if client.isInClose() || client.isClose() {
   243  		return
   244  	}
   245  	client.wg.Add(1)
   246  	client.setTopic(topic)
   247  	sub := client.q.chanSub(topic)
   248  	go func() {
   249  		defer func() {
   250  			client.wg.Done()
   251  		}()
   252  		for {
   253  			select {
   254  			case data, ok := <-sub.high:
   255  				if client.isEnd(data, ok) {
   256  					qlog.Info("unsub1", "topic", topic)
   257  					return
   258  				}
   259  				client.Recv() <- data
   260  			default:
   261  				select {
   262  				case data, ok := <-sub.high:
   263  					if client.isEnd(data, ok) {
   264  						qlog.Info("unsub2", "topic", topic)
   265  						return
   266  					}
   267  					client.Recv() <- data
   268  				case data, ok := <-sub.low:
   269  					if client.isEnd(data, ok) {
   270  						qlog.Info("unsub3", "topic", topic)
   271  						return
   272  					}
   273  					client.Recv() <- data
   274  				case <-client.done:
   275  					qlog.Error("unsub4", "topic", topic)
   276  					return
   277  				}
   278  			}
   279  		}
   280  	}()
   281  }