转自:http://blog.csdn.net/defonds/article/details/7699501
前言
流处理是 Red5 容器的一个核心。本文是一个 Red5 流处理的设计文档,来自于 Red5 团队的邮件列表,作者是 Steven Gong,起稿于 2006 年 4 月。本文的原文标题是《关于流处理架构设计的介绍》,原文可以点击这里进行查看。
虽然只是一个初始的设计构想,而且有些细节还没有敲定,但通过对本文的学习,我们仍然可以深入领会到 Red5 流处理架构的运行机制。现在的最新的 Red5 的源代码,关于流处理的整体架构也是基于本文进行设计。
终于涉及到 Red5 的架构设计了。虽然只是一个模块,但知识面以及个人水平所限,讲解肯定有不当、肤浅之处,有些地方讲的也很生硬,请各位大牛同学多多包涵。如果实在是看不下去了,也可以出来喷一下。
以下是邮件正文
==========================================================================================================
大家好,
最近几周的时间里我一直在思考并设计关于 Red5 的新的流处理架构。这样做的目的是使我们的流处理更加模块化,这样代码才可以更好地进行设计、实现和进行独立地测试。
我在建筑设计方面做出了一些努力并尽力去找到最适合我们需要的模式。最终我找到了解决方案。我不知道这些模式是不是最好的但至少他们可以灵活而轻松地和我们遗留的流处理代码进行集成。在此我想对这个架构及其背后的设计依据做一个介绍。我们开始吧...
* 设计模式(Patterns)
事实上这个解决方案相当简单。总之,就是消息传递(Messaging)模式加管道过滤器(Pipe-Filter)模式。在多数情况下消息传递(Messaging)模式构建在管道过滤器(Pipe-Filter)模式之上,但也只使用了管道过滤器(Pipe-Filter)模式的一部分(我会详细介绍他们是如何使用的)。我所做的就是将这些模式整合起来并为我所用。
* 背景(Background)
** 消息传递系统(Messaging Systems)
将 Red5 的流处理跟一个消息传递系统相比,你会发现它们有很多类似之处:消息提供者 --> 流发布者,消息消费者 --> 流播放者/文件池,消息路由器 --> 多汇聚节点,消息 --> RTMP 消息(RTMPMessage)等等。事实上 Red5 正是一个通信服务器,它从 RTMP 供应者那里获取信息,将信息进行过滤处理后发送给 RTMP 消费者。所以消息传递系统中的很多模式都可以直接集成到我们的设计中来。在第一本书中我们可以找到这些模式:消息通道(messaging channels)、消息路由器(messaging routers)和消息转换(messaging transformer),这些对我们都很有用。举例,
- 我们可以使用带宽滤器(Bandwidth Filter,基于消息过滤器)来控制某个特殊客户端的流量。
- 我们可以使用音频/视频过滤器(Audio/Video Filter,基于消息过滤器)来控制是否发布音频或者视频标签。
- 我们可以使用消息整合器(MessageCombiner,基于聚合器)来整合几个实时播放源,然后使用混合器(MessageMixer,基于内容过滤器)再混合音频标签,进而得到一个单视频多音频的流。
- 我们可以使用播放列表发布者(PlaylistPublisher,基于消息队列)来制作一个服务端伪在线发布流(类似于 FMS 的服务端流类)。
- 我们可以使用切换流发布者(SwitchStreamPublisher,基于消息队列)来让客户端选择他们想要实时播放的流。
当然,你可能会从中找到更多有用的模型。毕竟我自己的想象力有限的很。;-)
** 管道过滤器(Pipe-Filter)
管道过滤器(Pipe-Filter)模式有着很长的历史了。最著名的实现就是运行在 unix shell 之下的管道(pipe)。Unix 上所有的进程在同时进行并以管道(pipe)相连。供应者将流添加到标准输出设备 stdout,stdout 将其转发给输入终端的管道(pipe),消费者从标准输出设备 stdin 得到流并将其转发给终端的输出管道(pipe)。由于这种管道(pipe)的异步性质,应该维护一个缓存来保存临时片段,当缓存满时阻止供应者,当缓存为空时阻止消费者。
消息传递系统也依赖于管道过滤器(Pipe-Filter)模式。当消息供给者发送消息时,将其压入管道(pipe,或者用 Messaging 模式的话说是通道 channel)。然后管道(pipe)随之将消息推送给消费者。多数情况下,消费者是基于事件驱动的。你可能会发现在 JMS 中有很多传递消息的方法。事实上,这只是一个封装内部事件驱动消费者的高层编码。
但这只是管道过滤器(Pipe-Filter)模式的一部分而已。一个标准的管道过滤器(Pipe-Filter)模式包含四种管道(pipe):拉-拉(pull-pull),拉-推(pull-push),推-拉(push-pull)和推-推(push-push)。如果我们将它命名为 xxx-yyy,那么供给者就是 xxx 方式而消费者则是 yyy 方式。例如,Unix 系统使用的就是推-拉(push-pull)管道(pipe)而消息传递系统使用的是推-推(push-push)管道(pipe)。
* Red5 消息传递架构(Red5 Messaging Framework)
现在让我们回到我们的项目中来。无论是消息传递系统还是类 Unix 系统都无法满足我们的需求。对于前者我们有类似于 FileSourceStream 的被动消费者,它只按照要求“发送”消息。对于后者,让每个组件都包含一个活跃的线程的做法太昂贵了。在 Red5 中,我们有时需要消费者触发消息传递,有时需要供应者触发传递,甚至有时是混合。因此我们最好使用所有四种类型的管道(pipe)来使得我们的架构更加灵活。
我们来看一下 Red5 通信架构的类图(MessagingFramework.png)。这些类只是根据我的个人分支(branches/dev_steveng),而且这些类并非架构的全部只是一部分,因为许多决定还是需要大家一起来做,但架构的整体已经设计完毕。主要分为五个部分:Provider, Consumer, Pipe, Event/Listener 和 IMessage。
顾名思义,IProvider 是消息的制造者,而消息的消费者当然就是 IConsumer 了。它们都是标识接口。然后是 IPullableProvider 和 IPushableConsumer。为了使这个框架生效,我写了几个临时适配器封装了我们遗留的流处理类,使之能够 VOD,实时流及其录制。类图(adapter.png)显示了这些适配器是如何工作在 Red5 通信架构中的。
** 消费者(Consumers)现在我们来看一下这些适配器是如何组织提供 VOD,实时和播放服务的。(注:我基于遗留 API 开发了这些适配器,但它们也可以比较容易地为以后的类所用)
请参考图 PlayVOD.png。在 VOD 中,当客户端发起一个播放请求时,Stream 类将会使用 FileStreamSourceAdapter、DownStreamSourceAdapter 和 InMemoryPullPullPipe 创建一个管线(pipeline)。在播放时,RTMPMessage 由消费者 DownStreamSourceAdapter 获取(MINA 对它进行触发)。请参考图 PublishLive.png 和 PlayLive.png。在这里 StreamManager 充当了一个为连接供给者和消费者的中心管道记账人(pipe)的角色。我画的只是先有发布然后才有播放的情形。发布者和在线播放者由 InMemoryPushPushPipe 进行连接 -- LiveStreamSourceAdapter 充当了这一过程的主动的供给者而 DownStreamAdapter 则在推模式下进行连接。事实上,如果在线播放者先提出申请的话,它将会等待,直到发布者提出申请。所以我们不再需要 TemporaryStream 和 TemporaryStreamSink 两个类了。
请参考图 Recording.png。除了 StreamManager 同时创建了一个 FileStreamSinkAdapter 之外这个时序图几乎跟在线发布相同。理论上,当另一个播放者提出在线播放请求时,这个时序图就会和 PlayLive.png 一样了,但这样火狐会崩溃的... :-(
> 通过上面的时序图,我们有理由断定本通信架构可以使我们的代码更加模块化更加灵活。通过事件模型机制,资源释放就很容易实现,而且还松耦合。请参考图 Dispose.png,时序图表明当 DownStreamAdapter 消亡时,FileStreamSourceAdapter 也会相应地消亡。FileStreamSinkAdapter 同理。
在这里,DownStreamAdapter 关闭并且注销(断开)了和管道(pipe)的连接。另一端的供应者(FileStreamSourceAdapter)收到通知也相应地关闭。现在让我们想像一下当有一个 SpeedControllerFilter 在二者之间时的情形。过滤器将会被通知进而自行关闭。然后 FileStreamSourceAdapter 也会被通知到。所以事件从 DownStreamAdapter 向下通过管道(pipe)传递给了 FileStreamSourceAdapter。