πŸ“– Guide Documents
πŸ“‘ Remote Procedure Call

Outline

With TGrid, you can call remote procedures of provided by remote system.

If remote system provides a function, TGrid lets you call it as if it was a local function own. If remote system provides some functions that are capsuled in hierarchical structured objects, you still can call them as if they were your own. This is the concept of RPC (Remote Procedure Call) what TGrid is saying.

By the way, there are many other RPC (Remote Procedure Call) frameworks or libraries in the world. However, TGrid is different from them. RPC of TGrid does not mean only calling and getting returned value from the remote system's procedure, but also ensuring type safety. With the type safety, you can actually feel like that the remote procedure is your own.

Demonstration

examples/websocket/src/client.ts
import { Driver, WebSocketConnector } from "tgrid";
 
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEvent } from "./interfaces/ICalcEvent";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { ICompositeCalculator } from "./interfaces/ICompositeCalculator";
 
export const webSocketClientMain = async () => {
  const stack: ICalcEvent[] = [];
  const listener: ICalcEventListener = {
    on: (evt: ICalcEvent) => stack.push(evt),
  };
  const connector: WebSocketConnector<
    ICalcConfig,
    ICalcEventListener,
    ICompositeCalculator
  > = new WebSocketConnector(
    { precision: 2 }, // header
    listener, // provider for remote server
  );
  await connector.connect("ws://127.0.0.1:37000/composite");
 
  const remote: Driver<ICompositeCalculator> = connector.getDriver();
  console.log(
    await driver.plus(10, 20), // returns 30
    await driver.multiplies(3, 4), // returns 12
    await driver.divides(5, 3), // returns 1.67
    await driver.scientific.sqrt(2), // returns 1.41
    await driver.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 }
]

Here is the one of example programs that demonstrating the RPC (Remote Procedure Call) of TGrid.

At first, looking at the "Client Program" tab, you can find out that the "Client Program" is calling the "Server Program"'s functions as if they were its own, through the Driver<ICompositeCalculator> typed instance with await symbols.

At next, change the tab to "Server Program", then you can find out that the "Server Program" is serving CompositeCalculator class to the "Client Program". Calling the functions of CompositeCalculator in the "Server Program" from the "Client Program" through the Driver<ICompositeCalculator> typed instance, this is the RPC (Remote Procedure Call) of TGrid.

πŸ’»

Demonstration

You can run it on Playground Website (opens in a new tab), or local machine.

Terminal
git clone https://github.com/samchon/tgrid.example.websocket
npm install
npm start

RPC Driver

examples/websocket/src/client.ts
import { Driver, WebSocketConnector } from "tgrid";
 
import { ICalcConfig } from "./interfaces/ICalcConfig";
import { ICalcEvent } from "./interfaces/ICalcEvent";
import { ICalcEventListener } from "./interfaces/ICalcEventListener";
import { ICompositeCalculator } from "./interfaces/ICompositeCalculator";
 
export const webSocketClientMain = async () => {
  const stack: ICalcEvent[] = [];
  const listener: ICalcEventListener = {
    on: (evt: ICalcEvent) => stack.push(evt),
  };
  const connector: WebSocketConnector<
    ICalcConfig,
    ICalcEventListener,
    ICompositeCalculator
  > = new WebSocketConnector(
    { precision: 2 }, // header
    listener, // provider for remote server
  );
  await connector.connect("ws://127.0.0.1:37000/composite");
 
  const remote: Driver<ICompositeCalculator> = connector.getDriver();
  console.log(
    await driver.plus(10, 20), // returns 30
    await driver.multiplies(3, 4), // returns 12
    await driver.divides(5, 3), // returns 1.67
    await driver.scientific.sqrt(2), // returns 1.41
    await driver.statistics.mean(1, 3, 9), // returns 4.33
  );
 
  await connector.close();
  console.log(stack);
};

Looking at the above ICompositeCalculator type from the "Interfaces" tab, none of the functions are actually asynchrounous. However, the "Client Program" is attaching await symbols. It's because every return types of ICompositeCalculator have changed to Promise<R> types through the Driver<T> type.

As the Driver<ICompositeCalculator> typed instance is not a "Client Program"'s own, but the "Server Program"'s own (CompositeCalculator), the function call must be passed through the asynchronous network communication. In such reason, the remote function calling cannot be synchronous, but asynchronous, and Driver<T> type is casting them.

πŸ’‘

Tip

Description of Driver type in the Features > Components chatper.

Driver is a proxy instance designed to call functions of the remote system. It has a generic argument Remote which means the type of remote system's Provider, and you can remotely call the functions of the Provider asynchronously through the Drive<Remote> instance.

When you call some function of remote Provider by the Driver<Listener> instance, it hooks the function call expression, and delivers the function name and arguments (parameter values) to the remote system through the Communicator. If the remote system suceeded to reply the result of the function call, Communicator resolves the promise of the function call expression with the result, so that makes Driver<Remote> working.

Otherwise exception is thrown in the remote Provider function, Communicator deliveries the exception instance instead to the remote system, so that actual exception being thrown from the Driver<Remote> instance.

Restrictions

TGrid has implemented the RPC (Remote Procedure Call) by communicating JSON message. Therefore, if parameters or return values of the remote provided functions are not compatible JSON, you can't use them.

For example, JSON does not support bigint type of JavaScript. Therefore, if some of the remote provided functions are using bigint type on their parameters or return value, it would throw an exception.

Also, as JSON does not contain class transformation spec, every parameters and return values must be primitive types. If you try to deliver the class instance as a parameter or return value, it would be downgraded to primitive instance in the remote system.