Outline
TGrid
supports Worker
/SharedWorker
protocols.
With TGrid
, you can easily develop Worker
programs under the RPC (Remote Procedure Call) concept.
TGrid
considers Worker
as a 1: 1 dedicated server, and SharedWorker
as a 1: N multi-client acceptable server running on the local. Therefore, the interfaces of Worker
and SharedWorker
in the TGrid
are similar with WebSocket components. In such reason, if you're developing a complicate WebSocket system, you can simulate it in the local environment by using Worker
/SharedWorker
components.
Worker
Available in both Browser/NodeJS.
You can utilize RPC (Remote Procedure Call) even in the Worker
.
WorkerConnector
import { Driver, WorkerConnector } from "tgrid";
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEvent } from "./interfaces/ICalcEvent";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { ICompositeCalculator } from "./interfaces/ICompositeCalculator";
const EXTENSION = __filename.endsWith(".ts") ? "ts" : "js";
export const workerClientMain = async () => {
const stack: ICalcEvent[] = [];
const listener: ICalcEventListener = {
on: (evt: ICalcEvent) => stack.push(evt),
};
const connector: WorkerConnector<
ICalcConfig,
ICalcEventListener,
ICompositeCalculator
> = new WorkerConnector(
{ precision: 2 }, // header
listener, // provider for remote server
"process",
);
await connector.connect(`${__dirname}/server.${EXTENSION}`);
const remote: Driver<ICompositeCalculator> = connector.getDriver();
console.log(
await remote.plus(10, 20), // returns 30
await remote.multiplies(3, 4), // returns 12
await remote.divides(5, 3), // returns 1.67
await remote.scientific.sqrt(2), // returns 1.41
await remote.statistics.mean(1, 3, 9), // returns 4.33
);
await connector.close();
console.log(stack);
};
Terminal$ npm start 30 12 1.67 1.41 4.33 [ { type: 'plus', input: [ 10, 20 ], output: 30 }, { type: 'multiplies', input: [ 3, 4 ], output: 12 }, { type: 'divides', input: [ 5, 3 ], output: 1.67 }, { type: 'sqrt', input: [ 2 ], output: 1.41 }, { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 } ]
Worker Connector.
The WorkerConnector
is a communicator class, which creates an Worker
instance, and interacts with it through RPC (Remote Procedure Call). In other words, WorkerConnector
considers the Worker
instance as a remote server accepting only one client; WorkerServer
.
You can create the Worker
instance and communicate with it by WorkerConnector.connect()
or WorkerConnector.compile()
method. The WorkerConnector.connect()
method just opens an existing JS (or TS) file, and the WorkerConnector.compile()
method writes a temporary JS (TS) file and connects to it. Anyway, the Worker
instanced program must open the WorkerServer
.
By the way, don't forget closing the worker to clean up the resources. If the closing be performed by WorkerServer
, you can wait the worker server closing through the WorkerConnector.wait()
method.
Also, when declaring this WorkerConnector
type, you've to define three generic arguments; Header
, Provider
and Remote
. Those generic arguments must be same with the ones defined in the target WorkerServer
class (Provider
and Remote
must be reversed).
For reference, the first Header
type represents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider
represents a provider from client to server, and the other Remote
means a provider from the remote server to client.
- Above example case:
Header
:ICalcConfig
typeProvider
: Client is providingICalcEventListener
to the serverRemote
: Server is providingISimpleCalculator
to the client
Demonstration
You can run it on Playground Website (opens in a new tab), or local machine.
git clone https://github.com/samchon/tgrid.example.worker
npm install
npm start
WorkerServer
import { Driver, WorkerServer } from "tgrid";
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { CompositeCalculator } from "./providers/CompositeCalculator";
const main = async () => {
const server: WorkerServer<
ICalcConfig,
CompositeCalculator,
ICalcEventListener
> = new WorkerServer();
const header: ICalcConfig = await server.getHeader();
const listener: Driver<ICalcEventListener> = server.getDriver();
const provider: CompositeCalculator = new CompositeCalculator(
header,
listener,
);
await server.open(provider);
};
main().catch((exp) => {
console.error(exp);
process.exit(-1);
});
Worker Server.
The WorkerServer
is a class representing a Worker server which communicate with client (WorkerConnector
), through the RPC (Remote Procedure Call).
Unlike other servers, WorkerServer
can accept only one client (WorkerConnector
), because the Worker
is dependent on its parent instance (web page, node or parent worker). Thus, WorkerServer
does not have any acceptor and communicates with client (its parent) directly.
To start communication with the client, call the WorkerServer.open()
(opens in a new tab) method with Provider
instance. After your business, don't forget closing this Worker instance. If the termination is performed by the WorkerConnector
, you can wait the closing signal through the WorkerServer.join()
(opens in a new tab) method.
Also, when declaring this WorkerServer type, you've to define three generic arguments; Header
, Provider
and Remote
. Those generic arguments must be same with the ones defined in the target WorkerConnector
class (Provider
and Remote
must be reversed).
For reference, the first Header
type represents an initial data from the client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider
represents a provider from server to client, and the other Remote
means a provider from the client to server.
- Above example case:
Header
:ICalcConfig
typeProvider
: Server is providingCompositeCalculator
to the clientRemote
: Client is providingICalcEventListener
to the server
Shared Worker
Available only in the Web Browser.
In the Web Browser, you also can perform RPC (Remote Procedure Call) in the SharedWorker
.
Also, as SharedWorker
can accept multiple clients, TGrid
considers it as a local server running on the web browser, and its interfaces are similar with WebSocket components.
SharedWorkerServer
import { Driver, SharedWorkerServer } from "tgrid";
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { CompositeCalculator } from "./providers/CompositeCalculator";
const main = async () => {
let pool: number = 0;
const server: SharedWorkerServer<
ICalcConfig,
CompositeCalculator,
ICalcEventListener
> = new SharedWorkerServer();
await server.open(async (acceptor) => {
// LIST UP PROPERTIES
const config: ICalcConfig = acceptor.header;
const listener: Driver<ICalcEventListener> = acceptor.getDriver();
// ACCEPT OR REJECT THE CONNECTION
if (pool >= 8) {
await acceptor.reject("Too much connections.");
} else {
await acceptor.accept(new CompositeCalculator(config, listener));
++pool;
await acceptor.join();
--pool;
}
});
};
main().catch(console.error);
Shared Worker Server.
The SharedWorkerServer
is a class representing a server in SharedWorker
environment. Clients connecting to the SharedWorkerServer
would communicate with this server through SharedWorkerAcceptor
instances using RPC (Remote Procedure Call) concept.
To open the server, call the SharedWorkerServer.open()
method with your callback function which would be called whenever a SharedWorkerAcceptor
has been newly created by a new client's connection.
Also, when declaring this SharedWorkerServer
type, you have to define three generic arguments; Header
, Provider
and Remote
. Those generic arguments would be propagated to the SharedWorkerAcceptor
, so that SharedWorkerAcceptor
would have the same generic arguments, too.
For reference, the first Header
type represents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider
represents a provider from server to client, and the other Remote
means a provider from the remote client to server.
- Above example case:
Header
:ICalcConfig
typeProvider
: Server is providingCompositeCalculator
to the clientRemote
: Client is providingICalcEventListener
to the server
SharedWorkerAcceptor
import { Driver, SharedWorkerAcceptor, SharedWorkerServer } from "tgrid";
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { CompositeCalculator } from "./providers/CompositeCalculator";
const main = async () => {
let pool: number = 0;
const server: SharedWorkerServer<
ICalcConfig,
CompositeCalculator,
ICalcEventListener
> = new SharedWorkerServer();
await server.open(
async (
acceptor: SharedWorkerAcceptor<
ICalcConfig,
CompositeCalculator,
ICalcEventListener
>,
) => {
// LIST UP PROPERTIES
const config: ICalcConfig = acceptor.header;
const listener: Driver<ICalcEventListener> = acceptor.getDriver();
// ACCEPT OR REJECT THE CONNECTION
if (pool >= 8) {
await acceptor.reject("Too much connections.");
} else {
await acceptor.accept(new CompositeCalculator(config, listener));
++pool;
await acceptor.join();
--pool;
}
},
);
};
main().catch(console.error);
Shared Worker Acceptor.
The SharedWorkerAcceptor
is a communicator class interacting with the SharedWorkerConnector
through RFC (Remote Function Call), created by the SharedWorkerServer
class whenever a client connects to the SharedWorker
instance.
When a remote client connects to the SharedWorkerServer
, so that a new SharedWorkerAcceptor
instance being created, you can determine whether to accept the client's connection or not, reading the SharedWorkerAcceptor.header
property. If you've decided to accept the connection, call the SharedWorkerAcceptor.accept()
method with Provider instance. Otherwise, reject it thorugh the SharedWorkerAcceptor.reject()
method.
After accepting the connection, don't forget to closing the connection after your business has been completed to clean up the resources. Otherwise the closing must be performed by the remote client, you can wait the remote client's closing signal by the SharedWorkerAcceptor.join()
method.
Also, when declaring this SharedWorkerAcceptor
type, you have to define three generic arguments; Header
, Provider
and Remote
. Those generic arguments must be same with the ones defined in the SharedWorkerServer
class.
For reference, the first Header
type represents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider
represents a provider from server to client, and the other Remote
means a provider from the remote client to server.
SharedWorkerConnector
import { Driver, SharedWorkerConnector } from "tgrid";
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEvent } from "./interfaces/ICalcEvent";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { ICompositeCalculator } from "./interfaces/ICompositeCalculator";
const main = async () => {
const stack: ICalcEvent[] = [];
const listener: ICalcEventListener = {
on: (evt: ICalcEvent) => stack.push(evt),
};
const connector: SharedWorkerConnector<
ICalcConfig,
ICalcEventListener,
ICompositeCalculator
> = new SharedWorkerConnector(
{ precision: 2 }, // header
listener, // provider for remote server
);
await connector.connect(`./server.js`);
const remote: Driver<ICompositeCalculator> = connector.getDriver();
console.log(
await remote.plus(10, 20), // returns 30
await remote.multiplies(3, 4), // returns 12
await remote.divides(5, 3), // returns 1.67
await remote.scientific.sqrt(2), // returns 1.41
await remote.statistics.mean(1, 3, 9), // returns 4.33
);
await connector.close();
for (const evt of stack) console.log(JSON.stringify(evt));
};
main().catch(console.error);
console30 12 1.67 1.41 4.33 {"type":"plus","input":[10,20],"output":30} {"type":"multiplies","input":[3,4],"output":12} {"type":"divides","input":[5,3],"output":1.67} {"type":"sqrt","input":[2],"output":1.41} {"type":"mean","input":[1,3,9],"output":4.33}
Shared Worker Connector.
The SharedWorkerConnector
is a communicator class which connects to an SharedWorker
instance, and interacts with it through RFC (Remote Function Call) concept.
You can connect to the SharedWorkerServer
using SharedWorkerConnector.connect()
method. The interaction would be started if the server accepts your connection by calling the SharedWorkerAcceptor.accept()
method. If the remote server rejects your connection through SharedWorkerAcceptor.reject()
method, the exception would be thrown.
After the connection, don't forget to closing the connection, if your business logics have been completed, to clean up the resources. Otherwise, the closing must be performed by the remote shared worker server, you can wait the remote server's closing signal through the SharedWorkerConnector.join()
method.
Also, when declaring this SharedWorkerConnector
type, you've to define three generic arguments; Header
, Provider
and Remote
. Those generic arguments must be same with the ones defined in the target SharedWorkerServer
and SharedWorkerAcceptor
classes (Provider and Remote
must be reversed).
For reference, the first Header
type represents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider
represents a provider from client to server, and the other Remote
means a provider from the remote server to client.
- Above example case:
Header
:ICalcConfig
typeProvider
: Client is providingICalcEventListener
to the serverRemote
: Server is providingISimpleCalculator
to the client
Demonstration
You can run it on your local machine.
git clone https://github.com/samchon/tgrid.example.shared-worker
npm install
npm run build
npm start