原文地址:http://semi-sleep.javaeye.com/blog/348768

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的方法来实现自己的逻辑。

简单的流程图:

  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

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

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

Red5启动过程如下:

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

  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

可以看到它是调用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,这个下面会有介绍。

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

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

  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>

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

  1. <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。

  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>

查看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。

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

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

  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>

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

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

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

  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>

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

  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>

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。

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>/WEB-INF/red5-*.xml</param-value>
  4. </context-param>
  5. <context-param>
  6. <param-name>locatorFactorySelector</param-name>
  7. <param-value>red5.xml</param-value>
  8. </context-param>
  9. <context-param>
  10. <param-name>parentContextKey</param-name>
  11. <param-value>default.context</param-value>
  12. </context-param>
  13. <listener>
  14. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  15. </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的链条,由上至下分别是

  1. 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上面,至此所有的关键类的初始化完毕。

  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>

Spring beanFactory 的层次图

  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

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

最后,如果哪位成功在Tomcat上配置过Red5 0.7的war版本,还请告诉我一声,我试了0.6的war可以,不知道0.7为什么老不行。。。

Red5源代码分析 - 关键类及其初始化过程的更多相关文章

  1. 【深入理解Java虚拟机】类的初始化过程

    类的初始化过程 类的加载过程.png 加载 将 Class 文件以二进制的形式加载到内存中 验证 校验 Class 文件是否安全,是否被正确的修改等 准备 为类变量申请内存,设置默认值,(初始化变量的 ...

  2. 分析java类的初始化契机

    分析java类的静态成员变量初始化先于非静态成员变量   依上图中当class字节码文件被jvm虚拟机加载到内存中依次经过 连接 验证:对字节码进行验证 准备:给静态变量分配内存并赋予变量类型各自的默 ...

  3. Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源代码分析(三) 向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比. 对于匹配的设备,PCI模 ...

  4. jvm - 类的初始化过程

    我们知道,我们写的java代码称为源码,想要能够被jvm执行首先需要编译成.class文件,那么编译完到使用又都经理的哪些阶段呢?主要分为以下三个阶段: 加载:查找并加载类的二进制数据(.class文 ...

  5. Java类的初始化过程及清理

    一.类的数据成员初始化 Java中类的数据成员初试化可能有两种形式. 在定义类成员变量的地方直接提供初始化值(这是C++中不允许的) 在构造器中初试化.(Java中不存在类似C++中的初始化列表) 两 ...

  6. java类的初始化过程

    1 先初始化父类的静态成员和静态块,然后初始化子类的静态成员和静态块,然后再初始化父类,然后再初始化子类. 2 先初始化父类 3 单个类初始化的顺序 先初始化成员变量和代码块,后调用构造函数 4 如果 ...

  7. 类的初始化过程(难点)--------java基础总结

    前言:看到这么好的东西,忍不住又写到了博客上面 Student s = new Student();在内存中究竟做了哪些事情呢? ①加载student.class文件进内存. ②为栈内存s开辟空间. ...

  8. caffe源代码分析--Blob类代码研究

    作者:linger 转自须注明转自:http://blog.csdn.net/lingerlanlan/article/details/24379689 数据成员 shared_ptr<Sync ...

  9. 深入理解Java对象的创建过程:类的初始化与实例化

    摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...

随机推荐

  1. php获取apk包信息的方法

    /*解析安卓apk包中的压缩XML文件,还原和读取XML内容 依赖功能:需要PHP的ZIP包函数支持.*/ include('./Apkparser.php'); $appObj = new Apkp ...

  2. IOS基础框架

    GameKit 为游戏提供网络功能:点对点互联和游戏中的语音交流 AddressBook 提供访问用户联系人信息的功能 AddressBookUI 提供一个用户界面,用于显示存储在地址簿中的联系人信息 ...

  3. windows和mac下分别配置虚拟主机

    windows下配置 1.找到apache的配置文件,httpd.conf 2.找到 LoadModule rewrite_module modules/mod_rewrite.so 去掉前边的# 3 ...

  4. hdu 4973 A simple simulation problem. (线段树)

    题目链接 题意: 给定n长的序列 m个操作 序列默认为 1, 2, 3···n 操作1:D [l,r] 把[l,r]区间增长 :( 1,2,3,4 进行 D [1,3]变成 1,1,2,2,3,3,4 ...

  5. SQL全文搜索

    ( select dd.*,t.RANK from crm_CustomerAnalyzeDetails dd ) as t on dd.ID = t.[key] ) union all ( sele ...

  6. iOS开发:应用生命周期

    iOS应用通过委托对象AppDelegate类在应用周期的不同阶段会回调不同的方法,应用周期分为以下五种状态: Not Running(非运行状态).应用没有运行或被系统终止.   Inactive ...

  7. TCP/IP详解学习笔记(8)-DNS域名系统

    前面已经提到了访问一台机器要靠IP地址和MAC地址,其中,MAC地址可以通过ARP协议得到,所以这对用户是透明的,但是IP地址就不行,无论如何用户都需要用一个指定的IP来访问一台计算机,而IP地址又非 ...

  8. IOS 使用CoreText实现表情文本URL等混合显示控件

    实现了一个富文本视图控件.主要针对表情图片,文本字符,URL,等这种类型的文本进行显示. 源码地址 https://github.com/TinyQ/TQRichTextView 实现的效果如下图. ...

  9. [转] TreeList 当前节点图标和背景色设置

    高原之上原文TreeList 选中节点时图标状态和背景色 // 给TreeList加SelectImage this.treelArea.SelectImageList = imglCustom; / ...

  10. java jvm学习笔记十一(访问控制器)

     欢迎装载请说明出处: http://blog.csdn.net/yfqnihao/article/details/8271665 这一节,我们要学习的是访问控制器,在阅读本节之前,如果没有前面几节的 ...