grpc-xds/greeter-go/pkg/greeter/client.go (78 lines of code) (raw):

// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package greeter import ( "context" "fmt" "math" "time" "github.com/go-logr/logr" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" xdscredentials "google.golang.org/grpc/credentials/xds" helloworldpb "google.golang.org/grpc/examples/helloworld/helloworld" "google.golang.org/grpc/keepalive" "github.com/googlecloudplatform/solutions-workshops/grpc-xds/greeter-go/pkg/interceptors" "github.com/googlecloudplatform/solutions-workshops/grpc-xds/greeter-go/pkg/logging" ) const ( grpcClientDialTimeout = 10 * time.Second grpcClientKeepaliveTime = 30 * time.Second grpcClientKeepaliveTimeout = 5 * time.Second grpcClientIdleTimeout = math.MaxInt64 // good idea? ) type Client struct { logger logr.Logger nextHop string client helloworldpb.GreeterClient } func NewClient(ctx context.Context, nextHop string) (*Client, error) { logger := logging.FromContext(ctx) dialOpts, err := dialOptions(logger) if err != nil { return nil, fmt.Errorf("could not configure greeter client connection dial options: %w", err) } clientConn, err := grpc.NewClient(nextHop, dialOpts...) if err != nil { return nil, fmt.Errorf("could not create a virtual connection to target=%s: %w", nextHop, err) } addClientConnectionCloseBehavior(ctx, logger, clientConn) return &Client{ client: helloworldpb.NewGreeterClient(clientConn), logger: logger, nextHop: nextHop, }, nil } func (c *Client) SayHello(requestCtx context.Context, name string) (string, error) { resp, err := c.client.SayHello(requestCtx, &helloworldpb.HelloRequest{Name: name}, grpc.WaitForReady(true)) if err != nil { return "", fmt.Errorf("could not greet name=%s at target=%s: %w", name, c.nextHop, err) } return resp.GetMessage(), nil } // dialOptions sets parameters for client connection establishment. func dialOptions(logger logr.Logger) ([]grpc.DialOption, error) { logger.V(1).Info("Using xDS client-side credentials, with insecure as fallback") clientCredentials, err := xdscredentials.NewClientCredentials(xdscredentials.ClientOptions{FallbackCreds: insecure.NewCredentials()}) if err != nil { return nil, fmt.Errorf("could not create client-side transport credentials for xDS: %w", err) } return []grpc.DialOption{ grpc.WithChainStreamInterceptor(interceptors.StreamClientLogging(logger)), grpc.WithChainUnaryInterceptor(interceptors.UnaryClientLogging(logger)), grpc.WithIdleTimeout(time.Duration(grpcClientIdleTimeout)), grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: grpcClientKeepaliveTime, Timeout: grpcClientKeepaliveTimeout, PermitWithoutStream: true, }), grpc.WithTransportCredentials(clientCredentials), }, nil } func addClientConnectionCloseBehavior(ctx context.Context, logger logr.Logger, clientConn *grpc.ClientConn) { go func(cc *grpc.ClientConn) { <-ctx.Done() logger.Info("Closing the greeter client connection") err := cc.Close() if err != nil { logger.Error(err, "Error when closing the greeter client connection") } }(clientConn) }