轉自:http://semi-sleep.javaeye.com/blog/348768
Red5如何響應rmpt的請求,中間涉及哪些關鍵類?
1.Red5在啟動時會調(diào)用RTMPMinaTransport的start()方法,該方法會開啟rmtp的socket監(jiān)聽端口(默認是1935),然后使用mina(apache的io操作類庫)的api將RTMPMinaIoHandler綁定到該端口。
2.RTMPMinaIoHandler上定義了messageReceived、messageSent、sessionOpened和 sessionClosed等方法,當有socket請求時,相應的方法會被調(diào)用,這時RTMPMinaIoHandler會使用當前的socket連接 來創(chuàng)建一個RTMPMinaConnection(或者使用一個之前創(chuàng)建好的RTMPMinaConnection),并將其作為參數(shù)傳遞給定義于 RTMPHandler類上的相應的messageReceived、messageSent、connectionOpened和 connectionClosed方法。
3.RTMPHandler會調(diào)用Server類的lookupGlobal獲得當前的GlobalScope,然后再利用GlobalScope 找到當前socket請求應該使用的WebScope(這個WebScope就是我們在自己的項目的WEB-INF"red5-web.xml中定義的 啦)。最后,RTMPHandler會調(diào)用RTMPMinaConnection的connect方法連接到相應的WebScope。
4.至此,控制流進入了我們自己項目中了,通常來說,WebScope又會將請求轉移給ApplicationAdapter,由它來最終響應請求,而我們的項目通過重載ApplicationAdapter的方法來實現(xiàn)自己的邏輯。
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的啟動過程(也就是我們執(zhí)行red5.bat),關于Red5如何在tomcat中啟動,目前仍在研究中。
Red5啟動過程如下:
1.編輯red5.bat,找到關鍵的一行:
-Djava.security.manager
-Djava.security.policy=conf/red5.policy
-cp red5.jar;conf;bin org.red5.server.Standalone
可以看到它是調(diào)用org.red5.server.Standalone作為程序啟動的入口,這也是為什么使用eclipse在debug模式下啟動 Standalone就可以調(diào)試Red5代碼。需要注意的是,如果你要調(diào)試Red5,記得除了源代碼(src)之外,把conf和webapps兩個文件 夾都拷入項目中,并把conf加入classpath。
2.觀察Standalone的main方法,你會看到它使用spring的ContextSingletonBeanFactoryLocator來載 入classpath下面的red5.xml,注意ContextSingletonBeanFactoryLocator還會在下面的步驟中被使用,由 于它是singleton的,所以保證了我們自己的項目中定義的bean可以引用red5.xml中定義的bean,這個下面會有介紹。
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層次中的根節(jié)點,所以在red5-common.xml中定義的bean可以被其他地方所 引用。
<constructor-arg><list><value>classpath:/red5-common.xml</value></list></constructor-arg>
</bean>
這里我們主要留意red5-common.xml中定義的類型為org.red5.server.Server的“red5.server”,它會在接下來很多地方被用到。
4.回到red5.xml,接著定義指向classpath:/red5-core.xml的名字為“red5.core”的BeanFactory,注意“red5.core”是以“red5.common”為parent context。
<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初始化時調(diào)用,正如上面提到的,該方法會做開啟1935端口,綁定 RTMPMinaIoHandler到該端口等等的操作。對于RTMPHandler,注意它的server屬性通過“red5.server”引用了定 義在parent context(red5-common.xml)上面的Server,通過它RTMPHandler能夠找到GlobalScope,進而找到 WebScope。
<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,并在初始化后調(diào)用它的init方法。
<property name="parentContext" ref="red5.common" />
<property name="contextsConfig" value="red5.globals" />
</bean>
查看該方法的源代碼,可以看到它會讀取在classPath下面的red5.globals文件,對于每一行初始化一個以“red5.common”為 parent context的BeanFactory,具體來說,現(xiàn)在red5.globals中只有一行 default.context=${red5.root}/webapps/red5-default.xml,那么會創(chuàng)建一個名字為 “default.context”的指向webapps/red5-default.xml的Bean Factory,它以“red5.common”為parent context。
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,發(fā)現(xiàn)它主要是定義了GlobalScope的bean,然后把它注冊到“red5.server”上。
<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.繼續(xù)看red5.xml,最后定義類型為org.red5.server.jetty.JettyLoader的bean,并且在初始化后調(diào)用它的init方法,查看該方法源代碼,很明顯它是初始化并且啟動jetty這個web server。
<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,發(fā)現(xiàn)有以下三個參數(shù) contextConfigLocation,locatorFactorySelector和parentContextKey,同時還有一個 org.springframework.web.context.ContextLoaderListener。
<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(就是我們自己的項目)啟動時,創(chuàng)建一個指向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。基于以上的參數(shù),我們得到這樣一個Bean Factory的鏈條,由上至下分別是
這就使得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”)等屬性,最后調(diào)用它的register方法初始化,查看該方法源代碼,發(fā)現(xiàn)它會把自己注冊到GlobalScope上面, 至此所有的關鍵類的初始化完畢。
<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 的層次圖
|-- 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的啟動過程,感覺為了實現(xiàn)自定義項目集成到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為什么老不行。。。