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