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