gRPC service utilities providing health checking, server reflection, channelz observability, and binary logging capabilities
—
Collection of standard administrative services for gRPC servers including all built-in observability and management services. Provides a convenient way to add multiple admin services to your gRPC server.
Utility class that provides a collection of standard gRPC administrative services, including channelz, health checking, and server reflection.
/**
* Provides a class of services for exposing the overall state of gRPC activity.
* Offers standard administrative services in a single collection.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7929")
@ThreadSafe
public final class AdminInterface {
/**
* Returns a list of gRPC's built-in admin services
* @return List of ServerServiceDefinition containing standard admin services
*/
public static List<ServerServiceDefinition> getStandardServices();
}Usage Examples:
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.services.AdminInterface;
// Add all standard admin services to your server
public class AdminEnabledServer {
public static void main(String[] args) throws Exception {
Server server = ServerBuilder.forPort(8080)
// Add all standard admin services at once
.addServices(AdminInterface.getStandardServices())
// Add your business services
.addService(new UserService())
.addService(new OrderService())
.build();
server.start();
System.out.println("Server started with admin services on port 8080");
// Print available admin services
System.out.println("Available admin services:");
AdminInterface.getStandardServices().forEach(service ->
System.out.println(" - " + service.getServiceDescriptor().getName())
);
server.awaitTermination();
}
}The AdminInterface provides the following standard services:
grpc.channelz.v1.Channelzgrpc.health.v1.Healthgrpc.reflection.v1.ServerReflectionimport io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.services.AdminInterface;
import io.grpc.protobuf.services.HealthStatusManager;
public class ProductionServer {
private final Server server;
private final HealthStatusManager healthManager;
public ProductionServer(int port) {
// Create health manager for additional control
this.healthManager = new HealthStatusManager();
this.server = ServerBuilder.forPort(port)
// Add standard admin services
.addServices(AdminInterface.getStandardServices())
// Override health service with our managed instance
.addService(healthManager.getHealthService())
// Add business services
.addService(new UserService())
.addService(new OrderService())
.addService(new PaymentService())
.build();
}
public void start() throws IOException {
server.start();
// Set initial health status
healthManager.setStatus("", ServingStatus.SERVING);
healthManager.setStatus("com.example.UserService", ServingStatus.SERVING);
healthManager.setStatus("com.example.OrderService", ServingStatus.SERVING);
healthManager.setStatus("com.example.PaymentService", ServingStatus.SERVING);
System.out.println("Production server started on port " + server.getPort());
System.out.println("Admin services available for monitoring and debugging");
// Add shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(this::gracefulShutdown));
}
private void gracefulShutdown() {
System.out.println("Initiating graceful shutdown...");
// Mark services as not serving
healthManager.setStatus("", ServingStatus.NOT_SERVING);
// Wait for load balancers to drain traffic
try {
Thread.sleep(10000); // 10 second drain period
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Enter terminal state and shutdown
healthManager.enterTerminalState();
server.shutdown();
try {
if (!server.awaitTermination(30, TimeUnit.SECONDS)) {
server.shutdownNow();
}
} catch (InterruptedException e) {
server.shutdownNow();
Thread.currentThread().interrupt();
}
}
public void awaitTermination() throws InterruptedException {
server.awaitTermination();
}
}import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.services.AdminInterface;
import io.grpc.protobuf.services.ProtoReflectionServiceV1;
import io.grpc.protobuf.services.BinaryLogs;
public class DevelopmentServer {
public static void main(String[] args) throws Exception {
// Enhanced development server with additional debugging capabilities
BinaryLog binaryLog = BinaryLogs.createBinaryLog(); // Debug logging
Server server = ServerBuilder.forPort(8080)
// Add standard admin services
.addServices(AdminInterface.getStandardServices())
// Add enhanced reflection service (latest version)
.addService(ProtoReflectionServiceV1.newInstance())
// Enable binary logging for debugging
.setBinaryLog(binaryLog)
// Add business services
.addService(new UserService())
.addService(new OrderService())
.build();
server.start();
System.out.println("Development server started on port 8080");
System.out.println("\nAvailable debugging tools:");
System.out.println(" grpcurl -plaintext localhost:8080 list");
System.out.println(" grpcurl -plaintext localhost:8080 grpc.health.v1.Health/Check");
System.out.println(" grpcurl -plaintext localhost:8080 grpc.channelz.v1.Channelz/GetTopChannels");
server.awaitTermination();
}
}public class MicroserviceWithAdminServices {
private final Server server;
private final HealthStatusManager healthManager;
private final MetricRecorder metricRecorder;
public MicroserviceWithAdminServices(String serviceName, int port) {
this.healthManager = new HealthStatusManager();
this.metricRecorder = MetricRecorder.newInstance();
this.server = ServerBuilder.forPort(port)
// Standard admin services for observability
.addServices(AdminInterface.getStandardServices())
// Override with managed health service
.addService(healthManager.getHealthService())
// Add service-specific business logic
.addService(createBusinessService(serviceName))
// Add interceptors for metrics
.intercept(new MetricsInterceptor(metricRecorder))
.build();
}
private BindableService createBusinessService(String serviceName) {
switch (serviceName) {
case "user-service":
return new UserService();
case "order-service":
return new OrderService();
case "payment-service":
return new PaymentService();
default:
throw new IllegalArgumentException("Unknown service: " + serviceName);
}
}
public void start() throws IOException {
server.start();
// Set healthy status
healthManager.setStatus("", ServingStatus.SERVING);
// Start metrics reporting
startMetricsReporting();
System.out.println("Microservice started on port " + server.getPort());
System.out.println("Admin services enabled for service mesh integration");
}
private void startMetricsReporting() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
metricRecorder.setCpuUtilizationMetric(getCurrentCpuUsage());
metricRecorder.setMemoryUtilizationMetric(getCurrentMemoryUsage());
metricRecorder.setQpsMetric(getCurrentQps());
}, 0, 30, TimeUnit.SECONDS);
}
private double getCurrentCpuUsage() {
// Implementation to get CPU usage
return Math.random() * 0.8; // Example
}
private double getCurrentMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
return (double) (runtime.totalMemory() - runtime.freeMemory()) / runtime.maxMemory();
}
private double getCurrentQps() {
// Implementation to calculate QPS
return Math.random() * 1000; // Example
}
}public class ObservableServer {
public static void main(String[] args) throws Exception {
// Server with comprehensive observability
Server server = ServerBuilder.forPort(8080)
// Standard admin services
.addServices(AdminInterface.getStandardServices())
// Business services
.addService(new UserService())
// Add interceptors for detailed observability
.intercept(new LoggingInterceptor())
.intercept(new TracingInterceptor())
.intercept(new MetricsInterceptor())
.build();
server.start();
// Start admin service monitoring
startAdminServiceMonitoring(server.getPort());
server.awaitTermination();
}
private static void startAdminServiceMonitoring(int port) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {
// Monitor channelz data
monitorChannelzData(port);
// Monitor health status
monitorHealthStatus(port);
} catch (Exception e) {
System.err.println("Monitoring error: " + e.getMessage());
}
}, 60, 60, TimeUnit.SECONDS); // Monitor every minute
}
private static void monitorChannelzData(int port) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port)
.usePlaintext()
.build();
try {
ChannelzGrpc.ChannelzBlockingStub channelzStub =
ChannelzGrpc.newBlockingStub(channel);
GetServersResponse response = channelzStub.getServers(
GetServersRequest.newBuilder().setMaxResults(10).build());
response.getServerList().forEach(server -> {
ServerData data = server.getData();
System.out.printf("Server calls: started=%d, succeeded=%d, failed=%d%n",
data.getCallsStarted(),
data.getCallsSucceeded(),
data.getCallsFailed());
});
} finally {
channel.shutdown();
}
}
private static void monitorHealthStatus(int port) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port)
.usePlaintext()
.build();
try {
HealthGrpc.HealthBlockingStub healthStub =
HealthGrpc.newBlockingStub(channel);
HealthCheckResponse response = healthStub.check(
HealthCheckRequest.newBuilder().setService("").build());
System.out.println("Overall server health: " + response.getStatus());
} finally {
channel.shutdown();
}
}
}Admin services expose operational data and should be secured appropriately:
public class SecureAdminServer {
public static void main(String[] args) throws Exception {
// Check if admin services should be enabled
boolean enableAdminServices = Boolean.parseBoolean(
System.getProperty("enable.admin.services", "false"));
ServerBuilder<?> serverBuilder = ServerBuilder.forPort(8080);
if (enableAdminServices) {
System.out.println("Admin services enabled - ensure proper network security");
serverBuilder.addServices(AdminInterface.getStandardServices());
} else {
System.out.println("Admin services disabled for security");
}
Server server = serverBuilder
.addService(new UserService())
.build();
server.start();
server.awaitTermination();
}
}
// Alternative: Admin services on separate port
public class SeparateAdminPortServer {
public static void main(String[] args) throws Exception {
// Main business server
Server businessServer = ServerBuilder.forPort(8080)
.addService(new UserService())
.addService(new OrderService())
.build();
// Admin server on separate port (can be firewalled differently)
Server adminServer = ServerBuilder.forPort(9090)
.addServices(AdminInterface.getStandardServices())
.build();
businessServer.start();
adminServer.start();
System.out.println("Business server on port 8080");
System.out.println("Admin server on port 9090");
// Wait for both servers
businessServer.awaitTermination();
adminServer.awaitTermination();
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-grpc--grpc-services