设为首页 收藏本站
查看: 1668|回复: 0

[经验分享] Red5源代码分析

[复制链接]

尚未签到

发表于 2017-3-2 11:13:39 | 显示全部楼层 |阅读模式
  原文地址: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的方法来实现自己的逻辑。
  简单的流程图:



[java] view plain copy

  • 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] view plain copy

  • 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] view plain copy

  • 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可以被其他地方所引用。



[xhtml] view plain copy

  • <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”,它会在接下来很多地方被用到。



[xhtml] view plain copy

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



[xhtml] view plain copy

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



[xhtml] view plain copy

  • <!-- 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方法。



[xhtml] view plain copy

  • <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] view plain copy

  • 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”上。



[xhtml] view plain copy

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



[xhtml] view plain copy

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



[xhtml] view plain copy

  • <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] view plain copy

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



[xhtml] view plain copy

  • <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] view plain copy

  • 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版的原因。
  最后,如果哪位成功在Tomcat上配置过Red5 0.7的war版本,还请告诉我一声,我试了0.6的war可以,不知道0.7为什么老不行。。。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-349242-1-1.html 上篇帖子: (转)Spring MVC中处理静态资源的多种方法 下篇帖子: 【原】Spark中Master源码分析(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表