Red5源码分析(转载 semi-sleep)

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代码

   1. RTMPMinaIoHandler  
   2.   |--[delegate method call and pass RTMPMinaConnection to]-->RTMPHandler 
   3.      |--[call lookupGlobal method]-->Server 
   4.      |--[use globalScope to lookup webScope]-->GlobalScope 
   5.      |--[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代码

   1. C:\Program Files\Java\jre1.5.0_15\bin\java"  
   2.   -Djava.security.manager  
   3.   -Djava.security.policy=conf/red5.policy  
   4.   -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代码

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

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代码

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

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

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

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

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



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

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

<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>

查看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代码

   1. <!-- RTMP Handler --> 
   2. <bean id="rtmpHandler" 
   3.     class="org.red5.server.net.rtmp.RTMPHandler"> 
   4.     <property name="server" ref="red5.server" /> 
   5.     <property name="statusObjectService" ref="statusObjectService" /> 
   6. </bean> 
   7.  
   8. <!-- RTMP Mina IO Handler --> 
   9. <bean id="rtmpMinaIoHandler" 
  10.     class="org.red5.server.net.rtmp.RTMPMinaIoHandler"> 
  11.     <property name="handler" ref="rtmpHandler" /> 
  12.     <property name="codecFactory" ref="rtmpCodecFactory" /> 
  13.     <property name="rtmpConnManager" ref="rtmpMinaConnManager" /> 
  14. </bean> 
  15.  
  16. <!-- RTMP Mina Transport --> 
  17. <bean id="rtmpTransport" class="org.red5.server.net.rtmp.RTMPMinaTransport" init-method="start" destroy-method="stop"> 
  18.     <property name="ioHandler" ref="rtmpMinaIoHandler" /> 
  19.     <property name="address" value="${rtmp.host}" /> 
  20.     <property name="port" value="${rtmp.port}" /> 
  21.     <property name="receiveBufferSize" value="${rtmp.receive_buffer_size}" /> 
  22.     <property name="sendBufferSize" value="${rtmp.send_buffer_size}" /> 
  23.     <property name="eventThreadsCore" value="${rtmp.event_threads_core}" /> 
  24.     <property name="eventThreadsMax" value="${rtmp.event_threads_max}" /> 
  25.     <property name="eventThreadsQueue" value="${rtmp.event_threads_queue}" /> 
  26.     <property name="eventThreadsKeepalive" value="${rtmp.event_threads_keepalive}" /> 
  27.     <!-- This is the interval at which the sessions are polled for stats. If mina monitoring is not  enabled, polling will not occur. --> 
  28.     <property name="jmxPollInterval" value="1000" /> 
  29.     <property name="tcpNoDelay" value="${rtmp.tcp_nodelay}" /> 
  30. </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" 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>



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

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

<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>

查看该方法的源代码,可以看到它会读取在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代码

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

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代码

   1. <bean id="global.scope" class="org.red5.server.GlobalScope" init-method="register"> 
   2.     <property name="server" ref="red5.server" /> 
   3.     <property name="name" value="default" /> 
   4.     <property name="context" ref="global.context" /> 
   5.     <property name="handler" ref="global.handler" /> 
   6.     <property name="persistenceClass"> 
   7.         <value>org.red5.server.persistence.FilePersistence</value> 
   8.     </property> 
   9. </bean> 

<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>



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

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

<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>



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代码

   1. <context-param> 
   2.   <param-name>contextConfigLocation</param-name> 
   3.   <param-value>/WEB-INF/red5-*.xml</param-value> 
   4. </context-param> 
   5.  
   6. <context-param> 
   7.   <param-name>locatorFactorySelector</param-name> 
   8.   <param-value>red5.xml</param-value> 
   9. </context-param> 
  10.  
  11. <context-param> 
  12.   <param-name>parentContextKey</param-name> 
  13.   <param-value>default.context</param-value> 
  14. </context-param> 
  15.      
  16. <listener> 
  17.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
  18. </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代码

   1. 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代码

   1. <bean id="web.scope" class="org.red5.server.WebScope" init-method="register"> 
   2.     <property name="server" ref="red5.server" /> 
   3.     <property name="parent" ref="global.scope" /> 
   4.     <property name="context" ref="web.context" /> 
   5.     <property name="handler" ref="web.handler" /> 
   6.     <property name="contextPath" value="${webapp.contextPath}" /> 
   7.     <property name="virtualHosts" value="${webapp.virtualHosts}" /> 
   8. </bean> 

<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>


Spring beanFactory 的层次图
Java代码

   1. conf\red5-common.xml 
   2.   |-- conf\red5-core.xml 
   3.   |-- webapps\red5-default.xml 
   4.         |-- webapps\root\WEB-INF\red5-web.xml 
   5.         |-- webapps\SOSample\WEB-INF\red5-web.xml 
   6.         |-- 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

Node-RED 是一个基于 Node.js 构建的开源流程编排工具,它具有简单易用的特点,可以帮助用户快速搭建、管理和部署流程应用。Node-RED 的源码分析是指对其代码库进行深入剖析,以便了解其内部结构和工作原理。 首先,Node-RED 的源码主要由 JavaScript 编写,大部分代码都是围绕 Node.js 运行时环境展开。其核心功能是基于事件驱动的管道式数据处理,通过流程图的方式将各种节点(Node)串联起来,形成一个数据处理流程。因此,在源码分析中,需要重点关注事件驱动机制、节点的定义与扩展、消息传递机制等方面的实现细节。 其次,Node-RED 采用了 Express 框架来搭建 Web 服务,并通过 WebSocket 实现了实时通信。在源码分析中,需要深入了解其 Web 服务的实现方式,以及与客户端的交互方式和消息传递机制。此外,还需要对其对外部插件和节点的支持机制进行分析,以便了解其扩展性和定制化能力。 最后,在源码分析中还需要深入了解 Node-RED 的核心模块以及各种节点的实现方式,比如文件操作、网络请求、数据库操作等,以及其对于不同数据格式的处理能力。同时,还需要关注其错误处理、安全机制、性能优化等方面的实现方式。 总的来说,Node-RED 的源码分析是一个复杂而全面的工作,需要对 JavaScript 和 Node.js 相关技术有深入的了解,以便更好地理解其内部结构和工作原理,为进一步的定制和扩展工作提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值