github.com/TeaOSLab/EdgeNode@v1.3.8/internal/rpc/rpc_client.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "crypto/tls" 6 "encoding/base64" 7 "errors" 8 "fmt" 9 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 10 "github.com/TeaOSLab/EdgeNode/internal/configs" 11 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 12 "github.com/TeaOSLab/EdgeNode/internal/encrypt" 13 "github.com/TeaOSLab/EdgeNode/internal/utils" 14 "github.com/iwind/TeaGo/maps" 15 "github.com/iwind/TeaGo/rands" 16 "google.golang.org/grpc" 17 "google.golang.org/grpc/connectivity" 18 "google.golang.org/grpc/credentials" 19 "google.golang.org/grpc/credentials/insecure" 20 "google.golang.org/grpc/encoding/gzip" 21 "google.golang.org/grpc/keepalive" 22 "google.golang.org/grpc/metadata" 23 "net/url" 24 "sync" 25 "time" 26 ) 27 28 type RPCClient struct { 29 apiConfig *configs.APIConfig 30 conns []*grpc.ClientConn 31 32 locker sync.RWMutex 33 34 NodeRPC pb.NodeServiceClient 35 NodeLogRPC pb.NodeLogServiceClient 36 NodeTaskRPC pb.NodeTaskServiceClient 37 NodeValueRPC pb.NodeValueServiceClient 38 HTTPAccessLogRPC pb.HTTPAccessLogServiceClient 39 HTTPCacheTaskKeyRPC pb.HTTPCacheTaskKeyServiceClient 40 APINodeRPC pb.APINodeServiceClient 41 IPLibraryArtifactRPC pb.IPLibraryArtifactServiceClient 42 IPListRPC pb.IPListServiceClient 43 IPItemRPC pb.IPItemServiceClient 44 FileRPC pb.FileServiceClient 45 FileChunkRPC pb.FileChunkServiceClient 46 ACMEAuthenticationRPC pb.ACMEAuthenticationServiceClient 47 ServerRPC pb.ServerServiceClient 48 ServerDailyStatRPC pb.ServerDailyStatServiceClient 49 ServerBandwidthStatRPC pb.ServerBandwidthStatServiceClient 50 MetricStatRPC pb.MetricStatServiceClient 51 FirewallRPC pb.FirewallServiceClient 52 SSLCertRPC pb.SSLCertServiceClient 53 ScriptRPC pb.ScriptServiceClient 54 UserRPC pb.UserServiceClient 55 ClientAgentIPRPC pb.ClientAgentIPServiceClient 56 AuthorityKeyRPC pb.AuthorityKeyServiceClient 57 UpdatingServerListRPC pb.UpdatingServerListServiceClient 58 PlanRPC pb.PlanServiceClient 59 } 60 61 func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) { 62 if apiConfig == nil { 63 return nil, errors.New("api config should not be nil") 64 } 65 66 var client = &RPCClient{ 67 apiConfig: apiConfig, 68 } 69 70 // 初始化RPC实例 71 client.NodeRPC = pb.NewNodeServiceClient(client) 72 client.NodeLogRPC = pb.NewNodeLogServiceClient(client) 73 client.NodeTaskRPC = pb.NewNodeTaskServiceClient(client) 74 client.NodeValueRPC = pb.NewNodeValueServiceClient(client) 75 client.HTTPAccessLogRPC = pb.NewHTTPAccessLogServiceClient(client) 76 client.HTTPCacheTaskKeyRPC = pb.NewHTTPCacheTaskKeyServiceClient(client) 77 client.APINodeRPC = pb.NewAPINodeServiceClient(client) 78 client.IPLibraryArtifactRPC = pb.NewIPLibraryArtifactServiceClient(client) 79 client.IPListRPC = pb.NewIPListServiceClient(client) 80 client.IPItemRPC = pb.NewIPItemServiceClient(client) 81 client.FileRPC = pb.NewFileServiceClient(client) 82 client.FileChunkRPC = pb.NewFileChunkServiceClient(client) 83 client.ACMEAuthenticationRPC = pb.NewACMEAuthenticationServiceClient(client) 84 client.ServerRPC = pb.NewServerServiceClient(client) 85 client.ServerDailyStatRPC = pb.NewServerDailyStatServiceClient(client) 86 client.ServerBandwidthStatRPC = pb.NewServerBandwidthStatServiceClient(client) 87 client.MetricStatRPC = pb.NewMetricStatServiceClient(client) 88 client.FirewallRPC = pb.NewFirewallServiceClient(client) 89 client.SSLCertRPC = pb.NewSSLCertServiceClient(client) 90 client.ScriptRPC = pb.NewScriptServiceClient(client) 91 client.UserRPC = pb.NewUserServiceClient(client) 92 client.ClientAgentIPRPC = pb.NewClientAgentIPServiceClient(client) 93 client.AuthorityKeyRPC = pb.NewAuthorityKeyServiceClient(client) 94 client.UpdatingServerListRPC = pb.NewUpdatingServerListServiceClient(client) 95 client.PlanRPC = pb.NewPlanServiceClient(client) 96 97 err := client.init() 98 if err != nil { 99 return nil, err 100 } 101 102 return client, nil 103 } 104 105 // Context 节点上下文信息 106 func (this *RPCClient) Context() context.Context { 107 var m = maps.Map{ 108 "timestamp": time.Now().Unix(), 109 "type": "node", 110 "userId": 0, 111 } 112 method, err := encrypt.NewMethodInstance(teaconst.EncryptMethod, this.apiConfig.Secret, this.apiConfig.NodeId) 113 if err != nil { 114 utils.PrintError(err) 115 return context.Background() 116 } 117 data, err := method.Encrypt(m.AsJSON()) 118 if err != nil { 119 utils.PrintError(err) 120 return context.Background() 121 } 122 var token = base64.StdEncoding.EncodeToString(data) 123 124 var ctx = context.Background() 125 ctx = metadata.AppendToOutgoingContext(ctx, "nodeId", this.apiConfig.NodeId, "token", token) 126 return ctx 127 } 128 129 // ClusterContext 集群上下文 130 func (this *RPCClient) ClusterContext(clusterId string, clusterSecret string) context.Context { 131 ctx := context.Background() 132 m := maps.Map{ 133 "timestamp": time.Now().Unix(), 134 "type": "cluster", 135 "userId": 0, 136 } 137 method, err := encrypt.NewMethodInstance(teaconst.EncryptMethod, clusterSecret, clusterId) 138 if err != nil { 139 utils.PrintError(err) 140 return context.Background() 141 } 142 data, err := method.Encrypt(m.AsJSON()) 143 if err != nil { 144 utils.PrintError(err) 145 return context.Background() 146 } 147 token := base64.StdEncoding.EncodeToString(data) 148 ctx = metadata.AppendToOutgoingContext(ctx, "nodeId", clusterId, "token", token) 149 return ctx 150 } 151 152 // Close 关闭连接 153 func (this *RPCClient) Close() { 154 this.locker.Lock() 155 156 for _, conn := range this.conns { 157 _ = conn.Close() 158 } 159 160 this.locker.Unlock() 161 } 162 163 // UpdateConfig 修改配置 164 func (this *RPCClient) UpdateConfig(config *configs.APIConfig) error { 165 this.apiConfig = config 166 167 this.locker.Lock() 168 err := this.init() 169 this.locker.Unlock() 170 return err 171 } 172 173 // TestEndpoints 测试Endpoints是否可用 174 func (this *RPCClient) TestEndpoints(endpoints []string) bool { 175 if len(endpoints) == 0 { 176 return false 177 } 178 179 var wg = sync.WaitGroup{} 180 wg.Add(len(endpoints)) 181 182 var ok = false 183 184 for _, endpoint := range endpoints { 185 go func(endpoint string) { 186 defer wg.Done() 187 188 u, err := url.Parse(endpoint) 189 if err != nil { 190 return 191 } 192 193 ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second) 194 defer func() { 195 cancelFunc() 196 }() 197 var conn *grpc.ClientConn 198 if u.Scheme == "http" { 199 conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) 200 } else if u.Scheme == "https" { 201 conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ 202 InsecureSkipVerify: true, 203 })), grpc.WithBlock()) 204 } else { 205 return 206 } 207 if err != nil { 208 return 209 } 210 if conn == nil { 211 return 212 } 213 defer func() { 214 _ = conn.Close() 215 }() 216 217 var pingService = pb.NewPingServiceClient(conn) 218 _, err = pingService.Ping(this.Context(), &pb.PingRequest{}) 219 if err != nil { 220 return 221 } 222 223 ok = true 224 }(endpoint) 225 } 226 wg.Wait() 227 228 return ok 229 } 230 231 // 初始化 232 func (this *RPCClient) init() error { 233 // 重新连接 234 var conns = []*grpc.ClientConn{} 235 for _, endpoint := range this.apiConfig.RPCEndpoints { 236 u, err := url.Parse(endpoint) 237 if err != nil { 238 return fmt.Errorf("parse endpoint failed: %w", err) 239 } 240 var conn *grpc.ClientConn 241 var callOptions = grpc.WithDefaultCallOptions( 242 grpc.MaxCallRecvMsgSize(512<<20), 243 grpc.MaxCallSendMsgSize(512<<20), 244 grpc.UseCompressor(gzip.Name), 245 ) 246 var keepaliveParams = grpc.WithKeepaliveParams(keepalive.ClientParameters{ 247 Time: 30 * time.Second, 248 }) 249 if u.Scheme == "http" { 250 conn, err = grpc.Dial(u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions, keepaliveParams) 251 } else if u.Scheme == "https" { 252 conn, err = grpc.Dial(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ 253 InsecureSkipVerify: true, 254 })), callOptions, keepaliveParams) 255 } else { 256 return errors.New("parse endpoint failed: invalid scheme '" + u.Scheme + "'") 257 } 258 if err != nil { 259 return err 260 } 261 conns = append(conns, conn) 262 } 263 if len(conns) == 0 { 264 return errors.New("[RPC]no available endpoints") 265 } 266 267 // 这里不需要加锁,防止和pickConn()冲突 268 this.conns = conns 269 return nil 270 } 271 272 // 随机选择一个连接 273 func (this *RPCClient) pickConn() *grpc.ClientConn { 274 this.locker.Lock() 275 defer this.locker.Unlock() 276 277 // 检查连接状态 278 var countConns = len(this.conns) 279 if countConns > 0 { 280 if countConns == 1 { 281 return this.conns[0] 282 } 283 284 for _, stateArray := range [][2]connectivity.State{ 285 {connectivity.Ready, connectivity.Idle}, // 优先Ready和Idle 286 {connectivity.Connecting, connectivity.Connecting}, 287 {connectivity.TransientFailure, connectivity.TransientFailure}, 288 } { 289 var availableConns = []*grpc.ClientConn{} 290 for _, conn := range this.conns { 291 var state = conn.GetState() 292 if state == stateArray[0] || state == stateArray[1] { 293 availableConns = append(availableConns, conn) 294 } 295 } 296 if len(availableConns) > 0 { 297 return this.randConn(availableConns) 298 } 299 } 300 } 301 302 return this.randConn(this.conns) 303 } 304 305 func (this *RPCClient) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { 306 var conn = this.pickConn() 307 if conn == nil { 308 return errors.New("could not get available grpc connection") 309 } 310 return conn.Invoke(ctx, method, args, reply, opts...) 311 } 312 313 func (this *RPCClient) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { 314 var conn = this.pickConn() 315 if conn == nil { 316 return nil, errors.New("could not get available grpc connection") 317 } 318 return conn.NewStream(ctx, desc, method, opts...) 319 } 320 321 func (this *RPCClient) randConn(conns []*grpc.ClientConn) *grpc.ClientConn { 322 var l = len(conns) 323 if l == 0 { 324 return nil 325 } 326 if l == 1 { 327 return conns[0] 328 } 329 return conns[rands.Int(0, l-1)] 330 }