Layer 与 Stack
主线位置:两条主线开始之前的装配阶段。
Layer不参与运行时的poll_ready/call,它决定谁套在谁外面,也就决定了就绪主线往里传、请求主线往外包时经过各层的顺序。
flowchart TB svc[服务 svc] --> il["inner.layer(svc)"] il --> mid["Inner 包好的服务"] mid --> ol["outer.layer(...)"] ol --> out["Outer 在最外层"]
tower-layer 是一个 699 行的小 crate,五个文件:lib.rs 定义 Layer,stack.rs、identity.rs、layer_fn.rs 各一个类型,tuple.rs 给元组实现 Layer。tower 在 tower::layer 里原样重新导出它们(Identity、Stack 放在 tower::layer::util 下)。
Layer:服务进,服务出
pub trait Layer<S> {
/// The wrapped service
type Service;
/// Wrap the given service with the middleware, returning a new service
/// that has been decorated with the middleware.
fn layer(&self, inner: S) -> Self::Service;
}
三点:
- 没有任何
Service约束。S不要求是服务,Self::Service也不要求是服务。Layer在类型上就是一个Fn(S) -> Service。约束推迟到真正用这个服务的地方才检查,这既让任何中间件都能叠在一起,也让类型错误报得很晚,下一章的check_service就是为此准备的。 &self。 一个 layer 可以包任意多个服务。TimeoutLayer里只存一个Duration,每次layer都造一个新的Timeout<S>。- 一个
Layer通常配一个Service。 惯例是XxxLayer存配置,Xxx<S>存配置加内层服务并实现Service。
&T 也是 Layer(第 108 行),原样转发给 T。后面元组的实现靠它递归。
2019 年 4 月以前,Layer 长这样:pub trait Layer<S, Request>,有 Response、Error、LayerError 三个关联类型,Service 必须是 Service<Request, Response = Self::Response, Error = Self::Error>,layer 返回 Result<Self::Service, Self::LayerError>。
#251(「Allow ServiceBuilder to map requests」)把这些全删了。PR 说明:当时 ServiceBuilder 放不进改变请求类型的 layer,于是「All bounds are removed from the Layer trait, essentially reducing it to Fn(T) -> U (a trait is needed as one cannot impl Fn by hand). Bounds are delayed to the last possible moment.」第一个包含它的 tower 版本是 0.1.0。
今天的 Layer<S> 只剩一个关联类型 Service,ServiceBuilder::map_request 这种把 R1 变成 R2 的 layer 才能和别的 layer 叠在一起。
Stack:先 Inner,后 Outer
Stack 把两个 layer 串起来,它自己也是 layer(第 41 行):
pub struct Stack<Inner, Outer> {
inner: Inner,
outer: Outer,
}
impl<S, Inner, Outer> Layer<S> for Stack<Inner, Outer>
where
Inner: Layer<S>,
Outer: Layer<Inner::Service>,
{
type Service = Outer::Service;
fn layer(&self, service: S) -> Self::Service {
let inner = self.inner.layer(service);
self.outer.layer(inner)
}
}
Inner 先包,离服务近;Outer 再包,在外面。约束也是这样串的:Inner 要能包 S,Outer 要能包 Inner 产出的东西。tag 里 Stack 的文档用数字举了例子:inner 是 +2、outer 是 *2,Stack::new(inner, outer).layer(4) 是 (4 + 2) * 2 = 12。
Stack 的 Debug 不用 debug_struct,而是先打印 outer 再打印 inner,用逗号连起来。注释解释了原因:Stack 通常嵌套很深,而且在 ServiceBuilder 里,这样打印出来是一个平铺的列表,「the order of [outer, inner] is important, since it reflects the order that the layers were added to the stack」。为什么 outer 是先加的,见 ServiceBuilder。
问: Stack<Inner, Outer> 里的 Inner,是不是内层的服务?
答: 不是。两个参数都是 layer,服务是 layer(service) 调用时才传进来的。Inner 只是「先套上去、离服务更近的那个 layer」。ServiceBuilder 里每加一个 layer,新 layer 都放在 Inner 的位置,之前攒下的整串放在 Outer 的位置,所以 ServiceBuilder<Stack<ConcurrencyLimitLayer, Stack<TimeoutLayer, Identity>>> 包出来的是 Timeout<ConcurrencyLimit<S>>,类型名里的顺序和包出来的顺序是反的。
Identity:什么也不做
Identity 的 layer 原样返回(第 39 行):
impl<S> Layer<S> for Identity {
type Service = S;
fn layer(&self, inner: S) -> Self::Service {
inner
}
}
它有两个用处:ServiceBuilder::new() 从 Identity 开始往上叠;option_layer(None) 用它占位,让「有这一层」和「没有这一层」是同一个类型(Either<L, Identity>)。
layer_fn:闭包当 Layer
layer_fn 把一个 Fn(S) -> Out 包成 LayerFn:
pub fn layer_fn<T>(f: T) -> LayerFn<T> {
LayerFn { f }
}
#[derive(Clone, Copy)]
pub struct LayerFn<F> {
f: F,
}
impl<F, S, Out> Layer<S> for LayerFn<F>
where
F: Fn(S) -> Out,
{
type Service = Out;
fn layer(&self, inner: S) -> Self::Service {
(self.f)(inner)
}
}
要的是 Fn 而不是 FnOnce,因为 layer 拿的是 &self,同一个 layer 可能被调很多次。写中间件时,Service 实现总得自己写,layer_fn(|inner| MyService { inner, .. }) 省掉的只是那个 XxxLayer 结构体。
元组也是 Layer:顺序和 Stack 相反
tuple.rs 给 () 和 1 到 16 元的元组实现了 Layer,() 等于 Identity。两元的实现(第 23 行):
impl<S, L1, L2> Layer<S> for (L1, L2)
where
L1: Layer<L2::Service>,
L2: Layer<S>,
{
type Service = L1::Service;
fn layer(&self, service: S) -> Self::Service {
let (l1, l2) = self;
l1.layer(l2.layer(service))
}
}
L1 在最外层,和读代码的顺序一致。三元及以上是递归的(第 36 行):l1.layer((l2, l3).layer(service)),这里的 (l2, l3) 是两个引用组成的元组,所以要靠上面 &T 的实现才能成立。17 元以上没有实现。
| 写法 | 结果 | 最外层 |
|---|---|---|
Stack::new(a, b) | B(A(svc)) | 第二个参数 |
(a, b) | A(B(svc)) | 第一个元素 |
ServiceBuilder::new().layer(a).layer(b) | A(B(svc)) | 先写的 |
用三个 layer_fn 验证一下,每个 layer 把字符串包一层:
tower = { version = "=0.5.3" }
use tower::layer::util::{Identity, Stack};
use tower::layer::{layer_fn, Layer};
fn main() {
let a = layer_fn(|s: String| format!("A({s})"));
let b = layer_fn(|s: String| format!("B({s})"));
let c = layer_fn(|s: String| format!("C({s})"));
println!("Stack::new(a, b): {}", Stack::new(a, b).layer("svc".to_string()));
println!("(a, b, c): {}", (a, b, c).layer("svc".to_string()));
println!("Identity: {}", Identity::new().layer("svc".to_string()));
println!("&a: {}", (&a).layer("svc".to_string()));
}
M2 Max、rustc 1.99.0:
Stack::new(a, b): B(A(svc))
(a, b, c): A(B(C(svc)))
Identity: svc
&a: A(svc)
这里的「服务」只是一个 String,正好说明 Layer 不要求它是服务。a、b、c 能重复使用,是因为不捕获变量的闭包是 Copy,LayerFn 也派生了 Copy。
use tower::layer::util::{Identity, Stack};
use tower::layer::{layer_fn, Layer};
fn main() {
let a = layer_fn(|s: String| format!("A({s})"));
let b = layer_fn(|s: String| format!("B({s})"));
let c = layer_fn(|s: String| format!("C({s})"));
let l = Stack::new((a, b), Stack::new(c, Identity::new()));
println!("{}", l.layer("svc".to_string()));
}
动脑筋答案
M2 Max、rustc 1.99.0:
C(A(B(svc)))
- 外层
Stack的Inner是元组(a, b),先用它包:元组里a在外,得到A(B(svc))。 - 外层
Stack的Outer是Stack::new(c, Identity::new()),再用它包:它先用c包、再用Identity包(不变),得到C(A(B(svc)))。
小结
Layer<S>只有一个关联类型Service和一个layer(&self, S),没有任何Service约束;约束推迟到用的时候才检查。Stack<Inner, Outer>先用Inner包、再用Outer包,Outer在外面。它的Debug先打 outer 再打 inner,正好是添加顺序。Identity原样返回,是ServiceBuilder的起点,也是option_layer(None)的占位。layer_fn把Fn(S) -> Out变成Layer,LayerFn是Clone + Copy。()和 1 到 16 元的元组都是Layer,第一个元素在最外层,和Stack::new的参数顺序相反。