Skip to content

Instantly share code, notes, and snippets.

@imolorhe
Forked from creationix/rpc.md
Created September 10, 2019 18:14
Show Gist options
  • Select an option

  • Save imolorhe/ab23c3a9f4b10cb1fffa8b0a6e5658ce to your computer and use it in GitHub Desktop.

Select an option

Save imolorhe/ab23c3a9f4b10cb1fffa8b0a6e5658ce to your computer and use it in GitHub Desktop.

Revisions

  1. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion rpc.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ I've designed a lot of RPC protocols in my career. One pattern that's worked we

    // Client calls a function that throws an error
    -> [3, "crasher"]
    // Server sends error using lua/go style multiple return vaalues.
    // Server sends error using lua/go style multiple return values.
    <- [-3, null, "There was a problem"]

    // Client passes in a callback to be called later
  2. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion rpc.md
    Original file line number Diff line number Diff line change
    @@ -36,7 +36,7 @@ Or defined generally:
    [request_id, target, ...arguments]
    // Response
    [-request_id, ...return_values]
    // Message
    // Event
    [0, target, ...arguments]
    ```

  3. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions rpc.md
    Original file line number Diff line number Diff line change
    @@ -40,6 +40,8 @@ Or defined generally:
    [0, target, ...arguments]
    ```

    Where `request_id` is a positive integer, `target` is a string or positive integer, and `arguments` and `return_values` are any serializable values.

    So a few things to note:

    - Messages are always arrays. These serialize well and are compact in most formats. I tend to use either JSON or CBOR.
  4. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 11 additions and 0 deletions.
    11 changes: 11 additions & 0 deletions rpc.md
    Original file line number Diff line number Diff line change
    @@ -29,6 +29,17 @@ I've designed a lot of RPC protocols in my career. One pattern that's worked we
    -> [0, 2] // client calls anonymous clearInterval function
    ```

    Or defined generally:

    ```js
    // Request
    [request_id, target, ...arguments]
    // Response
    [-request_id, ...return_values]
    // Message
    [0, target, ...arguments]
    ```

    So a few things to note:

    - Messages are always arrays. These serialize well and are compact in most formats. I tend to use either JSON or CBOR.
  5. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 11 additions and 11 deletions.
    22 changes: 11 additions & 11 deletions rpc.md
    Original file line number Diff line number Diff line change
    @@ -2,31 +2,31 @@ I've designed a lot of RPC protocols in my career. One pattern that's worked we

    ```js
    // Client calls: print('Hello World\n')
    [1, "print", "Hello World!\n"]
    -> [1, "print", "Hello World!\n"]
    // Server sends return value (or lack of return vvalue)
    [-1]
    <- [-1]

    // Client calls: add(1, 2)
    [2, "add", 1, 2]
    -> [2, "add", 1, 2]
    // Server responds with result
    [-2, 3]
    <- [-2, 3]

    // Client calls a function that throws an error
    [3, "crasher"]
    -> [3, "crasher"]
    // Server sends error using lua/go style multiple return vaalues.
    [-3, null, "There was a problem"]
    <- [-3, null, "There was a problem"]

    // Client passes in a callback to be called later
    // The function is serialized into something special that both sides agree is a function.
    [4, "setInterval", {$:1}, 1000]
    -> [4, "setInterval", {$:1}, 1000]
    // server returns with function to cancel the interval
    [-4, {$:2}]
    <- [-4, {$:2}]
    // Then later the callback fires every second from Server
    [0, 1] // zero request id mean we don't need a response
    <- [0, 1] // zero request id mean we don't need a response
    // Server...
    [0, 1] // server is calling anonymous onInterval callback again
    <- [0, 1] // server is calling anonymous onInterval callback again
    // Eventually client wants to cancel interval
    [0, 2] // client calls anonymous clearInterval function
    -> [0, 2] // client calls anonymous clearInterval function
    ```

    So a few things to note:
  6. @creationix creationix revised this gist Sep 10, 2019. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion rpc.md
    Original file line number Diff line number Diff line change
    @@ -39,4 +39,8 @@ So a few things to note:

    I tend to use cbor (used to use msgpack a lot) because it's compact and allows binary values to be passed through. Also cbor allows registering custom types, so serializing functions doesn't need the `{$:id}` format used in JSON.

    This isn't perfect, but it works well for lots of use cases, is very fast and effecient, and is very easy to implement.
    This isn't perfect, but it works well for lots of use cases, is very fast and effecient, and is very easy to implement.

    The namespace for request/response IDs is defined by the caller. Request `1` by one peer is not the same as request `1` by the other peer. This is why negative is used for responses.

    In the same manner, anonymous callbacks are serialized using integers defined by the person who owns the function and sends the function. One interesting side effect is it's not possible to send a function back to it's owner because it would be interpreted as a new function owned by the sender.
  7. @creationix creationix created this gist Sep 10, 2019.
    42 changes: 42 additions & 0 deletions rpc.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    I've designed a lot of RPC protocols in my career. One pattern that's worked well basically goes as follows:

    ```js
    // Client calls: print('Hello World\n')
    [1, "print", "Hello World!\n"]
    // Server sends return value (or lack of return vvalue)
    [-1]

    // Client calls: add(1, 2)
    [2, "add", 1, 2]
    // Server responds with result
    [-2, 3]

    // Client calls a function that throws an error
    [3, "crasher"]
    // Server sends error using lua/go style multiple return vaalues.
    [-3, null, "There was a problem"]

    // Client passes in a callback to be called later
    // The function is serialized into something special that both sides agree is a function.
    [4, "setInterval", {$:1}, 1000]
    // server returns with function to cancel the interval
    [-4, {$:2}]
    // Then later the callback fires every second from Server
    [0, 1] // zero request id mean we don't need a response
    // Server...
    [0, 1] // server is calling anonymous onInterval callback again
    // Eventually client wants to cancel interval
    [0, 2] // client calls anonymous clearInterval function
    ```

    So a few things to note:

    - Messages are always arrays. These serialize well and are compact in most formats. I tend to use either JSON or CBOR.
    - First value is a request id (positive integer), response id (negative integer), or 0 for message that doesn't need response.
    - second value if request or message is the function to call. It can be a named endpoint or the integer value of an anonymous function.
    - The rest of the values are arguments for request/message and return values for responses
    - the convention for errors is two return values null, message. This matches lua style error handling, but can be mapped to many other languages.

    I tend to use cbor (used to use msgpack a lot) because it's compact and allows binary values to be passed through. Also cbor allows registering custom types, so serializing functions doesn't need the `{$:id}` format used in JSON.

    This isn't perfect, but it works well for lots of use cases, is very fast and effecient, and is very easy to implement.