@bigtyphoon/melo 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +55 -0
  3. package/bin/commadtest.ts +10 -0
  4. package/bin/commands/add.ts +43 -0
  5. package/bin/commands/init.ts +292 -0
  6. package/bin/commands/kill.ts +21 -0
  7. package/bin/commands/list.ts +60 -0
  8. package/bin/commands/masterha.ts +40 -0
  9. package/bin/commands/restart.ts +48 -0
  10. package/bin/commands/start.ts +65 -0
  11. package/bin/commands/stop.ts +26 -0
  12. package/bin/melo.ts +30 -0
  13. package/bin/utils/constants.ts +27 -0
  14. package/bin/utils/utils.ts +130 -0
  15. package/dist/bin/commadtest.js +9 -0
  16. package/dist/bin/commands/add.js +40 -0
  17. package/dist/bin/commands/init.js +279 -0
  18. package/dist/bin/commands/kill.js +21 -0
  19. package/dist/bin/commands/list.js +65 -0
  20. package/dist/bin/commands/masterha.js +36 -0
  21. package/dist/bin/commands/restart.js +45 -0
  22. package/dist/bin/commands/start.js +58 -0
  23. package/dist/bin/commands/stop.js +20 -0
  24. package/dist/bin/melo.js +26 -0
  25. package/dist/bin/utils/constants.js +28 -0
  26. package/dist/bin/utils/utils.js +134 -0
  27. package/dist/lib/application.js +888 -0
  28. package/dist/lib/common/manager/appManager.js +112 -0
  29. package/dist/lib/common/manager/taskManager.js +39 -0
  30. package/dist/lib/common/remote/backend/msgRemote.js +63 -0
  31. package/dist/lib/common/remote/frontend/channelRemote.js +78 -0
  32. package/dist/lib/common/remote/frontend/sessionRemote.js +76 -0
  33. package/dist/lib/common/service/backendSessionService.js +337 -0
  34. package/dist/lib/common/service/channelService.js +514 -0
  35. package/dist/lib/common/service/connectionService.js +95 -0
  36. package/dist/lib/common/service/filterService.js +112 -0
  37. package/dist/lib/common/service/handlerService.js +187 -0
  38. package/dist/lib/common/service/sessionService.js +610 -0
  39. package/dist/lib/components/backendSession.js +14 -0
  40. package/dist/lib/components/channel.js +13 -0
  41. package/dist/lib/components/connection.js +12 -0
  42. package/dist/lib/components/connector.js +437 -0
  43. package/dist/lib/components/dictionary.js +93 -0
  44. package/dist/lib/components/master.js +39 -0
  45. package/dist/lib/components/monitor.js +25 -0
  46. package/dist/lib/components/protobuf.js +156 -0
  47. package/dist/lib/components/proxy.js +236 -0
  48. package/dist/lib/components/pushScheduler.js +62 -0
  49. package/dist/lib/components/remote.js +127 -0
  50. package/dist/lib/components/server.js +63 -0
  51. package/dist/lib/components/session.js +20 -0
  52. package/dist/lib/connectors/commands/handshake.js +119 -0
  53. package/dist/lib/connectors/commands/heartbeat.js +67 -0
  54. package/dist/lib/connectors/commands/kick.js +15 -0
  55. package/dist/lib/connectors/common/coder.js +90 -0
  56. package/dist/lib/connectors/common/handler.js +57 -0
  57. package/dist/lib/connectors/hybrid/IHybridSocket.js +3 -0
  58. package/dist/lib/connectors/hybrid/switcher.js +100 -0
  59. package/dist/lib/connectors/hybrid/tcpprocessor.js +40 -0
  60. package/dist/lib/connectors/hybrid/tcpsocket.js +171 -0
  61. package/dist/lib/connectors/hybrid/wsprocessor.js +49 -0
  62. package/dist/lib/connectors/hybridconnector.js +89 -0
  63. package/dist/lib/connectors/hybridsocket.js +139 -0
  64. package/dist/lib/connectors/mqtt/generate.js +113 -0
  65. package/dist/lib/connectors/mqtt/mqttadaptor.js +81 -0
  66. package/dist/lib/connectors/mqtt/protocol.js +48 -0
  67. package/dist/lib/connectors/mqttconnector.js +107 -0
  68. package/dist/lib/connectors/mqttsocket.js +59 -0
  69. package/dist/lib/connectors/sioconnector.js +135 -0
  70. package/dist/lib/connectors/siosocket.js +69 -0
  71. package/dist/lib/connectors/udpconnector.js +76 -0
  72. package/dist/lib/connectors/udpsocket.js +93 -0
  73. package/dist/lib/filters/handler/serial.js +44 -0
  74. package/dist/lib/filters/handler/time.js +32 -0
  75. package/dist/lib/filters/handler/timeout.js +45 -0
  76. package/dist/lib/filters/handler/toobusy.js +36 -0
  77. package/dist/lib/filters/rpc/rpcLog.js +43 -0
  78. package/dist/lib/filters/rpc/toobusy.js +41 -0
  79. package/dist/lib/index.js +81 -0
  80. package/dist/lib/interfaces/IComponent.js +3 -0
  81. package/dist/lib/interfaces/IConnector.js +3 -0
  82. package/dist/lib/interfaces/IHandlerFilter.js +3 -0
  83. package/dist/lib/interfaces/ILifeCycle.js +3 -0
  84. package/dist/lib/interfaces/IPlugin.js +3 -0
  85. package/dist/lib/interfaces/IPushScheduler.js +3 -0
  86. package/dist/lib/interfaces/ISocket.js +3 -0
  87. package/dist/lib/interfaces/IStore.js +3 -0
  88. package/dist/lib/interfaces/define.js +3 -0
  89. package/dist/lib/master/master.js +129 -0
  90. package/dist/lib/master/starter.js +236 -0
  91. package/dist/lib/master/watchdog.js +120 -0
  92. package/dist/lib/melo.js +125 -0
  93. package/dist/lib/modules/console.js +436 -0
  94. package/dist/lib/modules/masterwatcher.js +98 -0
  95. package/dist/lib/modules/monitorwatcher.js +124 -0
  96. package/dist/lib/modules/onlineUser.js +69 -0
  97. package/dist/lib/modules/restartNotifyModule.js +107 -0
  98. package/dist/lib/modules/watchServer.js +737 -0
  99. package/dist/lib/monitor/monitor.js +80 -0
  100. package/dist/lib/pushSchedulers/buffer.js +96 -0
  101. package/dist/lib/pushSchedulers/direct.js +58 -0
  102. package/dist/lib/pushSchedulers/multi.js +80 -0
  103. package/dist/lib/server/server.js +500 -0
  104. package/dist/lib/util/appUtil.js +306 -0
  105. package/dist/lib/util/constants.js +117 -0
  106. package/dist/lib/util/countDownLatch.js +51 -0
  107. package/dist/lib/util/events.js +20 -0
  108. package/dist/lib/util/handlerHelper.js +8 -0
  109. package/dist/lib/util/log.js +14 -0
  110. package/dist/lib/util/moduleUtil.js +101 -0
  111. package/dist/lib/util/pathUtil.js +134 -0
  112. package/dist/lib/util/remoterHelper.js +8 -0
  113. package/dist/lib/util/utils.js +358 -0
  114. package/dist/test/application.js +522 -0
  115. package/dist/test/config/log4js.json +28 -0
  116. package/dist/test/config/master.json +9 -0
  117. package/dist/test/config/servers.json +6 -0
  118. package/dist/test/filters/handler/serial.js +41 -0
  119. package/dist/test/filters/handler/time.js +41 -0
  120. package/dist/test/filters/handler/timeout.js +41 -0
  121. package/dist/test/filters/handler/toobusy.js +57 -0
  122. package/dist/test/filters/rpc/rpcLog.js +22 -0
  123. package/dist/test/filters/rpc/toobusy.js +39 -0
  124. package/dist/test/manager/mockChannelManager.js +77 -0
  125. package/dist/test/manager/taskManager.js +68 -0
  126. package/dist/test/mock-base/app/servers/other-file +0 -0
  127. package/dist/test/mock-plugin/components/mockPlugin.js +10 -0
  128. package/dist/test/mock-plugin/events/mockEvent.js +12 -0
  129. package/dist/test/modules/console.js +242 -0
  130. package/dist/test/pomelo.js +19 -0
  131. package/dist/test/remote/channelRemote.js +159 -0
  132. package/dist/test/service/channel.js +134 -0
  133. package/dist/test/service/channelService.js +216 -0
  134. package/dist/test/service/connectionService.js +114 -0
  135. package/dist/test/service/filterService.js +144 -0
  136. package/dist/test/service/handlerService.js +65 -0
  137. package/dist/test/service/sessionService.js +387 -0
  138. package/dist/test/util/countDownLatch.js +70 -0
  139. package/dist/test/util/pathUtil.js +108 -0
  140. package/dist/test/util/utils.js +140 -0
  141. package/lib/application.ts +1240 -0
  142. package/lib/common/manager/appManager.ts +118 -0
  143. package/lib/common/manager/taskManager.ts +50 -0
  144. package/lib/common/remote/backend/msgRemote.ts +134 -0
  145. package/lib/common/remote/frontend/channelRemote.ts +91 -0
  146. package/lib/common/remote/frontend/sessionRemote.ts +91 -0
  147. package/lib/common/service/backendSessionService.ts +388 -0
  148. package/lib/common/service/channelService.ts +609 -0
  149. package/lib/common/service/connectionService.ts +112 -0
  150. package/lib/common/service/filterService.ts +118 -0
  151. package/lib/common/service/handlerService.ts +224 -0
  152. package/lib/common/service/sessionService.ts +731 -0
  153. package/lib/components/backendSession.ts +14 -0
  154. package/lib/components/channel.ts +11 -0
  155. package/lib/components/connection.ts +13 -0
  156. package/lib/components/connector.ts +533 -0
  157. package/lib/components/dictionary.ts +121 -0
  158. package/lib/components/master.ts +41 -0
  159. package/lib/components/monitor.ts +30 -0
  160. package/lib/components/protobuf.ts +208 -0
  161. package/lib/components/proxy.ts +282 -0
  162. package/lib/components/pushScheduler.ts +70 -0
  163. package/lib/components/remote.ts +166 -0
  164. package/lib/components/server.ts +71 -0
  165. package/lib/components/session.ts +22 -0
  166. package/lib/connectors/commands/handshake.ts +155 -0
  167. package/lib/connectors/commands/heartbeat.ts +83 -0
  168. package/lib/connectors/commands/kick.ts +11 -0
  169. package/lib/connectors/common/coder.ts +93 -0
  170. package/lib/connectors/common/handler.ts +62 -0
  171. package/lib/connectors/hybrid/IHybridSocket.ts +9 -0
  172. package/lib/connectors/hybrid/switcher.ts +142 -0
  173. package/lib/connectors/hybrid/tcpprocessor.ts +43 -0
  174. package/lib/connectors/hybrid/tcpsocket.ts +223 -0
  175. package/lib/connectors/hybrid/wsprocessor.ts +57 -0
  176. package/lib/connectors/hybridconnector.ts +134 -0
  177. package/lib/connectors/hybridsocket.ts +168 -0
  178. package/lib/connectors/mqtt/generate.ts +103 -0
  179. package/lib/connectors/mqtt/mqttadaptor.ts +114 -0
  180. package/lib/connectors/mqtt/protocol.ts +49 -0
  181. package/lib/connectors/mqttconnector.ts +134 -0
  182. package/lib/connectors/mqttsocket.ts +79 -0
  183. package/lib/connectors/sioconnector.ts +161 -0
  184. package/lib/connectors/siosocket.ts +85 -0
  185. package/lib/connectors/udpconnector.ts +113 -0
  186. package/lib/connectors/udpsocket.ts +110 -0
  187. package/lib/filters/handler/serial.ts +46 -0
  188. package/lib/filters/handler/time.ts +35 -0
  189. package/lib/filters/handler/timeout.ts +50 -0
  190. package/lib/filters/handler/toobusy.ts +37 -0
  191. package/lib/filters/rpc/rpcLog.ts +42 -0
  192. package/lib/filters/rpc/toobusy.ts +41 -0
  193. package/lib/index.ts +74 -0
  194. package/lib/interfaces/IComponent.ts +47 -0
  195. package/lib/interfaces/IConnector.ts +20 -0
  196. package/lib/interfaces/IHandlerFilter.ts +15 -0
  197. package/lib/interfaces/ILifeCycle.ts +16 -0
  198. package/lib/interfaces/IPlugin.ts +65 -0
  199. package/lib/interfaces/IPushScheduler.ts +52 -0
  200. package/lib/interfaces/ISocket.ts +26 -0
  201. package/lib/interfaces/IStore.ts +10 -0
  202. package/lib/interfaces/define.ts +15 -0
  203. package/lib/master/master.ts +148 -0
  204. package/lib/master/starter.ts +234 -0
  205. package/lib/master/watchdog.ts +135 -0
  206. package/lib/melo.ts +152 -0
  207. package/lib/modules/console.ts +465 -0
  208. package/lib/modules/masterwatcher.ts +120 -0
  209. package/lib/modules/monitorwatcher.ts +151 -0
  210. package/lib/modules/onlineUser.ts +78 -0
  211. package/lib/modules/restartNotifyModule.ts +128 -0
  212. package/lib/modules/watchServer.ts +766 -0
  213. package/lib/monitor/monitor.ts +99 -0
  214. package/lib/pushSchedulers/buffer.ts +117 -0
  215. package/lib/pushSchedulers/direct.ts +65 -0
  216. package/lib/pushSchedulers/multi.ts +94 -0
  217. package/lib/server/server.ts +554 -0
  218. package/lib/util/appUtil.ts +313 -0
  219. package/lib/util/constants.ts +154 -0
  220. package/lib/util/countDownLatch.ts +72 -0
  221. package/lib/util/events.ts +15 -0
  222. package/lib/util/handlerHelper.ts +5 -0
  223. package/lib/util/log.ts +11 -0
  224. package/lib/util/moduleUtil.ts +110 -0
  225. package/lib/util/pathUtil.ts +132 -0
  226. package/lib/util/remoterHelper.ts +68 -0
  227. package/lib/util/utils.ts +365 -0
  228. package/package.json +93 -0
  229. package/template/game-server/.vscode/launch.json +27 -0
  230. package/template/game-server/app/servers/connector/handler/entryHandler.ts +50 -0
  231. package/template/game-server/app/servers/connector/remote/authRemoter.ts +36 -0
  232. package/template/game-server/app.ts +30 -0
  233. package/template/game-server/app.ts.mqtt +32 -0
  234. package/template/game-server/app.ts.sio +36 -0
  235. package/template/game-server/app.ts.sio.wss +34 -0
  236. package/template/game-server/app.ts.udp +31 -0
  237. package/template/game-server/app.ts.wss +40 -0
  238. package/template/game-server/config/adminServer.json +5 -0
  239. package/template/game-server/config/adminUser.json +22 -0
  240. package/template/game-server/config/clientProtos.json +1 -0
  241. package/template/game-server/config/dictionary.json +1 -0
  242. package/template/game-server/config/log4js.json +150 -0
  243. package/template/game-server/config/master.json +8 -0
  244. package/template/game-server/config/serverProtos.json +1 -0
  245. package/template/game-server/config/servers.json +12 -0
  246. package/template/game-server/copy.js +5 -0
  247. package/template/game-server/package.json +28 -0
  248. package/template/game-server/preload.ts +40 -0
  249. package/template/game-server/tsconfig.json +36 -0
  250. package/template/npm-install.bat +4 -0
  251. package/template/npm-install.sh +5 -0
  252. package/template/readme.md +31 -0
  253. package/template/shared/server.crt +15 -0
  254. package/template/shared/server.key +15 -0
  255. package/template/web-server/app.js +25 -0
  256. package/template/web-server/app.js.https +36 -0
  257. package/template/web-server/bin/component.bat +1 -0
  258. package/template/web-server/bin/component.sh +1 -0
  259. package/template/web-server/package.json +10 -0
  260. package/template/web-server/public/css/base.css +76 -0
  261. package/template/web-server/public/image/logo.png +0 -0
  262. package/template/web-server/public/image/sp.png +0 -0
  263. package/template/web-server/public/index.html +57 -0
  264. package/template/web-server/public/index.html.sio +58 -0
  265. package/template/web-server/public/js/lib/build/build.js +1730 -0
  266. package/template/web-server/public/js/lib/build/build.js.wss +1721 -0
  267. package/template/web-server/public/js/lib/component.json +6 -0
  268. package/template/web-server/public/js/lib/local/boot/component.json +11 -0
  269. package/template/web-server/public/js/lib/local/boot/index.js +11 -0
  270. package/template/web-server/public/js/lib/meloclient.js +456 -0
  271. package/template/web-server/public/js/lib/meloclient.js.wss +456 -0
  272. package/template/web-server/public/js/lib/socket.io.js +3 -0
  273. package/test/application.ts +607 -0
  274. package/test/filters/handler/serial.ts +47 -0
  275. package/test/filters/handler/time.ts +47 -0
  276. package/test/filters/handler/timeout.ts +46 -0
  277. package/test/filters/handler/toobusy.ts +59 -0
  278. package/test/filters/rpc/rpcLog.ts +20 -0
  279. package/test/filters/rpc/toobusy.ts +40 -0
  280. package/test/manager/mockChannelManager.ts +92 -0
  281. package/test/manager/taskManager.ts +78 -0
  282. package/test/mock-base/app/servers/other-file +0 -0
  283. package/test/mock-plugin/components/mockPlugin.ts +8 -0
  284. package/test/mock-plugin/events/mockEvent.ts +12 -0
  285. package/test/modules/console.ts +264 -0
  286. package/test/pomelo.ts +18 -0
  287. package/test/remote/channelRemote.ts +176 -0
  288. package/test/service/channel.ts +161 -0
  289. package/test/service/channelService.ts +243 -0
  290. package/test/service/connectionService.ts +142 -0
  291. package/test/service/filterService.ts +165 -0
  292. package/test/service/handlerService.ts +77 -0
  293. package/test/service/sessionService.ts +464 -0
  294. package/test/util/countDownLatch.ts +81 -0
  295. package/test/util/pathUtil.ts +122 -0
  296. package/test/util/utils.ts +165 -0
@@ -0,0 +1,609 @@
1
+ import * as countDownLatch from '../../util/countDownLatch';
2
+ import * as utils from '../../util/utils';
3
+ import { ChannelRemote } from '../remote/frontend/channelRemote';
4
+ import { getLogger } from '@bigtyphoon/melo-logger';
5
+ import { Application } from '../../application';
6
+ import { IComponent } from '../../interfaces/IComponent';
7
+ import { IStore } from '../../interfaces/IStore';
8
+ import { IHandlerFilter } from '../../interfaces/IHandlerFilter';
9
+ import { FRONTENDID, UID, SID } from '../../util/constants';
10
+ import * as path from 'path';
11
+
12
+ let logger = getLogger('melo', path.basename(__filename));
13
+
14
+ /**
15
+ * constant
16
+ */
17
+ let ST_INITED = 0;
18
+ let ST_DESTROYED = 1;
19
+
20
+ export interface ChannelServiceOptions {
21
+ prefix?: string;
22
+ store?: IStore;
23
+ broadcastFilter?: IHandlerFilter;
24
+ }
25
+
26
+ /**
27
+ * Create and maintain channels for server local.
28
+ *
29
+ * ChannelService is created by channel component which is a default loaded
30
+ * component of melo and channel service would be accessed by `app.get('channelService')`.
31
+ *
32
+ * @class
33
+ * @constructor
34
+ */
35
+ export class ChannelService implements IComponent {
36
+ app: Application;
37
+ channels: { [key: string]: Channel };
38
+ prefix: string;
39
+ store: IStore;
40
+ broadcastFilter: any;
41
+ channelRemote: ChannelRemote;
42
+ name: string;
43
+
44
+ constructor(app: Application, opts ?: ChannelServiceOptions) {
45
+ opts = opts || {};
46
+ this.app = app;
47
+ this.channels = {};
48
+ this.prefix = opts.prefix;
49
+ this.store = opts.store;
50
+ this.broadcastFilter = opts.broadcastFilter;
51
+ this.channelRemote = new ChannelRemote(app);
52
+ }
53
+
54
+
55
+ start(cb: (err?: Error) => void) {
56
+ restoreChannel(this, cb);
57
+ }
58
+
59
+
60
+ /**
61
+ * Create channel with name.
62
+ *
63
+ * @param {String} name channel's name
64
+ * @memberOf ChannelService
65
+ */
66
+ createChannel(name: string) {
67
+ if (this.channels[name]) {
68
+ return this.channels[name];
69
+ }
70
+
71
+ let c = new Channel(name, this);
72
+ addToStore(this, genKey(this), genKey(this, name));
73
+ this.channels[name] = c;
74
+ return c;
75
+ }
76
+
77
+ /**
78
+ * Get channel by name.
79
+ *
80
+ * @param {String} name channel's name
81
+ * @param {Boolean} create if true, create channel
82
+ * @return {Channel}
83
+ * @memberOf ChannelService
84
+ */
85
+ getChannel(name: string, create ?: boolean) {
86
+ let channel = this.channels[name];
87
+ if (!channel && !!create) {
88
+ channel = this.channels[name] = new Channel(name, this);
89
+ addToStore(this, genKey(this), genKey(this, name));
90
+ }
91
+ return channel;
92
+ }
93
+
94
+ /**
95
+ * Destroy channel by name.
96
+ *
97
+ * @param {String} name channel name
98
+ * @memberOf ChannelService
99
+ */
100
+ destroyChannel(name: string) {
101
+ delete this.channels[name];
102
+ removeFromStore(this, genKey(this), genKey(this, name));
103
+ removeAllFromStore(this, genKey(this, name));
104
+ }
105
+
106
+ /**
107
+ * Push message by uids.
108
+ * Group the uids by group. ignore any uid if sid not specified.
109
+ *
110
+ * @param {String} route message route
111
+ * @param {Object} msg message that would be sent to client
112
+ * @param {Array} uids the receiver info list, [{uid: userId, sid: frontendServerId}]
113
+ * @param {Object} opts user-defined push options, optional
114
+ * @param {Function} cb cb(err)
115
+ * @memberOf ChannelService
116
+ */
117
+ pushMessageByUids(route: string, msg: any, uids: { uid: string, sid: string }[], cb?: (err?: Error, result?: void) => void): void;
118
+ pushMessageByUids(route: string, msg: any, uids: { uid: string, sid: string }[], opts: any, cb?: (err?: Error, result?: void) => void): void;
119
+ pushMessageByUids(route: string, msg: any, uids: { uid: string, sid: string }[], opts?: any, cb?: (err?: Error, result?: void) => void) {
120
+ if (typeof route !== 'string') {
121
+ cb = opts;
122
+ opts = uids;
123
+ uids = msg;
124
+ msg = route;
125
+ route = msg.route;
126
+ }
127
+
128
+ if (!cb && typeof opts === 'function') {
129
+ cb = opts;
130
+ opts = {};
131
+ }
132
+
133
+ if (!uids || uids.length === 0) {
134
+ utils.invokeCallback(cb, new Error('uids should not be empty'));
135
+ return;
136
+ }
137
+ let groups = {}, record;
138
+ for (let i = 0, l = uids.length; i < l; i++) {
139
+ record = uids[i];
140
+ add(record.uid, record.sid, groups);
141
+ }
142
+
143
+ sendMessageByGroup(this, route, msg, groups, opts, cb);
144
+ }
145
+
146
+ /**
147
+ * Broadcast message to all the connected clients.
148
+ *
149
+ * @param {String} stype frontend server type string
150
+ * @param {String} route route string
151
+ * @param {Object} msg message
152
+ * @param {Object} opts user-defined broadcast options, optional
153
+ * opts.binded: push to binded sessions or all the sessions
154
+ * opts.filterParam: parameters for broadcast filter.
155
+ * @param {Function} cb callback
156
+ * @memberOf ChannelService
157
+ */
158
+ broadcast(stype: string, route: string, msg: any, cb?: (err?: Error, result?: void) => void): void;
159
+ broadcast(stype: string, route: string, msg: any, opts: any, cb?: (err?: Error, result?: void) => void): void;
160
+ broadcast(stype: string, route: string, msg: any, opts?: any, cb?: (err?: Error, result?: void) => void) {
161
+ let app = this.app;
162
+ let namespace = 'sys';
163
+ let service = 'channelRemote';
164
+ let method = 'broadcast';
165
+ let servers = app.getServersByType(stype);
166
+
167
+ if (!servers || servers.length === 0) {
168
+ // server list is empty
169
+ utils.invokeCallback(cb);
170
+ return;
171
+ }
172
+ if (!cb && typeof opts === 'function') {
173
+ cb = opts;
174
+ opts = undefined;
175
+ }
176
+ let count = servers.length;
177
+ let successFlag = false;
178
+
179
+ let latch = countDownLatch.createCountDownLatch(count, function () {
180
+ if (!successFlag) {
181
+ utils.invokeCallback(cb, new Error('broadcast fails'));
182
+ return;
183
+ }
184
+ utils.invokeCallback(cb, null);
185
+ });
186
+
187
+ let genCB = function (serverId ?: string) {
188
+ return function (err: Error) {
189
+ if (err) {
190
+ logger.error('[broadcast] fail to push message to serverId: ' + serverId + ', err:' + err.stack);
191
+ latch.done();
192
+ return;
193
+ }
194
+ successFlag = true;
195
+ latch.done();
196
+ };
197
+ };
198
+
199
+ opts = { type: 'broadcast', userOptions: opts || {} };
200
+
201
+ // for compatiblity
202
+ opts.isBroadcast = true;
203
+ if (opts.userOptions) {
204
+ opts.binded = opts.userOptions.binded;
205
+ opts.filterParam = opts.userOptions.filterParam;
206
+ }
207
+
208
+ let self = this;
209
+ let sendMessage = function (serverId: string) {
210
+ return (function () {
211
+ if (serverId === app.serverId) {
212
+ (self.channelRemote as any)[method](route, msg, opts).then(() => genCB(serverId)(null)).catch((err: any) => genCB(serverId)(err));
213
+ } else {
214
+ app.rpcInvoke(serverId, {
215
+ namespace: namespace, service: service,
216
+ method: method, args: [route, msg, opts]
217
+ }, genCB(serverId));
218
+ }
219
+ }());
220
+ };
221
+
222
+ for (let i = 0, l = count; i < l; i++) {
223
+ sendMessage(servers[i].id);
224
+ }
225
+ }
226
+
227
+ apushMessageByUids: (route: string, msg: any, uids: { uid: string, sid: string }[], opts?: Object) => Promise<void> = utils.promisify(this.pushMessageByUids);
228
+ abroadcast: (stype: string, route: string, msg: any, opts?: any) => Promise<void> = utils.promisify(this.broadcast);
229
+ }
230
+
231
+ /**
232
+ * Channel maintains the receiver collection for a subject. You can
233
+ * add users into a channel and then broadcast message to them by channel.
234
+ *
235
+ * @class channel
236
+ * @constructor
237
+ */
238
+ export class Channel {
239
+ name: string;
240
+ groups: { [sid: string]: string[] };
241
+ records: { [key: string]: { sid: string, uid: string } };
242
+ __channelService__: ChannelService;
243
+ state: number;
244
+ userAmount: number;
245
+
246
+ constructor(name: string, service: ChannelService) {
247
+ this.name = name;
248
+ this.groups = {}; // group map for uids. key: sid, value: [uid]
249
+ this.records = {}; // member records. key: uid
250
+ this.__channelService__ = service;
251
+ this.state = ST_INITED;
252
+ this.userAmount = 0;
253
+ }
254
+
255
+ /**
256
+ * Add user to channel.
257
+ *
258
+ * @param {Number} uid user id
259
+ * @param {String} sid frontend server id which user has connected to
260
+ */
261
+ add(uid: string, sid: string) {
262
+ if (this.state > ST_INITED) {
263
+ return false;
264
+ } else {
265
+ let res = add(uid, sid, this.groups);
266
+ if (res) {
267
+ this.records[uid] = { sid: sid, uid: uid };
268
+ this.userAmount = this.userAmount + 1;
269
+ addToStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));
270
+ }
271
+ return res;
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Remove user from channel.
277
+ *
278
+ * @param {Number} uid user id
279
+ * @param {String} sid frontend server id which user has connected to.
280
+ * @return [Boolean] true if success or false if fail
281
+ */
282
+ leave(uid: UID, sid: FRONTENDID) {
283
+ if (!uid || !sid) {
284
+ return false;
285
+ }
286
+ let res = deleteFrom(uid, sid, this.groups[sid]);
287
+ if (res) {
288
+ delete this.records[uid];
289
+ this.userAmount = this.userAmount - 1;
290
+ removeFromStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));
291
+ }
292
+ if (this.userAmount < 0) this.userAmount = 0; // robust
293
+ if (this.groups[sid] && this.groups[sid].length === 0) {
294
+ delete this.groups[sid];
295
+ }
296
+ return res;
297
+ }
298
+
299
+ /**
300
+ * Get channel UserAmount in a channel.
301
+ *
302
+ * @return {number } channel member amount
303
+ */
304
+ getUserAmount() {
305
+ return this.userAmount;
306
+ }
307
+
308
+ /**
309
+ * Get channel members.
310
+ *
311
+ * <b>Notice:</b> Heavy operation.
312
+ *
313
+ * @return {Array} channel member uid list
314
+ */
315
+ getMembers() {
316
+ let res = [], groups = this.groups;
317
+ let group, i, l;
318
+ for (let sid in groups) {
319
+ group = groups[sid];
320
+ for (i = 0, l = group.length; i < l; i++) {
321
+ res.push(group[i]);
322
+ }
323
+ }
324
+ return res;
325
+ }
326
+
327
+ /**
328
+ * Get Member info.
329
+ *
330
+ * @param {String} uid user id
331
+ * @return {Object} member info
332
+ */
333
+ getMember(uid: UID) {
334
+ return this.records[uid];
335
+ }
336
+
337
+ /**
338
+ * Remove member by uid
339
+ * @param uid member to removed
340
+ */
341
+ removeMember(uid: UID) {
342
+ let member = this.getMember(uid);
343
+ if (member)
344
+ return this.leave(member.uid, member.sid);
345
+ else
346
+ return false;
347
+ }
348
+
349
+ /**
350
+ * Destroy channel.
351
+ */
352
+ destroy() {
353
+ this.state = ST_DESTROYED;
354
+ this.__channelService__.destroyChannel(this.name);
355
+ }
356
+
357
+ /**
358
+ * Push message to all the members in the channel
359
+ *
360
+ * @param {String} route message route
361
+ * @param {Object} msg message that would be sent to client
362
+ * @param {Object} opts user-defined push options, optional
363
+ * @param {Function} cb callback function
364
+ */
365
+ pushMessage(route: string, msg: any, opts ?: any, cb ?: (err: Error | null, result ?: void) => void) {
366
+ if (this.state !== ST_INITED) {
367
+ utils.invokeCallback(cb, new Error('channel is not running now'));
368
+ return;
369
+ }
370
+
371
+ if (typeof route !== 'string') {
372
+ cb = opts;
373
+ opts = msg;
374
+ msg = route;
375
+ route = msg.route;
376
+ }
377
+
378
+ if (!cb && typeof opts === 'function') {
379
+ cb = opts;
380
+ opts = {};
381
+ }
382
+
383
+ sendMessageByGroup(this.__channelService__, route, msg, this.groups, opts, cb);
384
+ }
385
+
386
+ apushMessage: (route: string, msg: any, opts ?: any) => Promise<void> = utils.promisify(this.pushMessage);
387
+ }
388
+
389
+ /**
390
+ * add uid and sid into group. ignore any uid that uid not specified.
391
+ *
392
+ * @param uid user id
393
+ * @param sid server id
394
+ * @param groups {Object} grouped uids, , key: sid, value: [uid]
395
+ */
396
+ let add = function (uid: UID, sid: FRONTENDID, groups: { [sid: string]: UID[] }) {
397
+ if (!sid) {
398
+ logger.warn('ignore uid %j for sid not specified.', uid);
399
+ return false;
400
+ }
401
+
402
+ let group = groups[sid];
403
+ if (!group) {
404
+ group = [];
405
+ groups[sid] = group;
406
+ }
407
+
408
+ group.push(uid);
409
+ return true;
410
+ };
411
+
412
+ /**
413
+ * delete element from array
414
+ */
415
+ let deleteFrom = function (uid: UID, sid: FRONTENDID, group: UID[]) {
416
+ if (!uid || !sid || !group) {
417
+ return false;
418
+ }
419
+
420
+ for (let i = 0, l = group.length; i < l; i++) {
421
+ if (group[i] === uid) {
422
+ group.splice(i, 1);
423
+ return true;
424
+ }
425
+ }
426
+
427
+ return false;
428
+ };
429
+
430
+ /**
431
+ * push message by group
432
+ *
433
+ * @param route {String} route route message
434
+ * @param msg {Object} message that would be sent to client
435
+ * @param groups {Object} grouped uids, , key: sid, value: [uid]
436
+ * @param opts {Object} push options
437
+ * @param cb {Function} cb(err)
438
+ *
439
+ * @api private
440
+ */
441
+ let sendMessageByGroup = function (channelService: ChannelService, route: string, msg: any, groups: { [sid: string]: UID[] }, opts: any, cb: Function) {
442
+ let app = channelService.app;
443
+ let namespace = 'sys';
444
+ let service = 'channelRemote';
445
+ let method = 'pushMessage';
446
+ let count = Object.keys(groups).length;
447
+ let successFlag = false;
448
+ let failIds: SID[] = [];
449
+
450
+ logger.debug('[%s] channelService sendMessageByGroup route: %s, msg: %j, groups: %j, opts: %j', app.serverId, route, msg, groups, opts);
451
+ if (count === 0) {
452
+ // group is empty
453
+ utils.invokeCallback(cb);
454
+ return;
455
+ }
456
+
457
+ let latch = countDownLatch.createCountDownLatch(count, function () {
458
+ if (!successFlag) {
459
+ utils.invokeCallback(cb, new Error('all uids push message fail'));
460
+ return;
461
+ }
462
+ utils.invokeCallback(cb, null, failIds);
463
+ });
464
+
465
+ let rpcCB = function (serverId: string) {
466
+ return function (err: Error, fails: SID[]) {
467
+ if (err) {
468
+ logger.error('[pushMessage] fail to dispatch msg to serverId: ' + serverId + ', err:' + err.stack);
469
+ latch.done();
470
+ return;
471
+ }
472
+ if (fails) {
473
+ failIds = failIds.concat(fails);
474
+ }
475
+ successFlag = true;
476
+ latch.done();
477
+ };
478
+ };
479
+
480
+ opts = { type: 'push', userOptions: opts || {} };
481
+ // for compatiblity
482
+ opts.isPush = true;
483
+
484
+ let sendMessage = function (sid: FRONTENDID) {
485
+ return (function () {
486
+ if (sid === app.serverId) {
487
+ (channelService.channelRemote as any)[method](route, msg, groups[sid], opts).then((fails: SID[]) => {
488
+ rpcCB(sid)(null, fails);
489
+ }, (err: Error) => {
490
+ rpcCB(sid)(err, null);
491
+ });
492
+ } else {
493
+ app.rpcInvoke(sid, {
494
+ namespace: namespace, service: service,
495
+ method: method, args: [route, msg, groups[sid], opts]
496
+ }, rpcCB(sid));
497
+ }
498
+ })();
499
+ };
500
+
501
+ let group;
502
+ for (let sid in groups) {
503
+ group = groups[sid];
504
+ if (group && group.length > 0) {
505
+ sendMessage(sid);
506
+ } else {
507
+ // empty group
508
+ process.nextTick(rpcCB(sid));
509
+ }
510
+ }
511
+ };
512
+
513
+ let restoreChannel = function (self: ChannelService, cb: Function) {
514
+ if (!self.store) {
515
+ utils.invokeCallback(cb);
516
+ return;
517
+ } else {
518
+ loadAllFromStore(self, genKey(self), function (err: Error, list) {
519
+ if (!!err) {
520
+ utils.invokeCallback(cb, err);
521
+ return;
522
+ } else {
523
+ if (!list.length || !Array.isArray(list)) {
524
+ utils.invokeCallback(cb);
525
+ return;
526
+ }
527
+ let load = function (key: string, name: string) {
528
+ return (function () {
529
+ let channelName = name;
530
+ loadAllFromStore(self, key, function (err, items) {
531
+ for (let j = 0; j < items.length; j++) {
532
+ let array = items[j].split(':');
533
+ let sid = array[0];
534
+ let uid = array[1];
535
+ let channel = self.channels[channelName];
536
+ let res = add(uid, sid, channel.groups);
537
+ if (res) {
538
+ channel.records[uid] = { sid: sid, uid: uid };
539
+ }
540
+ }
541
+ });
542
+ })();
543
+ };
544
+
545
+ for (let i = 0; i < list.length; i++) {
546
+ let name = list[i].slice(genKey(self).length + 1);
547
+ self.channels[name] = new Channel(name, self);
548
+ load(list[i], name);
549
+ }
550
+ utils.invokeCallback(cb);
551
+ }
552
+ });
553
+ }
554
+ };
555
+
556
+ let addToStore = function (self: ChannelService, key: string, value: string) {
557
+ if (!!self.store) {
558
+ self.store.add(key, value, function (err) {
559
+ if (!!err) {
560
+ logger.error('add key: %s value: %s to store, with err: %j', key, value, err);
561
+ }
562
+ });
563
+ }
564
+ };
565
+
566
+ let removeFromStore = function (self: ChannelService, key: string, value: string) {
567
+ if (!!self.store) {
568
+ self.store.remove(key, value, function (err) {
569
+ if (!!err) {
570
+ logger.error('remove key: %s value: %s from store, with err: %j', key, value, err);
571
+ }
572
+ });
573
+ }
574
+ };
575
+
576
+ let loadAllFromStore = function (self: ChannelService, key: string, cb: (err: Error, list: string[]) => void) {
577
+ if (!!self.store) {
578
+ self.store.load(key, function (err, list) {
579
+ if (!!err) {
580
+ logger.error('load key: %s from store, with err: %j', key, err);
581
+ utils.invokeCallback(cb, err);
582
+ } else {
583
+ utils.invokeCallback(cb, null, list);
584
+ }
585
+ });
586
+ }
587
+ };
588
+
589
+ let removeAllFromStore = function (self: ChannelService, key: string) {
590
+ if (!!self.store) {
591
+ self.store.removeAll(key, function (err) {
592
+ if (!!err) {
593
+ logger.error('remove key: %s all members from store, with err: %j', key, err);
594
+ }
595
+ });
596
+ }
597
+ };
598
+
599
+ let genKey = function (self: ChannelService, name ?: string) {
600
+ if (!!name) {
601
+ return self.prefix + ':' + self.app.serverId + ':' + name;
602
+ } else {
603
+ return self.prefix + ':' + self.app.serverId;
604
+ }
605
+ };
606
+
607
+ let genValue = function (sid: FRONTENDID, uid: UID) {
608
+ return sid + ':' + uid;
609
+ };
@@ -0,0 +1,112 @@
1
+ import {Application} from '../../application';
2
+ import {UID} from '../../util/constants';
3
+
4
+ export interface UserLoginRecord {
5
+ loginTime: number;
6
+ uid: UID;
7
+ address: string;
8
+ }
9
+
10
+ /**
11
+ * connection statistics service
12
+ * record connection, login count and list
13
+ */
14
+
15
+ export class ConnectionService {
16
+ serverId: string;
17
+ connCount = 0;
18
+ loginedCount = 0;
19
+ logined: { [uid: string]: UserLoginRecord } = {};
20
+
21
+
22
+ constructor(app: Application) {
23
+ this.serverId = app.getServerId();
24
+ }
25
+
26
+
27
+ /**
28
+ * Add logined user.
29
+ *
30
+ * @param uid {String} user id
31
+ * @param info {Object} record for logined user
32
+ */
33
+ addLoginedUser(uid: UID, info: UserLoginRecord) {
34
+ if (!this.logined[uid]) {
35
+ this.loginedCount++;
36
+ }
37
+ info.uid = uid;
38
+ this.logined[uid] = info;
39
+ }
40
+
41
+ /**
42
+ * Update user info.
43
+ * @param uid {String} user id
44
+ * @param info {Object} info for update.
45
+ */
46
+ updateUserInfo(uid: UID, info: UserLoginRecord) {
47
+ let user = this.logined[uid];
48
+ if (!user) {
49
+ return;
50
+ }
51
+
52
+ for (let p in info) {
53
+ if (info.hasOwnProperty(p) && typeof (info as any)[p] !== 'function') {
54
+ (user as any)[p] = (info as any)[p];
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Increase connection count
61
+ */
62
+ increaseConnectionCount() {
63
+ this.connCount++;
64
+ }
65
+
66
+ /**
67
+ * Remote logined user
68
+ *
69
+ * @param uid {String} user id
70
+ */
71
+ removeLoginedUser(uid: UID) {
72
+ if (!!this.logined[uid]) {
73
+ this.loginedCount--;
74
+ }
75
+ this.logined[uid] = undefined;
76
+ }
77
+
78
+ /**
79
+ * Decrease connection count
80
+ *
81
+ * @param uid {String} uid
82
+ */
83
+ decreaseConnectionCount(uid: UID) {
84
+ if (this.connCount) {
85
+ this.connCount--;
86
+ }
87
+ if (!!uid) {
88
+ this.removeLoginedUser(uid);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Get statistics info
94
+ *
95
+ * @return {Object} statistics info
96
+ */
97
+ getStatisticsInfo() {
98
+ let list = [];
99
+ for (let uid in this.logined) {
100
+ if (this.logined[uid]) {
101
+ list.push(this.logined[uid]);
102
+ }
103
+ }
104
+
105
+ return {
106
+ serverId: this.serverId,
107
+ totalConnCount: this.connCount,
108
+ loginedCount: this.loginedCount,
109
+ loginedList: list
110
+ };
111
+ }
112
+ }