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 }