sock-daemon
A utility for creating a daemon process that listens on a socket,
and a client that talks to it.
Handles automatically starting the daemon service (but only one
of them), logging to a file, and serializing messages over a
domain socket on unix systems or named pipe on Windows.
USAGE
Install with npm
npm install sock-daemon
There are two parts of this, the daemon script that listens on a
socket, and the client that connects to it.
The daemon is smart enough to only instantiate a single copy of
itself. The client knows how to connect to it, and knows how to
spin up an instance of the daemon if it's not already running.
To do this, the server daemon and the client need to both know
the name that they'll use for the socket, and the client needs to
know the location of the daemon script.
The type stuff is clearly only required when you are using
TypeScript, but it is a nice check to ensure that the server and
client are speaking the same language.
For example, you can create a service like this, defining a
client and server implementation:
import {
SockDaemonServer as Server,
SockDaemonClient as Client,
} from 'sock-daemon'
import type { MessageBase } from 'sock-daemon'
export const serviceName = 'my-service'
export interface Request extends MessageBase {
foo: string
}
export interface Response extends MessageBase {
bar: string
}
export class MyServiceServer extends Server<Request, Response> {
isRequest(msg: any): msg is Request {
return super.isMessage(msg) && typeof msg.foo === 'string'
}
static get serviceName() {
return serviceName
}
async handle(msg: Request) {
console.error('got request', msg)
return {
id: msg.id,
bar: 'bar: ' + msg.foo,
}
}
}
export class MyServiceClient extends Client<Request, Response> {
static get serviceName() {
return serviceName
}
static get daemonScript() {
return new URL('./daemon.mjs', import.meta.url)
}
isResponse(msg: any): msg is Response {
return super.isMessage(msg) && typeof msg.bar === 'string'
}
async fooIntoBar(foo: string) {
const { bar } = await super.request({ foo })
return bar
}
}
Create the daemon.mjs
script like this:
import { MyServiceServer } from './index.mjs'
const server = new MyServiceServer({
idleTimeout: 1000 * 60 * 60,
connectionTimeout: 1000,
})
server.listen()
And then using the client in a program somewhere:
import { MyServiceClient as Client } from 'my-service'
const client = new Client()
const result = await client.fooIntoBar('input string')
API
See the above example for most of it, or look at the
typedocs
Caveats
- Note that your Daemon and Client classes need to override the
static
serviceName
getter to a matching value, and the Client
needs to override the static daemonScript
to a reference to
the node program that will run the service. - The
daemonScript
will be passed to node with the
process.execArgv
provided to the main client program, but no
other arguments. - This will work best if your messages can be as small as
possible, to save on serialization costs. If you have to do
some large amount of work, it's often faster to write the
result to a file, and have the
Response
report the filename. - To my knowledge, this module is not responsible for anyone's
missing socks,
but please post an issue if you find this is not the case.