原文地址:Envoy 开发入门:搞懂 http filter 状态码
对于 http filter,Envoy 提供了一大堆状态码,尽管每个都有不少的正文,然而仍旧很头大,傻傻搞不清楚。
本文记录一下本人的了解,如有谬误,欢送斧正~
http filter 是什么
Envoy 提供的 http 层的扩大机制,开发者能够通过实现 Envoy 约定的接口,在 Envoy 的解决流程中注入逻辑
比方,这两个申请阶段的接口:
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream)
Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream)
如果想批改申请头,那就在 decodeHeaders
中批改 headers
,如果想批改申请的 body
,那就在 decodeData
中批改 data
。
本文所关注的状态码,就是这些函数的返回值,比方示例中的 FilterHeadersStatus
和 FilterDataStatus
。
Envoy 为 filter 提供了啥
-
流式
这个从下面的从 API 模式就可以看进去,外围是,http header 以及 http body 的每个 data 块,是一个个解决的。
-
异步
当一个 filter 在处理过程中,如果须要期待内部响应,也能够先反馈给 Envoy 某种状态,等 filter ready 之后再持续。
-
并发
这个其实很天然,因为有流式,所以,从逻辑上来说,不同的 filter 之间是存在并发的。
然而,又容易被忽视,因为 Envoy 的工作模式中,具体到某个 http 申请是工作在单线程上的,所以容易有误会。
所以,Envoy 外部有一个 filter manager 模块,目标是用于治理 filter 的运行,也能够简略了解为,一个简单的状态机。
状态码
交代完背景,咱们具体看看状态码,以及 filter 和 filter manager 的状态机运行关系
Header 状态码
以 header 的状态码为例,咱们先挨个解读一下,找找感觉
-
Continue
这个最简略,示意以后 filter 曾经处理完毕,能够持续交给下一个 filter 解决了
-
StopIteration
示意 header 还不能持续交给下一个 filter 来解决
-
ContinueAndDontEndStream
示意 header 能够持续交给下一个 fitler 解决,然而下一个 filter 收到的 end_stream = false,也就是标记申请还未完结;以便以后 fitler 再减少 body。
-
StopAllIterationAndBuffer
示意 header 不能持续交给下一个 filter,并且以后 filter 也不能收到 body data。
意思是,申请中的 body data 先 filter manager 缓存起来,如果缓存大小超过了 buffer limit(一个配置值),那就间接返回 413 了。
- StopAllIterationAndWatermark
同上,区别是,当缓存超过了 limit,filter manager 就会启动流控,也就是暂停从连贯上读数据了。
过了一遍之后,有这么几个关键点
- StopIteration,只是先不交给下一个 filter 解决,然而并不进行从连贯读数据,持续触发 body data 的解决
- header 阶段,也能够对 body data 进行写操作,可想而知,也就是 add 操作了。
- filter manager 有一个 data 的缓冲区,帮 filter 长期缓冲数据
data 状态码
再看 data 的状态码,
-
Continue
跟 header 相似,示意以后 filter 曾经处理完毕,能够持续交给下一个 filter 解决了。
只是,如果 header 之前返回的是 StopIteration,且尚未交给下一个 fitler,那么,此时 header 也会被交给下一个 fitler 解决。
-
StopIterationAndBuffer
示意以后 data 不能持续交给下一个 filter,由 fitler manager 缓存起来。
并且,与 header 相似,如果达到 buffer limit,间接返回 413。
-
StopIterationAndWatermark
同上,只是达到 buffer limit,只是触发流控。
-
StopIterationNoBuffer
示意以后 data 不能持续交给下一个 filter,然而,fitler manager 也不须要缓存 data。
这里也有几个点:
-
如果 data 要被交给下一个 filter 解决了,header 是必定也会被交给下一个 fitler 解决了。
咱们能够把 header 了解为,一个带有非凡语义的首个 data 块,无论怎么流式解决,数据的程序,是必须要保障的。
- data 阶段多了一个 NoBuffer 的状态,这又是什么目标呢?
要害解读
有几个容易误会的中央,咱们开展聊聊
异步
为了反对 filter 的异步,fitler 能够返回 Stop 语义的状态码,这样 filter manager 不会持续后续的 filter,然而:
并不意味着整个 filter manager 都进行了,以后 filter 以及之前的 filter,还是会承受到以后申请上的数据
并发
每个 filter 承受到的数据,在解决期间,是独享的,没有并发危险,然而 filter manager 的缓冲区,只有一个,这个是有并发危险的。
因为 filter 是逻辑上并发的,然而 filter manager 只有一个,所以是存在逻辑并发危险的。
举个例子:
filter A => filter B => filter C
,这样一个解决链表
先来了第一个 data 块:filter A 和 B 反馈 continue,filter C 返回 StopIterationAndBuffer
,此时 buffer 中是 data 1,
再来第二个 data 块,filter A 返回了 StopIterationAndBuffer
,此时 buffer 中是 data 1 和 2 了,
而后 filter A 告诉 filter manager 复原解决,那么此时,filter B 看到的数据,就是 data 1 和 2 了。
显然,这并不合乎预期。
也就是说,对于每个 filter 而言,如果在 data continue 之后,再返回 StopIterationAndBuffer
的话,就可能有缓冲区并发危险。
简略的了解,如果要复用 filter manager 的缓冲区,每个 filter 只有首次异步的机会。
如果须要随时异步,那怎么办呢?
解决方案,也就是 StopIterationNoBuffer
,filter 本人搞缓冲,也就不存在并发危险了。至于副作用嘛,次要就是本人多实现一些代码。
实践上来说,StopIterationNoBuffer
是最灵便的,不过也是更麻烦的,效率也会略差一点的。
集体领会
Envoy 的状态设计,为大部分常见的场景,提供了比拟不便易用的设计,然而绝对简单的场景,就须要本人多实现一些逻辑了。
只是这么搞了之后,对于新人的上手门槛就高了,如果不搞懂这些状态,闭着眼睛用,也是容易踩坑的。
最初
一个小问题,MOE 作为把 MOSN(Go)塞进 Envoy 的计划,此时的开发语言曾经是 Go 这种人造异步的,MOE 又提供的是什么样的开发体验呢?
MOE 近期会开源第一版,等开源之后,前面能够聊聊~
如果感觉有意思,欢送关注我的公众号「豆浆大叔」~