乐趣区

关于云计算:可编程网关-Pipy-第三弹事件模型设计

自从加入了 Flomesh 的 workshop,理解了可编程网关 Pipy。对这个“小东西”充斥了好奇,前后写了两篇文章,看了局部源码解开了其局部面纱。但始终未见其全貌,没有涉及其外围设计。

不是有句话,“好奇害死猫”。其实应该还有后半句,“满足了就没事”(见维基百科)。

所有就有了明天的这一篇,对前两篇感兴趣的能够跳转翻看。

  • 初探可编程网关 Pipy
  • 可编程网关 Pipy 第二弹:编程实现 Metrics 及源码解读

言归正传。

事件模型

上篇写了 Pipy 基于事件的信息流转,其实还未深刻涉及其外围的事件模型。既然是事件模型,先看事件。

src/event.hpp:41 中定义了 Pipy 的四种事件:

  • Data
  • MessageStart
  • MessageEnd
  • SessionEnd

翻看源码可知(必须吐槽文档太少)这几种事件其实是有程序的:MessageStart -> Data -> MessageEnd -> SessionEnd

这种面向事件模型,必然有生产者和消费者。又是翻看源码可知,生产者和消费者都是 pipy::Filter。咱们在上篇文章中讲过:每个 Pipeline 都有一个过滤器链,相似 单向链表的数据结构

那是不是依照下面说的,事件是从一个 Filter 流向下一个 Filter?也对,也不对。

矛盾?

先看 Filter 如何向下传递事件,src/session.cpp:55 处,Filter 持有 output 变量,相似为 Event::Receiver(参数为 Eventstd::function 的别名,作为在行的笔者并不懂 c++,但不障碍理解程序设计)。通过 Receiver 调用下一个 Filter#process 办法。

这里的 Receiver 就能够了解为事件发送的窗口,而 #process(Context *ctx, Event *inp) 就是事件的接管窗口。

这就是后面为什么说“事件是从一个 Filter 流向下一个 Filter”是正确的。

为什么不对?首先,一个 Filter 会产生多个事件,比方 decodeHttpRequest 可能会产生 MessageStartDataMessageEnd 事件,并且每产生一个事件都会通过Receiver 向下传递,不会等 #process 流程完结才传递事件;再就是下一个 Filter 可能并不会对某个事件感兴趣(下一个 Filter#process 办法不做任何解决就返回了)。

可能看下图会更容易了解(图中 no output 表明事件不会向下传递):

最简略的示例

test/001-echo/pipy.js 提供了的示例:

pipy()

.listen(6080)
  .decodeHttpRequest()
  .encodeHttpResponse()

发动申请

$ curl -X POST localhost:6080 -d '{}'
{}

HTTP 音讯体

#request
POST / HTTP/1.1
Content-Type: application/javascript
User-Agent: PostmanRuntime/7.28.1
Accept: */*
Postman-Token: fc84b575-7fea-487b-a55d-f6085bc62cf7
Host: localhost:6080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 2
 
{}
#response
HTTP/1.1 200 OK
postman-token: fc84b575-7fea-487b-a55d-f6085bc62cf7
accept-encoding: gzip, deflate, br
host: localhost:6080
accept: */*
user-agent: PostmanRuntime/7.28.1
content-type: application/javascript
Connection: keep-alive
Content-Length: 2
 
{}

这里咱们以过滤器 decodeHttpRequest 为例,官网的阐明是 Deframes an HTTP request message。后面提到它会产生 3 个事件,都是在 deframe 的过程中收回的。

Session 调用第一个 Filter 时,传入的事件类型是 event::DatadecodeHttpRequest 关注该事件,并依照 HTTP 协定开始解析。

在上图能够看到解析的不同阶段,会收回不同的事件。调用 Receiver 传输事件,调用 encodeHttpResponse#process() 办法。

这里又会好奇,如果下面的示例中去掉两个过滤器中的任何一个,或者都去掉,能不能失常工作?

答案是都不能!响应状态码都是 502 Bad Gateway(curl/httpie)。

剖析

这里须要联合本文的第一张图 event-handling-flow。

去掉两个过滤器

如果两个都去掉了,HTTP Request 申请音讯会被间接回传给客户端,协定谬误。

去掉 decodeHttpRequest

后面提到 Session 传给第一个 Filter 的事件是 event::Data。而 encodeHttpResponse 针对该事件只会将其保留到 buffer 中。

而后整个链路在此完结,没有回传任何数据。客户端会期待响应,超时退出(curl)。

去掉 encodeHttpResponse

先说后果,与后面一样超时退出。

为什么会这样,明明 decodeHttpRequest 产生了 3 个工夫,Session 里的 Receiver 也有收到,也的确写回了申请 body 里的 {}

encodeHttpResponse 过滤器有写回响应头,短少了这些信息,响应就不并不是非法的 HTTP 协定,只是一般的 TCP 协定。

总结

Pipy 基于事件模型的设计,提供了弱小的灵活性。容许咱们在“规定”中应用不同过滤器针对不同的事件,对申请和响应的信息进行解决。

“规定”就是业务逻辑的外围,而 Pipy 就是这逻辑的 执行引擎

最初,“好奇心是成长的驱动力,永远放弃好奇心。”

文章对立公布在公众号 云原生指北

退出移动版