@@ -6,6 +6,7 @@ const is = require('is-type-of');
6
6
const Base = require ( 'sdk-base' ) ;
7
7
const utils = require ( './utils' ) ;
8
8
const random = require ( 'utility' ) . random ;
9
+ const ClusterServer = require ( './server' ) ;
9
10
const Connection = require ( './connection' ) ;
10
11
const Request = require ( './protocol/request' ) ;
11
12
@@ -43,18 +44,12 @@ class Leader extends Base {
43
44
this . _realClient . ready ( ( ) => this . ready ( true ) ) ;
44
45
}
45
46
47
+ this . _handleClose = this . _handleClose . bind ( this ) ;
48
+ this . _handleConnection = this . _handleConnection . bind ( this ) ;
49
+
46
50
// subscribe its own channel
47
- this . _server . on ( `${ this . _options . name } _connection` , socket => this . _handleConnection ( socket ) ) ;
48
- this . _server . once ( 'close' , ( ) => {
49
- this . logger . info ( '[Loader:%s] leader server is closed' , this . _options . name ) ;
50
- // close the real client
51
- if ( this . _realClient && is . function ( this . _realClient . close ) ) {
52
- this . _realClient . close ( ) ;
53
- }
54
- clearInterval ( this . _heartbeatTimer ) ;
55
- this . _heartbeatTimer = null ;
56
- this . emit ( 'close' ) ;
57
- } ) ;
51
+ this . _server . on ( `${ this . _options . name } _connection` , this . _handleConnection ) ;
52
+ this . _server . once ( 'close' , this . _handleClose ) ;
58
53
59
54
this . _heartbeatTimer = setInterval ( ( ) => {
60
55
const now = Date . now ( ) ;
@@ -80,6 +75,7 @@ class Leader extends Base {
80
75
subscribe ( reg , listener ) {
81
76
const transcode = this . _transcode ;
82
77
const conn = Object . create ( Base . prototype , {
78
+ isMock : { value : true } ,
83
79
key : { value : `mock_conn_${ Date . now ( ) } ` } ,
84
80
lastActiveTime : {
85
81
get ( ) {
@@ -302,6 +298,55 @@ class Leader extends Base {
302
298
_errorHandler ( err ) {
303
299
setImmediate ( ( ) => this . emit ( 'error' , err ) ) ;
304
300
}
301
+
302
+ * _handleClose ( ) {
303
+ this . logger . info ( '[Loader:%s] leader server is closed' , this . _options . name ) ;
304
+ // close the real client
305
+ if ( this . _realClient && is . function ( this . _realClient . close ) ) {
306
+ // support common function, generatorFunction, and function returning a promise
307
+ yield utils . callFn ( this . _realClient . close . bind ( this . _realClient ) ) ;
308
+ }
309
+ clearInterval ( this . _heartbeatTimer ) ;
310
+ this . _heartbeatTimer = null ;
311
+ this . emit ( 'close' ) ;
312
+ }
313
+
314
+ close ( ) {
315
+ return co ( function * ( ) {
316
+ // 1. stop listening to server channel
317
+ this . _server . removeListener ( `${ this . _options . name } _connection` , this . _handleConnection ) ;
318
+
319
+ // 2. close all mock connections
320
+ for ( const [ key , conn ] of this . _connections . entries ( ) ) {
321
+ if ( conn . isMock ) this . _connections . delete ( key ) ;
322
+ }
323
+
324
+ // 3. wait all followers close
325
+ yield new Promise ( ( resolve , reject ) => {
326
+ if ( this . _connections . size === 0 ) return resolve ( ) ;
327
+
328
+ for ( const conn of this . _connections . values ( ) ) {
329
+ conn . once ( 'close' , ( ) => {
330
+ if ( this . _connections . size === 0 ) return resolve ( ) ;
331
+ } ) ;
332
+ }
333
+
334
+ setTimeout ( ( ) => {
335
+ reject ( new Error ( `[Leader#${ this . _options . name } ] close failed: follower connections are still not closed after 30s` ) ) ;
336
+ } , 30000 ) ;
337
+ } ) ;
338
+
339
+ // 4. close server
340
+ // CANNOT close server directly by server.close(), other cluster clients may be using it
341
+ this . _server . removeListener ( 'close' , this . _handleClose ) ;
342
+ yield ClusterServer . close ( this . _options . name , this . _server ) ;
343
+
344
+ // 5. close real client
345
+ yield this . _handleClose ( ) ;
346
+
347
+ this . removeAllListeners ( ) ;
348
+ } . bind ( this ) ) ;
349
+ }
305
350
}
306
351
307
352
module . exports = Leader ;
0 commit comments