/plugins/cluster/src/actionscript/org/flowplayer/cluster/RTMPCluster.as

https://github.com/longlho/flowplayer
ActionScript | 270 lines | 211 code | 40 blank | 19 comment | 34 complexity | f3150c7ab6dde825565bc2b830a1bcf0 MD5 | raw file
  1. /*
  2. * This file is part of Flowplayer, http://flowplayer.org
  3. *
  4. * By: Daniel Rossi, <electroteque@gmail.com>, Anssi Piirainen Flowplayer Oy
  5. * Copyright (c) 2008-2011 Flowplayer Oy
  6. *
  7. * Released under the MIT License:
  8. * http://www.opensource.org/licenses/mit-license.php
  9. */
  10. package org.flowplayer.cluster
  11. {
  12. import flash.events.TimerEvent;
  13. import flash.net.SharedObject;
  14. import flash.utils.Timer;
  15. import mx.utils.URLUtil;
  16. import org.flowplayer.flow_internal;
  17. import org.flowplayer.model.ClipEvent;
  18. import org.flowplayer.model.PluginEventDispatcher;
  19. import org.flowplayer.model.PluginEventType;
  20. import org.flowplayer.model.PluginModel;
  21. import org.flowplayer.model.PluginModelImpl;
  22. import org.flowplayer.util.Log;
  23. use namespace flow_internal;
  24. public class RTMPCluster {
  25. protected var _hosts:Array;
  26. protected var _timer:Timer;
  27. protected var _hostIndex:int = 0;
  28. protected var _hostCount:int = 0;
  29. protected var _connectCount:int = 0;
  30. protected var _reConnectCount:int = 0;
  31. protected var _connectTimeout:int = 2000;
  32. protected var _loadBalanceServers:Boolean = false;
  33. protected var _liveHosts:Array;
  34. protected var _liveRandomServers:Array = [];
  35. private var _startAfterConnect:Boolean;
  36. protected var _failureExpiry:int = 0;
  37. protected var _reconnectFailureExpiry:int = 0;
  38. private var _config:*;
  39. private var _dispatcher:PluginModelImpl;
  40. private var log:Log = new Log(this);
  41. private var _reconnectListener:Function;
  42. private var _failureListener:Function;
  43. private var _currentHost:Object;
  44. public function RTMPCluster(config:*)
  45. {
  46. _config = config;
  47. // there can be several hosts, or we are just using one single netConnectionUrl
  48. initHosts(_config.hosts, config.netConnectionUrl);
  49. _connectCount = config.connectCount;
  50. _connectTimeout = config.connectTimeout;
  51. _failureExpiry = config.failureExpiry;
  52. _currentHost = _hosts ? _hosts[0] : null;
  53. }
  54. private function initHosts(hosts:Array, fallback:String):void {
  55. log.debug("initHosts()");
  56. var myHosts:Array = [];
  57. if (! hosts || hosts.length == 0) {
  58. if (! fallback) {
  59. throw new Error("A hosts array or a netConnectionUrl must be configured");
  60. }
  61. myHosts.push({ 'host': fallback});
  62. } else {
  63. for (var i:int = 0; i < hosts.length; i++) {
  64. myHosts.push(hosts[i] is String ? { 'host': hosts[i] } : hosts[i]);
  65. }
  66. }
  67. _hosts = myHosts;
  68. _liveHosts = _hosts;
  69. log.debug("initHosts(), we have " + _liveHosts.length + " live hosts initially");
  70. }
  71. public function onReconnected(listener:Function):void {
  72. _reconnectListener = listener;
  73. }
  74. public function onFailed(listener:Function):void {
  75. _failureListener = listener;
  76. }
  77. public function get currentHosts():Array
  78. {
  79. return _hosts.filter(_checkLiveHost);
  80. }
  81. public function get hosts():Array
  82. {
  83. return _hosts;
  84. }
  85. public function get nextHost():String
  86. {
  87. if (hasMultipleHosts())
  88. {
  89. _liveHosts = currentHosts;
  90. if (_liveHosts.length == 0) {
  91. log.error("no live hosts available");
  92. if (_failureListener != null) {
  93. _failureListener();
  94. return null;
  95. }
  96. }
  97. if (_config.loadBalance)
  98. {
  99. _hostIndex = getRandomIndex();
  100. log.debug("Load balanced index " + _hostIndex);
  101. }
  102. if (_liveHosts.length > _hostIndex) {
  103. log.debug("cluster has multiple hosts");
  104. _currentHost = _liveHosts[_hostIndex];
  105. return _currentHost["host"];
  106. }
  107. }
  108. log.error("no hosts available");
  109. return null;
  110. }
  111. public function start():void
  112. {
  113. if (_timer && _timer.running) {
  114. _timer.stop();
  115. }
  116. _timer = new Timer(_connectTimeout, _liveHosts.length);
  117. _timer.addEventListener(TimerEvent.TIMER , tryFallBack);
  118. if (hasMultipleHosts()) {
  119. log.debug("starting connection timeout timer, with a delay of " + _connectTimeout);
  120. _timer.start();
  121. }
  122. }
  123. public function stop():void
  124. {
  125. if (_timer != null && _timer.running) _timer.stop();
  126. }
  127. public function hasMultipleHosts():Boolean
  128. {
  129. return _liveHosts.length > 0;
  130. }
  131. public function getRandomIndex():uint {
  132. return Math.round(Math.random() * (_liveHosts.length - 1));
  133. }
  134. public function get liveServers():Array
  135. {
  136. return _liveHosts;
  137. }
  138. private function _checkLiveHost(element:*, index:int, arr:Array):Boolean
  139. {
  140. return _isLiveServer(element);
  141. }
  142. private function _getFailedServerSO(host:String):SharedObject
  143. {
  144. var domain:String = URLUtil.getServerName(host);
  145. return SharedObject.getLocal(domain,"/");
  146. }
  147. public function setFailedServer(host:String):void
  148. {
  149. log.debug("Setting Failed Server: " + host);
  150. var server:SharedObject = _getFailedServerSO(host);
  151. server.data.failureTimestamp = new Date();
  152. }
  153. public function set connectCount(count:Number):void
  154. {
  155. _connectCount = count;
  156. }
  157. internal function hasMoreHosts():Boolean
  158. {
  159. if (_failureExpiry == 0)
  160. _hostIndex++
  161. else
  162. _hostIndex = 0;
  163. _liveHosts = currentHosts;
  164. if (_hostIndex >= _liveHosts.length)
  165. {
  166. //
  167. _reConnectCount++;
  168. if (_reConnectCount < _connectCount)
  169. {
  170. log.debug("Restarting Connection Attempts");
  171. _hostIndex = 0;
  172. //#427 when reconnecting to the max reconnect count, clear the failure expiry and reset the live hosts to enable to try again ?
  173. _reconnectFailureExpiry = 0;
  174. _liveHosts = currentHosts;
  175. }
  176. } else {
  177. //#427 set the normal failure expiry during retries.
  178. _reconnectFailureExpiry = _failureExpiry;
  179. }
  180. log.debug("Host Index: " + _hostIndex + " LiveServers: " + _liveHosts.length);
  181. return (_hostIndex <= _liveHosts.length && _liveHosts[_hostIndex]);
  182. }
  183. private function _isLiveServer(element:*):Boolean
  184. {
  185. var host:String = element.host;
  186. var server:SharedObject = _getFailedServerSO(host);
  187. // Server is failed, determine if the failure expiry interval has been reached and clear it
  188. if (server.data.failureTimestamp)
  189. {
  190. var date:Date = new Date();
  191. // Determine the failure offset
  192. var offset:int = date.getTime() - server.data.failureTimestamp.getTime();
  193. log.debug("Failed Server Remaining Expiry: " + offset + " Start Time: " + server.data.failureTimestamp.getTime() + " Current Time: " + date.getTime());
  194. // Failure offset has reached the failureExpiry setting, clear it from the list to allow a connection
  195. //#427 failure expiry was not being reset to honour connect retry.
  196. if (offset >= _reconnectFailureExpiry && _reConnectCount < _connectCount)
  197. {
  198. log.debug("Clearing Failure Period " + _config.failureExpiry);
  199. server.clear();
  200. return true;
  201. }
  202. return false;
  203. }
  204. return true;
  205. }
  206. protected function tryFallBack(e:TimerEvent):void
  207. {
  208. // Check if there is more hosts to attempt reconnection to
  209. if (hasMoreHosts())
  210. {
  211. log.debug("invoking reconnect listener");
  212. if (_reconnectListener != null) {
  213. _reconnectListener();
  214. }
  215. } else {
  216. // we have reached the end of the hosts list stop reconnection attempts and send a failed event
  217. stop();
  218. if (_failureListener != null) {
  219. _failureListener();
  220. }
  221. }
  222. }
  223. public function get currentHost():String {
  224. return _currentHost["host"];
  225. }
  226. public function get currentHostIndex():int {
  227. return _hosts.indexOf(_currentHost);
  228. }
  229. }
  230. }