Commit | Line | Data |
---|---|---|
a449b585 JB |
1 | import type { Server } from 'node:http' |
2 | import type { AddressInfo } from 'node:net' | |
ded253e2 JB |
3 | |
4 | import express, { type Express, type Request, type Response } from 'express' | |
d0ed34c9 | 5 | import { |
ded253e2 | 6 | availableParallelism, |
d0ed34c9 | 7 | ClusterWorker, |
3a502712 | 8 | DynamicThreadPool, |
d0ed34c9 | 9 | } from 'poolifier' |
ded253e2 | 10 | |
80115618 JB |
11 | import type { |
12 | ClusterWorkerData, | |
13 | ClusterWorkerResponse, | |
14 | DataPayload, | |
15 | ThreadWorkerData, | |
3a502712 | 16 | ThreadWorkerResponse, |
d0ed34c9 JB |
17 | } from './types.js' |
18 | ||
19 | const emptyFunction = (): void => { | |
8ea47589 | 20 | /* Intentional */ |
d0ed34c9 JB |
21 | } |
22 | ||
23 | class ExpressWorker extends ClusterWorker< | |
3a502712 JB |
24 | ClusterWorkerData, |
25 | ClusterWorkerResponse | |
d0ed34c9 JB |
26 | > { |
27 | private static server: Server | |
28 | private static requestHandlerPool: DynamicThreadPool< | |
3a502712 JB |
29 | ThreadWorkerData<DataPayload>, |
30 | ThreadWorkerResponse<DataPayload> | |
d0ed34c9 JB |
31 | > |
32 | ||
33 | private static readonly startExpress = ( | |
34 | workerData?: ClusterWorkerData | |
35 | ): ClusterWorkerResponse => { | |
7d9756e3 | 36 | const { port, workerFile, minWorkers, maxWorkers, ...poolOptions } = |
3a502712 | 37 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
67f3f2d6 | 38 | workerData! |
d0ed34c9 JB |
39 | |
40 | ExpressWorker.requestHandlerPool = new DynamicThreadPool< | |
3a502712 JB |
41 | ThreadWorkerData<DataPayload>, |
42 | ThreadWorkerResponse<DataPayload> | |
d0ed34c9 JB |
43 | >( |
44 | minWorkers ?? 1, | |
45 | maxWorkers ?? availableParallelism(), | |
46 | workerFile, | |
7d9756e3 | 47 | poolOptions |
d0ed34c9 JB |
48 | ) |
49 | ||
50 | const application: Express = express() | |
51 | ||
52 | // Parse only JSON requests body | |
53 | application.use(express.json()) | |
54 | ||
55 | application.all('/api/echo', (req: Request, res: Response) => { | |
56 | ExpressWorker.requestHandlerPool | |
57 | .execute({ data: req.body }, 'echo') | |
041dc05b | 58 | .then(response => { |
d0ed34c9 JB |
59 | return res.send(response.data).end() |
60 | }) | |
61 | .catch(emptyFunction) | |
62 | }) | |
63 | ||
64 | application.get('/api/factorial/:number', (req: Request, res: Response) => { | |
65 | const { number } = req.params | |
66 | ExpressWorker.requestHandlerPool | |
80115618 | 67 | .execute({ data: { number: Number.parseInt(number) } }, 'factorial') |
041dc05b | 68 | .then(response => { |
d0ed34c9 JB |
69 | return res.send(response.data).end() |
70 | }) | |
71 | .catch(emptyFunction) | |
72 | }) | |
73 | ||
b7172bc0 | 74 | let listenerPort: number | undefined |
d0ed34c9 | 75 | ExpressWorker.server = application.listen(port, () => { |
b7172bc0 | 76 | listenerPort = (ExpressWorker.server.address() as AddressInfo).port |
d0ed34c9 | 77 | console.info( |
6e5d7052 | 78 | `⚡️[express server]: Express server is started in cluster worker at http://localhost:${listenerPort.toString()}/` |
d0ed34c9 JB |
79 | ) |
80 | }) | |
81 | return { | |
82 | status: true, | |
3a502712 | 83 | port: listenerPort ?? port, |
d0ed34c9 JB |
84 | } |
85 | } | |
86 | ||
87 | public constructor () { | |
88 | super(ExpressWorker.startExpress, { | |
89 | killHandler: async () => { | |
90 | await ExpressWorker.requestHandlerPool.destroy() | |
91 | ExpressWorker.server.close() | |
3a502712 | 92 | }, |
d0ed34c9 JB |
93 | }) |
94 | } | |
95 | } | |
96 | ||
97 | export const expressWorker = new ExpressWorker() |