github.com/lzy4123/fabric@v2.1.1+incompatible/orderer/common/cluster/comm.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cluster 8 9 import ( 10 "bytes" 11 "context" 12 "crypto/x509" 13 "encoding/pem" 14 "fmt" 15 "sync" 16 "sync/atomic" 17 "time" 18 19 "github.com/golang/protobuf/proto" 20 "github.com/hyperledger/fabric-protos-go/orderer" 21 "github.com/hyperledger/fabric/common/flogging" 22 "github.com/hyperledger/fabric/internal/pkg/comm" 23 "github.com/pkg/errors" 24 "go.uber.org/zap" 25 "google.golang.org/grpc" 26 "google.golang.org/grpc/connectivity" 27 ) 28 29 const ( 30 // MinimumExpirationWarningInterval is the default minimum time interval 31 // between consecutive warnings about certificate expiration. 32 MinimumExpirationWarningInterval = time.Minute * 5 33 ) 34 35 var ( 36 errOverflow = errors.New("send queue overflown") 37 errAborted = errors.New("aborted") 38 errTimeout = errors.New("rpc timeout expired") 39 ) 40 41 // ChannelExtractor extracts the channel of a given message, 42 // or returns an empty string if that's not possible 43 type ChannelExtractor interface { 44 TargetChannel(message proto.Message) string 45 } 46 47 //go:generate mockery -dir . -name Handler -case underscore -output ./mocks/ 48 49 // Handler handles Step() and Submit() requests and returns a corresponding response 50 type Handler interface { 51 OnConsensus(channel string, sender uint64, req *orderer.ConsensusRequest) error 52 OnSubmit(channel string, sender uint64, req *orderer.SubmitRequest) error 53 } 54 55 // RemoteNode represents a cluster member 56 type RemoteNode struct { 57 // ID is unique among all members, and cannot be 0. 58 ID uint64 59 // Endpoint is the endpoint of the node, denoted in %s:%d format 60 Endpoint string 61 // ServerTLSCert is the DER encoded TLS server certificate of the node 62 ServerTLSCert []byte 63 // ClientTLSCert is the DER encoded TLS client certificate of the node 64 ClientTLSCert []byte 65 } 66 67 // String returns a string representation of this RemoteNode 68 func (rm RemoteNode) String() string { 69 return fmt.Sprintf("ID: %d,\nEndpoint: %s,\nServerTLSCert:%s, ClientTLSCert:%s", 70 rm.ID, rm.Endpoint, DERtoPEM(rm.ServerTLSCert), DERtoPEM(rm.ClientTLSCert)) 71 } 72 73 //go:generate mockery -dir . -name Communicator -case underscore -output ./mocks/ 74 75 // Communicator defines communication for a consenter 76 type Communicator interface { 77 // Remote returns a RemoteContext for the given RemoteNode ID in the context 78 // of the given channel, or error if connection cannot be established, or 79 // the channel wasn't configured 80 Remote(channel string, id uint64) (*RemoteContext, error) 81 // Configure configures the communication to connect to all 82 // given members, and disconnect from any members not among the given 83 // members. 84 Configure(channel string, members []RemoteNode) 85 // Shutdown shuts down the communicator 86 Shutdown() 87 } 88 89 // MembersByChannel is a mapping from channel name 90 // to MemberMapping 91 type MembersByChannel map[string]MemberMapping 92 93 // Comm implements Communicator 94 type Comm struct { 95 MinimumExpirationWarningInterval time.Duration 96 CertExpWarningThreshold time.Duration 97 shutdownSignal chan struct{} 98 shutdown bool 99 SendBufferSize int 100 Lock sync.RWMutex 101 Logger *flogging.FabricLogger 102 ChanExt ChannelExtractor 103 H Handler 104 Connections *ConnectionStore 105 Chan2Members MembersByChannel 106 Metrics *Metrics 107 } 108 109 type requestContext struct { 110 channel string 111 sender uint64 112 } 113 114 // DispatchSubmit identifies the channel and sender of the submit request and passes it 115 // to the underlying Handler 116 func (c *Comm) DispatchSubmit(ctx context.Context, request *orderer.SubmitRequest) error { 117 reqCtx, err := c.requestContext(ctx, request) 118 if err != nil { 119 return err 120 } 121 return c.H.OnSubmit(reqCtx.channel, reqCtx.sender, request) 122 } 123 124 // DispatchConsensus identifies the channel and sender of the step request and passes it 125 // to the underlying Handler 126 func (c *Comm) DispatchConsensus(ctx context.Context, request *orderer.ConsensusRequest) error { 127 reqCtx, err := c.requestContext(ctx, request) 128 if err != nil { 129 return err 130 } 131 return c.H.OnConsensus(reqCtx.channel, reqCtx.sender, request) 132 } 133 134 // requestContext identifies the sender and channel of the request and returns 135 // it wrapped in a requestContext 136 func (c *Comm) requestContext(ctx context.Context, msg proto.Message) (*requestContext, error) { 137 channel := c.ChanExt.TargetChannel(msg) 138 if channel == "" { 139 return nil, errors.Errorf("badly formatted message, cannot extract channel") 140 } 141 142 c.Lock.RLock() 143 mapping, exists := c.Chan2Members[channel] 144 c.Lock.RUnlock() 145 146 if !exists { 147 return nil, errors.Errorf("channel %s doesn't exist", channel) 148 } 149 150 cert := comm.ExtractRawCertificateFromContext(ctx) 151 if len(cert) == 0 { 152 return nil, errors.Errorf("no TLS certificate sent") 153 } 154 155 stub := mapping.LookupByClientCert(cert) 156 if stub == nil { 157 return nil, errors.Errorf("certificate extracted from TLS connection isn't authorized") 158 } 159 return &requestContext{ 160 channel: channel, 161 sender: stub.ID, 162 }, nil 163 } 164 165 // Remote obtains a RemoteContext linked to the destination node on the context 166 // of a given channel 167 func (c *Comm) Remote(channel string, id uint64) (*RemoteContext, error) { 168 c.Lock.RLock() 169 defer c.Lock.RUnlock() 170 171 if c.shutdown { 172 return nil, errors.New("communication has been shut down") 173 } 174 175 mapping, exists := c.Chan2Members[channel] 176 if !exists { 177 return nil, errors.Errorf("channel %s doesn't exist", channel) 178 } 179 stub := mapping.ByID(id) 180 if stub == nil { 181 return nil, errors.Errorf("node %d doesn't exist in channel %s's membership", id, channel) 182 } 183 184 if stub.Active() { 185 return stub.RemoteContext, nil 186 } 187 188 err := stub.Activate(c.createRemoteContext(stub, channel)) 189 if err != nil { 190 return nil, errors.WithStack(err) 191 } 192 return stub.RemoteContext, nil 193 } 194 195 // Configure configures the channel with the given RemoteNodes 196 func (c *Comm) Configure(channel string, newNodes []RemoteNode) { 197 c.Logger.Infof("Entering, channel: %s, nodes: %v", channel, newNodes) 198 defer c.Logger.Infof("Exiting") 199 200 c.Lock.Lock() 201 defer c.Lock.Unlock() 202 203 c.createShutdownSignalIfNeeded() 204 205 if c.shutdown { 206 return 207 } 208 209 beforeConfigChange := c.serverCertsInUse() 210 // Update the channel-scoped mapping with the new nodes 211 c.applyMembershipConfig(channel, newNodes) 212 // Close connections to nodes that are not present in the new membership 213 c.cleanUnusedConnections(beforeConfigChange) 214 } 215 216 func (c *Comm) createShutdownSignalIfNeeded() { 217 if c.shutdownSignal == nil { 218 c.shutdownSignal = make(chan struct{}) 219 } 220 } 221 222 // Shutdown shuts down the instance 223 func (c *Comm) Shutdown() { 224 c.Lock.Lock() 225 defer c.Lock.Unlock() 226 227 c.createShutdownSignalIfNeeded() 228 if !c.shutdown { 229 close(c.shutdownSignal) 230 } 231 232 c.shutdown = true 233 for _, members := range c.Chan2Members { 234 for _, member := range members { 235 c.Connections.Disconnect(member.ServerTLSCert) 236 } 237 } 238 } 239 240 // cleanUnusedConnections disconnects all connections that are un-used 241 // at the moment of the invocation 242 func (c *Comm) cleanUnusedConnections(serverCertsBeforeConfig StringSet) { 243 // Scan all nodes after the reconfiguration 244 serverCertsAfterConfig := c.serverCertsInUse() 245 // Filter out the certificates that remained after the reconfiguration 246 serverCertsBeforeConfig.subtract(serverCertsAfterConfig) 247 // Close the connections to all these nodes as they shouldn't be in use now 248 for serverCertificate := range serverCertsBeforeConfig { 249 c.Connections.Disconnect([]byte(serverCertificate)) 250 } 251 } 252 253 // serverCertsInUse returns the server certificates that are in use 254 // represented as strings. 255 func (c *Comm) serverCertsInUse() StringSet { 256 endpointsInUse := make(StringSet) 257 for _, mapping := range c.Chan2Members { 258 endpointsInUse.union(mapping.ServerCertificates()) 259 } 260 return endpointsInUse 261 } 262 263 // applyMembershipConfig sets the given RemoteNodes for the given channel 264 func (c *Comm) applyMembershipConfig(channel string, newNodes []RemoteNode) { 265 mapping := c.getOrCreateMapping(channel) 266 newNodeIDs := make(map[uint64]struct{}) 267 268 for _, node := range newNodes { 269 newNodeIDs[node.ID] = struct{}{} 270 c.updateStubInMapping(channel, mapping, node) 271 } 272 273 // Remove all stubs without a corresponding node 274 // in the new nodes 275 for id, stub := range mapping { 276 if _, exists := newNodeIDs[id]; exists { 277 c.Logger.Info(id, "exists in both old and new membership for channel", channel, ", skipping its deactivation") 278 continue 279 } 280 c.Logger.Info("Deactivated node", id, "who's endpoint is", stub.Endpoint, "as it's removed from membership") 281 delete(mapping, id) 282 stub.Deactivate() 283 } 284 } 285 286 // updateStubInMapping updates the given RemoteNode and adds it to the MemberMapping 287 func (c *Comm) updateStubInMapping(channel string, mapping MemberMapping, node RemoteNode) { 288 stub := mapping.ByID(node.ID) 289 if stub == nil { 290 c.Logger.Info("Allocating a new stub for node", node.ID, "with endpoint of", node.Endpoint, "for channel", channel) 291 stub = &Stub{} 292 } 293 294 // Check if the TLS server certificate of the node is replaced 295 // and if so - then deactivate the stub, to trigger 296 // a re-creation of its gRPC connection 297 if !bytes.Equal(stub.ServerTLSCert, node.ServerTLSCert) { 298 c.Logger.Info("Deactivating node", node.ID, "in channel", channel, 299 "with endpoint of", node.Endpoint, "due to TLS certificate change") 300 stub.Deactivate() 301 } 302 303 // Overwrite the stub Node data with the new data 304 stub.RemoteNode = node 305 306 // Put the stub into the mapping 307 mapping.Put(stub) 308 309 // Check if the stub needs activation. 310 if stub.Active() { 311 return 312 } 313 314 // Activate the stub 315 stub.Activate(c.createRemoteContext(stub, channel)) 316 } 317 318 // createRemoteStub returns a function that creates a RemoteContext. 319 // It is used as a parameter to Stub.Activate() in order to activate 320 // a stub atomically. 321 func (c *Comm) createRemoteContext(stub *Stub, channel string) func() (*RemoteContext, error) { 322 return func() (*RemoteContext, error) { 323 cert, err := x509.ParseCertificate(stub.ServerTLSCert) 324 if err != nil { 325 pemString := string(pem.EncodeToMemory(&pem.Block{Bytes: stub.ServerTLSCert})) 326 c.Logger.Errorf("Invalid DER for channel %s, endpoint %s, ID %d: %v", channel, stub.Endpoint, stub.ID, pemString) 327 return nil, errors.Wrap(err, "invalid certificate DER") 328 } 329 330 c.Logger.Debug("Connecting to", stub.RemoteNode, "for channel", channel) 331 332 conn, err := c.Connections.Connection(stub.Endpoint, stub.ServerTLSCert) 333 if err != nil { 334 c.Logger.Warningf("Unable to obtain connection to %d(%s) (channel %s): %v", stub.ID, stub.Endpoint, channel, err) 335 return nil, err 336 } 337 338 probeConnection := func(conn *grpc.ClientConn) error { 339 connState := conn.GetState() 340 if connState == connectivity.Connecting { 341 return errors.Errorf("connection to %d(%s) is in state %s", stub.ID, stub.Endpoint, connState) 342 } 343 return nil 344 } 345 346 clusterClient := orderer.NewClusterClient(conn) 347 348 workerCountReporter := workerCountReporter{ 349 channel: channel, 350 } 351 352 rc := &RemoteContext{ 353 expiresAt: cert.NotAfter, 354 minimumExpirationWarningInterval: c.MinimumExpirationWarningInterval, 355 certExpWarningThreshold: c.CertExpWarningThreshold, 356 workerCountReporter: workerCountReporter, 357 Channel: channel, 358 Metrics: c.Metrics, 359 SendBuffSize: c.SendBufferSize, 360 shutdownSignal: c.shutdownSignal, 361 endpoint: stub.Endpoint, 362 Logger: c.Logger, 363 ProbeConn: probeConnection, 364 conn: conn, 365 Client: clusterClient, 366 } 367 return rc, nil 368 } 369 } 370 371 // getOrCreateMapping creates a MemberMapping for the given channel 372 // or returns the existing one. 373 func (c *Comm) getOrCreateMapping(channel string) MemberMapping { 374 // Lazily create a mapping if it doesn't already exist 375 mapping, exists := c.Chan2Members[channel] 376 if !exists { 377 mapping = make(MemberMapping) 378 c.Chan2Members[channel] = mapping 379 } 380 return mapping 381 } 382 383 // Stub holds all information about the remote node, 384 // including the RemoteContext for it, and serializes 385 // some operations on it. 386 type Stub struct { 387 lock sync.RWMutex 388 RemoteNode 389 *RemoteContext 390 } 391 392 // Active returns whether the Stub 393 // is active or not 394 func (stub *Stub) Active() bool { 395 stub.lock.RLock() 396 defer stub.lock.RUnlock() 397 return stub.isActive() 398 } 399 400 // Active returns whether the Stub 401 // is active or not. 402 func (stub *Stub) isActive() bool { 403 return stub.RemoteContext != nil 404 } 405 406 // Deactivate deactivates the Stub and 407 // ceases all communication operations 408 // invoked on it. 409 func (stub *Stub) Deactivate() { 410 stub.lock.Lock() 411 defer stub.lock.Unlock() 412 if !stub.isActive() { 413 return 414 } 415 stub.RemoteContext.Abort() 416 stub.RemoteContext = nil 417 } 418 419 // Activate creates a remote context with the given function callback 420 // in an atomic manner - if two parallel invocations are invoked on this Stub, 421 // only a single invocation of createRemoteStub takes place. 422 func (stub *Stub) Activate(createRemoteContext func() (*RemoteContext, error)) error { 423 stub.lock.Lock() 424 defer stub.lock.Unlock() 425 // Check if the stub has already been activated while we were waiting for the lock 426 if stub.isActive() { 427 return nil 428 } 429 remoteStub, err := createRemoteContext() 430 if err != nil { 431 return errors.WithStack(err) 432 } 433 434 stub.RemoteContext = remoteStub 435 return nil 436 } 437 438 // RemoteContext interacts with remote cluster 439 // nodes. Every call can be aborted via call to Abort() 440 type RemoteContext struct { 441 expiresAt time.Time 442 minimumExpirationWarningInterval time.Duration 443 certExpWarningThreshold time.Duration 444 Metrics *Metrics 445 Channel string 446 SendBuffSize int 447 shutdownSignal chan struct{} 448 Logger *flogging.FabricLogger 449 endpoint string 450 Client orderer.ClusterClient 451 ProbeConn func(conn *grpc.ClientConn) error 452 conn *grpc.ClientConn 453 nextStreamID uint64 454 streamsByID streamsMapperReporter 455 workerCountReporter workerCountReporter 456 } 457 458 // Stream is used to send/receive messages to/from the remote cluster member. 459 type Stream struct { 460 abortChan <-chan struct{} 461 sendBuff chan *orderer.StepRequest 462 commShutdown chan struct{} 463 abortReason *atomic.Value 464 metrics *Metrics 465 ID uint64 466 Channel string 467 NodeName string 468 Endpoint string 469 Logger *flogging.FabricLogger 470 Timeout time.Duration 471 orderer.Cluster_StepClient 472 Cancel func(error) 473 canceled *uint32 474 expCheck *certificateExpirationCheck 475 } 476 477 // StreamOperation denotes an operation done by a stream, such a Send or Receive. 478 type StreamOperation func() (*orderer.StepResponse, error) 479 480 // Canceled returns whether the stream was canceled. 481 func (stream *Stream) Canceled() bool { 482 return atomic.LoadUint32(stream.canceled) == uint32(1) 483 } 484 485 // Send sends the given request to the remote cluster member. 486 func (stream *Stream) Send(request *orderer.StepRequest) error { 487 if stream.Canceled() { 488 return errors.New(stream.abortReason.Load().(string)) 489 } 490 var allowDrop bool 491 // We want to drop consensus transactions if the remote node cannot keep up with us, 492 // otherwise we'll slow down the entire FSM. 493 if request.GetConsensusRequest() != nil { 494 allowDrop = true 495 } 496 497 return stream.sendOrDrop(request, allowDrop) 498 } 499 500 // sendOrDrop sends the given request to the remote cluster member, or drops it 501 // if it is a consensus request and the queue is full. 502 func (stream *Stream) sendOrDrop(request *orderer.StepRequest, allowDrop bool) error { 503 msgType := "transaction" 504 if allowDrop { 505 msgType = "consensus" 506 } 507 508 stream.metrics.reportQueueOccupancy(stream.Endpoint, msgType, stream.Channel, len(stream.sendBuff), cap(stream.sendBuff)) 509 510 if allowDrop && len(stream.sendBuff) == cap(stream.sendBuff) { 511 stream.Cancel(errOverflow) 512 stream.metrics.reportMessagesDropped(stream.Endpoint, stream.Channel) 513 return errOverflow 514 } 515 516 select { 517 case <-stream.abortChan: 518 return errors.Errorf("stream %d aborted", stream.ID) 519 case stream.sendBuff <- request: 520 return nil 521 case <-stream.commShutdown: 522 return nil 523 } 524 } 525 526 // sendMessage sends the request down the stream 527 func (stream *Stream) sendMessage(request *orderer.StepRequest) { 528 start := time.Now() 529 var err error 530 defer func() { 531 if !stream.Logger.IsEnabledFor(zap.DebugLevel) { 532 return 533 } 534 var result string 535 if err != nil { 536 result = fmt.Sprintf("but failed due to %s", err.Error()) 537 } 538 stream.Logger.Debugf("Send of %s to %s(%s) took %v %s", requestAsString(request), 539 stream.NodeName, stream.Endpoint, time.Since(start), result) 540 }() 541 542 f := func() (*orderer.StepResponse, error) { 543 startSend := time.Now() 544 stream.expCheck.checkExpiration(startSend, stream.Channel) 545 err := stream.Cluster_StepClient.Send(request) 546 stream.metrics.reportMsgSendTime(stream.Endpoint, stream.Channel, time.Since(startSend)) 547 return nil, err 548 } 549 550 _, err = stream.operateWithTimeout(f) 551 } 552 553 func (stream *Stream) serviceStream() { 554 defer stream.Cancel(errAborted) 555 556 for { 557 select { 558 case msg := <-stream.sendBuff: 559 stream.sendMessage(msg) 560 case <-stream.abortChan: 561 return 562 case <-stream.commShutdown: 563 return 564 } 565 } 566 } 567 568 // Recv receives a message from a remote cluster member. 569 func (stream *Stream) Recv() (*orderer.StepResponse, error) { 570 start := time.Now() 571 defer func() { 572 if !stream.Logger.IsEnabledFor(zap.DebugLevel) { 573 return 574 } 575 stream.Logger.Debugf("Receive from %s(%s) took %v", stream.NodeName, stream.Endpoint, time.Since(start)) 576 }() 577 578 f := func() (*orderer.StepResponse, error) { 579 return stream.Cluster_StepClient.Recv() 580 } 581 582 return stream.operateWithTimeout(f) 583 } 584 585 // operateWithTimeout performs the given operation on the stream, and blocks until the timeout expires. 586 func (stream *Stream) operateWithTimeout(invoke StreamOperation) (*orderer.StepResponse, error) { 587 timer := time.NewTimer(stream.Timeout) 588 defer timer.Stop() 589 590 var operationEnded sync.WaitGroup 591 operationEnded.Add(1) 592 593 responseChan := make(chan struct { 594 res *orderer.StepResponse 595 err error 596 }, 1) 597 598 go func() { 599 defer operationEnded.Done() 600 res, err := invoke() 601 responseChan <- struct { 602 res *orderer.StepResponse 603 err error 604 }{res: res, err: err} 605 }() 606 607 select { 608 case r := <-responseChan: 609 if r.err != nil { 610 stream.Cancel(r.err) 611 } 612 return r.res, r.err 613 case <-timer.C: 614 stream.Logger.Warningf("Stream %d to %s(%s) was forcibly terminated because timeout (%v) expired", 615 stream.ID, stream.NodeName, stream.Endpoint, stream.Timeout) 616 stream.Cancel(errTimeout) 617 // Wait for the operation goroutine to end 618 operationEnded.Wait() 619 return nil, errTimeout 620 } 621 } 622 623 func requestAsString(request *orderer.StepRequest) string { 624 switch t := request.GetPayload().(type) { 625 case *orderer.StepRequest_SubmitRequest: 626 if t.SubmitRequest == nil || t.SubmitRequest.Payload == nil { 627 return fmt.Sprintf("Empty SubmitRequest: %v", t.SubmitRequest) 628 } 629 return fmt.Sprintf("SubmitRequest for channel %s with payload of size %d", 630 t.SubmitRequest.Channel, len(t.SubmitRequest.Payload.Payload)) 631 case *orderer.StepRequest_ConsensusRequest: 632 return fmt.Sprintf("ConsensusRequest for channel %s with payload of size %d", 633 t.ConsensusRequest.Channel, len(t.ConsensusRequest.Payload)) 634 default: 635 return fmt.Sprintf("unknown type: %v", request) 636 } 637 } 638 639 // NewStream creates a new stream. 640 // It is not thread safe, and Send() or Recv() block only until the timeout expires. 641 func (rc *RemoteContext) NewStream(timeout time.Duration) (*Stream, error) { 642 if err := rc.ProbeConn(rc.conn); err != nil { 643 return nil, err 644 } 645 646 ctx, cancel := context.WithCancel(context.TODO()) 647 stream, err := rc.Client.Step(ctx) 648 if err != nil { 649 cancel() 650 return nil, errors.WithStack(err) 651 } 652 653 streamID := atomic.AddUint64(&rc.nextStreamID, 1) 654 nodeName := commonNameFromContext(stream.Context()) 655 656 var canceled uint32 657 658 abortChan := make(chan struct{}) 659 660 abort := func() { 661 cancel() 662 rc.streamsByID.Delete(streamID) 663 rc.Metrics.reportEgressStreamCount(rc.Channel, atomic.LoadUint32(&rc.streamsByID.size)) 664 rc.Logger.Debugf("Stream %d to %s(%s) is aborted", streamID, nodeName, rc.endpoint) 665 atomic.StoreUint32(&canceled, 1) 666 close(abortChan) 667 } 668 669 once := &sync.Once{} 670 abortReason := &atomic.Value{} 671 cancelWithReason := func(err error) { 672 abortReason.Store(err.Error()) 673 once.Do(abort) 674 } 675 676 logger := flogging.MustGetLogger("orderer.common.cluster.step") 677 stepLogger := logger.WithOptions(zap.AddCallerSkip(1)) 678 679 s := &Stream{ 680 Channel: rc.Channel, 681 metrics: rc.Metrics, 682 abortReason: abortReason, 683 abortChan: abortChan, 684 sendBuff: make(chan *orderer.StepRequest, rc.SendBuffSize), 685 commShutdown: rc.shutdownSignal, 686 NodeName: nodeName, 687 Logger: stepLogger, 688 ID: streamID, 689 Endpoint: rc.endpoint, 690 Timeout: timeout, 691 Cluster_StepClient: stream, 692 Cancel: cancelWithReason, 693 canceled: &canceled, 694 } 695 696 s.expCheck = &certificateExpirationCheck{ 697 minimumExpirationWarningInterval: rc.minimumExpirationWarningInterval, 698 expirationWarningThreshold: rc.certExpWarningThreshold, 699 expiresAt: rc.expiresAt, 700 endpoint: s.Endpoint, 701 nodeName: s.NodeName, 702 alert: func(template string, args ...interface{}) { 703 s.Logger.Warningf(template, args...) 704 }, 705 } 706 707 rc.Logger.Debugf("Created new stream to %s with ID of %d and buffer size of %d", 708 rc.endpoint, streamID, cap(s.sendBuff)) 709 710 rc.streamsByID.Store(streamID, s) 711 rc.Metrics.reportEgressStreamCount(rc.Channel, atomic.LoadUint32(&rc.streamsByID.size)) 712 713 go func() { 714 rc.workerCountReporter.increment(s.metrics) 715 s.serviceStream() 716 rc.workerCountReporter.decrement(s.metrics) 717 }() 718 719 return s, nil 720 } 721 722 // Abort aborts the contexts the RemoteContext uses, thus effectively 723 // causes all operations that use this RemoteContext to terminate. 724 func (rc *RemoteContext) Abort() { 725 rc.streamsByID.Range(func(_, value interface{}) bool { 726 value.(*Stream).Cancel(errAborted) 727 return false 728 }) 729 } 730 731 func commonNameFromContext(ctx context.Context) string { 732 cert := comm.ExtractCertificateFromContext(ctx) 733 if cert == nil { 734 return "unidentified node" 735 } 736 return cert.Subject.CommonName 737 } 738 739 type streamsMapperReporter struct { 740 size uint32 741 sync.Map 742 } 743 744 func (smr *streamsMapperReporter) Delete(key interface{}) { 745 smr.Map.Delete(key) 746 atomic.AddUint32(&smr.size, ^uint32(0)) 747 } 748 749 func (smr *streamsMapperReporter) Store(key, value interface{}) { 750 smr.Map.Store(key, value) 751 atomic.AddUint32(&smr.size, 1) 752 } 753 754 type workerCountReporter struct { 755 channel string 756 workerCount uint32 757 } 758 759 func (wcr *workerCountReporter) increment(m *Metrics) { 760 count := atomic.AddUint32(&wcr.workerCount, 1) 761 m.reportWorkerCount(wcr.channel, count) 762 } 763 764 func (wcr *workerCountReporter) decrement(m *Metrics) { 765 // ^0 flips all zeros to ones, which means 766 // 2^32 - 1, and then we add this number wcr.workerCount. 767 // It follows from commutativity of the unsigned integers group 768 // that wcr.workerCount + 2^32 - 1 = wcr.workerCount - 1 + 2^32 769 // which is just wcr.workerCount - 1. 770 count := atomic.AddUint32(&wcr.workerCount, ^uint32(0)) 771 m.reportWorkerCount(wcr.channel, count) 772 }