Red5源代码分析 - 关键类及其初始化过程

原文地址: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的方法来实现自己的逻辑。

简单的流程图:

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

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

Red5启动过程如下:

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

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

3.查看red5.xml,这个文件首先定义了指向classpath:/red5-common.xml的名字为“red5.common”的BeanFactory,注意它会是整个BeanFactory层次中的根节点,所以在red5-common.xml中定义的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。

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

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

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

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

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

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。

查看这个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的链条,由上至下分别是

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

Spring beanFactory 的层次图

看清了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为什么老不行。。。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
很高兴为您解答这个问题。以下是Python小游戏之300行代码实现俄罗斯方块的源代码: ```python import pygame import random # 初始化游戏 pygame.init() # 颜色 WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) # 屏幕大小 SCREEN_WIDTH = 600 SCREEN_HEIGHT = 800 # 方块大小 BLOCK_SIZE = 30 # 方块型及其形状 BLOCKS = { "I": [(0, 0), (1, 0), (2, 0), (3, 0)], "J": [(0, 0), (0, 1), (1, 1), (2, 1)], "L": [(2, 0), (0, 1), (1, 1), (2, 1)], "O": [(0, 0), (1, 0), (0, 1), (1, 1)], "S": [(1, 0), (2, 0), (0, 1), (1, 1)], "T": [(1, 0), (0, 1), (1, 1), (2, 1)], "Z": [(0, 0), (1, 0), (1, 1), (2, 1)] } # 方向 DIRECTIONS = { "LEFT": (-1, 0), "RIGHT": (1, 0), "DOWN": (0, 1), "UP": (0, -1) } # 游戏状态 STATE_RUNNING = 0 STATE_PAUSED = 1 STATE_GAME_OVER = 2 # 字体 FONT = pygame.font.SysFont(None, 50) class Block: def __init__(self, x, y, block_type): self.x = x self.y = y self.block_type = block_type self.shape = BLOCKS[block_type] self.color = self.get_color() def get_color(self): if self.block_type == "I": return BLUE elif self.block_type == "J": return RED elif self.block_type == "L": return GREEN elif self.block_type == "O": return BLUE elif self.block_type == "S": return RED elif self.block_type == "T": return GREEN elif self.block_type == "Z": return BLUE def move(self, direction): dx, dy = DIRECTIONS[direction] self.x += dx self.y += dy def rotate(self): new_shape = [] for x, y in self.shape: new_x = y new_y = -x new_shape.append((new_x, new_y)) self.shape = new_shape class Board: def __init__(self): self.width = SCREEN_WIDTH // BLOCK_SIZE self.height = SCREEN_HEIGHT // BLOCK_SIZE self.grid = [[BLACK for _ in range(self.width)] for _ in range(self.height)] self.current_block = self.generate_new_block() self.next_block = self.generate_new_block() self.score = 0 self.state = STATE_RUNNING def generate_new_block(self): block_type = random.choice(list(BLOCKS.keys())) x = self.width // 2 - 2 y = 0 return Block(x, y, block_type) def update(self): if self.state == STATE_RUNNING: if self.is_valid(self.current_block, 0, 1): self.current_block.move("DOWN") else: self.lock_block() self.clear_lines() self.current_block = self.next_block self.next_block = self.generate_new_block() if not self.is_valid(self.current_block, 0, 0): self.state = STATE_GAME_OVER def lock_block(self): for x, y in self.current_block.shape: self.grid[self.current_block.y + y][self.current_block.x + x] = self.current_block.color def clear_lines(self): lines_cleared = 0 for y in range(self.height): if all([self.grid[y][x] != BLACK for x in range(self.width)]): for y1 in range(y, 0, -1): for x in range(self.width): self.grid[y1][x] = self.grid[y1 - 1][x] for x in range(self.width): self.grid[0][x] = BLACK lines_cleared += 1 self.score += lines_cleared ** 2 def move_current_block(self, direction): if self.is_valid(self.current_block, *DIRECTIONS[direction]): self.current_block.move(direction) def rotate_current_block(self): if self.is_valid(self.current_block, 0, 0, True): self.current_block.rotate() def is_valid(self, block, dx, dy, rotated=False): if rotated: new_shape = [] for x, y in block.shape: new_x = y new_y = -x new_shape.append((new_x, new_y)) else: new_shape = block.shape for x, y in new_shape: new_x = block.x + x + dx new_y = block.y + y + dy if not (0 <= new_x < self.width and 0 <= new_y < self.height): return False if self.grid[new_y][new_x] != BLACK: return False return True def draw(self, screen): for y in range(self.height): for x in range(self.width): pygame.draw.rect(screen, self.grid[y][x], (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0) for x, y in self.current_block.shape: pygame.draw.rect(screen, self.current_block.color, ((self.current_block.x + x) * BLOCK_SIZE, (self.current_block.y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0) text = FONT.render(f"Score: {self.score}", True, WHITE) screen.blit(text, (10, 10)) text = FONT.render("Next Block:", True, WHITE) screen.blit(text, (400, 10)) for x, y in self.next_block.shape: pygame.draw.rect(screen, self.next_block.color, ((x + 16) * BLOCK_SIZE, (y + 3) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0) def main(): # 创建屏幕 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Tetris") # 创建时钟 clock = pygame.time.Clock() # 创建棋盘 board = Board() # 游戏循环 while True: # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: board.move_current_block("LEFT") elif event.key == pygame.K_RIGHT: board.move_current_block("RIGHT") elif event.key == pygame.K_DOWN: board.move_current_block("DOWN") elif event.key == pygame.K_UP: board.rotate_current_block() elif event.key == pygame.K_SPACE: board.state = STATE_PAUSED if board.state == STATE_RUNNING else STATE_RUNNING # 更新游戏状态 board.update() # 绘制屏幕 screen.fill(BLACK) board.draw(screen) # 显示屏幕 pygame.display.update() # 控制帧率 clock.tick(10) if __name__ == "__main__": main() ``` 希望能够帮到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值