1 /* 2 * Copyright 2022 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.examples.orca; 18 19 import com.google.common.collect.ImmutableMap; 20 import io.grpc.BindableService; 21 import io.grpc.examples.helloworld.GreeterGrpc; 22 import io.grpc.examples.helloworld.HelloReply; 23 import io.grpc.examples.helloworld.HelloRequest; 24 import io.grpc.Grpc; 25 import io.grpc.InsecureServerCredentials; 26 import io.grpc.Server; 27 import io.grpc.services.CallMetricRecorder; 28 import io.grpc.services.InternalCallMetricRecorder; 29 import io.grpc.services.MetricRecorder; 30 import io.grpc.stub.StreamObserver; 31 import io.grpc.xds.orca.OrcaMetricReportingServerInterceptor; 32 import io.grpc.xds.orca.OrcaServiceImpl; 33 import java.io.IOException; 34 import java.util.Map; 35 import java.util.Random; 36 import java.util.concurrent.Executors; 37 import java.util.concurrent.ScheduledExecutorService; 38 import java.util.concurrent.TimeUnit; 39 import java.util.logging.Logger; 40 41 /** 42 * Server that manages startup/shutdown of a {@code Greeter} server. 43 */ 44 public class CustomBackendMetricsServer { 45 private static final Logger logger = Logger.getLogger(CustomBackendMetricsServer.class.getName()); 46 47 private Server server; 48 private static Random random = new Random(); 49 private MetricRecorder metricRecorder; 50 start()51 private void start() throws IOException { 52 /* The port on which the server should run */ 53 int port = 50051; 54 55 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 56 metricRecorder = MetricRecorder.newInstance(); 57 // Configure OOB metrics reporting minimum report interval to be 1s. This allows client 58 // configuration to be as short as 1s, suitable for test demonstration. 59 BindableService orcaOobService = 60 OrcaServiceImpl.createService(executor, metricRecorder, 1, TimeUnit.SECONDS); 61 server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()) 62 .addService(new GreeterImpl()) 63 // Enable OOB custom backend metrics reporting. 64 .addService(orcaOobService) 65 // Enable per-query custom backend metrics reporting. 66 .intercept(OrcaMetricReportingServerInterceptor.create(metricRecorder)) 67 .build() 68 .start(); 69 logger.info("Server started, listening on " + port); 70 Runtime.getRuntime().addShutdownHook(new Thread() { 71 @Override 72 public void run() { 73 // Use stderr here since the logger may have been reset by its JVM shutdown hook. 74 System.err.println("*** shutting down gRPC server since JVM is shutting down"); 75 try { 76 CustomBackendMetricsServer.this.stop(); 77 } catch (InterruptedException e) { 78 e.printStackTrace(System.err); 79 } 80 System.err.println("*** server shut down"); 81 } 82 }); 83 } 84 stop()85 private void stop() throws InterruptedException { 86 if (server != null) { 87 server.shutdown().awaitTermination(30, TimeUnit.SECONDS); 88 } 89 } 90 91 /** 92 * Await termination on the main thread since the grpc library uses daemon threads. 93 */ blockUntilShutdown()94 private void blockUntilShutdown() throws InterruptedException { 95 if (server != null) { 96 server.awaitTermination(); 97 } 98 } 99 100 /** 101 * Main launches the server from the command line. 102 */ main(String[] args)103 public static void main(String[] args) throws IOException, InterruptedException { 104 CustomBackendMetricsServer server = new CustomBackendMetricsServer(); 105 server.start(); 106 server.blockUntilShutdown(); 107 } 108 109 class GreeterImpl extends GreeterGrpc.GreeterImplBase { 110 111 @Override sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver)112 public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { 113 HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); 114 double cpuUtilization = random.nextDouble(); 115 double memoryUtilization = random.nextDouble(); 116 Map<String, Double> utilization = ImmutableMap.of("util", random.nextDouble()); 117 Map<String, Double> requestCost = ImmutableMap.of("cost", random.nextDouble()); 118 // Sets per-query backend metrics to a random test report. 119 CallMetricRecorder.getCurrent() 120 .recordCpuUtilizationMetric(cpuUtilization) 121 .recordMemoryUtilizationMetric(memoryUtilization) 122 .recordCallMetric("cost", requestCost.get("cost")) 123 .recordUtilizationMetric("util", utilization.get("util")); 124 System.out.println(String.format("Hello World Server updates RPC metrics data:\n" + 125 "cpu: %s, memory: %s, request cost: %s, utilization: %s\n", 126 cpuUtilization, memoryUtilization, requestCost, utilization)); 127 128 cpuUtilization = random.nextDouble(); 129 memoryUtilization = random.nextDouble(); 130 utilization = ImmutableMap.of("util", random.nextDouble()); 131 // Sets OOB backend metrics to a random test report. 132 metricRecorder.setCpuUtilizationMetric(cpuUtilization); 133 metricRecorder.setMemoryUtilizationMetric(memoryUtilization); 134 metricRecorder.setAllUtilizationMetrics(utilization); 135 System.out.println(String.format("Hello World Server updates OOB metrics data:\n" + 136 "cpu: %s, memory: %s, utilization: %s\n", 137 cpuUtilization, memoryUtilization, utilization)); 138 responseObserver.onNext(reply); 139 responseObserver.onCompleted(); 140 } 141 } 142 } 143