mirror of https://github.com/grpc/grpc.git
server: per-rpc backend metric reporting (#29621)
Users can now report per-rpc metrics from servers to clients.pull/29763/head
parent
eed6711be9
commit
667691c499
34 changed files with 601 additions and 87 deletions
@ -0,0 +1,94 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2022 gRPC authors. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPCPP_EXT_CALL_METRIC_RECORDER_H |
||||||
|
#define GRPCPP_EXT_CALL_METRIC_RECORDER_H |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "absl/types/optional.h" |
||||||
|
|
||||||
|
#include <grpcpp/impl/codegen/slice.h> |
||||||
|
#include <grpcpp/impl/codegen/sync.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
class Arena; |
||||||
|
struct BackendMetricData; |
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
class ServerBuilder; |
||||||
|
|
||||||
|
namespace experimental { |
||||||
|
class OrcaServerInterceptor; |
||||||
|
|
||||||
|
// Registers the per-rpc orca load reporter into the \a ServerBuilder.
|
||||||
|
// Once this is done, the server will automatically send the load metrics
|
||||||
|
// after each RPC as they were reported. In order to report load metrics,
|
||||||
|
// call the \a ServerContext::ExperimentalGetCallMetricRecorder() method to
|
||||||
|
// retrieve the recorder for the current call.
|
||||||
|
void EnableCallMetricRecording(ServerBuilder*); |
||||||
|
|
||||||
|
/// Records call metrics for the purpose of load balancing.
|
||||||
|
/// During an RPC, call \a ServerContext::ExperimentalGetCallMetricRecorder()
|
||||||
|
/// method to retrive the recorder for the current call.
|
||||||
|
class CallMetricRecorder { |
||||||
|
public: |
||||||
|
explicit CallMetricRecorder(grpc_core::Arena* arena); |
||||||
|
~CallMetricRecorder(); |
||||||
|
|
||||||
|
/// Records a call metric measurement for CPU utilization.
|
||||||
|
/// Multiple calls to this method will override the stored value.
|
||||||
|
CallMetricRecorder& RecordCpuUtilizationMetric(double value); |
||||||
|
|
||||||
|
/// Records a call metric measurement for memory utilization.
|
||||||
|
/// Multiple calls to this method will override the stored value.
|
||||||
|
CallMetricRecorder& RecordMemoryUtilizationMetric(double value); |
||||||
|
|
||||||
|
/// Records a call metric measurement for utilization.
|
||||||
|
/// Multiple calls to this method with the same name will
|
||||||
|
/// override the corresponding stored value. The lifetime of the
|
||||||
|
/// name string needs to be longer than the lifetime of the RPC
|
||||||
|
/// itself, since it's going to be sent as trailers after the RPC
|
||||||
|
/// finishes. It is assumed the strings are common names that
|
||||||
|
/// are global constants.
|
||||||
|
CallMetricRecorder& RecordUtilizationMetric(string_ref name, double value); |
||||||
|
|
||||||
|
/// Records a call metric measurement for request cost.
|
||||||
|
/// Multiple calls to this method with the same name will
|
||||||
|
/// override the corresponding stored value. The lifetime of the
|
||||||
|
/// name string needs to be longer than the lifetime of the RPC
|
||||||
|
/// itself, since it's going to be sent as trailers after the RPC
|
||||||
|
/// finishes. It is assumed the strings are common names that
|
||||||
|
/// are global constants.
|
||||||
|
CallMetricRecorder& RecordRequestCostMetric(string_ref name, double value); |
||||||
|
|
||||||
|
private: |
||||||
|
absl::optional<std::string> CreateSerializedReport(); |
||||||
|
|
||||||
|
internal::Mutex mu_; |
||||||
|
grpc_core::BackendMetricData* backend_metric_data_ ABSL_GUARDED_BY(&mu_); |
||||||
|
friend class experimental::OrcaServerInterceptor; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif // GRPCPP_EXT_CALL_METRIC_RECORDER_H
|
@ -0,0 +1,49 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2022 gRPC authors. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_BACKEND_METRIC_DATA_H |
||||||
|
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_BACKEND_METRIC_DATA_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <map> |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
// Represents backend metrics reported by the backend to the client.
|
||||||
|
struct BackendMetricData { |
||||||
|
/// CPU utilization expressed as a fraction of available CPU resources.
|
||||||
|
double cpu_utilization = -1; |
||||||
|
/// Memory utilization expressed as a fraction of available memory
|
||||||
|
/// resources.
|
||||||
|
double mem_utilization = -1; |
||||||
|
/// Application-specific requests cost metrics. Metric names are
|
||||||
|
/// determined by the application. Each value is an absolute cost
|
||||||
|
/// (e.g. 3487 bytes of storage) associated with the request.
|
||||||
|
std::map<absl::string_view, double> request_cost; |
||||||
|
/// Application-specific resource utilization metrics. Metric names
|
||||||
|
/// are determined by the application. Each value is expressed as a
|
||||||
|
/// fraction of total resources available.
|
||||||
|
std::map<absl::string_view, double> utilization; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_BACKEND_METRIC_DATA_H
|
@ -0,0 +1,116 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "absl/types/optional.h" |
||||||
|
#include "upb/upb.h" |
||||||
|
#include "upb/upb.hpp" |
||||||
|
#include "xds/data/orca/v3/orca_load_report.upb.h" |
||||||
|
|
||||||
|
#include <grpcpp/ext/call_metric_recorder.h> |
||||||
|
#include <grpcpp/impl/codegen/sync.h> |
||||||
|
#include <grpcpp/support/config.h> |
||||||
|
#include <grpcpp/support/string_ref.h> |
||||||
|
|
||||||
|
#include "src/core/ext/filters/client_channel/lb_policy/backend_metric_data.h" |
||||||
|
#include "src/core/lib/resource_quota/arena.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace experimental { |
||||||
|
|
||||||
|
CallMetricRecorder::CallMetricRecorder(grpc_core::Arena* arena) |
||||||
|
: backend_metric_data_(arena->New<grpc_core::BackendMetricData>()) {} |
||||||
|
|
||||||
|
CallMetricRecorder::~CallMetricRecorder() { |
||||||
|
backend_metric_data_->~BackendMetricData(); |
||||||
|
} |
||||||
|
|
||||||
|
CallMetricRecorder& CallMetricRecorder::RecordCpuUtilizationMetric( |
||||||
|
double value) { |
||||||
|
internal::MutexLock lock(&mu_); |
||||||
|
backend_metric_data_->cpu_utilization = value; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
CallMetricRecorder& CallMetricRecorder::RecordMemoryUtilizationMetric( |
||||||
|
double value) { |
||||||
|
internal::MutexLock lock(&mu_); |
||||||
|
backend_metric_data_->mem_utilization = value; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
CallMetricRecorder& CallMetricRecorder::RecordUtilizationMetric( |
||||||
|
grpc::string_ref name, double value) { |
||||||
|
internal::MutexLock lock(&mu_); |
||||||
|
absl::string_view name_sv(name.data(), name.length()); |
||||||
|
backend_metric_data_->utilization[name_sv] = value; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
CallMetricRecorder& CallMetricRecorder::RecordRequestCostMetric( |
||||||
|
grpc::string_ref name, double value) { |
||||||
|
internal::MutexLock lock(&mu_); |
||||||
|
absl::string_view name_sv(name.data(), name.length()); |
||||||
|
backend_metric_data_->request_cost[name_sv] = value; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
absl::optional<std::string> CallMetricRecorder::CreateSerializedReport() { |
||||||
|
upb::Arena arena; |
||||||
|
internal::MutexLock lock(&mu_); |
||||||
|
bool has_data = backend_metric_data_->cpu_utilization != -1 || |
||||||
|
backend_metric_data_->mem_utilization != -1 || |
||||||
|
!backend_metric_data_->utilization.empty() || |
||||||
|
!backend_metric_data_->request_cost.empty(); |
||||||
|
if (!has_data) { |
||||||
|
return absl::nullopt; |
||||||
|
} |
||||||
|
xds_data_orca_v3_OrcaLoadReport* response = |
||||||
|
xds_data_orca_v3_OrcaLoadReport_new(arena.ptr()); |
||||||
|
if (backend_metric_data_->cpu_utilization != -1) { |
||||||
|
xds_data_orca_v3_OrcaLoadReport_set_cpu_utilization( |
||||||
|
response, backend_metric_data_->cpu_utilization); |
||||||
|
} |
||||||
|
if (backend_metric_data_->mem_utilization != -1) { |
||||||
|
xds_data_orca_v3_OrcaLoadReport_set_mem_utilization( |
||||||
|
response, backend_metric_data_->mem_utilization); |
||||||
|
} |
||||||
|
for (const auto& p : backend_metric_data_->request_cost) { |
||||||
|
xds_data_orca_v3_OrcaLoadReport_request_cost_set( |
||||||
|
response, |
||||||
|
upb_StringView_FromDataAndSize(p.first.data(), p.first.size()), |
||||||
|
p.second, arena.ptr()); |
||||||
|
} |
||||||
|
for (const auto& p : backend_metric_data_->utilization) { |
||||||
|
xds_data_orca_v3_OrcaLoadReport_utilization_set( |
||||||
|
response, |
||||||
|
upb_StringView_FromDataAndSize(p.first.data(), p.first.size()), |
||||||
|
p.second, arena.ptr()); |
||||||
|
} |
||||||
|
size_t buf_length; |
||||||
|
char* buf = xds_data_orca_v3_OrcaLoadReport_serialize(response, arena.ptr(), |
||||||
|
&buf_length); |
||||||
|
return std::string(buf, buf_length); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc
|
@ -0,0 +1,79 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "src/cpp/server/orca/orca_interceptor.h" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <map> |
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
#include <type_traits> |
||||||
|
#include <utility> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "absl/memory/memory.h" |
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "absl/types/optional.h" |
||||||
|
|
||||||
|
#include <grpcpp/ext/call_metric_recorder.h> |
||||||
|
#include <grpcpp/server_builder.h> |
||||||
|
#include <grpcpp/server_context.h> |
||||||
|
#include <grpcpp/support/config.h> |
||||||
|
|
||||||
|
#include "src/core/lib/transport/metadata_batch.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace experimental { |
||||||
|
|
||||||
|
void OrcaServerInterceptor::Intercept(InterceptorBatchMethods* methods) { |
||||||
|
if (methods->QueryInterceptionHookPoint( |
||||||
|
InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) { |
||||||
|
auto context = info_->server_context(); |
||||||
|
context->CreateCallMetricRecorder(); |
||||||
|
} else if (methods->QueryInterceptionHookPoint( |
||||||
|
InterceptionHookPoints::PRE_SEND_STATUS)) { |
||||||
|
auto trailers = methods->GetSendTrailingMetadata(); |
||||||
|
if (trailers != nullptr) { |
||||||
|
auto context = info_->server_context(); |
||||||
|
auto* recorder = context->call_metric_recorder_; |
||||||
|
auto serialized = recorder->CreateSerializedReport(); |
||||||
|
if (serialized.has_value() && !serialized->empty()) { |
||||||
|
std::string key = |
||||||
|
std::string(grpc_core::EndpointLoadMetricsBinMetadata::key()); |
||||||
|
trailers->emplace( |
||||||
|
std::make_pair(std::move(key), std::move(serialized.value()))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
methods->Proceed(); |
||||||
|
} |
||||||
|
|
||||||
|
Interceptor* OrcaServerInterceptorFactory::CreateServerInterceptor( |
||||||
|
ServerRpcInfo* info) { |
||||||
|
return new OrcaServerInterceptor(info); |
||||||
|
} |
||||||
|
|
||||||
|
void OrcaServerInterceptorFactory::Register(grpc::ServerBuilder* builder) { |
||||||
|
builder->internal_interceptor_creators_.push_back( |
||||||
|
absl::make_unique<OrcaServerInterceptorFactory>()); |
||||||
|
} |
||||||
|
|
||||||
|
void EnableCallMetricRecording(grpc::ServerBuilder* builder) { |
||||||
|
OrcaServerInterceptorFactory::Register(builder); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc
|
@ -0,0 +1,49 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GRPC_INTERNAL_CPP_ORCA_ORCA_INTERCEPTOR_H |
||||||
|
#define GRPC_INTERNAL_CPP_ORCA_ORCA_INTERCEPTOR_H |
||||||
|
|
||||||
|
#include <grpcpp/support/interceptor.h> |
||||||
|
#include <grpcpp/support/server_interceptor.h> |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
|
||||||
|
class ServerBuilder; |
||||||
|
|
||||||
|
namespace experimental { |
||||||
|
class ServerRpcInfo; |
||||||
|
|
||||||
|
class OrcaServerInterceptor : public Interceptor { |
||||||
|
public: |
||||||
|
explicit OrcaServerInterceptor(ServerRpcInfo* info) : info_(info) {} |
||||||
|
|
||||||
|
void Intercept(InterceptorBatchMethods* methods) override; |
||||||
|
|
||||||
|
private: |
||||||
|
ServerRpcInfo* info_; |
||||||
|
}; |
||||||
|
|
||||||
|
class OrcaServerInterceptorFactory : public ServerInterceptorFactoryInterface { |
||||||
|
public: |
||||||
|
static void Register(ServerBuilder* builder); |
||||||
|
Interceptor* CreateServerInterceptor(ServerRpcInfo* info) override; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif // GRPC_INTERNAL_CPP_ORCA_ORCA_INTERCEPTOR_H
|
Loading…
Reference in new issue