mirror of https://github.com/grpc/grpc.git
commit
b289c399fe
188 changed files with 8103 additions and 1974 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,243 @@ |
||||
//
|
||||
// Copyright 2015 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_SRC_CORE_CLIENT_CHANNEL_CLIENT_CHANNEL_H |
||||
#define GRPC_SRC_CORE_CLIENT_CHANNEL_CLIENT_CHANNEL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
#include "src/core/client_channel/client_channel_factory.h" |
||||
#include "src/core/client_channel/config_selector.h" |
||||
#include "src/core/client_channel/subchannel.h" |
||||
#include "src/core/ext/filters/channel_idle/idle_filter_state.h" |
||||
#include "src/core/lib/gprpp/single_set_ptr.h" |
||||
#include "src/core/lib/promise/observable.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/transport/metadata.h" |
||||
#include "src/core/load_balancing/lb_policy.h" |
||||
#include "src/core/resolver/resolver.h" |
||||
#include "src/core/service_config/service_config.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class ClientChannel : public Channel { |
||||
public: |
||||
using PickerObservable = |
||||
Observable<RefCountedPtr<LoadBalancingPolicy::SubchannelPicker>>; |
||||
|
||||
class CallDestinationFactory { |
||||
public: |
||||
struct RawPointerChannelArgTag {}; |
||||
|
||||
static absl::string_view ChannelArgName() { |
||||
return "grpc.internal.client_channel_call_destination"; |
||||
} |
||||
|
||||
virtual RefCountedPtr<UnstartedCallDestination> CreateCallDestination( |
||||
PickerObservable) = 0; |
||||
|
||||
protected: |
||||
~CallDestinationFactory() = default; |
||||
}; |
||||
|
||||
static absl::StatusOr<OrphanablePtr<Channel>> Create( |
||||
std::string target, ChannelArgs channel_args); |
||||
|
||||
// Do not instantiate directly -- use Create() instead.
|
||||
ClientChannel(std::string target_uri, ChannelArgs args, |
||||
std::string uri_to_resolve, |
||||
RefCountedPtr<ServiceConfig> default_service_config, |
||||
ClientChannelFactory* client_channel_factory, |
||||
CallDestinationFactory* call_destination_factory); |
||||
|
||||
~ClientChannel() override; |
||||
|
||||
void Orphan() override; |
||||
|
||||
grpc_call* CreateCall(grpc_call* parent_call, uint32_t propagation_mask, |
||||
grpc_completion_queue* cq, |
||||
grpc_pollset_set* /*pollset_set_alternative*/, |
||||
Slice path, absl::optional<Slice> authority, |
||||
Timestamp deadline, bool registered_method) override; |
||||
|
||||
CallInitiator CreateCall(ClientMetadataHandle client_initial_metadata); |
||||
|
||||
grpc_event_engine::experimental::EventEngine* event_engine() const override { |
||||
return event_engine_.get(); |
||||
} |
||||
|
||||
// TODO(ctiller): lame channels
|
||||
bool IsLame() const override { return false; } |
||||
|
||||
bool SupportsConnectivityWatcher() const override { return true; } |
||||
|
||||
// Returns the current connectivity state. If try_to_connect is true,
|
||||
// triggers a connection attempt if not already connected.
|
||||
grpc_connectivity_state CheckConnectivityState(bool try_to_connect) override; |
||||
|
||||
void WatchConnectivityState(grpc_connectivity_state last_observed_state, |
||||
Timestamp deadline, grpc_completion_queue* cq, |
||||
void* tag) override; |
||||
|
||||
// Starts and stops a connectivity watch. The watcher will be initially
|
||||
// notified as soon as the state changes from initial_state and then on
|
||||
// every subsequent state change until either the watch is stopped or
|
||||
// it is notified that the state has changed to SHUTDOWN.
|
||||
//
|
||||
// This is intended to be used when starting watches from code inside of
|
||||
// C-core (e.g., for a nested control plane channel for things like xds).
|
||||
void AddConnectivityWatcher( |
||||
grpc_connectivity_state initial_state, |
||||
OrphanablePtr<AsyncConnectivityStateWatcherInterface> watcher) override; |
||||
void RemoveConnectivityWatcher( |
||||
AsyncConnectivityStateWatcherInterface* watcher) override; |
||||
|
||||
void GetInfo(const grpc_channel_info* channel_info) override; |
||||
|
||||
void ResetConnectionBackoff() override; |
||||
|
||||
void Ping(grpc_completion_queue* cq, void* tag) override; |
||||
|
||||
// Flag that this object gets stored in channel args as a raw pointer.
|
||||
struct RawPointerChannelArgTag {}; |
||||
static absl::string_view ChannelArgName() { |
||||
return "grpc.internal.client_channel"; |
||||
} |
||||
|
||||
private: |
||||
class ClientChannelControlHelper; |
||||
class ResolverResultHandler; |
||||
class SubchannelWrapper; |
||||
|
||||
void CreateResolverLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
void DestroyResolverAndLbPolicyLocked() |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void TryToConnectLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void OnResolverResultChangedLocked(Resolver::Result result) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
void OnResolverErrorLocked(absl::Status status) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
absl::Status CreateOrUpdateLbPolicyLocked( |
||||
RefCountedPtr<LoadBalancingPolicy::Config> lb_policy_config, |
||||
const absl::optional<std::string>& health_check_service_name, |
||||
Resolver::Result result) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
OrphanablePtr<LoadBalancingPolicy> CreateLbPolicyLocked( |
||||
const ChannelArgs& args) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void UpdateServiceConfigInControlPlaneLocked( |
||||
RefCountedPtr<ServiceConfig> service_config, |
||||
RefCountedPtr<ConfigSelector> config_selector, std::string lb_policy_name) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void UpdateServiceConfigInDataPlaneLocked() |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void UpdateStateLocked(grpc_connectivity_state state, |
||||
const absl::Status& status, const char* reason) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void UpdateStateAndPickerLocked( |
||||
grpc_connectivity_state state, const absl::Status& status, |
||||
const char* reason, |
||||
RefCountedPtr<LoadBalancingPolicy::SubchannelPicker> picker) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*work_serializer_); |
||||
|
||||
void StartIdleTimer(); |
||||
|
||||
// Applies service config settings from config_selector to the call.
|
||||
// May modify call context and client_initial_metadata.
|
||||
absl::Status ApplyServiceConfigToCall( |
||||
ConfigSelector& config_selector, |
||||
ClientMetadata& client_initial_metadata) const; |
||||
|
||||
const ChannelArgs channel_args_; |
||||
const std::shared_ptr<grpc_event_engine::experimental::EventEngine> |
||||
event_engine_; |
||||
const std::string uri_to_resolve_; |
||||
const size_t service_config_parser_index_; |
||||
const RefCountedPtr<ServiceConfig> default_service_config_; |
||||
ClientChannelFactory* const client_channel_factory_; |
||||
const std::string default_authority_; |
||||
channelz::ChannelNode* const channelz_node_; |
||||
GlobalStatsPluginRegistry::StatsPluginGroup stats_plugin_group_; |
||||
|
||||
//
|
||||
// Idleness state.
|
||||
//
|
||||
const Duration idle_timeout_; |
||||
IdleFilterState idle_state_{false}; |
||||
SingleSetPtr<Activity, typename ActivityPtr::deleter_type> idle_activity_; |
||||
|
||||
//
|
||||
// Fields related to name resolution.
|
||||
//
|
||||
struct ResolverDataForCalls { |
||||
RefCountedPtr<ConfigSelector> config_selector; |
||||
RefCountedPtr<UnstartedCallDestination> call_destination; |
||||
}; |
||||
Observable<absl::StatusOr<ResolverDataForCalls>> resolver_data_for_calls_; |
||||
|
||||
//
|
||||
// Fields related to LB picks.
|
||||
//
|
||||
PickerObservable picker_; |
||||
const RefCountedPtr<UnstartedCallDestination> call_destination_; |
||||
|
||||
//
|
||||
// Fields used in the control plane. Guarded by work_serializer.
|
||||
//
|
||||
std::shared_ptr<WorkSerializer> work_serializer_; |
||||
ConnectivityStateTracker state_tracker_ ABSL_GUARDED_BY(*work_serializer_); |
||||
OrphanablePtr<Resolver> resolver_ ABSL_GUARDED_BY(*work_serializer_); |
||||
bool previous_resolution_contained_addresses_ |
||||
ABSL_GUARDED_BY(*work_serializer_) = false; |
||||
RefCountedPtr<ServiceConfig> saved_service_config_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
RefCountedPtr<ConfigSelector> saved_config_selector_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
OrphanablePtr<LoadBalancingPolicy> lb_policy_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
RefCountedPtr<SubchannelPoolInterface> subchannel_pool_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
// The number of SubchannelWrapper instances referencing a given Subchannel.
|
||||
std::map<Subchannel*, int> subchannel_refcount_map_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
// The set of SubchannelWrappers that currently exist.
|
||||
// No need to hold a ref, since the set is updated in the control-plane
|
||||
// work_serializer when the SubchannelWrappers are created and destroyed.
|
||||
absl::flat_hash_set<SubchannelWrapper*> subchannel_wrappers_ |
||||
ABSL_GUARDED_BY(*work_serializer_); |
||||
int keepalive_time_ ABSL_GUARDED_BY(*work_serializer_) = -1; |
||||
absl::Status disconnect_error_ ABSL_GUARDED_BY(*work_serializer_); |
||||
|
||||
//
|
||||
// Fields accessed via GetChannelInfo().
|
||||
//
|
||||
Mutex info_mu_; |
||||
std::string info_lb_policy_name_ ABSL_GUARDED_BY(info_mu_); |
||||
std::string info_service_config_json_ ABSL_GUARDED_BY(info_mu_); |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_CLIENT_CHANNEL_CLIENT_CHANNEL_H
|
@ -0,0 +1,335 @@ |
||||
// Copyright 2024 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/core/client_channel/load_balanced_call_destination.h" |
||||
|
||||
#include "src/core/client_channel/client_channel.h" |
||||
#include "src/core/client_channel/client_channel_internal.h" |
||||
#include "src/core/client_channel/subchannel.h" |
||||
#include "src/core/lib/channel/status_util.h" |
||||
#include "src/core/lib/promise/loop.h" |
||||
#include "src/core/telemetry/call_tracer.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Defined in legacy client channel filter.
|
||||
// TODO(roth): Move these here when we remove the legacy filter.
|
||||
extern TraceFlag grpc_client_channel_trace; |
||||
extern TraceFlag grpc_client_channel_call_trace; |
||||
extern TraceFlag grpc_client_channel_lb_call_trace; |
||||
|
||||
namespace { |
||||
|
||||
class LbMetadata : public LoadBalancingPolicy::MetadataInterface { |
||||
public: |
||||
explicit LbMetadata(grpc_metadata_batch* batch) : batch_(batch) {} |
||||
|
||||
void Add(absl::string_view key, absl::string_view value) override { |
||||
if (batch_ == nullptr) return; |
||||
// Gross, egregious hack to support legacy grpclb behavior.
|
||||
// TODO(ctiller): Use a promise context for this once that plumbing is done.
|
||||
if (key == GrpcLbClientStatsMetadata::key()) { |
||||
batch_->Set( |
||||
GrpcLbClientStatsMetadata(), |
||||
const_cast<GrpcLbClientStats*>( |
||||
reinterpret_cast<const GrpcLbClientStats*>(value.data()))); |
||||
return; |
||||
} |
||||
batch_->Append(key, Slice::FromStaticString(value), |
||||
[key](absl::string_view error, const Slice& value) { |
||||
gpr_log(GPR_ERROR, "%s", |
||||
absl::StrCat(error, " key:", key, |
||||
" value:", value.as_string_view()) |
||||
.c_str()); |
||||
}); |
||||
} |
||||
|
||||
std::vector<std::pair<std::string, std::string>> TestOnlyCopyToVector() |
||||
override { |
||||
if (batch_ == nullptr) return {}; |
||||
Encoder encoder; |
||||
batch_->Encode(&encoder); |
||||
return encoder.Take(); |
||||
} |
||||
|
||||
absl::optional<absl::string_view> Lookup(absl::string_view key, |
||||
std::string* buffer) const override { |
||||
if (batch_ == nullptr) return absl::nullopt; |
||||
return batch_->GetStringValue(key, buffer); |
||||
} |
||||
|
||||
private: |
||||
class Encoder { |
||||
public: |
||||
void Encode(const Slice& key, const Slice& value) { |
||||
out_.emplace_back(std::string(key.as_string_view()), |
||||
std::string(value.as_string_view())); |
||||
} |
||||
|
||||
template <class Which> |
||||
void Encode(Which, const typename Which::ValueType& value) { |
||||
auto value_slice = Which::Encode(value); |
||||
out_.emplace_back(std::string(Which::key()), |
||||
std::string(value_slice.as_string_view())); |
||||
} |
||||
|
||||
void Encode(GrpcTimeoutMetadata, |
||||
const typename GrpcTimeoutMetadata::ValueType&) {} |
||||
void Encode(HttpPathMetadata, const Slice&) {} |
||||
void Encode(HttpMethodMetadata, |
||||
const typename HttpMethodMetadata::ValueType&) {} |
||||
|
||||
std::vector<std::pair<std::string, std::string>> Take() { |
||||
return std::move(out_); |
||||
} |
||||
|
||||
private: |
||||
std::vector<std::pair<std::string, std::string>> out_; |
||||
}; |
||||
|
||||
grpc_metadata_batch* batch_; |
||||
}; |
||||
|
||||
void MaybeCreateCallAttemptTracer(bool is_transparent_retry) { |
||||
auto* call_tracer = MaybeGetContext<ClientCallTracer>(); |
||||
if (call_tracer == nullptr) return; |
||||
auto* tracer = call_tracer->StartNewAttempt(is_transparent_retry); |
||||
SetContext<CallTracerInterface>(tracer); |
||||
} |
||||
|
||||
class LbCallState : public ClientChannelLbCallState { |
||||
public: |
||||
void* Alloc(size_t size) override { return GetContext<Arena>()->Alloc(size); } |
||||
|
||||
// Internal API to allow first-party LB policies to access per-call
|
||||
// attributes set by the ConfigSelector.
|
||||
ServiceConfigCallData::CallAttributeInterface* GetCallAttribute( |
||||
UniqueTypeName type) const override { |
||||
auto* service_config_call_data = GetContext<ServiceConfigCallData>(); |
||||
return service_config_call_data->GetCallAttribute(type); |
||||
} |
||||
|
||||
ClientCallTracer::CallAttemptTracer* GetCallAttemptTracer() const override { |
||||
auto* legacy_context = GetContext<grpc_call_context_element>(); |
||||
return static_cast<ClientCallTracer::CallAttemptTracer*>( |
||||
legacy_context[GRPC_CONTEXT_CALL_TRACER].value); |
||||
} |
||||
}; |
||||
|
||||
// TODO(roth): Remove this in favor of the gprpp Match() function once
|
||||
// we can do that without breaking lock annotations.
|
||||
template <typename T> |
||||
T HandlePickResult( |
||||
LoadBalancingPolicy::PickResult* result, |
||||
std::function<T(LoadBalancingPolicy::PickResult::Complete*)> complete_func, |
||||
std::function<T(LoadBalancingPolicy::PickResult::Queue*)> queue_func, |
||||
std::function<T(LoadBalancingPolicy::PickResult::Fail*)> fail_func, |
||||
std::function<T(LoadBalancingPolicy::PickResult::Drop*)> drop_func) { |
||||
auto* complete_pick = |
||||
absl::get_if<LoadBalancingPolicy::PickResult::Complete>(&result->result); |
||||
if (complete_pick != nullptr) { |
||||
return complete_func(complete_pick); |
||||
} |
||||
auto* queue_pick = |
||||
absl::get_if<LoadBalancingPolicy::PickResult::Queue>(&result->result); |
||||
if (queue_pick != nullptr) { |
||||
return queue_func(queue_pick); |
||||
} |
||||
auto* fail_pick = |
||||
absl::get_if<LoadBalancingPolicy::PickResult::Fail>(&result->result); |
||||
if (fail_pick != nullptr) { |
||||
return fail_func(fail_pick); |
||||
} |
||||
auto* drop_pick = |
||||
absl::get_if<LoadBalancingPolicy::PickResult::Drop>(&result->result); |
||||
CHECK(drop_pick != nullptr); |
||||
return drop_func(drop_pick); |
||||
} |
||||
|
||||
// Does an LB pick for a call. Returns one of the following things:
|
||||
// - Continue{}, meaning to queue the pick
|
||||
// - a non-OK status, meaning to fail the call
|
||||
// - a call destination, meaning that the pick is complete
|
||||
// When the pick is complete, pushes client_initial_metadata onto
|
||||
// call_initiator. Also adds the subchannel call tracker (if any) to
|
||||
// context.
|
||||
LoopCtl<absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>> PickSubchannel( |
||||
LoadBalancingPolicy::SubchannelPicker& picker, |
||||
UnstartedCallHandler& unstarted_handler) { |
||||
// Perform LB pick.
|
||||
auto& client_initial_metadata = |
||||
unstarted_handler.UnprocessedClientInitialMetadata(); |
||||
LoadBalancingPolicy::PickArgs pick_args; |
||||
Slice* path = client_initial_metadata.get_pointer(HttpPathMetadata()); |
||||
CHECK(path != nullptr); |
||||
pick_args.path = path->as_string_view(); |
||||
LbCallState lb_call_state; |
||||
pick_args.call_state = &lb_call_state; |
||||
LbMetadata initial_metadata(&client_initial_metadata); |
||||
pick_args.initial_metadata = &initial_metadata; |
||||
auto result = picker.Pick(pick_args); |
||||
// Handle result.
|
||||
return HandlePickResult< |
||||
LoopCtl<absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>>>( |
||||
&result, |
||||
// CompletePick
|
||||
[&](LoadBalancingPolicy::PickResult::Complete* complete_pick) |
||||
-> LoopCtl<absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>> { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { |
||||
gpr_log(GPR_INFO, |
||||
"client_channel: %sLB pick succeeded: subchannel=%p", |
||||
GetContext<Activity>()->DebugTag().c_str(), |
||||
complete_pick->subchannel.get()); |
||||
} |
||||
CHECK(complete_pick->subchannel != nullptr); |
||||
// Grab a ref to the call destination while we're still
|
||||
// holding the data plane mutex.
|
||||
auto call_destination = |
||||
DownCast<SubchannelInterfaceWithCallDestination*>( |
||||
complete_pick->subchannel.get()) |
||||
->call_destination(); |
||||
// If the subchannel has no call destination (e.g., if the
|
||||
// subchannel has moved out of state READY but the LB policy hasn't
|
||||
// yet seen that change and given us a new picker), then just
|
||||
// queue the pick. We'll try again as soon as we get a new picker.
|
||||
if (call_destination == nullptr) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { |
||||
gpr_log(GPR_INFO, |
||||
"client_channel: %ssubchannel returned by LB picker " |
||||
"has no connected subchannel; queueing pick", |
||||
GetContext<Activity>()->DebugTag().c_str()); |
||||
} |
||||
return Continue{}; |
||||
} |
||||
// If the LB policy returned a call tracker, inform it that the
|
||||
// call is starting and add it to context, so that we can notify
|
||||
// it when the call finishes.
|
||||
if (complete_pick->subchannel_call_tracker != nullptr) { |
||||
complete_pick->subchannel_call_tracker->Start(); |
||||
SetContext(complete_pick->subchannel_call_tracker.release()); |
||||
} |
||||
// Return the connected subchannel.
|
||||
return call_destination; |
||||
}, |
||||
// QueuePick
|
||||
[&](LoadBalancingPolicy::PickResult::Queue* /*queue_pick*/) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { |
||||
gpr_log(GPR_INFO, "client_channel: %sLB pick queued", |
||||
GetContext<Activity>()->DebugTag().c_str()); |
||||
} |
||||
return Continue{}; |
||||
}, |
||||
// FailPick
|
||||
[&](LoadBalancingPolicy::PickResult::Fail* fail_pick) |
||||
-> LoopCtl<absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>> { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { |
||||
gpr_log(GPR_INFO, "client_channel: %sLB pick failed: %s", |
||||
GetContext<Activity>()->DebugTag().c_str(), |
||||
fail_pick->status.ToString().c_str()); |
||||
} |
||||
// If wait_for_ready is false, then the error indicates the RPC
|
||||
// attempt's final status.
|
||||
if (!unstarted_handler.UnprocessedClientInitialMetadata() |
||||
.GetOrCreatePointer(WaitForReady()) |
||||
->value) { |
||||
return MaybeRewriteIllegalStatusCode(std::move(fail_pick->status), |
||||
"LB pick"); |
||||
} |
||||
// If wait_for_ready is true, then queue to retry when we get a new
|
||||
// picker.
|
||||
return Continue{}; |
||||
}, |
||||
// DropPick
|
||||
[&](LoadBalancingPolicy::PickResult::Drop* drop_pick) |
||||
-> LoopCtl<absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>> { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) { |
||||
gpr_log(GPR_INFO, "client_channel: %sLB pick dropped: %s", |
||||
GetContext<Activity>()->DebugTag().c_str(), |
||||
drop_pick->status.ToString().c_str()); |
||||
} |
||||
return grpc_error_set_int(MaybeRewriteIllegalStatusCode( |
||||
std::move(drop_pick->status), "LB drop"), |
||||
StatusIntProperty::kLbPolicyDrop, 1); |
||||
}); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void LoadBalancedCallDestination::StartCall( |
||||
UnstartedCallHandler unstarted_handler) { |
||||
// If there is a call tracer, create a call attempt tracer.
|
||||
bool* is_transparent_retry_metadata = |
||||
unstarted_handler.UnprocessedClientInitialMetadata().get_pointer( |
||||
IsTransparentRetry()); |
||||
bool is_transparent_retry = is_transparent_retry_metadata != nullptr |
||||
? *is_transparent_retry_metadata |
||||
: false; |
||||
MaybeCreateCallAttemptTracer(is_transparent_retry); |
||||
// Spawn a promise to do the LB pick.
|
||||
// This will eventually start the call.
|
||||
unstarted_handler.SpawnGuardedUntilCallCompletes( |
||||
"lb_pick", [unstarted_handler, picker = picker_]() mutable { |
||||
return Map( |
||||
// Wait for the LB picker.
|
||||
CheckDelayed(Loop( |
||||
[last_picker = |
||||
RefCountedPtr<LoadBalancingPolicy::SubchannelPicker>(), |
||||
unstarted_handler, picker]() mutable { |
||||
return Map( |
||||
picker.Next(last_picker), |
||||
[unstarted_handler, &last_picker]( |
||||
RefCountedPtr<LoadBalancingPolicy::SubchannelPicker> |
||||
picker) mutable { |
||||
last_picker = std::move(picker); |
||||
// Returns 3 possible things:
|
||||
// - Continue to queue the pick
|
||||
// - non-OK status to fail the pick
|
||||
// - a connected subchannel to complete the pick
|
||||
return PickSubchannel(*last_picker, unstarted_handler); |
||||
}); |
||||
})), |
||||
// Create call stack on the connected subchannel.
|
||||
[unstarted_handler]( |
||||
std::tuple< |
||||
absl::StatusOr<RefCountedPtr<UnstartedCallDestination>>, |
||||
bool> |
||||
pick_result) { |
||||
auto& call_destination = std::get<0>(pick_result); |
||||
const bool was_queued = std::get<1>(pick_result); |
||||
if (!call_destination.ok()) { |
||||
return call_destination.status(); |
||||
} |
||||
// LB pick is done, so indicate that we've committed.
|
||||
auto* on_commit = MaybeGetContext<LbOnCommit>(); |
||||
if (on_commit != nullptr && *on_commit != nullptr) { |
||||
(*on_commit)(); |
||||
} |
||||
// If it was queued, add a trace annotation.
|
||||
if (was_queued) { |
||||
auto* tracer = |
||||
MaybeGetContext<ClientCallTracer::CallAttemptTracer>(); |
||||
if (tracer != nullptr) { |
||||
tracer->RecordAnnotation("Delayed LB pick complete."); |
||||
} |
||||
} |
||||
// Delegate to connected subchannel.
|
||||
// TODO(ctiller): need to insert LbCallTracingFilter at the top of
|
||||
// the stack
|
||||
(*call_destination)->StartCall(unstarted_handler); |
||||
return absl::OkStatus(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,49 @@ |
||||
// Copyright 2024 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_SRC_CORE_CLIENT_CHANNEL_LOAD_BALANCED_CALL_DESTINATION_H |
||||
#define GRPC_SRC_CORE_CLIENT_CHANNEL_LOAD_BALANCED_CALL_DESTINATION_H |
||||
|
||||
#include "absl/functional/any_invocable.h" |
||||
|
||||
#include "src/core/client_channel/client_channel.h" |
||||
#include "src/core/lib/promise/context.h" |
||||
#include "src/core/lib/transport/call_destination.h" |
||||
#include "src/core/load_balancing/lb_policy.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Context type for LB on_commit callback.
|
||||
// TODO(ctiller): make this a struct, so we don't accidentally alias context
|
||||
// types
|
||||
using LbOnCommit = absl::AnyInvocable<void()>; |
||||
template <> |
||||
struct ContextType<LbOnCommit> {}; |
||||
|
||||
class LoadBalancedCallDestination final : public UnstartedCallDestination { |
||||
public: |
||||
explicit LoadBalancedCallDestination(ClientChannel::PickerObservable picker) |
||||
: picker_(std::move(picker)) {} |
||||
|
||||
void Orphaned() override {} |
||||
|
||||
void StartCall(UnstartedCallHandler unstarted_handler) override; |
||||
|
||||
private: |
||||
ClientChannel::PickerObservable picker_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_CLIENT_CHANNEL_LOAD_BALANCED_CALL_DESTINATION_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue