12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- #!/usr/bin/env node
- import http from 'node:http'
- import httpProxy from 'http-proxy'
- import process from 'node:process'
- import { execaNode } from 'execa';
- const numInstances = 3
- const lbPort = 3020
- const companionStartPort = 3021
- // simple load balancer that will direct requests round robin between companion instances
- function createLoadBalancer (baseUrls) {
- const proxy = httpProxy.createProxyServer({ ws: true })
- let i = 0
- function getTarget () {
- return baseUrls[i % baseUrls.length]
- }
- const server = http.createServer((req, res) => {
- const target = getTarget()
- // console.log('req', req.method, target, req.url)
- proxy.web(req, res, { target }, (err) => {
- console.error('Load balancer failed to proxy request', err.message)
- res.statusCode = 500
- res.end()
- })
- i++
- })
- server.on('upgrade', (req, socket, head) => {
- const target = getTarget()
- // console.log('upgrade', target, req.url)
- proxy.ws(req, socket, head, { target }, (err) => {
- console.error('Load balancer failed to proxy websocket', err.message)
- console.error(err)
- socket.destroy()
- })
- i++
- })
- server.listen(lbPort)
- console.log('Load balancer listening', lbPort)
- return server
- }
- const isWindows = process.platform === 'win32'
- const isOSX = process.platform === 'darwin'
- const startCompanion = ({ name, port }) => execaNode('packages/@uppy/companion/src/standalone/start-server.js', {
- nodeOptions: [
- '-r', 'dotenv/config',
- // Watch mode support is limited to Windows and macOS at the time of writing.
- ...(isWindows || isOSX ? ['--watch-path', 'packages/@uppy/companion/src', '--watch'] : []),
- ],
- cwd: new URL('../', import.meta.url),
- stdio: 'inherit',
- env: {
- // Note: these env variables will override anything set in .env
- ...process.env,
- COMPANION_PORT: port,
- COMPANION_SECRET: 'development', // multi instance will not work without secret set
- COMPANION_PREAUTH_SECRET: 'development', // multi instance will not work without secret set
- COMPANION_ALLOW_LOCAL_URLS: 'true',
- COMPANION_ENABLE_URL_ENDPOINT: 'true',
- COMPANION_LOGGER_PROCESS_NAME: name,
- COMPANION_CLIENT_ORIGINS: 'true',
- },
- })
- const hosts = Array.from({ length: numInstances }, (_, index) => {
- const port = companionStartPort + index
- return { index, port }
- })
- console.log('Starting companion instances on ports', hosts.map(({ port }) => port))
- const companions = hosts.map(({ index, port }) => startCompanion({ name: `companion${index}`, port }))
- let loadBalancer
- try {
- loadBalancer = createLoadBalancer(hosts.map(({ port }) => `http://localhost:${port}`))
- await Promise.all(companions)
- } finally {
- loadBalancer?.close()
- companions.forEach((companion) => companion.kill())
- }
|