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  }