github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/rpc/subscription.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:45</date> 10 //</624342665420345344> 11 12 13 package rpc 14 15 import ( 16 "context" 17 "errors" 18 "sync" 19 ) 20 21 var ( 22 //当连接不支持通知时,返回errNotificationsUnsupported。 23 ErrNotificationsUnsupported = errors.New("notifications not supported") 24 //找不到给定ID的通知时返回errNotificationNotFound 25 ErrSubscriptionNotFound = errors.New("subscription not found") 26 ) 27 28 //ID定义用于标识RPC订阅的伪随机数。 29 type ID string 30 31 //订阅由通知程序创建,并与该通知程序紧密相连。客户可以使用 32 //此订阅要等待客户端的取消订阅请求,请参阅err()。 33 type Subscription struct { 34 ID ID 35 namespace string 36 err chan error //取消订阅时关闭 37 } 38 39 //err返回当客户端发送取消订阅请求时关闭的通道。 40 func (s *Subscription) Err() <-chan error { 41 return s.err 42 } 43 44 //notifierkey用于在连接上下文中存储通知程序。 45 type notifierKey struct{} 46 47 //通知程序与支持订阅的RPC连接紧密相连。 48 //服务器回调使用通知程序发送通知。 49 type Notifier struct { 50 codec ServerCodec 51 subMu sync.RWMutex //防护活动和非活动地图 52 active map[ID]*Subscription 53 inactive map[ID]*Subscription 54 } 55 56 //NewNotifier创建可用于发送订阅的新通知程序 57 //通知客户端。 58 func newNotifier(codec ServerCodec) *Notifier { 59 return &Notifier{ 60 codec: codec, 61 active: make(map[ID]*Subscription), 62 inactive: make(map[ID]*Subscription), 63 } 64 } 65 66 //notifierFromContext返回存储在CTX中的notifier值(如果有)。 67 func NotifierFromContext(ctx context.Context) (*Notifier, bool) { 68 n, ok := ctx.Value(notifierKey{}).(*Notifier) 69 return n, ok 70 } 71 72 //CreateSubscription返回耦合到 73 //RPC连接。默认情况下,订阅不活动,通知 74 //删除,直到订阅标记为活动。这样做了 75 //由RPC服务器在订阅ID发送到客户端之后发送。 76 func (n *Notifier) CreateSubscription() *Subscription { 77 s := &Subscription{ID: NewID(), err: make(chan error)} 78 n.subMu.Lock() 79 n.inactive[s.ID] = s 80 n.subMu.Unlock() 81 return s 82 } 83 84 //通知将给定数据作为有效负载发送给客户机。 85 //如果发生错误,则关闭RPC连接并返回错误。 86 func (n *Notifier) Notify(id ID, data interface{}) error { 87 n.subMu.RLock() 88 defer n.subMu.RUnlock() 89 90 sub, active := n.active[id] 91 if active { 92 notification := n.codec.CreateNotification(string(id), sub.namespace, data) 93 if err := n.codec.Write(notification); err != nil { 94 n.codec.Close() 95 return err 96 } 97 } 98 return nil 99 } 100 101 //CLOSED返回在RPC连接关闭时关闭的通道。 102 func (n *Notifier) Closed() <-chan interface{} { 103 return n.codec.Closed() 104 } 105 106 //取消订阅订阅。 107 //如果找不到订阅,则返回errscriptionNotFound。 108 func (n *Notifier) unsubscribe(id ID) error { 109 n.subMu.Lock() 110 defer n.subMu.Unlock() 111 if s, found := n.active[id]; found { 112 close(s.err) 113 delete(n.active, id) 114 return nil 115 } 116 return ErrSubscriptionNotFound 117 } 118 119 //激活启用订阅。在启用订阅之前 120 //通知被删除。此方法由RPC服务器在 121 //订阅ID已发送到客户端。这将阻止通知 122 //在将订阅ID发送到客户端之前发送到客户端。 123 func (n *Notifier) activate(id ID, namespace string) { 124 n.subMu.Lock() 125 defer n.subMu.Unlock() 126 if sub, found := n.inactive[id]; found { 127 sub.namespace = namespace 128 n.active[id] = sub 129 delete(n.inactive, id) 130 } 131 } 132