github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/rpc/client.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 //</624450108456439808> 11 12 13 package rpc 14 15 import ( 16 "bytes" 17 "container/list" 18 "context" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "net" 23 "net/url" 24 "reflect" 25 "strconv" 26 "strings" 27 "sync" 28 "sync/atomic" 29 "time" 30 31 "github.com/ethereum/go-ethereum/log" 32 ) 33 34 var ( 35 ErrClientQuit = errors.New("client is closed") 36 ErrNoResult = errors.New("no result in JSON-RPC response") 37 ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow") 38 ) 39 40 const ( 41 //超时 42 tcpKeepAliveInterval = 30 * time.Second 43 defaultDialTimeout = 10 * time.Second //如果上下文没有截止时间,则在拨号时使用 44 defaultWriteTimeout = 10 * time.Second //如果上下文没有截止时间,则用于调用 45 subscribeTimeout = 5 * time.Second //总超时eth_subscribe,rpc_模块调用 46 ) 47 48 const ( 49 //当订阅服务器无法跟上时,将删除订阅。 50 // 51 //这可以通过为通道提供足够大的缓冲区来解决, 52 //但这在文件中可能不方便也很难解释。另一个问题是 53 //缓冲通道是指即使不需要缓冲区,缓冲区也是静态的。 54 //大部分时间。 55 // 56 //这里采用的方法是维护每个订阅的链表缓冲区 57 //按需缩小。如果缓冲区达到以下大小,则订阅为 58 //下降。 59 maxClientSubscriptionBuffer = 20000 60 ) 61 62 //batchelem是批处理请求中的元素。 63 type BatchElem struct { 64 Method string 65 Args []interface{} 66 //结果未显示在此字段中。结果必须设置为 67 //所需类型的非零指针值,否则响应将为 68 //丢弃的。 69 Result interface{} 70 //如果服务器返回此请求的错误,或如果 71 //解组为结果失败。未设置I/O错误。 72 Error error 73 } 74 75 //此类型的值可以是JSON-RPC请求、通知、成功响应或 76 //错误响应。它是哪一个取决于田地。 77 type jsonrpcMessage struct { 78 Version string `json:"jsonrpc"` 79 ID json.RawMessage `json:"id,omitempty"` 80 Method string `json:"method,omitempty"` 81 Params json.RawMessage `json:"params,omitempty"` 82 Error *jsonError `json:"error,omitempty"` 83 Result json.RawMessage `json:"result,omitempty"` 84 } 85 86 func (msg *jsonrpcMessage) isNotification() bool { 87 return msg.ID == nil && msg.Method != "" 88 } 89 90 func (msg *jsonrpcMessage) isResponse() bool { 91 return msg.hasValidID() && msg.Method == "" && len(msg.Params) == 0 92 } 93 94 func (msg *jsonrpcMessage) hasValidID() bool { 95 return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' 96 } 97 98 func (msg *jsonrpcMessage) String() string { 99 b, _ := json.Marshal(msg) 100 return string(b) 101 } 102 103 //客户端表示到RPC服务器的连接。 104 type Client struct { 105 idCounter uint32 106 connectFunc func(ctx context.Context) (net.Conn, error) 107 isHTTP bool 108 109 //WRITECONN只能安全地访问外部调度,使用 110 //写入锁定保持。通过发送 111 //请求操作并通过发送发送完成释放。 112 writeConn net.Conn 113 114 //派遣 115 close chan struct{} 116 closing chan struct{} //客户端退出时关闭 117 didClose chan struct{} //客户端退出时关闭 118 reconnected chan net.Conn //写入/重新连接发送新连接的位置 119 readErr chan error //读取错误 120 readResp chan []*jsonrpcMessage //读取的有效消息 121 requestOp chan *requestOp //用于注册响应ID 122 sendDone chan error //信号写入完成,释放写入锁定 123 respWait map[string]*requestOp //主动请求 124 subs map[string]*ClientSubscription //活动订阅 125 } 126 127 type requestOp struct { 128 ids []json.RawMessage 129 err error 130 resp chan *jsonrpcMessage //接收最多len(id)响应 131 sub *ClientSubscription //仅为ethsubscribe请求设置 132 } 133 134 func (op *requestOp) wait(ctx context.Context) (*jsonrpcMessage, error) { 135 select { 136 case <-ctx.Done(): 137 return nil, ctx.Err() 138 case resp := <-op.resp: 139 return resp, op.err 140 } 141 } 142 143 //拨号为给定的URL创建新的客户端。 144 // 145 //当前支持的URL方案有“http”、“https”、“ws”和“wss”。如果RAWURL是 146 //没有URL方案的文件名,使用Unix建立本地套接字连接 147 //支持的平台上的域套接字和Windows上的命名管道。如果你想 148 //配置传输选项,使用dialHTTP、dialWebSocket或dialIPC。 149 // 150 //对于WebSocket连接,原点设置为本地主机名。 151 // 152 //如果连接丢失,客户端将自动重新连接。 153 func Dial(rawurl string) (*Client, error) { 154 return DialContext(context.Background(), rawurl) 155 } 156 157 //DialContext创建一个新的RPC客户端,就像Dial一样。 158 // 159 //上下文用于取消或超时初始连接建立。它确实 160 //不影响与客户的后续交互。 161 func DialContext(ctx context.Context, rawurl string) (*Client, error) { 162 u, err := url.Parse(rawurl) 163 if err != nil { 164 return nil, err 165 } 166 switch u.Scheme { 167 case "http", "https": 168 return DialHTTP(rawurl) 169 case "ws", "wss": 170 return DialWebsocket(ctx, rawurl, "") 171 case "stdio": 172 return DialStdIO(ctx) 173 case "": 174 return DialIPC(ctx, rawurl) 175 default: 176 return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme) 177 } 178 } 179 180 func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) { 181 conn, err := connectFunc(initctx) 182 if err != nil { 183 return nil, err 184 } 185 _, isHTTP := conn.(*httpConn) 186 c := &Client{ 187 writeConn: conn, 188 isHTTP: isHTTP, 189 connectFunc: connectFunc, 190 close: make(chan struct{}), 191 closing: make(chan struct{}), 192 didClose: make(chan struct{}), 193 reconnected: make(chan net.Conn), 194 readErr: make(chan error), 195 readResp: make(chan []*jsonrpcMessage), 196 requestOp: make(chan *requestOp), 197 sendDone: make(chan error, 1), 198 respWait: make(map[string]*requestOp), 199 subs: make(map[string]*ClientSubscription), 200 } 201 if !isHTTP { 202 go c.dispatch(conn) 203 } 204 return c, nil 205 } 206 207 func (c *Client) nextID() json.RawMessage { 208 id := atomic.AddUint32(&c.idCounter, 1) 209 return []byte(strconv.FormatUint(uint64(id), 10)) 210 } 211 212 //supportedmodules调用rpc_modules方法,检索 213 //服务器上可用的API。 214 func (c *Client) SupportedModules() (map[string]string, error) { 215 var result map[string]string 216 ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) 217 defer cancel() 218 err := c.CallContext(ctx, &result, "rpc_modules") 219 return result, err 220 } 221 222 //CLOSE关闭客户机,中止任何飞行中的请求。 223 func (c *Client) Close() { 224 if c.isHTTP { 225 return 226 } 227 select { 228 case c.close <- struct{}{}: 229 <-c.didClose 230 case <-c.didClose: 231 } 232 } 233 234 //调用使用给定的参数执行JSON-RPC调用,并将其解组为 235 //如果没有发生错误,则返回结果。 236 // 237 //结果必须是一个指针,以便包JSON可以解组到其中。你 238 //也可以传递nil,在这种情况下,结果将被忽略。 239 func (c *Client) Call(result interface{}, method string, args ...interface{}) error { 240 ctx := context.Background() 241 return c.CallContext(ctx, result, method, args...) 242 } 243 244 //callContext使用给定的参数执行JSON-RPC调用。如果上下文是 245 //在成功返回调用之前取消,callContext立即返回。 246 // 247 //结果必须是一个指针,以便包JSON可以解组到其中。你 248 //也可以传递nil,在这种情况下,结果将被忽略。 249 func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { 250 msg, err := c.newMessage(method, args...) 251 if err != nil { 252 return err 253 } 254 op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)} 255 256 if c.isHTTP { 257 err = c.sendHTTP(ctx, op, msg) 258 } else { 259 err = c.send(ctx, op, msg) 260 } 261 if err != nil { 262 return err 263 } 264 265 //调度已接受请求,并将在退出时关闭通道。 266 switch resp, err := op.wait(ctx); { 267 case err != nil: 268 return err 269 case resp.Error != nil: 270 return resp.Error 271 case len(resp.Result) == 0: 272 return ErrNoResult 273 default: 274 return json.Unmarshal(resp.Result, &result) 275 } 276 } 277 278 //批处理调用将所有给定的请求作为单个批处理发送,并等待服务器 279 //为所有人返回响应。 280 // 281 //与调用不同,批调用只返回I/O错误。任何特定于的错误 282 //通过相应批处理元素的错误字段报告请求。 283 // 284 //注意,批处理调用不能在服务器端以原子方式执行。 285 func (c *Client) BatchCall(b []BatchElem) error { 286 ctx := context.Background() 287 return c.BatchCallContext(ctx, b) 288 } 289 290 //批处理调用将所有给定的请求作为单个批处理发送,并等待服务器 291 //为所有人返回响应。等待持续时间由 292 //上下文的最后期限。 293 // 294 //与callContext不同,batchcallContext只返回已发生的错误 295 //发送请求时。任何特定于请求的错误都会通过 296 //对应批处理元素的错误字段。 297 // 298 //注意,批处理调用不能在服务器端以原子方式执行。 299 func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { 300 msgs := make([]*jsonrpcMessage, len(b)) 301 op := &requestOp{ 302 ids: make([]json.RawMessage, len(b)), 303 resp: make(chan *jsonrpcMessage, len(b)), 304 } 305 for i, elem := range b { 306 msg, err := c.newMessage(elem.Method, elem.Args...) 307 if err != nil { 308 return err 309 } 310 msgs[i] = msg 311 op.ids[i] = msg.ID 312 } 313 314 var err error 315 if c.isHTTP { 316 err = c.sendBatchHTTP(ctx, op, msgs) 317 } else { 318 err = c.send(ctx, op, msgs) 319 } 320 321 //等待所有响应返回。 322 for n := 0; n < len(b) && err == nil; n++ { 323 var resp *jsonrpcMessage 324 resp, err = op.wait(ctx) 325 if err != nil { 326 break 327 } 328 //找到与此响应对应的元素。 329 //由于调度,元素被保证存在 330 //只向我们的频道发送有效的ID。 331 var elem *BatchElem 332 for i := range msgs { 333 if bytes.Equal(msgs[i].ID, resp.ID) { 334 elem = &b[i] 335 break 336 } 337 } 338 if resp.Error != nil { 339 elem.Error = resp.Error 340 continue 341 } 342 if len(resp.Result) == 0 { 343 elem.Error = ErrNoResult 344 continue 345 } 346 elem.Error = json.Unmarshal(resp.Result, elem.Result) 347 } 348 return err 349 } 350 351 //ethsubscribe在“eth”名称空间下注册一个订阅。 352 func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { 353 return c.Subscribe(ctx, "eth", channel, args...) 354 } 355 356 //shhsubscribe在“shh”名称空间下注册一个订阅。 357 func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { 358 return c.Subscribe(ctx, "shh", channel, args...) 359 } 360 361 //subscribe使用给定的参数调用“<namespace>\u subscribe”方法, 362 //注册订阅。订阅的服务器通知为 363 //发送到给定的频道。通道的元素类型必须与 364 //订阅返回的内容类型应为。 365 // 366 //context参数取消设置订阅但没有的RPC请求 367 //订阅返回后对订阅的影响。 368 // 369 //缓慢的订户最终将被删除。客户端缓冲区最多8000个通知 370 //在考虑用户死亡之前。订阅错误通道将接收 371 //errSubscriptionQueueOverflow。在通道上使用足够大的缓冲区或确保 372 //通道通常至少有一个读卡器来防止这个问题。 373 func (c *Client) Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*ClientSubscription, error) { 374 //首先检查通道类型。 375 chanVal := reflect.ValueOf(channel) 376 if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { 377 panic("first argument to Subscribe must be a writable channel") 378 } 379 if chanVal.IsNil() { 380 panic("channel given to Subscribe must not be nil") 381 } 382 if c.isHTTP { 383 return nil, ErrNotificationsUnsupported 384 } 385 386 msg, err := c.newMessage(namespace+subscribeMethodSuffix, args...) 387 if err != nil { 388 return nil, err 389 } 390 op := &requestOp{ 391 ids: []json.RawMessage{msg.ID}, 392 resp: make(chan *jsonrpcMessage), 393 sub: newClientSubscription(c, namespace, chanVal), 394 } 395 396 //发送订阅请求。 397 //响应的到达和有效性在Sub.Quit上发出信号。 398 if err := c.send(ctx, op, msg); err != nil { 399 return nil, err 400 } 401 if _, err := op.wait(ctx); err != nil { 402 return nil, err 403 } 404 return op.sub, nil 405 } 406 407 func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { 408 params, err := json.Marshal(paramsIn) 409 if err != nil { 410 return nil, err 411 } 412 return &jsonrpcMessage{Version: "2.0", ID: c.nextID(), Method: method, Params: params}, nil 413 } 414 415 //发送寄存器op和调度循环,然后在连接上发送msg。 416 //如果发送失败,则注销OP。 417 func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { 418 select { 419 case c.requestOp <- op: 420 log.Trace("", "msg", log.Lazy{Fn: func() string { 421 return fmt.Sprint("sending ", msg) 422 }}) 423 err := c.write(ctx, msg) 424 c.sendDone <- err 425 return err 426 case <-ctx.Done(): 427 //如果客户端过载或无法跟上,就会发生这种情况。 428 //订阅通知。 429 return ctx.Err() 430 case <-c.closing: 431 return ErrClientQuit 432 case <-c.didClose: 433 return ErrClientQuit 434 } 435 } 436 437 func (c *Client) write(ctx context.Context, msg interface{}) error { 438 deadline, ok := ctx.Deadline() 439 if !ok { 440 deadline = time.Now().Add(defaultWriteTimeout) 441 } 442 //上一次写入失败。尝试建立新连接。 443 if c.writeConn == nil { 444 if err := c.reconnect(ctx); err != nil { 445 return err 446 } 447 } 448 c.writeConn.SetWriteDeadline(deadline) 449 err := json.NewEncoder(c.writeConn).Encode(msg) 450 c.writeConn.SetWriteDeadline(time.Time{}) 451 if err != nil { 452 c.writeConn = nil 453 } 454 return err 455 } 456 457 func (c *Client) reconnect(ctx context.Context) error { 458 newconn, err := c.connectFunc(ctx) 459 if err != nil { 460 log.Trace(fmt.Sprintf("reconnect failed: %v", err)) 461 return err 462 } 463 select { 464 case c.reconnected <- newconn: 465 c.writeConn = newconn 466 return nil 467 case <-c.didClose: 468 newconn.Close() 469 return ErrClientQuit 470 } 471 } 472 473 //调度是客户机的主循环。 474 //它向等待调用和批调用发送读取消息 475 //注册订阅的订阅通知。 476 func (c *Client) dispatch(conn net.Conn) { 477 //生成初始读取循环。 478 go c.read(conn) 479 480 var ( 481 lastOp *requestOp //跟踪上次发送操作 482 requestOpLock = c.requestOp //保持发送锁时为零 483 reading = true //如果为真,则运行读取循环 484 ) 485 defer close(c.didClose) 486 defer func() { 487 close(c.closing) 488 c.closeRequestOps(ErrClientQuit) 489 conn.Close() 490 if reading { 491 //清空读取通道,直到读取结束。 492 for { 493 select { 494 case <-c.readResp: 495 case <-c.readErr: 496 return 497 } 498 } 499 } 500 }() 501 502 for { 503 select { 504 case <-c.close: 505 return 506 507 //读取路径。 508 case batch := <-c.readResp: 509 for _, msg := range batch { 510 switch { 511 case msg.isNotification(): 512 log.Trace("", "msg", log.Lazy{Fn: func() string { 513 return fmt.Sprint("<-readResp: notification ", msg) 514 }}) 515 c.handleNotification(msg) 516 case msg.isResponse(): 517 log.Trace("", "msg", log.Lazy{Fn: func() string { 518 return fmt.Sprint("<-readResp: response ", msg) 519 }}) 520 c.handleResponse(msg) 521 default: 522 log.Debug("", "msg", log.Lazy{Fn: func() string { 523 return fmt.Sprint("<-readResp: dropping weird message", msg) 524 }}) 525 //托多:也许接近 526 } 527 } 528 529 case err := <-c.readErr: 530 log.Debug("<-readErr", "err", err) 531 c.closeRequestOps(err) 532 conn.Close() 533 reading = false 534 535 case newconn := <-c.reconnected: 536 log.Debug("<-reconnected", "reading", reading, "remote", conn.RemoteAddr()) 537 if reading { 538 //等待上一个读取循环退出。这是一个罕见的病例。 539 conn.Close() 540 <-c.readErr 541 } 542 go c.read(newconn) 543 reading = true 544 conn = newconn 545 546 //发送路径。 547 case op := <-requestOpLock: 548 //停止侦听进一步的发送操作,直到当前操作完成。 549 requestOpLock = nil 550 lastOp = op 551 for _, id := range op.ids { 552 c.respWait[string(id)] = op 553 } 554 555 case err := <-c.sendDone: 556 if err != nil { 557 //删除上次发送的响应处理程序。我们把它们移到这里 558 //因为错误已经在call或batchcall中处理。当 559 //读取循环停止,它将发出所有其他当前操作的信号。 560 for _, id := range lastOp.ids { 561 delete(c.respWait, string(id)) 562 } 563 } 564 //再次收听发送操作。 565 requestOpLock = c.requestOp 566 lastOp = nil 567 } 568 } 569 } 570 571 //closerequestops取消阻止挂起的发送操作和活动订阅。 572 func (c *Client) closeRequestOps(err error) { 573 didClose := make(map[*requestOp]bool) 574 575 for id, op := range c.respWait { 576 //删除op,以便以后的调用不会再次关闭op.resp。 577 delete(c.respWait, id) 578 579 if !didClose[op] { 580 op.err = err 581 close(op.resp) 582 didClose[op] = true 583 } 584 } 585 for id, sub := range c.subs { 586 delete(c.subs, id) 587 sub.quitWithError(err, false) 588 } 589 } 590 591 func (c *Client) handleNotification(msg *jsonrpcMessage) { 592 if !strings.HasSuffix(msg.Method, notificationMethodSuffix) { 593 log.Debug("dropping non-subscription message", "msg", msg) 594 return 595 } 596 var subResult struct { 597 ID string `json:"subscription"` 598 Result json.RawMessage `json:"result"` 599 } 600 if err := json.Unmarshal(msg.Params, &subResult); err != nil { 601 log.Debug("dropping invalid subscription message", "msg", msg) 602 return 603 } 604 if c.subs[subResult.ID] != nil { 605 c.subs[subResult.ID].deliver(subResult.Result) 606 } 607 } 608 609 func (c *Client) handleResponse(msg *jsonrpcMessage) { 610 op := c.respWait[string(msg.ID)] 611 if op == nil { 612 log.Debug("unsolicited response", "msg", msg) 613 return 614 } 615 delete(c.respWait, string(msg.ID)) 616 //对于正常响应,只需将响应转发到call/batchcall。 617 if op.sub == nil { 618 op.resp <- msg 619 return 620 } 621 //对于订阅响应,如果服务器 622 //表示成功。ethsubscribe在任何情况下都可以通过 623 //Op.Resp频道。 624 defer close(op.resp) 625 if msg.Error != nil { 626 op.err = msg.Error 627 return 628 } 629 if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { 630 go op.sub.start() 631 c.subs[op.sub.subid] = op.sub 632 } 633 } 634 635 //阅读发生在一个专门的Goroutine上。 636 637 func (c *Client) read(conn net.Conn) error { 638 var ( 639 buf json.RawMessage 640 dec = json.NewDecoder(conn) 641 ) 642 readMessage := func() (rs []*jsonrpcMessage, err error) { 643 buf = buf[:0] 644 if err = dec.Decode(&buf); err != nil { 645 return nil, err 646 } 647 if isBatch(buf) { 648 err = json.Unmarshal(buf, &rs) 649 } else { 650 rs = make([]*jsonrpcMessage, 1) 651 err = json.Unmarshal(buf, &rs[0]) 652 } 653 return rs, err 654 } 655 656 for { 657 resp, err := readMessage() 658 if err != nil { 659 c.readErr <- err 660 return err 661 } 662 c.readResp <- resp 663 } 664 } 665 666 //订阅。 667 668 //客户端订阅表示通过ethsubscribe建立的订阅。 669 type ClientSubscription struct { 670 client *Client 671 etype reflect.Type 672 channel reflect.Value 673 namespace string 674 subid string 675 in chan json.RawMessage 676 677 quitOnce sync.Once //确保退出关闭一次 678 quit chan struct{} //退出订阅时将关闭Quit 679 errOnce sync.Once //确保err关闭一次 680 err chan error 681 } 682 683 func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription { 684 sub := &ClientSubscription{ 685 client: c, 686 namespace: namespace, 687 etype: channel.Type().Elem(), 688 channel: channel, 689 quit: make(chan struct{}), 690 err: make(chan error, 1), 691 in: make(chan json.RawMessage), 692 } 693 return sub 694 } 695 696 //err返回订阅错误通道。err的预期用途是 697 //客户端连接意外关闭时重新订阅。 698 // 699 //当订阅到期时,错误通道接收到一个值。 700 //出错。如果调用了close,则接收到的错误为nil。 701 //在基础客户端上,没有发生其他错误。 702 // 703 //当对订阅调用Unsubscribe时,错误通道将关闭。 704 func (sub *ClientSubscription) Err() <-chan error { 705 return sub.err 706 } 707 708 //取消订阅取消订阅通知并关闭错误通道。 709 //它可以安全地被多次调用。 710 func (sub *ClientSubscription) Unsubscribe() { 711 sub.quitWithError(nil, true) 712 sub.errOnce.Do(func() { close(sub.err) }) 713 } 714 715 func (sub *ClientSubscription) quitWithError(err error, unsubscribeServer bool) { 716 sub.quitOnce.Do(func() { 717 //调度循环将无法执行取消订阅调用 718 //如果交货时受阻。关闭Sub.Quit First,因为它 719 //解除锁定传递。 720 close(sub.quit) 721 if unsubscribeServer { 722 sub.requestUnsubscribe() 723 } 724 if err != nil { 725 if err == ErrClientQuit { 726 err = nil //遵循订阅语义。 727 } 728 sub.err <- err 729 } 730 }) 731 } 732 733 func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { 734 select { 735 case sub.in <- result: 736 return true 737 case <-sub.quit: 738 return false 739 } 740 } 741 742 func (sub *ClientSubscription) start() { 743 sub.quitWithError(sub.forward()) 744 } 745 746 func (sub *ClientSubscription) forward() (err error, unsubscribeServer bool) { 747 cases := []reflect.SelectCase{ 748 {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, 749 {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, 750 {Dir: reflect.SelectSend, Chan: sub.channel}, 751 } 752 buffer := list.New() 753 defer buffer.Init() 754 for { 755 var chosen int 756 var recv reflect.Value 757 if buffer.Len() == 0 { 758 //空闲,省略发送案例。 759 chosen, recv, _ = reflect.Select(cases[:2]) 760 } else { 761 //非空缓冲区,发送第一个排队的项目。 762 cases[2].Send = reflect.ValueOf(buffer.Front().Value) 763 chosen, recv, _ = reflect.Select(cases) 764 } 765 766 switch chosen { 767 case 0: //<退出 768 return nil, false 769 case 1: //<-in in 770 val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) 771 if err != nil { 772 return err, true 773 } 774 if buffer.Len() == maxClientSubscriptionBuffer { 775 return ErrSubscriptionQueueOverflow, true 776 } 777 buffer.PushBack(val) 778 case 2: //子通道< 779 cases[2].Send = reflect.Value{} //不要抓住价值。 780 buffer.Remove(buffer.Front()) 781 } 782 } 783 } 784 785 func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { 786 val := reflect.New(sub.etype) 787 err := json.Unmarshal(result, val.Interface()) 788 return val.Elem().Interface(), err 789 } 790 791 func (sub *ClientSubscription) requestUnsubscribe() error { 792 var result interface{} 793 return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid) 794 } 795