Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split service discovery into composable components (#341)
The `linkerd2_proxy_resolve` crate, which was split out of the main application in #318, contains a monolithic `Resolve` implementation. Over time, this code has evolved in ways that have made it difficult to modify and extend, especially and functionality cannot be tested independently. This change reorganizes the proxy's service discovery logic into a few composable components. The `Resolve` API has been updated to facilitate this. Service discovery consists of two primary traits: `Resolve` and `Discover`. `Resolve` is used within the proxy to model discovery sources like the Destination API and DNS; and `Discover` updates a load balancer with changes to its replica pool. The former `Resolve` trait has been replaced with a trait alias for `Service`, which allows for back-pressure, etc. Furthermore, the API has been updated so that it is easier to gracefully recover after a resolution stream is lost (by changing `Update` to contain a `Vec`). Now that `Resolve` is implemented as a `Service`, it becomes straightforward to model service discovery in terms of a stack of services, as we do throughout the project: * The `linkerd2-proxy-api-resolve` crate provides an implementation of `Resolve` backed by the destination API. It does NOT handle reconnects, buffering/spawning, etc. * The `linkerd2-request-filter` crate provides a simple middleware that can fail requests based on the request. This is used to implement the destination client's name/suffix restrictions. * The `linkerd2-proxy-resolve` crate provides `Resolve`-middlewares that implement error recovery/backoff and endpoint-type-mapping. * The `linkerd2-proxy-discover crate provides a suite of middlewares that facilitate powering a balancer with a `Resolve`. Specifically: * `buffer` ensures that discovery updates are processed even when the balancer is not polling for updates. * `from_resolve` wraps a `Resolve` to produce `Discover` response. * `make_endpoint` wraps a `Discover`-producing service to build each inserted endpoint into a Service. Almost all of this logic was happening in a single module, coupled to our gRPC interface. Now, these components may be easily re-purposed (for instance, in order to back discovery by DNS).
- Loading branch information
1 parent
7c4d0fe
commit 8ae7272
Showing
33 changed files
with
1,944 additions
and
1,206 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "linkerd2-proxy-api-resolve" | ||
version = "0.1.0" | ||
authors = ["Linkerd Developers <cncf-linkerd-dev@lists.cncf.io>"] | ||
edition = "2018" | ||
publish = false | ||
|
||
[dependencies] | ||
futures = "0.1" | ||
linkerd2-identity = { path = "../linkerd2-identity" } | ||
linkerd2-proxy-api = { git = "https://github.com/linkerd/linkerd2-proxy-api", rev = "ddbc3a4f7f8b0058801f896d27974d19ee98094c" } | ||
linkerd2-proxy-core = { path = "../linkerd2-proxy-core" } | ||
prost = "0.5.0" | ||
tower-grpc = { git = "https://github.com/tower-rs/tower-grpc", default-features = false, features = ["protobuf"] } | ||
indexmap = "1.0" | ||
tokio-sync = "0.1" | ||
tower = "0.1" | ||
tracing = "0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
use linkerd2_identity as identity; | ||
use linkerd2_proxy_api as api; | ||
use linkerd2_proxy_core as core; | ||
|
||
mod metadata; | ||
mod pb; | ||
mod resolve; | ||
|
||
pub use self::metadata::{Metadata, ProtocolHint}; | ||
pub use self::resolve::Resolve; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use crate::identity; | ||
use indexmap::IndexMap; | ||
|
||
/// Metadata describing an endpoint. | ||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
pub struct Metadata { | ||
/// An endpoint's relative weight. | ||
/// | ||
/// A weight of 0 means that the endpoint should never be preferred over a | ||
/// non 0-weighted endpoint. | ||
/// | ||
/// The default weight, corresponding to 1.0, is 10,000. This enables us to | ||
/// specify weights as small as 0.0001 and as large as 400,000+. | ||
/// | ||
/// A float is not used so that this type can implement `Eq`. | ||
weight: u32, | ||
|
||
/// Arbitrary endpoint labels. Primarily used for telemetry. | ||
labels: IndexMap<String, String>, | ||
|
||
/// A hint from the controller about what protocol (HTTP1, HTTP2, etc) the | ||
/// destination understands. | ||
protocol_hint: ProtocolHint, | ||
|
||
/// How to verify TLS for the endpoint. | ||
identity: Option<identity::Name>, | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
pub enum ProtocolHint { | ||
/// We don't what the destination understands, so forward messages in the | ||
/// protocol we received them in. | ||
Unknown, | ||
/// The destination can receive HTTP2 messages. | ||
Http2, | ||
} | ||
|
||
// === impl Metadata === | ||
|
||
impl Metadata { | ||
pub fn empty() -> Self { | ||
Self { | ||
labels: IndexMap::default(), | ||
protocol_hint: ProtocolHint::Unknown, | ||
identity: None, | ||
weight: 10_000, | ||
} | ||
} | ||
|
||
pub fn new( | ||
labels: IndexMap<String, String>, | ||
protocol_hint: ProtocolHint, | ||
identity: Option<identity::Name>, | ||
weight: u32, | ||
) -> Self { | ||
Self { | ||
labels, | ||
protocol_hint, | ||
identity, | ||
weight, | ||
} | ||
} | ||
|
||
/// Returns the endpoint's labels from the destination service, if it has them. | ||
pub fn labels(&self) -> &IndexMap<String, String> { | ||
&self.labels | ||
} | ||
|
||
pub fn protocol_hint(&self) -> ProtocolHint { | ||
self.protocol_hint | ||
} | ||
|
||
pub fn identity(&self) -> Option<&identity::Name> { | ||
self.identity.as_ref() | ||
} | ||
} |
Oops, something went wrong.