github.com/anycable/anycable-go@v1.5.1/docs/broadcasting.md (about)

     1  # Broadcasting
     2  
     3  Publishing messages from your application to connected clients (aka _broadcasting_) is an essential component of any real-time application.
     4  
     5  AnyCable comes with multiple options on how to broadcast messages. We call them _broadcasters_. Currently, we support HTTP, Redis, and NATS-based broadcasters.
     6  
     7  **NOTE:** The default broadcaster is Redis Pub/Sub for backward-compatibility reasons. This is going to change in v2.
     8  
     9  ## HTTP
    10  
    11  > Enable via `--broadcast_adapter=http` (or `ANYCABLE_BROADCAST_ADAPTER=http`).
    12  
    13  HTTP broadcaster has zero-dependencies and, thus, allows you to quickly start using AnyCable, and it's good enough to keep using it at scale.
    14  
    15  By default, HTTP broadcaster accepts publications as POST requests to the `/_broadcast` path of your server\*. The request body MUST contain the publication payload (see below to learn about [the format](#publication-format)).
    16  
    17  Here is a basic cURL example:
    18  
    19  ```bash
    20  curl -X POST -H "Content-Type: application/json" -d '{"stream":"my_stream","data":"{\"text\":\"Hello, world!\"}"}' http://localhost:8090/_broadcast
    21  ```
    22  
    23  \* If neither the broadcast key nor the application secret is specified, we configure HTTP broadcaster to use a different port by default (`:8090`) for security reasons. You can handle broadcast requests at the main AnyCable port by specifying it explicitly (via the `http_broadcast_port` option). If the broadcast key is specified or explicitly set to "none" or auto-generated from the application secret (see below), we run it on the main port. You will see the notice in the startup logs telling you how the HTTP broadcaster endpoint was configured:
    24  
    25  ```sh
    26  2024-03-06 10:35:39.297 INF Accept broadcast requests at http://localhost:8090/_broadcast (no authorization) nodeid=uE3mZ7 context=broadcast provider=http
    27  
    28  # OR
    29  2024-03-06 10:35:39.297 INF Accept broadcast requests at http://localhost:8080/_broadcast (authorization required) nodeid=uE3mZ7 context=broadcast provider=http
    30  ```
    31  
    32  ### Securing HTTP endpoint
    33  
    34  We automatically secure the HTTP broadcaster endpoint if the application broadcast key (`--broadcast_key`) is specified or inferred\* from the application secret (`--secret`) and the server is not running in the public mode (`--public`).
    35  
    36  Every request MUST include an "Authorization" header with the `Bearer <broadcast-key>` value:
    37  
    38  ```sh
    39  # Run AnyCable
    40  $ anycable-go --broadcast_key=my-secret-key
    41  
    42  2024-03-06 10:35:39.296 INF Starting AnyCable 1.5.0-a7aa9b4 (with mruby 1.2.0 (2015-11-17)) (pid: 57260, open file limit: 122880, gomaxprocs: 8) nodeid=uE3mZ7
    43  ...
    44  2024-03-06 10:35:39.297 INF Accept broadcast requests at http://localhost:8080/_broadcast (authorization required) nodeid=uE3mZ7 context=broadcast provider=http
    45  
    46  # Broadcast a message
    47  $ curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer my-secret-key" -d '{"stream":"my_stream","data":"{\"text\":\"Hello, world!\"}"}' http://localhost:8080/_broadcast -w "%{http_code}"
    48  
    49  201
    50  ```
    51  
    52  \* When the broadcast key is missing but the application secret is present, we automatically generate a broadcast key using the following formula (in Ruby):
    53  
    54  ```ruby
    55  broadcast_key = OpenSSL::HMAC.hexdigest("SHA256", "<APPLICATION SECRET>", "broadcast-cable")
    56  ```
    57  
    58  When using official AnyCable server libraries, you don't need to calculate it yourself (they all use the same inference mechanism). But if you want to publish broadcasts using a custom implementation, you can generate a broadcast key for your secret key as follows:
    59  
    60  ```sh
    61  echo -n 'broadcast-cable' | openssl dgst -sha256 -hmac '<your secret>' | awk '{print $2}'
    62  ```
    63  
    64  ## Redis Pub/Sub
    65  
    66  > Enable via `--broadcast_adapter=redis` (or `ANYCABLE_BROADCAST_ADAPTER=redis`).
    67  
    68  This broadcaster uses Redis [Pub/Sub](https://redis.io/topics/pubsub) feature under the hood, and, thus, publications are delivered to all subscribed AnyCable servers simultaneously.
    69  
    70  All broadcast messages are published to a single channel (configured via the `--redis_channel`, defaults to `__anycable__`) as follows:
    71  
    72  ```sh
    73  $ redis-cli PUBLISH __anycable__ '{"stream":"my_stream","data":"{\"text\":\"Hello, world!\"}"}'
    74  
    75  (integer) 1
    76  ```
    77  
    78  Note that since all AnyCable server receive each publication, we cannot use [broker](./broker.md) to provide stream history support when using Redis Pub/Sub.
    79  
    80  See [configuration](./configuration.md#redis-configuration) for available Redis options.
    81  
    82  ## Redis X
    83  
    84  > Enable via `--broadcast_adapter=redisx` (or `ANYCABLE_BROADCAST_ADAPTER=redisx`).
    85  
    86  **IMPORTANT:** Redis v6.2+ is required.
    87  
    88  Redis X broadcaster uses [Redis Streams][redis-streams] instead of Publish/Subscribe to _consume_ publications from your application. That gives us the following benefits:
    89  
    90  - **Broker compatibility**. This broadcaster uses a [broker](/anycable-go/broker.md) to store messages in a cache and distribute them within a cluster. This is possible due to the usage of Redis Streams consumer groups.
    91  
    92  - **Better delivery guarantees**. Even if there is no AnyCable server available at the broadcast time, the message will be stored in Redis and delivered to an AnyCable server once it is available. In combination with the [broker feature](./broker.md), you can achieve at-least-once delivery guarantees (compared to at-most-once provided by Redis Pub/Sub).
    93  
    94  To broadcast a message, you publish it to a dedicated Redis stream (configured via the `--redis_channel` option, defaults to `__anycable__`) with the publication JSON provided as the `payload` field value:
    95  
    96  ```sh
    97  $ redis-cli XADD __anycable__ "*" payload '{"stream":"my_stream","data":"{\"text\":\"Hello, world!\"}"}'
    98  
    99  "1709754437079-0"
   100  ```
   101  
   102  See [configuration](./configuration.md#redis-configuration) for available Redis options.
   103  
   104  ## NATS Pub/Sub
   105  
   106  > Enable via `--broadcast_adapter=nats` (or `ANYCABLE_BROADCAST_ADAPTER=nats`).
   107  
   108  NATS broadcaster uses [NATS publish/subscribe](https://docs.nats.io/nats-concepts/core-nats/pubsub) functionality and supports cluster features out-of-the-box. It works to Redis Pub/Sub: distribute publications to all subscribed AnyCable servers. Thus, it's incompatible with [broker](./broker.md) (stream history support), too.
   109  
   110  To broadcast a message, you publish it to a NATS stream (configured via the `--nats_channel` option, defaults to `__anycable__`) as follows:
   111  
   112  ```sh
   113  $ nats pub __anycable__ '{"stream":"my_stream","data":"{\"text\":\"Hello, world!\"}"}'
   114  
   115  12:03:39 Published 60 bytes to "__anycable__"
   116  ```
   117  
   118  NATS Pub/Sub is useful when you want to set up an AnyCable cluster using our [embedded NATS](./embedded_nats.md) feature, so you can avoid having additional infrastructure components.
   119  
   120  See [configuration](./configuration.md#nats-configuration) for available NATS options.
   121  
   122  ## Publication format
   123  
   124  AnyCable accepts broadcast messages encoded as JSON and having the following properties:
   125  
   126  ```js
   127  {
   128    "stream": "<publication stream name>", // string
   129    "data": "<payload>", // string, usually a JSON-encoded object, but not necessarily
   130    "meta": "{}" // object, publication metadata, optional
   131  }
   132  ```
   133  
   134  It's also possible to publish multiple messages at once. For that, you just send them as an array of publications:
   135  
   136  ```js
   137  [
   138    {
   139      "stream": "...",
   140      "data": "...",
   141    },
   142    {
   143      "stream": "...",
   144      "data": "..."
   145    }
   146  ]
   147  ```
   148  
   149  The `meta` field MAY contain additional instructions for servers on how to deliver the publication. Currently, the following fields are supported:
   150  
   151  - `exclude_socket`: you can specify a unique client identifier (returned by the server in the `welcome` message as `sid`) to remove this client from the list of recipients.
   152  
   153  All other meta fields are ignored for now.
   154  
   155  Here is a JSON Schema describing this format:
   156  
   157  ```json
   158  {
   159    "$schema": "http://json-schema.org/draft-07/schema",
   160    "definitions": {
   161      "publication": {
   162        "type": "object",
   163        "properties": {
   164        "stream": {
   165          "type": "string",
   166          "description": "Publication stream name"
   167        },
   168        "data": {
   169          "type": "string",
   170          "description": "Payload, usually a JSON-encoded object, but not necessarily"
   171        },
   172        "meta": {
   173          "type": "object",
   174          "description": "Publication metadata, optional",
   175          "properties": {
   176            "exclude_socket": {
   177              "type": "string",
   178              "description": "Unique client identifier to remove this client from the list of recipients"
   179            }
   180          },
   181          "additionalProperties": true
   182        }
   183      },
   184      "required": ["stream", "data"]
   185      }
   186    },
   187    "anyOf": [
   188      {
   189        "$ref": "#/definitions/publication"
   190      },
   191      {
   192        "type": "array",
   193        "items":{"$ref": "#/definitions/publication"}
   194      }
   195    ]
   196  }
   197  ```
   198  
   199  [redis-streams]: https://redis.io/docs/data-types/streams-tutorial/