CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-grpc--grpc-services

gRPC service utilities providing health checking, server reflection, channelz observability, and binary logging capabilities

Pending
Overview
Eval results
Files

load-balancing.mddocs/

Load Balancing Utilities

Client-side health checking utilities for load balancers and connection management. Enables intelligent routing decisions based on server health and performance metrics.

Capabilities

HealthCheckingLoadBalancerUtil

Utility for enabling client-side health checking for LoadBalancers, allowing load balancers to automatically route traffic away from unhealthy servers.

/**
 * Utility for enabling client-side health checking for LoadBalancers.
 * Wraps existing load balancer factories with health checking capabilities.
 */
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5025")
public final class HealthCheckingLoadBalancerUtil {
  
  /**
   * Creates a health-checking-capable LoadBalancer
   * @param factory The underlying load balancer factory to wrap
   * @param helper The load balancer helper for managing subchannels
   * @return LoadBalancer instance with health checking enabled
   */
  public static LoadBalancer newHealthCheckingLoadBalancer(
    LoadBalancer.Factory factory, 
    Helper helper
  );
}

Usage Examples:

import io.grpc.LoadBalancer;
import io.grpc.ManagedChannelBuilder;
import io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil;
import io.grpc.util.RoundRobinLoadBalancerFactory;

// Create a health-checking round-robin load balancer
public class HealthAwareClient {
    private final ManagedChannel channel;
    
    public HealthAwareClient(String target) {
        // Create channel with health-checking load balancer
        this.channel = ManagedChannelBuilder.forTarget(target)
            .defaultLoadBalancingPolicy("health_checking_round_robin")
            .usePlaintext()
            .build();
    }
    
    // Alternative: Programmatic load balancer configuration
    public HealthAwareClient(String target, LoadBalancer.Factory baseFactory) {
        LoadBalancerRegistry registry = LoadBalancerRegistry.getDefaultRegistry();
        
        // Register health-checking variant
        registry.register(new LoadBalancer.Factory() {
            @Override
            public LoadBalancer newLoadBalancer(LoadBalancer.Helper helper) {
                return HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer(
                    baseFactory, helper);
            }
            
            @Override
            public String getPolicyName() {
                return "health_checking_custom";
            }
        });
        
        this.channel = ManagedChannelBuilder.forTarget(target)
            .defaultLoadBalancingPolicy("health_checking_custom")
            .usePlaintext()
            .build();
    }
}

Integration with Service Discovery

Health checking load balancers work seamlessly with service discovery systems:

Consul Integration Example

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.protobuf.services.HealthCheckingLoadBalancerUtil;

public class ConsulAwareHealthCheckingClient {
    private final ManagedChannel channel;
    
    public ConsulAwareHealthCheckingClient() {
        // Service discovery target that resolves to multiple endpoints
        String consulTarget = "consul://user-service";
        
        this.channel = ManagedChannelBuilder.forTarget(consulTarget)
            // Health checking will verify each discovered endpoint
            .defaultLoadBalancingPolicy("health_checking_round_robin")
            .usePlaintext()
            .build();
    }
    
    public void makeRequest() {
        UserServiceGrpc.UserServiceBlockingStub stub = 
            UserServiceGrpc.newBlockingStub(channel);
            
        try {
            // Request will be routed only to healthy servers
            GetUserResponse response = stub.getUser(
                GetUserRequest.newBuilder()
                    .setUserId(12345)
                    .build()
            );
            
            System.out.println("User retrieved: " + response.getUser().getName());
            
        } catch (StatusRuntimeException e) {
            System.err.println("Request failed: " + e.getStatus());
        }
    }
}

Kubernetes Service Integration

public class KubernetesHealthCheckingClient {
    private final ManagedChannel channel;
    
    public KubernetesHealthCheckingClient(String serviceName, String namespace) {
        // Kubernetes DNS target
        String kubernetesTarget = String.format("dns:///%s.%s.svc.cluster.local:8080", 
                                              serviceName, namespace);
        
        this.channel = ManagedChannelBuilder.forTarget(kubernetesTarget)
            .defaultLoadBalancingPolicy("health_checking_round_robin")
            // Use keep-alive for better health detection
            .keepAliveTime(30, TimeUnit.SECONDS)
            .keepAliveTimeout(5, TimeUnit.SECONDS)
            .keepAliveWithoutCalls(true)
            .usePlaintext()
            .build();
    }
    
    public void shutdown() {
        channel.shutdown();
        try {
            if (!channel.awaitTermination(60, TimeUnit.SECONDS)) {
                channel.shutdownNow();
            }
        } catch (InterruptedException e) {
            channel.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Advanced Health Checking Patterns

Custom Health Check Configuration

import io.grpc.ChannelLogger;
import io.grpc.ConnectivityState;
import io.grpc.LoadBalancer;

public class AdvancedHealthCheckingClient {
    
    public static class CustomHealthCheckingFactory extends LoadBalancer.Factory {
        private final LoadBalancer.Factory delegate;
        private final String healthServiceName;
        
        public CustomHealthCheckingFactory(LoadBalancer.Factory delegate, 
                                         String healthServiceName) {
            this.delegate = delegate;
            this.healthServiceName = healthServiceName;
        }
        
        @Override
        public LoadBalancer newLoadBalancer(LoadBalancer.Helper helper) {
            // Custom helper that configures health checking
            LoadBalancer.Helper healthCheckingHelper = new LoadBalancer.Helper() {
                @Override
                public ManagedChannel createSubchannel(
                    CreateSubchannelArgs args) {
                    
                    // Configure health checking for each subchannel
                    CreateSubchannelArgs.Builder argsBuilder = args.toBuilder();
                    
                    // Add health check configuration
                    Map<String, Object> healthCheckConfig = new HashMap<>();
                    healthCheckConfig.put("serviceName", healthServiceName);
                    
                    // Apply health check configuration to subchannel
                    return helper.createSubchannel(argsBuilder.build());
                }
                
                // Delegate other methods to original helper
                @Override
                public void updateBalancingState(ConnectivityState newState, 
                                               SubchannelPicker newPicker) {
                    helper.updateBalancingState(newState, newPicker);
                }
                
                @Override
                public ChannelLogger getChannelLogger() {
                    return helper.getChannelLogger();
                }
                
                // ... other delegated methods
            };
            
            return HealthCheckingLoadBalancerUtil.newHealthCheckingLoadBalancer(
                delegate, healthCheckingHelper);
        }
        
        @Override
        public String getPolicyName() {
            return "custom_health_checking";
        }
    }
    
    public static ManagedChannel createHealthCheckingChannel(String target, 
                                                           String serviceName) {
        LoadBalancer.Factory baseFactory = new RoundRobinLoadBalancerFactory();
        LoadBalancer.Factory healthCheckingFactory = 
            new CustomHealthCheckingFactory(baseFactory, serviceName);
        
        LoadBalancerRegistry.getDefaultRegistry()
            .register(healthCheckingFactory);
        
        return ManagedChannelBuilder.forTarget(target)
            .defaultLoadBalancingPolicy("custom_health_checking")
            .usePlaintext()
            .build();
    }
}

Health Check Monitoring

public class HealthCheckMonitor {
    private final ManagedChannel channel;
    private final ScheduledExecutorService scheduler;
    
    public HealthCheckMonitor(ManagedChannel channel) {
        this.channel = channel;
        this.scheduler = Executors.newScheduledThreadPool(1);
    }
    
    public void startMonitoring() {
        scheduler.scheduleAtFixedRate(this::checkChannelHealth, 0, 10, TimeUnit.SECONDS);
    }
    
    private void checkChannelHealth() {
        ConnectivityState state = channel.getState(false);
        
        switch (state) {
            case READY:
                System.out.println("Channel is healthy and ready");
                break;
            case TRANSIENT_FAILURE:
                System.out.println("Channel experiencing transient failure");
                break;
            case SHUTDOWN:
                System.out.println("Channel is shutdown");
                break;
            case IDLE:
                System.out.println("Channel is idle");
                // Trigger connection attempt
                channel.getState(true);
                break;
            case CONNECTING:
                System.out.println("Channel is connecting");
                break;
        }
    }
    
    public void shutdown() {
        scheduler.shutdown();
    }
}

Integration with Metrics

Health checking load balancers can be combined with metrics collection for comprehensive observability:

public class MetricsAwareHealthCheckingClient {
    private final ManagedChannel channel;
    private final CallMetricRecorder metricsRecorder;
    
    public MetricsAwareHealthCheckingClient(String target) {
        this.channel = ManagedChannelBuilder.forTarget(target)
            .defaultLoadBalancingPolicy("health_checking_round_robin")
            // Interceptor to record client-side metrics
            .intercept(new ClientMetricsInterceptor())
            .usePlaintext()
            .build();
    }
    
    private static class ClientMetricsInterceptor implements ClientInterceptor {
        @Override
        public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions,
            Channel next) {
            
            return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
                private long startTime = System.nanoTime();
                
                @Override
                public void start(Listener<RespT> responseListener, Metadata headers) {
                    super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
                        @Override
                        public void onClose(Status status, Metadata trailers) {
                            long duration = System.nanoTime() - startTime;
                            double durationMs = duration / 1_000_000.0;
                            
                            // Record client-side metrics
                            if (status.isOk()) {
                                System.out.println("Successful call duration: " + durationMs + "ms");
                            } else {
                                System.out.println("Failed call duration: " + durationMs + "ms, " +
                                                 "Status: " + status.getCode());
                            }
                            
                            super.onClose(status, trailers);
                        }
                    }, headers);
                }
            };
        }
    }
    
    public void makeHealthyRequest() {
        UserServiceGrpc.UserServiceBlockingStub stub = 
            UserServiceGrpc.newBlockingStub(channel);
        
        try {
            // This request will only go to healthy servers
            // thanks to the health checking load balancer
            GetUserResponse response = stub.getUser(
                GetUserRequest.newBuilder().setUserId(123).build());
                
            System.out.println("Request successful: " + response.getUser().getName());
            
        } catch (StatusRuntimeException e) {
            System.err.println("Request failed even with health checking: " + 
                             e.getStatus().getCode());
        }
    }
}

Best Practices

Connection Management

public class RobustHealthCheckingClient {
    private final ManagedChannel channel;
    
    public RobustHealthCheckingClient(String target) {
        this.channel = ManagedChannelBuilder.forTarget(target)
            .defaultLoadBalancingPolicy("health_checking_round_robin")
            
            // Configure connection parameters for better health detection
            .keepAliveTime(30, TimeUnit.SECONDS)
            .keepAliveTimeout(5, TimeUnit.SECONDS)
            .keepAliveWithoutCalls(true)
            
            // Configure retry behavior
            .enableRetry()
            .maxRetryAttempts(3)
            
            .usePlaintext()
            .build();
    }
    
    public void gracefulShutdown() {
        System.out.println("Initiating graceful shutdown...");
        
        channel.shutdown();
        
        try {
            // Wait for ongoing calls to complete
            if (!channel.awaitTermination(60, TimeUnit.SECONDS)) {
                System.out.println("Forcing shutdown...");
                channel.shutdownNow();
                
                // Final wait for forced shutdown
                if (!channel.awaitTermination(10, TimeUnit.SECONDS)) {
                    System.err.println("Channel did not terminate cleanly");
                }
            }
        } catch (InterruptedException e) {
            System.out.println("Shutdown interrupted, forcing immediate shutdown");
            channel.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-grpc--grpc-services

docs

admin-services.md

binary-logging.md

channelz.md

health-checking.md

index.md

load-balancing.md

metrics.md

server-reflection.md

tile.json