11
11
const Registry = require ( './registry' ) ;
12
12
const { Grouper } = require ( './util' ) ;
13
13
const { aggregators } = require ( './metricAggregators' ) ;
14
+
15
+ let parentPort , MessageChannel , isMainThread , Worker ;
16
+ try {
17
+ /* eslint-disable node/no-unsupported-features/node-builtins */
18
+ const worker_threads = require ( 'worker_threads' ) ;
19
+
20
+ parentPort = worker_threads . parentPort ;
21
+ MessageChannel = worker_threads . MessageChannel ;
22
+ isMainThread = worker_threads . isMainThread ;
23
+ Worker = worker_threads . Worker ;
24
+ } catch {
25
+ // node version is too old
26
+ }
27
+
14
28
// We need to lazy-load the 'cluster' module as some application servers -
15
29
// namely Passenger - crash when it is imported.
16
30
let cluster = ( ) => {
@@ -25,6 +39,7 @@ const GET_METRICS_RES = 'prom-client:getMetricsRes';
25
39
let registries = [ Registry . globalRegistry ] ;
26
40
let requestCtr = 0 ; // Concurrency control
27
41
let listenersAdded = false ;
42
+ const workersQueue = [ ] ;
28
43
const requests = new Map ( ) ; // Pending requests for workers' local metrics.
29
44
30
45
class AggregatorRegistry extends Registry {
@@ -33,6 +48,14 @@ class AggregatorRegistry extends Registry {
33
48
addListeners ( ) ;
34
49
}
35
50
51
+ attachWorkers ( workers = [ ] ) {
52
+ for ( const worker in workers ) {
53
+ if ( worker instanceof Worker ) {
54
+ workersQueue . push ( worker ) ;
55
+ }
56
+ }
57
+ }
58
+
36
59
/**
37
60
* Gets aggregated metrics for all workers. The optional callback and
38
61
* returned Promise resolve with the same value; either may be used.
@@ -76,6 +99,9 @@ class AggregatorRegistry extends Registry {
76
99
}
77
100
}
78
101
102
+ getWorkerThreadsMetrics ( message ) ;
103
+ request . pending += workersQueue . length || 0 ;
104
+
79
105
if ( request . pending === 0 ) {
80
106
// No workers were up
81
107
clearTimeout ( request . errorTimeout ) ;
@@ -145,6 +171,31 @@ class AggregatorRegistry extends Registry {
145
171
}
146
172
}
147
173
174
+ function handleWorkerResponse ( worker , message ) {
175
+ if ( message . type === GET_METRICS_RES ) {
176
+ const request = requests . get ( message . requestId ) ;
177
+ request . pending += message . workerRequests || 0 ;
178
+
179
+ if ( message . error ) {
180
+ request . done ( new Error ( message . error ) ) ;
181
+ return ;
182
+ }
183
+
184
+ message . metrics . forEach ( registry => request . responses . push ( registry ) ) ;
185
+ request . pending -- ;
186
+
187
+ if ( request . pending === 0 ) {
188
+ // finalize
189
+ requests . delete ( message . requestId ) ;
190
+ clearTimeout ( request . errorTimeout ) ;
191
+
192
+ const registry = AggregatorRegistry . aggregate ( request . responses ) ;
193
+ const promString = registry . metrics ( ) ;
194
+ request . done ( null , promString ) ;
195
+ }
196
+ }
197
+ }
198
+
148
199
/**
149
200
* Adds event listeners for cluster aggregation. Idempotent (safe to call more
150
201
* than once).
@@ -155,42 +206,53 @@ function addListeners() {
155
206
listenersAdded = true ;
156
207
157
208
if ( cluster ( ) . isMaster ) {
158
- // Listen for worker responses to requests for local metrics
159
- cluster ( ) . on ( 'message' , ( worker , message ) => {
160
- if ( message . type === GET_METRICS_RES ) {
161
- const request = requests . get ( message . requestId ) ;
162
-
163
- if ( message . error ) {
164
- request . done ( new Error ( message . error ) ) ;
165
- return ;
166
- }
209
+ // Listen for cluster responses to requests for local metrics
210
+ cluster ( ) . on ( 'message' , handleWorkerResponse ) ;
211
+ }
212
+ }
167
213
168
- message . metrics . forEach ( registry => request . responses . push ( registry ) ) ;
169
- request . pending -- ;
214
+ function getWorkerThreadsMetrics ( message ) {
215
+ workersQueue . forEach ( worker => {
216
+ if ( worker && worker instanceof Worker ) {
217
+ const metricsChannel = new MessageChannel ( ) ;
170
218
171
- if ( request . pending === 0 ) {
172
- // finalize
173
- requests . delete ( message . requestId ) ;
174
- clearTimeout ( request . errorTimeout ) ;
219
+ worker . postMessage (
220
+ {
221
+ ...message ,
222
+ port : metricsChannel . port1 ,
223
+ } ,
224
+ [ metricsChannel . port1 ] ,
225
+ ) ;
175
226
176
- const registry = AggregatorRegistry . aggregate ( request . responses ) ;
177
- const promString = registry . metrics ( ) ;
178
- request . done ( null , promString ) ;
227
+ metricsChannel . port2 . on ( 'message' , response => {
228
+ if ( response . type === GET_METRICS_RES ) {
229
+ if ( cluster ( ) . isWorker ) {
230
+ process . send ( response ) ;
231
+ }
232
+
233
+ if ( cluster ( ) . isMaster ) {
234
+ handleWorkerResponse ( worker , response ) ;
235
+ }
236
+
237
+ metricsChannel . port2 . close ( ) ;
179
238
}
180
- }
181
- } ) ;
182
- }
239
+ } ) ;
240
+ }
241
+ } ) ;
183
242
}
184
243
185
244
// Respond to master's requests for worker's local metrics.
186
245
process . on ( 'message' , message => {
187
246
if ( cluster ( ) . isWorker && message . type === GET_METRICS_REQ ) {
247
+ getWorkerThreadsMetrics ( message ) ;
248
+
188
249
Promise . all ( registries . map ( r => r . getMetricsAsJSON ( ) ) )
189
250
. then ( metrics => {
190
251
process . send ( {
191
252
type : GET_METRICS_RES ,
192
253
requestId : message . requestId ,
193
254
metrics,
255
+ workerRequests : workersQueue . length ,
194
256
} ) ;
195
257
} )
196
258
. catch ( error => {
@@ -203,4 +265,27 @@ process.on('message', message => {
203
265
}
204
266
} ) ;
205
267
268
+ // Respond to master's request for worker_threads worker local metrics
269
+ if ( ! isMainThread ) {
270
+ parentPort . on ( 'message' , ( { type, requestId, port } = { } ) => {
271
+ if ( type === GET_METRICS_REQ ) {
272
+ Promise . all ( registries . map ( r => r . getMetricsAsJSON ( ) ) )
273
+ . then ( metrics => {
274
+ port . postMessage ( {
275
+ type : GET_METRICS_RES ,
276
+ requestId,
277
+ metrics,
278
+ } ) ;
279
+ } )
280
+ . catch ( error => {
281
+ port . postMessage ( {
282
+ type : GET_METRICS_RES ,
283
+ requestId,
284
+ error : error . message ,
285
+ } ) ;
286
+ } ) ;
287
+ }
288
+ } ) ;
289
+ }
290
+
206
291
module . exports = AggregatorRegistry ;
0 commit comments