github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/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 19:16:42</date> 10 //</624450109681176576> 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.Mutex 52 active map[ID]*Subscription 53 inactive map[ID]*Subscription 54 buffer map[ID][]interface{} //未发送的非活动订阅通知 55 } 56 57 //NewNotifier创建可用于发送订阅的新通知程序 58 //通知客户端。 59 func newNotifier(codec ServerCodec) *Notifier { 60 return &Notifier{ 61 codec: codec, 62 active: make(map[ID]*Subscription), 63 inactive: make(map[ID]*Subscription), 64 buffer: make(map[ID][]interface{}), 65 } 66 } 67 68 //notifierFromContext返回存储在CTX中的notifier值(如果有)。 69 func NotifierFromContext(ctx context.Context) (*Notifier, bool) { 70 n, ok := ctx.Value(notifierKey{}).(*Notifier) 71 return n, ok 72 } 73 74 //CreateSubscription返回耦合到 75 //RPC连接。默认情况下,订阅不活动,通知 76 //删除,直到订阅标记为活动。这样做了 77 //由RPC服务器在订阅ID发送到客户端之后发送。 78 func (n *Notifier) CreateSubscription() *Subscription { 79 s := &Subscription{ID: NewID(), err: make(chan error)} 80 n.subMu.Lock() 81 n.inactive[s.ID] = s 82 n.subMu.Unlock() 83 return s 84 } 85 86 //通知将给定数据作为有效负载发送给客户机。 87 //如果发生错误,则关闭RPC连接并返回错误。 88 func (n *Notifier) Notify(id ID, data interface{}) error { 89 n.subMu.Lock() 90 defer n.subMu.Unlock() 91 92 if sub, active := n.active[id]; active { 93 n.send(sub, data) 94 } else { 95 n.buffer[id] = append(n.buffer[id], data) 96 } 97 return nil 98 } 99 100 func (n *Notifier) send(sub *Subscription, data interface{}) error { 101 notification := n.codec.CreateNotification(string(sub.ID), sub.namespace, data) 102 err := n.codec.Write(notification) 103 if err != nil { 104 n.codec.Close() 105 } 106 return err 107 } 108 109 //CLOSED返回在RPC连接关闭时关闭的通道。 110 func (n *Notifier) Closed() <-chan interface{} { 111 return n.codec.Closed() 112 } 113 114 //取消订阅订阅。 115 //如果找不到订阅,则返回errscriptionNotFound。 116 func (n *Notifier) unsubscribe(id ID) error { 117 n.subMu.Lock() 118 defer n.subMu.Unlock() 119 if s, found := n.active[id]; found { 120 close(s.err) 121 delete(n.active, id) 122 return nil 123 } 124 return ErrSubscriptionNotFound 125 } 126 127 //激活启用订阅。在启用订阅之前 128 //通知被删除。此方法由RPC服务器在 129 //订阅ID已发送到客户端。这将阻止通知 130 //在将订阅ID发送到客户端之前发送到客户端。 131 func (n *Notifier) activate(id ID, namespace string) { 132 n.subMu.Lock() 133 defer n.subMu.Unlock() 134 135 if sub, found := n.inactive[id]; found { 136 sub.namespace = namespace 137 n.active[id] = sub 138 delete(n.inactive, id) 139 //发送缓冲通知。 140 for _, data := range n.buffer[id] { 141 n.send(sub, data) 142 } 143 delete(n.buffer, id) 144 } 145 } 146