github.com/igggame/nebulas-go@v2.1.0+incompatible/net/dispatcher.go (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package net
    20  
    21  import (
    22  	"fmt"
    23  	"sync"
    24  	"time"
    25  
    26  	lru "github.com/hashicorp/golang-lru"
    27  	"github.com/nebulasio/go-nebulas/metrics"
    28  	"github.com/nebulasio/go-nebulas/util/logging"
    29  	"github.com/sirupsen/logrus"
    30  )
    31  
    32  var (
    33  	metricsDispatcherCached     = metrics.NewGauge("neb.net.dispatcher.cached")
    34  	metricsDispatcherDuplicated = metrics.NewMeter("neb.net.dispatcher.duplicated")
    35  )
    36  
    37  // Dispatcher a message dispatcher service.
    38  type Dispatcher struct {
    39  	subscribersMap     *sync.Map
    40  	quitCh             chan bool
    41  	receivedMessageCh  chan Message
    42  	dispatchedMessages *lru.Cache
    43  	filters            map[string]bool
    44  }
    45  
    46  // NewDispatcher create Dispatcher instance.
    47  func NewDispatcher() *Dispatcher {
    48  	dp := &Dispatcher{
    49  		subscribersMap:    new(sync.Map),
    50  		quitCh:            make(chan bool, 10),
    51  		receivedMessageCh: make(chan Message, 65536),
    52  		filters:           make(map[string]bool),
    53  	}
    54  
    55  	dp.dispatchedMessages, _ = lru.New(51200)
    56  
    57  	return dp
    58  }
    59  
    60  // Register register subscribers.
    61  func (dp *Dispatcher) Register(subscribers ...*Subscriber) {
    62  	for _, v := range subscribers {
    63  		mt := v.MessageType()
    64  		m, _ := dp.subscribersMap.LoadOrStore(mt, new(sync.Map))
    65  		m.(*sync.Map).Store(v, true)
    66  		dp.filters[mt] = v.DoFilter()
    67  	}
    68  }
    69  
    70  // Deregister deregister subscribers.
    71  func (dp *Dispatcher) Deregister(subscribers ...*Subscriber) {
    72  
    73  	for _, v := range subscribers {
    74  		mt := v.MessageType()
    75  		m, _ := dp.subscribersMap.Load(mt)
    76  		if m == nil {
    77  			continue
    78  		}
    79  		m.(*sync.Map).Delete(v)
    80  		delete(dp.filters, mt)
    81  	}
    82  }
    83  
    84  // Start start message dispatch goroutine.
    85  func (dp *Dispatcher) Start() {
    86  	logging.CLog().Info("Starting NebService Dispatcher...")
    87  	go dp.loop()
    88  }
    89  
    90  func (dp *Dispatcher) loop() {
    91  	logging.CLog().Info("Started NewService Dispatcher.")
    92  
    93  	timerChan := time.NewTicker(time.Second).C
    94  	for {
    95  		select {
    96  		case <-timerChan:
    97  			metricsDispatcherCached.Update(int64(len(dp.receivedMessageCh)))
    98  		case <-dp.quitCh:
    99  			logging.CLog().Info("Stoped NebService Dispatcher.")
   100  			return
   101  		case msg := <-dp.receivedMessageCh:
   102  			msgType := msg.MessageType()
   103  
   104  			v, _ := dp.subscribersMap.Load(msgType)
   105  			if v == nil {
   106  				continue
   107  			}
   108  			m, _ := v.(*sync.Map)
   109  
   110  			m.Range(func(key, value interface{}) bool {
   111  				select {
   112  				case key.(*Subscriber).msgChan <- msg:
   113  				default:
   114  					logging.VLog().WithFields(logrus.Fields{
   115  						"msgType": msgType,
   116  					}).Warn("timeout to dispatch message.")
   117  				}
   118  				return true
   119  			})
   120  		}
   121  	}
   122  }
   123  
   124  // Stop stop goroutine.
   125  func (dp *Dispatcher) Stop() {
   126  	logging.CLog().Info("Stopping NebService Dispatcher...")
   127  
   128  	dp.quitCh <- true
   129  }
   130  
   131  // PutMessage put new message to chan, then subscribers will be notified to process.
   132  func (dp *Dispatcher) PutMessage(msg Message) {
   133  	// it's a optimize strategy for message dispatch, according to https://github.com/nebulasio/go-nebulas/issues/50
   134  	hash := msg.Hash()
   135  	if dp.filters[msg.MessageType()] {
   136  		if exist, _ := dp.dispatchedMessages.ContainsOrAdd(hash, hash); exist == true {
   137  			// duplicated message, ignore.
   138  			metricsDuplicatedMessage(msg.MessageType())
   139  			return
   140  		}
   141  	}
   142  
   143  	dp.receivedMessageCh <- msg
   144  }
   145  
   146  func metricsDuplicatedMessage(messageName string) {
   147  	metricsDispatcherDuplicated.Mark(int64(1))
   148  	meter := metrics.NewMeter(fmt.Sprintf("neb.net.dispatcher.duplicated.%s", messageName))
   149  	meter.Mark(int64(1))
   150  }