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