code: func NewGRPCClient(config ClientConfig) (*GRPCClient, error) {
  	client := &GRPCClient{}
  
  	// parse secure options
  	err := client.parseSecureOptions(config.SecOpts)
  	if err != nil {
  		return client, err
  	}
  
  	// keepalive options
  	var kap keepalive.ClientParameters
  	if config.KaOpts != nil {
  		kap = keepalive.ClientParameters{
  			Time:    config.KaOpts.ClientInterval,
  			Timeout: config.KaOpts.ClientTimeout}
  	} else {
  		// use defaults
  		kap = keepalive.ClientParameters{
  			Time:    DefaultKeepaliveOptions.ClientInterval,
  			Timeout: DefaultKeepaliveOptions.ClientTimeout}
  	}
  	kap.PermitWithoutStream = true
  	// set keepalive
  	client.dialOpts = append(client.dialOpts, grpc.WithKeepaliveParams(kap))
  	// Unless asynchronous connect is set, make connection establishment blocking.
  	if !config.AsyncConnect {
  		client.dialOpts = append(client.dialOpts, grpc.WithBlock())
  		client.dialOpts = append(client.dialOpts, grpc.FailOnNonTempDialError(true))
  	}
  	client.timeout = config.Timeout
  	// set send/recv message size to package defaults
  	client.maxRecvMsgSize = MaxRecvMsgSize
  	client.maxSendMsgSize = MaxSendMsgSize
  
  	return client, nil
  }
docs: // NewGRPCClient creates a new implementation of GRPCClient given an address
  // and client configuration