In-memory Channel是当前Knative Eventing中默认的Channel, 也是通常刚接触Knative Eventing首先了解到的Channel。本文经过分析 In-memory Channel 来进一步了解 Knative Eventing 中Broker/Trigger事件处理机制。异步
咱们先总体看一下Knative Eventing 工做机制示意图:性能
经过 namespace 建立默认 Broker 若是不指定Channel,会使用默认的 Inmemory Channel。spa
下面咱们从数据平面开始分析Event事件是如何经过In-memory Channel分发到Knative Service设计
Ingress是事件进入Channel前的第一级过滤,但目前的功能仅仅是接收事件而后转发到Channel。过滤功能处理TODO状态。3d
func (h *handler) serveHTTP(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error { tctx := cloudevents.HTTPTransportContextFrom(ctx) if tctx.Method != http.MethodPost { resp.Status = http.StatusMethodNotAllowed return nil } // tctx.URI is actually the path... if tctx.URI != "/" { resp.Status = http.StatusNotFound return nil } ctx, _ = tag.New(ctx, tag.Insert(TagBroker, h.brokerName)) defer func() { stats.Record(ctx, MeasureEventsTotal.M(1)) }() send := h.decrementTTL(&event) if !send { ctx, _ = tag.New(ctx, tag.Insert(TagResult, "droppedDueToTTL")) return nil } // TODO Filter. ctx, _ = tag.New(ctx, tag.Insert(TagResult, "dispatched")) return h.sendEvent(ctx, tctx, event) }
Broker 字面意思为代理者,那么它代理的是谁呢?是Channel。为何要代理Channel呢,而不直接发给访问Channel。这个其实涉及到Broker/Trigger设计的初衷:对事件过滤处理。咱们知道Channel(消息通道)负责事件传递,Subscription(订阅)负责订阅事件,一般这两者的模型以下:代理
这里就涉及到消息队列和订阅分发的实现。那么在In-memory Channel中如何实现的呢?
其实 In-memory 的核心处理在Fanout Handler中,它负责将接收到的事件分发到不一样的 Subscription。
In-memory Channel处理示意图:code
事件接收并分发核心代码以下:blog
func createReceiverFunction(f *Handler) func(provisioners.ChannelReference, *provisioners.Message) error { return func(_ provisioners.ChannelReference, m *provisioners.Message) error { if f.config.AsyncHandler { go func() { // Any returned error is already logged in f.dispatch(). _ = f.dispatch(m) }() return nil } return f.dispatch(m) } }
当前分发机制默认是异步机制(可经过AsyncHandler参数控制分发机制)。队列
消息分发机制:事件
// dispatch takes the request, fans it out to each subscription in f.config. If all the fanned out // requests return successfully, then return nil. Else, return an error. func (f *Handler) dispatch(msg *provisioners.Message) error { errorCh := make(chan error, len(f.config.Subscriptions)) for _, sub := range f.config.Subscriptions { go func(s eventingduck.SubscriberSpec) { errorCh <- f.makeFanoutRequest(*msg, s) }(sub) } for range f.config.Subscriptions { select { case err := <-errorCh: if err != nil { f.logger.Error("Fanout had an error", zap.Error(err)) return err } case <-time.After(f.timeout): f.logger.Error("Fanout timed out") return errors.New("fanout timed out") } } // All Subscriptions returned err = nil. return nil }
经过这里的代码,咱们能够看到分发处理超时机制。默认为60s。也就是说若是分发的请求响应超过60s,那么In-memory会报错:Fanout timed out。
通常的消息分发会将消息发送给订阅的服务,但在 Broker/Trigger 模型中须要对事件进行过滤处理,这个处理的地方就是在Filter 中。
其中过滤规则处理代码以下:
func (r *Receiver) shouldSendMessage(ctx context.Context, ts *eventingv1alpha1.TriggerSpec, event *cloudevents.Event) bool { if ts.Filter == nil || ts.Filter.SourceAndType == nil { r.logger.Error("No filter specified") ctx, _ = tag.New(ctx, tag.Upsert(TagFilterResult, "empty-fail")) return false } // Record event count and filtering time startTS := time.Now() defer func() { filterTimeMS := int64(time.Now().Sub(startTS) / time.Millisecond) stats.Record(ctx, MeasureTriggerFilterTime.M(filterTimeMS)) }() filterType := ts.Filter.SourceAndType.Type if filterType != eventingv1alpha1.TriggerAnyFilter && filterType != event.Type() { r.logger.Debug("Wrong type", zap.String("trigger.spec.filter.sourceAndType.type", filterType), zap.String("event.Type()", event.Type())) ctx, _ = tag.New(ctx, tag.Upsert(TagFilterResult, "fail")) return false } filterSource := ts.Filter.SourceAndType.Source s := event.Context.AsV01().Source actualSource := s.String() if filterSource != eventingv1alpha1.TriggerAnyFilter && filterSource != actualSource { r.logger.Debug("Wrong source", zap.String("trigger.spec.filter.sourceAndType.source", filterSource), zap.String("message.source", actualSource)) ctx, _ = tag.New(ctx, tag.Upsert(TagFilterResult, "fail")) return false } ctx, _ = tag.New(ctx, tag.Upsert(TagFilterResult, "pass")) return true }
当前的机制是全部的订阅事件都会经过 Filter 集中进行事件过滤,若是一个Broker有大量的订阅Trigger,是否会给Filter带来性能上的压力? 这个在实际场景 Broker/Trigger 的运用中须要考虑到这个问题。
做为内置的默认Channel实现,In-memory 能够说很好的完成了事件接收并转发的使命,而且 Knative Eventing 在接下来的迭代中会支持部署时指定设置默认的Channel。有兴趣的同窗能够持续关注一下。
原文连接 本文为云栖社区原创内容,未经容许不得转载。