65.9K
CodeProject 正在变化。 阅读更多。
Home

Darwin Streaming Server 6.0.3 - 设置、定制、插件或模块开发、在 32 位和 64 位 Redhat Linux 上的性能和负载测试

starIconstarIconstarIconstarIconstarIcon

5.00/5 (18投票s)

2009 年 8 月 28 日

CPOL

30分钟阅读

viewsIcon

165242

downloadIcon

2459

如何在 32 位或 64 位 Linux 平台上设置 Darwin Streaming Server 6.0.3,通过开发插件(Apple 称之为“模块”)添加自定义功能,以及我进行的一些性能和负载测试结果。

引言

Darwin Streaming Server 是 Apple 的开源互联网流媒体服务器。本文将带您入门,如果您愿意,还将深入探讨如何修改其代码以添加您需要的功能。它具有非常完善的模块化架构,这意味着您可以添加一个或多个自定义模块 - 只需将二进制文件放在某个文件夹中,服务器即可识别并使用它。这才是即插即用。如果您不想重新构建服务器代码本身,这会非常有用。本文将确切演示如何做到这一点。自定义模块可用于执行自定义身份验证/授权、自定义日志记录、报告、数据收集或任何您想到的内容。

本文还将讨论我对服务器进行的一些负载测试和性能测试的结果,试图发现其极限。我的平台是 Redhat Linux,但 DSS 的设置步骤对于大多数 Unix 平台来说几乎相同。

提示

Darwin Streaming Server 可以流式传输提示(hinted)的媒体文件,如 mp4 或 mov 文件 - 这是可以使用 Quick Time Player 播放的 Apple 格式。事实上,只要文件被“提示”过,它几乎可以流式传输任何其他类型的内容。您可以使用 Apple 提供的工具来“提示”媒体文件。如果您需要提示媒体文件,请另外研究。我将假设您有一个提示过的文件目录,您的 Darwin 服务器将从中流式传输到运行在可以与您的服务器通信的某个计算机上的 Quicktime Player(或其他能够播放 Apple 流的播放器)。Darwin 的默认安装已经包含了一些示例提示过的媒体文件。要进行设置和测试,您不需要任何其他文件。

平台和硬件


服务器平台

$ cat /etc/redhat-release

Red Hat Enterprise Linux AS release 4 (Nahant Update 6)

$ gcc -v

从 /usr/lib/gcc/x86_64-redhat-linux/3.4.6/specs 读取配置信息
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=x86_64-redhat-linux
线程模型:posix
gcc 版本 3.4.6 20060404 (Red Hat 3.4.6-9)

服务器硬件

摘要:Dell SC1425,2 x Xeon 2.80GHz,3.9GB / 4GB 400MHz
系统:Dell PowerEdge SC1425 (Dell 0D7449)
处理器:2 x Xeon 2.80GHz 800MHz FSB (HT 已启用,4 个线程) - Nocona E0,64 位,90nm,L2:1MB
内存:3.9GB / 4GB 400MHz == 2 x 2GB - 2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R
4 个空槽
硬盘:sda (ata_piix0):80GB (63%) JBOD == 1 x 80GB 7.2K SATA/300 WD Caviar SE 8MB
硬盘控制器:piix_ide0:Dell/Intel ICH5 82801EB ATA/100
硬盘控制器:ata_piix0:Dell/Intel ICH5 82801EB SATA/150
芯片组:Intel E7520 C1 (Lindenhurst),82801E (ICH5)
网络:eth0 (e1000):Dell/Intel 82541PI 千兆,*** 已屏蔽 MAC ***,1Gb/s <全双工>
网络:eth1 (e1000):Dell/Intel 82541PI 千兆,*** 已屏蔽 MAC ***,无载波
操作系统:RHEL AS 4 U6,Linux 2.6.9-67.ELsmp x86_64,64 位
BIOS:Dell A02 2005/08/23
主机名:**** 已屏蔽主机名 ****  
 

客户端 

播放时,我使用了一台运行 Quicktime Player 的 Windows 计算机。进行负载测试时,我使用了另一台配置与服务器相似的 Linux 计算机。

下载、安装和设置 DSS 6.0.3 的步骤

以下步骤应适用于 32 位和 64 位 Linux。步骤 3 和 4 使用了补丁文件,这些补丁文件可使 DSS 代码在 64 位系统上无错误地构建。即使您使用的是 32 位系统,也可以应用这些补丁,这样代码更具可移植性。但是,在 64 位系统上,您必须应用这些补丁,否则将无法构建。

但是,Apple 可能会在 macosforge 服务器上更改代码而不更改版本号,届时这些补丁可能无法正常工作。我始终鼓励您在不带选项的情况下使用 `patch` 命令的 `--dry-run` 选项,以确保补丁在实际应用之前能够成功应用。

同样,步骤 5 使用了一个“Install”脚本,该脚本与从 macosforge 服务器下载的版本不同。新的 install 脚本包含 64 位安装顺利进行所需的更改。

步骤 3、4 和 5 引用的 3 个文件在源代码的“Patches”目录中。

  1. 从 http://dss.macosforge.org/ 下载源代码压缩包 --> Darwin Streaming Server 6.0.3 --> 源代码
  2. tar xf DarwinStreamingSrvr6.0.3-Source.tar --> 将会创建目录 "DarwinStreamingSrvr6.0.3-Source"
  3. patch -p0 < dss-6.0.3.patch - 补丁文件包含在上传的源代码中 - 该补丁使其兼容 64 位,从包含 DarwinStreamingSrvr6.0.3-Source 的目录运行
  4. patch -p0 < dss-hh-20080728-1.patch - 补丁文件包含在上传的源代码中 - 该补丁使其兼容 64 位,从包含 DarwinStreamingSrvr6.0.3-Source 的目录运行
  5. cp Install DarwinStreamingSrvr6.0.3-Source(新的 Install 脚本文件包含在上传的源代码中 - 使其兼容 64 位,将覆盖现有 Install 脚本)
  6. sudo groupadd qtss
  7. sudo useradd qtss -g qtss
  8. cd DarwinStreamingSrvr6.0.3-Source; ./Buildit 进行构建 - 这将在 DarwinStreamingSrvr6.0.3-Source 目录中创建服务器二进制文件,以及在其他子目录中的一些其他二进制文件。“BuildIt”脚本随下载提供。不要在意警告。如果构建失败,您必须调整此脚本。它不像看起来那么难。找到脚本中的行 "case $PLAT"。从那里开始,它将根据您的 $PLAT 为重要的编译/链接变量分配一些值。 $PLAT 是 `uname` 和 `uname -m` 的连接,中间有一个点。继续查找您的 $PLAT。检查 Build 脚本是否处理了它。如果您使用的是 32 位 Linux,它应该是 "Linux.i686" 或 "Linux.i586"。如果您是 64 位,它应该是 "Linux.x86_64"。所有这三种情况都在 Build 脚本中处理 - 因此,如果仍然无法正常工作,您需要调整该部分下的各个变量。如果您的 $PLAT 是其他名称,并且您认为另一个 case 处理程序的值对您有用,请继续添加一个新的 case 部分,并复制您正在使用的那个部分的内容。
  9. 要安装:运行 sudo ./Install(您必须以 root 用户身份登录才能安装 Darwin Streaming Server)
    • 安装过程中,它会询问:“请输入新的管理员用户名:” - 输入某个用户名
    • 它会询问“请输入新的管理员密码:” - 输入某个管理员密码
  10. 安装完成后,DSS 会将二进制文件和文件写入各种位置。重要的目录是
    • /usr/local/sbin:服务器可执行文件 (DarwinStreamingServer) 和管理服务器 (streamingadminserver.pl)
    • /usr/local/movies:默认内容目录(流从中提供,已包含示例提示过的流)
    • /usr/local/bin:一些其他二进制文件,包括“StreamingLoadTool” - 一个负载测试工具(在性能/负载测试部分讨论)
    • /etc/streaming:streamingserver.xml(DSS 的配置文件)、streamingloadtool.conf(负载测试工具 StreamingLoadTool 的配置文件)、qtusers qtgroups 文件(包含 DSS 的用户和组,此时只有安装期间创建的管理员用户)以及
    • /var/streaming/logs:DSS 日志文件夹(StreamingServer.log、Error.log 最重要,还有 streamingadminserver.log 和 mp3_access.log)
  11. 运行服务器:cd /usr/local/sbin; sudo ./DarwinStreamingServer。将运行 2 个进程 - 第一个进程以 root 用户身份运行,并 fork 出主要的服务器进程作为 qtss。在执行 `ps aux | grep Darwin` 时,进程 ID 较高的那个是 fork 出来的服务器进程。如果它崩溃或被终止,另一个进程会立即重新启动一个新的。
  12. 运行管理服务器:sudo -u qtss /usr/local/sbin/streamingadminserver.pl。目标机器应已安装并配置好 yapache 作为 Web 服务器以访问管理控制台。如果您不允许冒充 qtss,只需执行 "sudo /usr/local/sbin/streamingadminserver.pl"
  13. 从浏览器访问管理控制台:http://<您的服务器名称>:1220/parse_xml.cgi。提供安装期间创建的管理员用户 ID 和密码。您可以从管理控制台更改的大多数参数都会写入 /etc/streaming/streamingserver.xml。
  14. 播放流:在任何可以解析您的 Linux 机器名称的 Windows 计算机上安装 Apple 的 Quick Time Player,选择 文件->打开 URL... 并使用 rtsp://<您的服务器名称>/<任何来自 /use/local/movies 的示例电影>。请注意,DSS 只能播放“提示过的”流。提示可以使用 Apple 的工具完成,如果您想播放示例以外的流,请了解如何提示自定义轨道。
  15. 停止服务器:终止 2 个服务器进程。如果您只终止进程 ID 较高的那个,它会不断被重新启动,所以请先终止较低的那个。如果您使用 -9,服务器将不会调用清理模块,请不要将其作为常规操作。

在开始编写自己的模块来定制 DSS 之前...

对于 C++ 开发者来说,这是有趣的部分。在尝试创建自己的模块之前,您必须了解服务器的架构。该架构非常解耦,因为整个服务器都是用各种“模块”连接起来的 - 许多核心服务器功能都实现为模块。

核心服务器代码位于 Server.tproj 文件夹中。启动后,服务器会加载模块。模块可以是“静态”或“动态”的。静态模块在服务器构建时构建。服务器运行时,它会先加载动态模块。它在 /usr/local/sbin/StreamingServerModules 目录下查找共享库(unix so 文件,只是它们不带 so 扩展名)。在此之后,它加载静态模块。服务器已提供的所有模块的代码都在“APIModules”目录中。

如果您只想添加自定义功能而不修改实际的服务器代码,最简单的方法是编写和构建一个动态模块 - 您只需要将动态模块构建为共享对象,并将二进制文件放在 /usr/local/sbin/StreamingServerModules 目录中。重启服务器后,将从该目录加载动态模块,包括您的。

角色

您必须了解角色才能开始创建自己的模块。

每个模块都必须在“角色”中注册自己。有许多角色:Register、Initialize、Filter、Route、RTSP Pre-Processing、RTSP Request、RTSP Post Processing、RTP Roles、RTSP Authentication Role、RTSP Authorization Role、Error Logging Role 等。将角色视为流程图中的构建块。服务器首先调用 Register 角色。然后是 Initialize 角色。然后它会安静地等待请求。一旦收到 RTSP 请求,它会调用 Filter 角色,并将整个 URI 传递给它进行“过滤” - 订阅此角色的模块有权获取 RTSP 请求的可写副本:它可以实际更改请求的任何部分!然后服务器调用 Route 角色 - 订阅此角色的模块将获得一个可写副本,其中包含要从中提供内容的文件夹,并且可以有效地更改提供点(从而将请求重新路由到新点)。依此类推。

服务器调用其角色的顺序是固定的(在服务器中编码)。多个模块可以订阅一个角色,一个模块可以订阅多个角色。有些角色是例外 - 只有一个模块可以注册它们。这种“订阅”发生在“Register Role”的代码处理程序内部 - 所有模块都必须注册到此角色。当在此角色中被调用时,模块会告知服务器它正在订阅哪些其他角色。

先决条件 - QTSSAPIDocs.pdf

阅读 QTSSAPIDocs.pdf 文件,无论它多么枯燥。虽然它在“Documentation”目录中可用,但我将其与源 zip 文件一起上传,以便于查找。它是 Documentation 目录中所有文件中最重要的文档。它是模块开发者的圣经。不幸的是,它还有很多不足之处。它是有组织的,但很混乱。尽管如此,本文不能替代阅读此文档。本文就像一个指南针 - 它会指导您,给您一个更好的全局图景,但代码的细节必须参考此 pdf 文件。完成 QTSSAPIDocs.pdf 后,请再次阅读本文的其余部分。

让我们创建一个自定义 Auth 模块

让我们设想一个可能的情况:您希望根据一个考虑了连接 URL(如果您强制用户在查询字符串中提供某些特定签名,则情况如此)和客户端 IP 地址(如果您想限制对某个子网的访问,则情况如此)的逻辑来允许/拒绝请求。您需要一个自定义 Auth 模块。 

DSS 中的授权/身份验证

DSS 如何处理身份验证和授权?授权与身份验证有何不同?

我选择自定义 Auth 模块的示例是因为这将引导我们讨论 DSS 如何处理授权/身份验证。它在它支持的任何基本方案方面都做得相当不错 - 但那充其量是“基本的”。它使用一种粗糙的机制,这可能不足以满足您的需求。让我们回顾一下 DSS 为我们提供了什么。

安装后,所有用户都可以访问 movies 目录中的所有电影。但假设您想设置 n 个用户帐户,以便每个用户只能访问自己的内容。请参阅 http://dss.macosforge.org/ --> “Administrator’s Guide”了解如何添加用户帐户。基本上,这归结为每个用户在 movies 目录(/usr/local/movies - 主 movies 目录)下都有一个子目录。每个这样的子目录都包含一个“qtaccess”文件,该文件列出了可以访问该给定子目录的用户。所有用户及其凭据的主列表存储在 /etc/streaming/qtusers 文件中(密码以加密格式存储),只有 Linux 用户 qtss 可以访问它。 

现在,当给定用户想要播放文件时,他/她可以在 RTSP 请求中发送用户 ID 和密码。将这对用户 ID 和密码与 qtusers 中的主列表进行验证的过程称为**身份验证**。因此,身份验证过程的输出是“是的,这是一个有效的用户 ID - 密码对”或“不,它不是”。仅仅通过身份验证并不能保证用户能够播放请求的文件,因为用户可能仍然不在请求内容所在的子目录的 qtaccess 文件中。换句话说,通过身份验证意味着“您是系统的有效用户”。它并不意味着“您有权访问请求的文件”。**授权**是另一半,它是允许用户访问所请求内容的进程。授权可能会由于各种原因而失败,包括上述情况:用户未列在 qtaccess 文件中。所以底线是:如果用户提供了正确的密码,他/她将通过身份验证,但如果他/她未能通过授权(这可能由于各种原因而发生),他/她仍将无法播放流(更笼统地说,*访问资源*)。

不用说,服务器在调用“RTSP Authorize 角色”*之前*调用“RTSP Authenticate Role”。

有趣且正确的是,DSS 强制只有一个模块可以订阅“Authenticate”角色,但可以无限数量的模块订阅“Authorize”角色。这证实了我们上面的结论:身份验证仅在一种情况下失败:如果您是未知用户。但授权可能由于各种情况而失败,并且可能有多个模块,每个模块验证授权的某个方面。因此,当服务器调用订阅“Authenticate”角色的模块时,它期望该模块验证用户 ID/密码。如果成功,它将调用所有订阅“Authorize”角色的模块 - 每一个模块都可以应用自定义规则来批准或拒绝该 RTSP 请求。例如,一个 Authorize 模块可能会因为带宽过高或开放连接数过多而拒绝请求。此类自定义授权必须实现为单独的模块,订阅“Authorize”角色。

实现完全不同的身份验证方案

这种验证用户(在 qtusers 文件中查找等)的机制实现为一个静态模块(您猜对了 - 这是好事 guys - DSS 的核心功能就是模块)。它是 QTSSAccessModule。继续,查看 APIModules 目录中的代码,弄湿您的双手总是一件好事。

如果您查看 QTSSAccessModule 的代码,您会发现它已注册到 Authenticate Role(查看 `QTSSAccessModuleDispatch` 方法)。当然,此外,它还注册到 5 个其他角色,包括 Authorize Role。但它注册到 Authenticate Role 的事实很重要,因为 Authenticate Role 是只有一个模块可以注册的角色之一。因此,如果您想向 Authenticate Role 注册一个新模块,那是不行的!

因此,如果我们坚持认为我们的新模块必须注册到 Authenticate Role,我们将被迫停用 QTSSAccessModule。这可能没问题,但不能就这样放弃 DSS 的核心模块。谁知道那个模块还在做什么?总之,QTSSAccessModule 是一个静态模块,不是动态的 - 所以摆脱它意味着弄脏服务器代码。或者,我们可以编辑 QTSSAccessModule,并在该模块内编写我们的代码。同样,这并不优雅。

对于我们试图解决的问题,Authorize Role 非常适合。

一种方法是避免在任何地方放置 qtaccess 文件,以阻止 QTSSAccessModule 的任何操作。这意味着所有用户,包括匿名用户(那些在提交 RTSP 请求时未提供用户名或凭据的用户),都被允许访问所有内容。QTSSAccessModule 将通过所有请求。现在我们可以添加一个实现 Authorize 角色 的自定义模块,并在其中添加自定义逻辑来允许/拒绝。这确实是一种简化的方法,旨在解决我们虚构的需求。这只是给您一个想法 - 您的情况肯定需要稍微不同的解决方案。

编写代码

关于您的参考副本:**QTSSMyAuthModule**(我们光荣的第一个 DSS 模块)的全部代码都在源 zip 文件中:作为一个目录,您可以将其复制到 APIModules 文件夹中,与现有模块并列。

将现有模块构建为动态库

在开始编写新模块的代码之前,您应该学习如何构建(make)现有模块 - 如何*仅*将模块构建为共享库,以便您可以简单地将模块二进制文件放入 /usr/local/sbin/StreamingServerModules 目录中。

通过对构建脚本进行一些调整,我发现了一个可以将其直接复制到包含模块代码的子目录中的脚本。APIModules 目录下的每个子目录都已经包含一个 Makefile.POSIX - 但不能直接使用它,因为它期望全局构建脚本(还记得 "Buildit" 吗?)在调用它之前填充一些导出的变量。但是,要单独构建一个模块,我们需要一个本地构建脚本,该脚本将使我们能够类似地使用本地 Makefile.POSIX。除此之外,我还对其进行了更多调整,使其输出一个共享库,而不是一个静态库。结果是“build” - 请参阅源 zip/QTSSMyAuthModule。

首先在一个现有模块上尝试它,例如 QTSSHomeDirectoryModule。只需将我上传的“build”脚本复制到 QTSSHomeDirectoryModule 文件夹。编辑脚本的最后一个 echo 语句(您可能还需要运行 chmod 使其可执行)以打印正确的模块名称,然后您就完成了。首先运行“build clean”,然后运行“build”,您将看到对象文件和名为“QTSSHomeDirectoryModule”的二进制文件被创建。

现在将该二进制文件复制到 /usr/local/sbin/StreamingServerModules。重启服务器。此时查看 /var/streaming/logs/Error.log,它应该会 freshly 记录服务器刚刚加载的模块列表。对于每个模块,它会指定它是静态的还是动态的。如果您成功构建了 QTSSHomeDirectoryModule,它应该会列为已加载且为动态。通过从 /usr/local/sbin/StreamingServerModules 中删除该二进制文件重复该实验将导致该日志行不出现。

是时候创建 QTSSMyAuthModule 了

将任何现有模块(以下教程假设您使用的是 QTSSHomeDirectoryModule)的源代码复制到一个名为 "QTSSMyAuthModule" 的新子目录中,并更改对旧名称的所有引用为新名称。

QTSS Home Directory Module 处理 2 个我们不需要的角色:RTSPRoute Role 和 ClientSessionClosing Role。我不会在此解释所有这些角色,请阅读圣经。删除处理这些角色的代码。其他 4 个(Register、Initialize、RereadPrefs 和 RTSPAuthorize)是需要的。并且我们需要一个额外的角色,它不在那里:Shutdown Role,所以添加处理该角色的代码(请参阅上传的源 zip)。

让我快速介绍一下我们将在 QTSSMyAuthModule 中处理的角色。

RegisterInitialize Shutdown:Register 是强制性的(这是您告诉服务器您*计划*注册到*其他*角色的地方)。Initialize 最好有一个 - 例如,我在一个单独的 H - CPP 文件对中添加了一个新类,并且我需要一个我的类类型的静态对象。因此,我在 Initialize 中创建了一个实例(通过调用 `new()` 或 `QTSS_New()`)。自然,内存清理的问题就随之而来。在这种需要调用清理代码时,我们必须订阅另一个角色:Shutdown Role,以便在服务器关闭时调用我们的模块。

RereadPrefs:RereadPrefs 也很适合,特别是如果您希望您的新模块由配置设置控制。添加配置设置非常容易:只需编辑 /etc/streaming/streamingserver.xml,然后在“<CONFIGURATION>”下添加一个新的节点:<MODULE NAME="<您的模块名称,在本例中为 QTSSMyAuthModule>>,其中包含任意数量的配置参数。例如,我想要一个主设置来启用/禁用我的 auth 插件,所以我添加了 <PREF NAME="enabled" TYPE="Bool16">true</PREF>。添加设置后,RereadPrefs Role 的处理程序应该只需使用 QTSSModuleUtils::GetAttribute(...)(请参阅我的代码)读取设置值即可。让模块订阅 RereadPrefs Role 仅确保您的模块可以在不重启服务器的情况下读取更改后的设置。

RTSPAuthorize:对于 RTSPAuthorize 处理程序,我仿照 QTSSHomeDirectoryModule 处理 Authorize Role 的方式对其代码进行了建模。我注意到服务器会多次调用我的模块来播放单个流 - 而且我只需要授权一次(第一次)才能提高效率。在所有后续调用中,我必须弄清楚这个 rtsp 会话是否已获授权。添加到 RTSPSession 对象中的自定义属性帮助我做到了这一点,并且该技术在 QTSSHomeDirectory 代码的现有代码中得到了清晰的实现。作为我实现 Authorize 角色的部分,我需要提取请求的 RTSP URL 和客户端 IP。通过梳理现有模块的代码,我从 QTSSSplitterModule.cpp(参见 QTSSReflectorModule 子目录)中找到了 URL 提取技术 - 但在那里提取的字符串并不以“rtsp://”开头,而只以文件名部分开头。扫描 QTSSAPIDocs.pdf,我找到了所有标准对象支持的属性列表,并找到了一个包含我所需格式 URL 的属性。

因此,正如您所见,没有哪篇文章能向您展示您需要知道的确切技术 - 现有模块的代码和 QTSSAPIDocs.pdf 中有大量信息,尽管分散且难以查找。您从现有代码库和/或文档中找到所需技术的速度取决于您在这方面付出了多少努力。

使用您自己的类

我将不解释 QTSSMyAuthModule 中每一行代码 - 执行确切身份验证的地方充满了运行注释。我将允许/拒绝的功能分离到一个单独的类 - MyAuth.cpp、MyAuth.h。

我发现我不能直接在 QTSSMyAuthModule.cpp(包含模块必须具备的强制性方法的模板文件)中使用 STL 类和方法 - 它们与 DSS 使用的某些头文件冲突。这应该可以通过一些努力来修复,但我选择了更简单的路线:如果我遵循包含单独类的方案,我就可以在 MyAuth.h/cpp 中使用 STL 而没有任何问题。

配置如何工作

/etc/streaming/streamingserver.xml 精确且清晰。它在 /CONFIGURATION/SERVER 下列出了所有服务器的设置,然后是所有模块的设置,每个模块都有一个节点 /CONFIGURATION/MODULE@NAME。更改这些设置不需要重启服务器 - 只需向服务器进程(进程 ID 较高的那个)发送 HUP 信号即可。为了让自定义动态模块在服务器响应 HUP 信号重新加载首选项时同时重新加载首选项,它必须实现 RereadPrefs Role。

日志记录如何工作


错误日志记录

尽管 DSS 在 APIModules 目录中拥有所有模块,但我发现了一个隐藏在 Server.tproj 文件夹中的模块 - QTSSErrorLogModule。这可能是因为该模块被认为是服务器本身的一个组成部分,而且它非常重要。正是因为这个模块,任何其他模块和服务器本身都可以写入位于 /var/streaming/logs 的 Error.log 文件。

即使是信息性日志也可以简单地写入 Error.log 文件,并将详细程度参数设置为 qtssDebugVerbosity,除非 /CONFIGURATION/SERVER/error_logfile_verbosity 设置为 4(qtssDebugVerbosity),否则它实际上不会显示出来。此配置设置的可能值为:0(致命详细程度)、1(警告详细程度)、2(消息或 INFO 详细程度)、3(断言详细程度)和 4(调试)。无论设置为哪个值,都会实际写入详细程度参数等于或小于该值的日志。以致命详细程度写入日志会导致服务器关闭。

为了从自定义模块中的任何位置写入错误日志流,我们需要在 `Initialize` 处理程序方法中将错误日志流保存在一个静态 `QTSS_StreamRef` 对象中,该方法的参数包含对该流的引用。请参考我的代码。

流请求日志记录

QTSSAccessLogModule 模块为我们完成了这项工作:它记录每次 RTSP 会话关闭/播放停止的大量统计信息(到 /var/streaming/logs/StreamingServer.log)。与 flash media server 不同,它不会捕获暂停/取消暂停。通过调整此模块的代码,可以轻松实现对正在记录的确切字段的任何更改/添加。streamingserver.xml 文件在相应的 MODULE 节点中包含此模块的所有可配置参数。

服务器监控/健康检查支持

DSS 有一个很酷的功能:在 streamingserver.xml 中启用条目 "enable_monitor_stats_file",它将在 Error.log 写入的同一个文件夹中输出一个统计文件(即 /var/streaming/logs,但您可以使用 streamingserver.xml 中的 "error_logfile_dir" 条目进行更改)。此统计文件每 X 秒刷新一次,其中 X 是 "monitor_stats_file_interval_seconds"。

值得在此提一下我遇到的此监控功能:该文件是 XML 格式的,因此我决定使用浏览器监控我的服务器。但不知何故,DSS 在统计文件的顶部写入了一些乱码,导致 XML 格式错误,浏览器无法渲染。此外,文件的权限设置不允许所有人读取文件。我还想将文件输出到任何我想要的位置(使用 "monitor_stats_file_name" 条目),但 DSS 的编写只期望从该条目中获取文件名,而不是整个路径。我尝试修复这些问题的努力让我找到了 Server.tproj/RunServer.cpp 中的 `LogStatus()` 函数,我可以在那里修复所有问题。现在我将我的统计文件输出到 /tmp,我的 apache 服务器可以很好地服务该文件,因为我已在 apache 的 htdocs 目录中创建了一个指向实际文件的符号链接。现在它是纯 XML,没有乱码。

性能和负载测试

我对 DSS 进行的负载测试并不满意。它无法处理数千个并发用户。总有一天我可能会花时间和精力尝试在代码中修复(读取改进)它,但首先让我告诉你我是如何进行这些测试的。

StreamingLoadTool

DSS 自带一个负载测试工具,位于 /usr/local/bin/StreamingLoadTool。传递 -v 参数可以了解它接受的其他参数。我主要使用 -f <配置文件> 参数,并使用配置文件指定所有其他内容。示例配置文件位于 /etc/streaming/streamingloadtool.conf。

我成功地从另一台客户端机器运行了负载测试 - 只需将负载测试二进制文件和配置文件复制过去即可。

我发现 DSS 6.0.3 的 StreamingLoadTool 在我使用 runforever=yes 和并发客户端数为 100 时会崩溃。Pfft。

openRTSP

测试 DSS 的一个更好的工具是来自 www.live555.com 的 openRTSP。如果您想从源代码构建它,您必须下载整个 live555 streaming media 源代码。我按照他们的说明进行了操作,一切都很顺利。最终您会在一个名为“live”的目录中找到一切 - 在哪里放置它取决于您。openRTSP,命令行 RTSP 客户端工具,位于 testProgs 文件夹中。它有出色的文档,所以我将不解释如何使用它。我编写了一个脚本,该脚本生成了许多 openRTSP 会话,每个会话都带有 -c 选项(用于连续播放)、-r(不渲染视频)和 -p 80(将视频重定向到该端口),并且我控制了生成数量和速率。

结果

结果是,DSS 在我能想到的所有优化(使用 /etc/security/limits.conf 将系统的最大打开文件句柄数增加到一个巨大的数字,在 streamingserver.xml 中将“maximum_bandwidth”和“maximum_connection”设置为 -1,将“run_num_threads”设置为各种值,包括 0 用于自动检测核心数)之后 - DSS 仍然无法处理超过 350 个并发用户。


并发用户数 CPU(Forked 服务器) CPU 空闲(总体) 内存(服务器进程) 稳定? 带宽(MBPS)
100 1% 99% 4 MB 3.75
200 2% 97% 7 MB 7.5
300 3% 96% 12 MB 11.25
350 4% 96% 21 MB 13.125
400 4% 96% 22 MB  - 

上面的带宽数字是基于我流式传输 300 kbps 文件的直接计算。DSS 示例文件还提供 1000 kbps 文件和高清文件,我相信这些文件将产生更高的带宽,但我未在实验中测量高比特率文件的性能和延迟 - 我将等待读者反馈测试结果。

我没有注意到任何内存泄漏 - 在稳定模式下,它持续运行了数周,处理了 350 个连续并发用户,未发现任何泄漏。

运行多个 DSS 服务器以实现可扩展性

对某些人来说,这可能就足够了。但我需要更多。不过有一个解决方案 - 即使 DSS 在 400 个用户时就会崩溃,它使用的 CPU 和内存也几乎为零(这表明瓶颈在代码的某个地方,这让我更有信心它可能可以修复)。这种“精简”可以有效地通过运行多个 DSS 服务器来实现!

在执行服务器时,我们可以传递 -c <任何我们想要的 streamingserver.xml>。如果我们创建多个 streamingserver.xml 文件,每个文件在“rtsp_port”条目中列出不同的端口(例如,一个在 7070,另一个在 554,等等),然后执行多个 DSS 实例,每个实例使用一个单独的 streamingserver.xml 作为配置文件,我们可以实现这一点。我并行运行了 3 个服务器实例,每个实例处理 350 个并发用户 - 这是达到四位数(1000 用户)的一种方式!

唯一的缺点是您传递给 openRTSP(或任何其他客户端)的 URL 必须使用主机名后面的冒号指定的端口号来访问正确的服务器实例。如果您考虑在实际世界中使用此设置,您必须编写另一个应用程序 - 一个软件负载均衡器,它将监听任何给定的端口,并将请求委托给任何一个正在运行的 DSS 实例。客户端然后必须始终连接到该端口。我还没有尝试过这个。

RTSP、RTP、UDP、RUDP、TCP...

在使用 Darwin Streaming Server 时,您会遇到这些缩写。有时您需要在一者和另一者之间进行选择。例如,在设置 StreamingLoadTool(DSS 自带的负载测试工具)时,其配置文件中有条目,您可以在其中选择执行模式 - RUDP?UDP?TCP?如果您不确定该选择什么,可以保留默认设置。

解释这些差异和含义超出了本文的范围。也许以后会有文章。我在这里提到它们,是因为您需要理解这些概念才能充分利用 DSS。如果您只是为用户设置服务器 - 您可能会问 - 您为什么关心它是使用 RUDP 还是 UDP?起初,您可能不需要关心。但是,如果大量用户正在使用您的服务,您可能会被问到哪个提供的吞吐量更高。他们可能使用不同功能的客户端,并且有些客户端提供选项让用户选择模式。

对于绝对的初学者,只需了解这一点:RTSP 是传输协议(对应于 HTTP)。RTP 是 RTSP 用于数据包传输的底层协议。TCP 和 UDP 是备用的网络协议 - TCP 的特点是高可靠性但低及时性,因为它涉及大量的握手(ACK、NACK 等)。UDP 的特点是低可靠性(数据包可能无法到达另一端)但高及时性。R-UDP 是 Apple 的专有 UDP 版本,它添加了一些握手,因此他们称之为“可靠 UDP”。这是一篇试图提供更好解释的文章:http://www.vbrick.net/Topics/RTSP.asp

您可能会问 DSS 为什么需要处理这些概念?任何流媒体服务器都会这样做,因为它的唯一工作就是将流媒体内容传输到客户端 - 与播放文件不同,流媒体的特点是客户端没有任何内容被存储。比特必须以视频播放所需的速度传输 - 一个较亮的场景可能需要每秒 100,000 比特,而一个较暗的场景可能需要每秒 700,000 比特。这个数字称为视频的*比特率*。协调这种速度上下波动的传输,确保可靠性和握手,是流媒体服务器的复杂领域。而 HTTP 无法解决这个问题,因为 HTTP 只能获取整个文件(它也可以根据字节范围请求获取部分文件),但它无法在*同一连接上持续*获取文件。RTSP、RTMP 等协议就是为此目的而使用的。

结论

Apple 的工程师们编写得很好。审阅者在事后总能指出改进的领域,但这个项目如此庞大,最终能够得到一套连贯、可用且大部分无 bug 的如此规模的文件,是一项值得称赞的成就。

我不太喜欢使用如此多的静态变量 - 它会对堆造成压力。而像 StrPtrLen 这样业余设计的实用类简直让我抓狂 - 这些家伙创建了一个类来保存指向别人数据的指针!在使用这个几乎是犯罪的类时请极其小心 - 它容易被滥用。总的来说,代码可以更整洁,但嘿,我没有为此付费。我将接受 DSS 的价值。

© . All rights reserved.