dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/controller/loadreport.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * 20 * Copyright 2021 gRPC authors. 21 * 22 */ 23 24 package controller 25 26 import ( 27 "context" 28 ) 29 30 import ( 31 "google.golang.org/grpc" 32 ) 33 34 import ( 35 "dubbo.apache.org/dubbo-go/v3/xds/client/controller/version" 36 "dubbo.apache.org/dubbo-go/v3/xds/client/load" 37 ) 38 39 // ReportLoad starts an load reporting stream to the given server. If the server 40 // is not an empty string, and is different from the management server, a new 41 // ClientConn will be created. 42 // 43 // The same options used for creating the Client will be used (including 44 // NodeProto, and dial options if necessary). 45 // 46 // It returns a Store for the user to report loads, a function to cancel the 47 // load reporting stream. 48 // 49 // TODO: LRS refactor; maybe a new controller should be created for a separate 50 // server, so that the same stream can be shared by different reporters to the 51 // same server, even if they originate from different Controllers. 52 func (c *Controller) ReportLoad(server string) (*load.Store, func()) { 53 c.lrsMu.Lock() 54 defer c.lrsMu.Unlock() 55 56 // If there's already a client to this server, use it. Otherwise, create 57 // one. 58 lrsC, ok := c.lrsClients[server] 59 if !ok { 60 lrsC = newLRSClient(c, server) 61 c.lrsClients[server] = lrsC 62 } 63 64 store := lrsC.ref() 65 return store, func() { 66 // This is a callback, need to hold lrsMu. 67 c.lrsMu.Lock() 68 defer c.lrsMu.Unlock() 69 if lrsC.unRef() { 70 // Delete the lrsClient from map if this is the last reference. 71 delete(c.lrsClients, server) 72 } 73 } 74 } 75 76 // lrsClient maps to one lrsServer. It contains: 77 // - a ClientConn to this server (only if it's different from the management 78 // server) 79 // - a load.Store that contains loads only for this server 80 type lrsClient struct { 81 parent *Controller 82 server string 83 84 cc *grpc.ClientConn // nil if the server is same as the management server 85 refCount int 86 cancelStream func() 87 loadStore *load.Store 88 } 89 90 // newLRSClient creates a new LRS stream to the server. 91 func newLRSClient(parent *Controller, server string) *lrsClient { 92 return &lrsClient{ 93 parent: parent, 94 server: server, 95 refCount: 0, 96 } 97 } 98 99 // ref increments the refCount. If this is the first ref, it starts the LRS stream. 100 // 101 // Not thread-safe, caller needs to synchronize. 102 func (lrsC *lrsClient) ref() *load.Store { 103 lrsC.refCount++ 104 if lrsC.refCount == 1 { 105 lrsC.startStream() 106 } 107 return lrsC.loadStore 108 } 109 110 // unRef decrements the refCount, and closes the stream if refCount reaches 0 111 // (and close the cc if cc is not xDS cc). It returns whether refCount reached 0 112 // after this call. 113 // 114 // Not thread-safe, caller needs to synchronize. 115 func (lrsC *lrsClient) unRef() (closed bool) { 116 lrsC.refCount-- 117 if lrsC.refCount != 0 { 118 return false 119 } 120 lrsC.parent.logger.Infof("Stopping load report to server: %s", lrsC.server) 121 lrsC.cancelStream() 122 if lrsC.cc != nil { 123 lrsC.cc.Close() 124 } 125 return true 126 } 127 128 // startStream starts the LRS stream to the server. If server is not the same 129 // management server from the parent, it also creates a ClientConn. 130 func (lrsC *lrsClient) startStream() { 131 var cc *grpc.ClientConn 132 133 lrsC.parent.logger.Infof("Starting load report to server: %s", lrsC.server) 134 if lrsC.server == "" || lrsC.server == lrsC.parent.config.ServerURI { 135 // Reuse the xDS client if server is the same. 136 cc = lrsC.parent.cc 137 } else { 138 lrsC.parent.logger.Infof("LRS server is different from management server, starting a new ClientConn") 139 ccNew, err := grpc.Dial(lrsC.server, lrsC.parent.config.Creds) 140 if err != nil { 141 // An error from a non-blocking dial indicates something serious. 142 lrsC.parent.logger.Infof("xds: failed to dial load report server {%s}: %v", lrsC.server, err) 143 return 144 } 145 cc = ccNew 146 lrsC.cc = ccNew 147 } 148 149 var ctx context.Context 150 ctx, lrsC.cancelStream = context.WithCancel(context.Background()) 151 152 // Create the store and stream. 153 lrsC.loadStore = load.NewStore() 154 go lrsC.parent.reportLoad(ctx, cc, version.LoadReportingOptions{LoadStore: lrsC.loadStore}) 155 }