Getting started: Pub/Sub in Go

This guide will get you started with Ably Pub/Sub in Go.

It will take you through the following steps:

  • Create a client and establish a realtime connection to Ably.
  • Attach to a channel and subscribe to its messages.
  • Publish a message to the channel for your client to receive.
  • Join and subscribe to the presence set of the channel.
  • Retrieve the messages you sent in the guide from history.
  • Close a connection to Ably when it is no longer needed.

  • Sign up for an Ably account.
    • Create a new app, and create your first API key.
    • Your API key will need the publish, subscribe, presence and history capabilities.
  • Install the Ably CLI:
npm install -g @ably/cli
Copied!
  • Run the following to log in to your Ably account and set the default app and API key:
ably login ably apps switch ably auth keys switch
Copied!
  • Install Go version 1.18 or greater.
  • Create a new project in your IDE and install the Ably Pub/Sub Go SDK:
mkdir ably-go-quickstart cd ably-go-quickstart go mod init ably-go-quickstart go get -u github.com/ably/ably-go/ably
Copied!

Clients establish a connection with Ably when they instantiate an SDK instance. This enables them to send and receive messages in realtime across channels.

  • Create a main.go file in your project and add the following function to instantiate the SDK and establish a connection to Ably. At the minimum you need to provide an authentication mechanism. Use an API key for simplicity, but you should use token authentication in a production app. A clientId ensures the client is identified, which is required to use certain features, such as presence:
Go v1.2
package main import ( "context" "fmt" "log" "github.com/ably/ably-go/ably" ) func main() { client, err := ably.NewRealtime( ably.WithKey("<loading API key, please wait>"), ably.WithClientID("my-first-client"), ) if err != nil { log.Fatal(err) } defer client.Close() // Wait for the connection to be connected connStateChan := make(chan ably.ConnectionStateChange, 1) client.Connection.On(ably.ConnectionEventConnected, func(change ably.ConnectionStateChange) { connStateChan <- change }) select { case <-connStateChan: fmt.Println("Made my first connection!") case <-context.Background().Done(): log.Fatal("Context cancelled before connection established") } // Keep the program running select {} }
Demo Only
Copied!

You can monitor the lifecycle of clients’ connections by registering a listener that will emit an event every time the connection state changes. For now, run the function with go run main.go to log a message to the console to know that the connection attempt was successful. You’ll see the message printed to your console, and you can also inspect the connection event in the dev console of your app.

Messages contain the data that a client is communicating, such as a short ‘hello’ from a colleague, or a financial update being broadcast to subscribers from a server. Ably uses channels to separate messages into different topics, so that clients only ever receive messages on the channels they are subscribed to.

  • In your main() function, before the line // Keep the program running, add the following to create a channel instance and register a listener to subscribe to the channel. Then run it with go run main.go:
Go v1.2
channel := client.Channels.Get("my-first-channel") // Subscribe to messages unsubscribe, err := channel.SubscribeAll(context.Background(), func(msg *ably.Message) { fmt.Printf("Received message: %s\n", msg.Data) }) if err != nil { log.Fatal(err) } defer unsubscribe()
Copied!
  • Use the Ably CLI to publish a message to your first channel. The message will be received by the client you’ve subscribed to the channel, and be logged to the console.
ably channels publish my-first-channel 'Hello!'
Copied!
  • In a new terminal tab, subscribe to the same channel using the CLI:
ably channels subscribe my-first-channel
Copied!

Publish another message using the CLI and you will see that it’s received instantly by the client you have running locally, as well as the subscribed terminal instance.

To publish a message in your code, you can add the following line to your main function after subscribing to the channel:

Go v1.2
err = channel.Publish(context.Background(), "example", "A message sent from my first client!") if err != nil { log.Fatal(err) }
Copied!

Presence enables clients to be aware of one another if they are present on the same channel. You can then show clients who else is online, provide a custom status update for each, and notify the channel when someone goes offline.

  • In your main() function, before the line // Keep the program running, add the following to subscribe to, and join, the presence set of the channel. Then run it with go run main.go:
Go v1.2
// Subscribe to presence events presenceUnsubscribe, err := channel.Presence.Subscribe(context.Background(), ably.PresenceActionEnter, func(msg *ably.PresenceMessage) { fmt.Printf("Event type: %s from %s with the data %v\n", msg.Action, msg.ClientID, msg.Data) }) if err != nil { log.Fatal(err) } defer presenceUnsubscribe() // Enter the presence set err = channel.Presence.Enter(context.Background(), "I'm here!") if err != nil { log.Fatal(err) }
Copied!
  • You can have another client join the presence set using the Ably CLI:
ably channels presence enter my-first-channel --client-id "my-cli" --data '{"status":"learning about Ably!"}'
Copied!

You can retrieve previously sent messages using the history feature. Ably stores all messages for 2 minutes by default in the event a client experiences network connectivity issues. This can be extended for longer if required.

If more than 2 minutes has passed since you published a regular message (excluding the presence events), then you can publish some more before trying out history. You can use the Pub/Sub SDK, Ably CLI or the dev console to do this.

For example, using the Ably CLI to publish 5 messages:

ably channels publish --count 5 my-first-channel "Message number {{.Count}}"
Copied!
  • Update your main() function to retrieve any messages that were recently published to the channel. Then run it with go run main.go:
Go v1.2
// Add this after channel subscription pages, err := channel.History().Pages(context.Background()) if err != nil { log.Fatal(err) } var messages []string for pages.Next(context.Background()) { for _, msg := range pages.Items() { messages = append(messages, fmt.Sprintf("%v", msg.Data)) } } if err := pages.Err(); err != nil { log.Fatal(err) } // Print messages in reverse order (newest first) for i := len(messages) - 1; i >= 0; i-- { fmt.Println(messages[i]) }
Copied!

The output will look similar to the following:

[ 'Message number 5', 'Message number 4', 'Message number 3', 'Message number 2', 'Message number 1' ]
Copied!

Connections are automatically closed approximately 2 minutes after no heartbeat is detected by Ably. Explicitly closing connections when they are no longer needed is good practice to help save costs. It will also remove all listeners that were registered by the client.

Note that messages are streamed to clients as soon as they attach to a channel, as long as they have the necessary capabilities. Clients are implicitly attached to a channel when they call subscribe(). Detaching from a channel using the detach() method will stop the client from being streamed messages by Ably.

Listeners registered when subscribing to a channel are registered client-side. Unsubscribing by calling unsubscribe() will remove previously registered listeners for that channel. Detaching from a channel has no impact on listeners. As such, if a client reattaches to a channel that they previously registered listeners for, then those listeners will continue to function upon reattachment.

  • Update your main() function to close the connection after a simulated 10 seconds. Run it with go run main.go.

First add the time package to your imports:

Go v1.2
import ( "context" "fmt" "log" "time" "github.com/ably/ably-go/ably" )
Copied!

Now replace the select {} statement with a sleep statement to simulate a delay before closing the connection:

Go v1.2
// Replace the select {} with: time.Sleep(10 * time.Second) fmt.Println("Closing the connection to Ably!")
Copied!

Note that the defer client.Close() statement at the beginning of the function ensures the connection is properly closed when the program exits.

Continue to explore the documentation with Go as the selected language:

Read more about the concepts covered in this guide:

You can also explore the Ably CLI further, or visit the Pub/Sub API references.

Select...