`
zcdxzsz
  • 浏览: 73188 次
  • 来自: ...
社区版块
存档分类
最新评论

[转]Red5源代码分析 – 关键类及其初始化过程

    博客分类:
  • java
阅读更多
Red5如何响应rmpt的请求,中间涉及哪些关键类?
响应请求的流程如下:

1.Red5在启动时会调用RTMPMinaTransport的start()方法,该方法会开启rmtp的socket监听端口(默认是1935),然后使用mina(apache的io操作类库)的api将RTMPMinaIoHandler绑定到该端口。

2.RTMPMinaIoHandler上定义了messageReceived、messageSent、sessionOpened和 sessionClosed等方法,当有socket请求时,相应的方法会被调用,这时RTMPMinaIoHandler会使用当前的socket连接 来创建一个RTMPMinaConnection(或者使用一个之前创建好的RTMPMinaConnection),并将其作为参数传递给定义于 RTMPHandler类上的相应的messageReceived、messageSent、connectionOpened和 connectionClosed方法。

3.RTMPHandler会调用Server类的lookupGlobal获得当前的GlobalScope,然后再利用GlobalScope 找到当前socket请求应该使用的WebScope(这个WebScope就是我们在自己的项目的WEB-INF\red5-web.xml中定义的 啦)。最后,RTMPHandler会调用RTMPMinaConnection的connect方法连接到相应的WebScope。

4.至此,控制流进入了我们自己项目中了,通常来说,WebScope又会将请求转移给ApplicationAdapter,由它来最终响应请求,而我们的项目通过重载ApplicationAdapter的方法来实现自己的逻辑。
简单的流程图:
Java代码

    RTMPMinaIoHandler
    |–[delegate method call and pass RTMPMinaConnection to]–>RTMPHandler
    |–[call lookupGlobal method]–>Server
    |–[use globalScope to lookup webScope]–>GlobalScope
    |–[call connect method and pass WebScope to]–>RTMPMinaConnection

RTMPMinaIoHandler
  |--[delegate method call and pass RTMPMinaConnection to]-->RTMPHandler
     |--[call lookupGlobal method]-->Server
     |--[use globalScope to lookup webScope]-->GlobalScope
     |--[call connect method and pass WebScope to]-->RTMPMinaConnection

Red5如何启动?在它的启动过程中如何初始化这些关键类?

这里探讨的是Red5 standalone的启动过程(也就是我们执行red5.bat),关于Red5如何在tomcat中启动,目前仍在研究中。
Red5启动过程如下:

1.编辑red5.bat,找到关键的一行:
Java代码

    C:\Program Files\Java\jre1.5.0_15\bin\java”
    -Djava.security.manager
    -Djava.security.policy=conf/red5.policy
    -cp red5.jar;conf;bin org.red5.server.Standalone

C:\Program Files\Java\jre1.5.0_15\bin\java"
  -Djava.security.manager
  -Djava.security.policy=conf/red5.policy
  -cp red5.jar;conf;bin org.red5.server.Standalone

可以看到它是调用org.red5.server.Standalone作为程序启动的入口,这也是为什么使用eclipse在debug模式下 启动Standalone就可以调试Red5代码。需要注意的是,如果你要调试Red5,记得除了源代码(src)之外,把conf和webapps两个 文件夹都拷入项目中,并把conf加入classpath。
2.观察Standalone的main方法,你会看到它使用spring的 ContextSingletonBeanFactoryLocator来载入classpath下面的red5.xml,注意 ContextSingletonBeanFactoryLocator还会在下面的步骤中被使用,由于它是singleton的,所以保证了我们自己的 项目中定义的bean可以引用red5.xml中定义的bean,这个下面会有介绍。
Java代码

    try {
    ContextSingletonBeanFactoryLocator.getInstance(red5Config).useBeanFactory(“red5.common”);
    } catch (Exception e) {
    // Don’t raise wrapped exceptions as their stacktraces may confuse people…
    raiseOriginalException(e);
    }

try {
ContextSingletonBeanFactoryLocator.getInstance(red5Config).useBeanFactory("red5.common");
} catch (Exception e) {
// Don't raise wrapped exceptions as their stacktraces may confuse people...
raiseOriginalException(e);
}

3.查看red5.xml,这个文件首先定义了指向classpath:/red5-common.xml的名字为“red5.common”的 BeanFactory,注意它会是整个BeanFactory层次中的根节点,所以在red5-common.xml中定义的bean可以被其他地方所 引用。
Xml代码

    <bean id=”red5.common” class=”org.springframework.context.support.FileSystemXmlApplicationContext”>
    <constructor-arg><list><value>classpath:/red5-common.xml</value></list></constructor-arg>
    </bean>

<bean id="red5.common">
    <constructor-arg><list><value>classpath:/red5-common.xml</value></list></constructor-arg>
</bean>

这里我们主要留意red5-common.xml中定义的类型为org.red5.server.Server的“red5.server”,它会在接下来很多地方被用到。
Xml代码

    <bean id=”red5.server” class=”org.red5.server.Server”/>

<bean id="red5.server"/>

4.回到red5.xml,接着定义指向classpath:/red5-core.xml的名字为“red5.core”的BeanFactory,注意“red5.core”是以“red5.common”为parent context。
Xml代码

    <bean id=”red5.core” class=”org.springframework.context.support.FileSystemXmlApplicationContext”>
    <constructor-arg><list><value>classpath:/red5-core.xml</value></list></constructor-arg>
    <constructor-arg><ref bean=”red5.common” /></constructor-arg>
    </bean>

<bean id="red5.core">
    <constructor-arg><list><value>classpath:/red5-core.xml</value></list></constructor-arg>
    <constructor-arg><ref bean="red5.common" /></constructor-arg>
</bean>

查看red5-core.xml,这个文件主要定义了之前说过的RTMPMinaTransport,RMTPMinaIoHandler和 RTMPHandler这些类的Bean。对于RTMPMinaTransport,注意init-method=”start”这段代码,这说明 RTMPMinaTransport的start方法会在该Bean初始化时调用,正如上面提到的,该方法会做开启1935端口,绑定 RTMPMinaIoHandler到该端口等等的操作。对于RTMPHandler,注意它的server属性通过“red5.server”引用了定 义在parent context(red5-common.xml)上面的Server,通过它RTMPHandler能够找到GlobalScope,进而找到 WebScope。
Xml代码

    <!– RTMP Handler –>
    <bean id=”rtmpHandler”
    class=”org.red5.server.net.rtmp.RTMPHandler”>
    <property name=”server” ref=”red5.server” />
    <property name=”statusObjectService” ref=”statusObjectService” />
    </bean>
    
    <!– RTMP Mina IO Handler –>
    <bean id=”rtmpMinaIoHandler”
    class=”org.red5.server.net.rtmp.RTMPMinaIoHandler”>
    <property name=”handler” ref=”rtmpHandler” />
    <property name=”codecFactory” ref=”rtmpCodecFactory” />
    <property name=”rtmpConnManager” ref=”rtmpMinaConnManager” />
    </bean>
    
    <!– RTMP Mina Transport –>
    <bean id=”rtmpTransport” class=”org.red5.server.net.rtmp.RTMPMinaTransport” init-method=”start” destroy-method=”stop”>
    <property name=”ioHandler” ref=”rtmpMinaIoHandler” />
    <property name=”address” value=”${rtmp.host}” />
    <property name=”port” value=”${rtmp.port}” />
    <property name=”receiveBufferSize” value=”${rtmp.receive_buffer_size}” />
    <property name=”sendBufferSize” value=”${rtmp.send_buffer_size}” />
    <property name=”eventThreadsCore” value=”${rtmp.event_threads_core}” />
    <property name=”eventThreadsMax” value=”${rtmp.event_threads_max}” />
    <property name=”eventThreadsQueue” value=”${rtmp.event_threads_queue}” />
    <property name=”eventThreadsKeepalive” value=”${rtmp.event_threads_keepalive}” />
    <!– This is the interval at which the sessions are polled for stats. If mina monitoring is not  enabled, polling will not occur. –>
    <property name=”jmxPollInterval” value=”1000″ />
    <property name=”tcpNoDelay” value=”${rtmp.tcp_nodelay}” />
    </bean>

<!-- RTMP Handler -->
<bean id="rtmpHandler"
class="org.red5.server.net.rtmp.RTMPHandler">
<property name="server" ref="red5.server" />
<property name="statusObjectService" ref="statusObjectService" />
</bean>

<!-- RTMP Mina IO Handler -->
<bean id="rtmpMinaIoHandler"
class="org.red5.server.net.rtmp.RTMPMinaIoHandler">
<property name="handler" ref="rtmpHandler" />
<property name="codecFactory" ref="rtmpCodecFactory" />
<property name="rtmpConnManager" ref="rtmpMinaConnManager" />
</bean>

<!-- RTMP Mina Transport -->
<bean id="rtmpTransport" init-method="start" destroy-method="stop">
<property name="ioHandler" ref="rtmpMinaIoHandler" />
<property name="address" value="${rtmp.host}" />
<property name="port" value="${rtmp.port}" />
<property name="receiveBufferSize" value="${rtmp.receive_buffer_size}" />
<property name="sendBufferSize" value="${rtmp.send_buffer_size}" />
<property name="eventThreadsCore" value="${rtmp.event_threads_core}" />
<property name="eventThreadsMax" value="${rtmp.event_threads_max}" />
<property name="eventThreadsQueue" value="${rtmp.event_threads_queue}" />
<property name="eventThreadsKeepalive" value="${rtmp.event_threads_keepalive}" />
<!-- This is the interval at which the sessions are polled for stats. If mina monitoring is not enabled, polling will not occur. -->
<property name="jmxPollInterval" value="1000" />
<property name="tcpNoDelay" value="${rtmp.tcp_nodelay}" />
</bean>

5.再次回到red5.xml,接下来定义类型为org.red5.server.ContextLoader的bean,并在初始化后调用它的init方法。
Xml代码

    <bean id=”context.loader” class=”org.red5.server.ContextLoader”  init-method=”init”>
    <property name=”parentContext” ref=”red5.common” />
    <property name=”contextsConfig” value=”red5.globals” />
    </bean>

<bean id="context.loader" init-method="init">
<property name="parentContext" ref="red5.common" />
<property name="contextsConfig" value="red5.globals" />
</bean>

查看该方法的源代码,可以看到它会读取在classPath下面的red5.globals文件,对于每一行初始化一个以 “red5.common”为parent context的BeanFactory,具体来说,现在red5.globals中只有一行 default.context=${red5.root}/webapps/red5-default.xml,那么会创建一个名字为 “default.context”的指向webapps/red5-default.xml的Bean Factory,它以“red5.common”为parent context。
Java代码

    protected void loadContext(String name, String config) {
    log.debug(“Load context - name: ” + name + ” config: ” + config);
    ApplicationContext context = new FileSystemXmlApplicationContext(
    new String[] { config }, parentContext);
    contextMap.put(name, context);
    // add the context to the parent, this will be red5.xml
    ConfigurableBeanFactory factory = ((ConfigurableApplicationContext) applicationContext)
    .getBeanFactory();
    // Register context in parent bean factory
    factory.registerSingleton(name, context);
    }

protected void loadContext(String name, String config) {
log.debug("Load context - name: " + name + " config: " + config);
ApplicationContext context = new FileSystemXmlApplicationContext(
new String[] { config }, parentContext);
contextMap.put(name, context);
// add the context to the parent, this will be red5.xml
ConfigurableBeanFactory factory = ((ConfigurableApplicationContext) applicationContext)
.getBeanFactory();
// Register context in parent bean factory
factory.registerSingleton(name, context);
}

查看red5-default.xml,发现它主要是定义了GlobalScope的bean,然后把它注册到“red5.server”上。
Xml代码

    <bean id=”global.scope” class=”org.red5.server.GlobalScope” init-method=”register”>
    <property name=”server” ref=”red5.server” />
    <property name=”name” value=”default” />
    <property name=”context” ref=”global.context” />
    <property name=”handler” ref=”global.handler” />
    <property name=”persistenceClass”>
    <value>org.red5.server.persistence.FilePersistence</value>
    </property>
    </bean>

<bean id="global.scope" init-method="register">
<property name="server" ref="red5.server" />
<property name="name" value="default" />
<property name="context" ref="global.context" />
<property name="handler" ref="global.handler" />
<property name="persistenceClass">
<value>org.red5.server.persistence.FilePersistence</value>
</property>
</bean>

6.继续看red5.xml,最后定义类型为org.red5.server.jetty.JettyLoader的bean,并且在初始化后调用它的init方法,查看该方法源代码,很明显它是初始化并且启动jetty这个web server。
Xml代码

    <bean id=”jetty6.server” class=”org.red5.server.jetty.JettyLoader” init-method=”init” autowire=”byType” depends-on=”context.loader”>
    <property name=”webappFolder” value=”${red5.root}/webapps” />
    </bean>

<bean id="jetty6.server" init-method="init" autowire="byType" depends-on="context.loader">
<property name="webappFolder" value="${red5.root}/webapps" />
</bean>

7.到了这里似乎所有的初始化和启动都完毕了,但是问题就来了,这里仅仅定义了 RTMPMinaIoHandler,RTMPHandler,Server和GlobalScope,但是在我们之前提到过的Red5响应rmpt的请 求的过程中,还需要有WebScope来最终处理RTMPMinaConnection,这个WebScope又是怎么配置并且加进来的呢?
8.查看webapps下的项目,这里以oflaDemo为例,查看WEB-INF下面的web.xml,发现有以下三个参数 contextConfigLocation,locatorFactorySelector和parentContextKey,同时还有一个 org.springframework.web.context.ContextLoaderListener。
Xml代码

    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/red5-*.xml</param-value>
    </context-param>
    
    <context-param>
    <param-name>locatorFactorySelector</param-name>
    <param-value>red5.xml</param-value>
    </context-param>
    
    <context-param>
    <param-name>parentContextKey</param-name>
    <param-value>default.context</param-value>
    </context-param>
    
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/red5-*.xml</param-value>
</context-param>

<context-param>
  <param-name>locatorFactorySelector</param-name>
  <param-value>red5.xml</param-value>
</context-param>

<context-param>
  <param-name>parentContextKey</param-name>
  <param-value>default.context</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

查看这个listener的javadoc,其实这个listener会在web app(就是我们自己的项目)启动时,创建一个指向contextConfigLocation(其实就是WEB-INF\red5-web.xml)的 Bean Factory,同时为它设置parent context。这个parent context实际上是使用locatorFactorySelector找到ContextSingletonBeanFactoryLocator, 进而使用parentContextKey找到定义在这个locator里面的Bean Fanctory,由于ContextSingletonBeanFactoryLocator是singleton的,所以这个 ContextSingletonBeanFactoryLocator对象跟我们在第2步中拿到的对象是一样的,而由于 parentContextKey被设置成“default.context”,这就意味着该parent context是第5步中定义的名为“default.context”的Bean Factory。基于以上的参数,我们得到这样一个Bean Factory的链条,由上至下分别是
Java代码

    conf\red5-common.xml -> webapps\red5-default.xml -> webapps\oflaDemo\WEB-INF\red5-web.xml

conf\red5-common.xml -> webapps\red5-default.xml -> webapps\oflaDemo\WEB-INF\red5-web.xml

这就使得red5-web.xml可以使用red5-common.xml和red5-default.xml中定义的bean。
9.最后查看webapps\oflaDemo\WEB-INF\red5-web.xml,它定义了类型为 org.red5.server.WebScope的bean,初始化了它的server(指向“red5.server”),parent(指向 “global.scope”)等属性,最后调用它的register方法初始化,查看该方法源代码,发现它会把自己注册到GlobalScope上面, 至此所有的关键类的初始化完毕。
Xml代码

    <bean id=”web.scope” class=”org.red5.server.WebScope” init-method=”register”>
    <property name=”server” ref=”red5.server” />
    <property name=”parent” ref=”global.scope” />
    <property name=”context” ref=”web.context” />
    <property name=”handler” ref=”web.handler” />
    <property name=”contextPath” value=”${webapp.contextPath}” />
    <property name=”virtualHosts” value=”${webapp.virtualHosts}” />
    </bean>

<bean id="web.scope" init-method="register">
<property name="server" ref="red5.server" />
<property name="parent" ref="global.scope" />
<property name="context" ref="web.context" />
<property name="handler" ref="web.handler" />
<property name="contextPath" value="${webapp.contextPath}" />
<property name="virtualHosts" value="${webapp.virtualHosts}" />
</bean>

Spring beanFactory 的层次图
Java代码

    conf\red5-common.xml
    |– conf\red5-core.xml
    |– webapps\red5-default.xml
    |– webapps\root\WEB-INF\red5-web.xml
    |– webapps\SOSample\WEB-INF\red5-web.xml
    |– webapps\oflaDemo\WEB-INF\red5-web.xml

conf\red5-common.xml
  |-- conf\red5-core.xml
  |-- webapps\red5-default.xml
        |-- webapps\root\WEB-INF\red5-web.xml
        |-- webapps\SOSample\WEB-INF\red5-web.xml
        |-- webapps\oflaDemo\WEB-INF\red5-web.xml

看清了Red5 Standalone的启动过程,感觉为了实现自定义项目集成到Red5的核心服务上,Red5 Standalone非常依赖于spring的多个Bean Factory之间的复杂层次关系,之所以Red5能建立这样一种层次关系,是因为它能够控制jetty这样一个嵌入式的web server。问题在于,一旦Red5需要作为一个web app运行在类似Tomcat这样的独立的web server上面,那么整个过程就很不一样了,所以后很多东西都要改,我想这也是为什么Red5 0.8 RC1为什么只有安装版但还没有war版的原因。

分享到:
评论

相关推荐

    JAVA上百实例源码以及开源项目源代码

    Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...

    STM32F103单片机读写 HC05蓝牙串口模块实验(函数库版)软件例程源码.zip

    //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init(115200); //串口初始化为9600 LED_Init(); //初始化与LED连接的...

    MATLAB智能算法30个案例分析 源代码

    MATLAB智能算法的源代码%% 清空环境 clc;clear %% 障碍物数据 position = load('barrier.txt'); plot([0,200],[0,200],'.'); hold on B = load('barrier.txt'); xlabel('km','fontsize',12) ylabel('km','fontsize'...

    Python-游戏源代码(Pygame)-中国象棋(基本)

    #初始化 pygame.init() # 设置窗口大小 图片大小是460*532 , window = pygame.display.set_mode((460, 560)) # 设置窗口标题 if len(sys.argv) &gt; 1: pygame.display.set_caption('Chinese Chess black') else: ...

    华容道 小游戏 源代码

    //初始化按钮的位子 person[0].requestFocus(); left=new Button(); right=new Button(); above=new Button(); below=new Button(); left.setBounds(49,49,5,260); right.setBounds(254,49,5,260); above....

    RED HAT LINUX 6大全

    6.2 初始化进程和启动脚本 90 6.2.1 init和/etc/inittab 90 6.2.2 /etc/inittab和系统状态 92 6.3 linuxconf与管理服务 96 6.4 关闭Linux系统 98 6.4.1 shutdown 98 6.4.2 halt与reboot 99 6.5 当系统崩溃时 100 ...

    JAVA上百实例源码以及开源项目

     Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系,这些代码面向初级、中级Java程序员。 Java访问权限控制源代码 1个目标文件 摘要:Java源码,...

    C#串口介绍以及简单串口通信程序设计和实现

    2.初始化参数绑定接收数据事件 [csharp] view plain copy 在CODE上查看代码片派生到我的代码片 public void init() { btnSend.Enabled = false; cbbComList.Items.AddRange(SerialPort.GetPortNames()); if ...

    rosa

    通过运行rosa create cluster --interactive创建第一个ROSA集群从源代码构建如果您想从源代码构建此项目,请执行以下步骤:将邮局签出到您的$GOPATH go get -u github.com/openshift/rosacd到签出源目录cd $GOPATH/...

    华为编程开发规范与案例

    1、变量/指针在使用前就必须初始化 第5页 【案例1.1.1】 第5页 2、防止指针/数组操作越界 第5页 【案例1.2.1】 第5页 【案例1.2.2】 第6页 【案例1.2.3】 第7页 【案例1.2.4】 第8页 3、避免指针的非法引用 第9页 ...

    语言程序设计课后习题答案

    2-11 在一个for循环中,可以初始化多个变量吗?如何实现? 解: 在for循环设置条件的第一个";"前,用,分隔不同的赋值表达式。 例如: for (x = 0, y = 10; x ; x++, y++) 2-12 执行完下列语句后,n的值为多少? ...

    redux-orchestrate:Red一个轻量级的框架,倡导在Redux中以App.svelte的方式实现减脂

    这是通过初始化过程来实现的,该过程使商店可以装入新的状态树切片并更新reducer以在您使用它时立即处理新的操作类型。 它具有以下特点: 兼容:在不破坏任何项目的情况下运行现有项目,通过完全选择加入 可组合的...

    LiveSmashBar:一个外观优雅且易于使用的信息库,与 Android 的 LiveData 集成

    它还支持LiveData ,这对于显示重复消息是有益的,只是单个初始化。 库完全是在Kotlin 中设计和开发的。 :red_heart:传播你的 :red_heart: :样品您可以查看在 kotlin 中开发的以更好地理解概念和用法。下载这个库...

    嵌入式红绿灯控制系统

    LED 灯具大多使用低压电源,因此在这类灯具的电路设计上,LED的串联个数在1-9 颗,尤以1-3 颗为常见。串联的总△VF 应低于电源Vin。如三颗LED 串联,△VF=3.4V X 3=10.2V。在Vin&gt;12V,能正常工作。MR11、MR16射灯...

    C# for CSDN 乱七八糟的看不懂

    因 此在赋值后应该及时销毁或者初始化 L2,以免发生不可预见的错误。 比较 使用 Contains 方法。 ArrayList Array1=new ArrayList(); Array1.Add("as"); bool b1=Array1.Contains("as"); MessageBox.Show(b1....

    PHP程序开发范例宝典III

    光盘提供了书中所有实例的源代码。代码都经过精心调试,在Windows XP/Windows 2000/Windows 2003 Server/Linux/UNIX下测试通过。 PHP程序开发范例宝典 目录 第1章 PHP的运行环境配置 1 1.1 Windows...

    生产服务器部署规范.docx

    初始化系统安装包 系统 包名 用途 Suse 服务器基本系统包 系统日常工具包 c/c++编译器和工具 用于程序的开发编辑 生产服务器部署规范全文共7页,当前为第2页。生产服务器部署规范全文共7页,当前为第2页。redhat ...

    彩虹UDA软件狗工具带硬复制工具

    软件狗开发商工具可以使开发商方便地对软件狗存储区进行编辑、读取系列号以及连续初始化等操作。 工作原理: 开发商程序通过调用软件狗开发套件提供的接口模块对软件狗操作,软件狗响应该操作并通过接口模块将...

    PersonaSkin:支持角色皮肤(通过角色创建者创建)

    您需要直接从分支从源代码构建 :wrench: 安装 从发行版下载.phar 将.phar文件移动到服务器的/ plugins /文件夹中 重新启动服务器或执行/reload命令 :package: 资料下载 版本 Phar下载 更新说明 1.0.0 的GitHub ...

Global site tag (gtag.js) - Google Analytics