项目作者: ably

项目描述 :
Go client library SDK for Ably realtime messaging service
高级语言: Go
项目地址: git://github.com/ably/ably-go.git
创建时间: 2014-12-01T11:57:59Z
项目社区:https://github.com/ably/ably-go

开源协议:Other

下载


Ably Go

.github/workflows/check.yml
.github/workflows/integration-test.yml
Features

Go Reference

Ably is the platform that powers synchronized digital experiences in realtime. Whether attending an event in a virtual venue, receiving realtime financial information, or monitoring live car performance data – consumers simply expect realtime digital experiences as standard. Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime for more than 250 million devices across 80 countries each month. Organizations like Bloomberg, HubSpot, Verizon, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale. For more information, see the Ably documentation.

Overview

This is a Go client library for Ably. Supported features and known limitations are documented here.

Installation

  1. ~ $ go get -u github.com/ably/ably-go/ably

See Requirements

Usage

Using the Realtime API

Creating a client

  1. client, err := ably.NewRealtime(ably.WithKey("xxx:xxx"))
  2. if err != nil {
  3. panic(err)
  4. }
  5. channel := client.Channels.Get("test")

Subscribing to events

You may monitor events on connections and channels.

  1. client, err = ably.NewRealtime(
  2. ably.WithKey("xxx:xxx"),
  3. ably.WithAutoConnect(false), // Set this option to avoid missing state changes.
  4. )
  5. if err != nil {
  6. panic(err)
  7. }
  8. // Set up connection events handler.
  9. client.Connection.OnAll(func(change ably.ConnectionStateChange) {
  10. fmt.Printf("Connection event: %s state=%s reason=%s", change.Event, change.Current, change.Reason)
  11. })
  12. // Then connect.
  13. client.Connect()
  14. channel = client.Channels.Get("test")
  15. channel.OnAll(func(change ably.ChannelStateChange) {
  16. fmt.Printf("Channel event event: %s channel=%s state=%s reason=%s", channel.Name, change.Event, change.Current, change.Reason)
  17. })

Subscribing to a channel for all messages

  1. unsubscribe, err := channel.SubscribeAll(ctx, func(msg *ably.Message) {
  2. fmt.Printf("Received message: name=%s data=%v\n", msg.Name, msg.Data)
  3. })
  4. if err != nil {
  5. panic(err)
  6. }

Subscribing to a channel for EventName1 and EventName2 message names

  1. unsubscribe1, err := channel.Subscribe(ctx, "EventName1", func(msg *ably.Message) {
  2. fmt.Printf("Received message: name=%s data=%v\n", msg.Name, msg.Data)
  3. })
  4. if err != nil {
  5. panic(err)
  6. }
  7. unsubscribe2, err := channel.Subscribe(ctx, "EventName2", func(msg *ably.Message) {
  8. fmt.Printf("Received message: name=%s data=%v\n", msg.Name, msg.Data)
  9. })
  10. if err != nil {
  11. panic(err)
  12. }

Publishing to a channel

  1. err = channel.Publish(ctx, "EventName1", "EventData1")
  2. if err != nil {
  3. panic(err)
  4. }

Publish will block until either the publish is acknowledged or failed to
deliver.

Alternatively you can use PublishAsync which does not block:

  1. channel.PublishAsync("EventName1", "EventData11", func(err error) {
  2. if err != nil {
  3. fmt.Println("failed to publish", err)
  4. } else {
  5. fmt.Println("publish ok")
  6. }
  7. })

Note the onAck callback must not block as it would block the internal client.

Handling errors

Errors returned by this library may have an underlying *ErrorInfo type.

See Ably documentation for ErrorInfo.

  1. badClient, err := ably.NewRealtime(ably.WithKey("invalid:key"))
  2. if err != nil {
  3. panic(err)
  4. }
  5. err = badClient.Channels.Get("test").Publish(ctx, "event", "data")
  6. if errInfo := (*ably.ErrorInfo)(nil); errors.As(err, &errInfo) {
  7. fmt.Printf("Error publishing message: code=%v status=%v cause=%v", errInfo.Code, errInfo.StatusCode, errInfo.Cause)
  8. } else if err != nil {
  9. panic(err)
  10. }

Announcing presence on a channel

  1. err = channel.Presence.Enter(ctx, "presence data")
  2. if err != nil {
  3. panic(err)
  4. }

Announcing presence on a channel on behalf of other client

  1. err = channel.Presence.EnterClient(ctx, "clientID", "presence data")
  2. if err != nil {
  3. panic(err)
  4. }

Updating and leaving presence

  1. // Update also has an UpdateClient variant.
  2. err = channel.Presence.Update(ctx, "new presence data")
  3. if err != nil {
  4. panic(err)
  5. }
  6. // Leave also has an LeaveClient variant.
  7. err = channel.Presence.Leave(ctx, "last presence data")
  8. if err != nil {
  9. panic(err)
  10. }

Getting all clients present on a channel

  1. clients, err := channel.Presence.Get(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. for _, client := range clients {
  6. fmt.Println("Present client:", client)
  7. }

Subscribing to all presence messages

  1. unsubscribe, err = channel.Presence.SubscribeAll(ctx, func(msg *ably.PresenceMessage) {
  2. fmt.Printf("Presence event: action=%v data=%v", msg.Action, msg.Data)
  3. })
  4. if err != nil {
  5. panic(err)
  6. }

Subscribing to ‘Enter’ presence messages only

  1. unsubscribe, err = channel.Presence.Subscribe(ctx, ably.PresenceActionEnter, func(msg *ably.PresenceMessage) {
  2. fmt.Printf("Presence event: action=%v data=%v", msg.Action, msg.Data)
  3. })
  4. if err != nil {
  5. panic(err)
  6. }

Update MaxMessageSize/read limit for realtime message subscription

  • The default MaxMessageSize is automatically configured by Ably when connection is established with Ably.
  • This value defaults to 16kb for free and 64kb for PAYG account, please get in touch if you would like to request a higher limit for your account.
  • Upgrading your account to higher limit will automatically update MaxMessageSize property and should accordingly set the client side connection read limit.
  • If you are still facing issues when receiving large messages or intentionally want to reduce the limit, you can explicitly update the connection read limit:
  1. client, err = ably.NewRealtime(ably.WithKey("xxx:xxx"))
  2. if err != nil {
  3. panic(err)
  4. }
  5. client.Connection.SetReadLimit(131072) // Set read limit to 128kb, overriding default ConnectionDetails.MaxMessageSize

Note - If connection read limit is less than size of received message, the client will throw an error “failed to read: read limited at {READ_LIMIT + 1} bytes” and will close the connection.

Using the REST API

Introduction

All examples assume a client and/or channel has been created as follows:

  1. client, err := ably.NewREST(ably.WithKey("xxx:xxx"))
  2. if err != nil {
  3. panic(err)
  4. }
  5. channel := client.Channels.Get("test")

Publishing a message to a channel

  1. err = channel.Publish(ctx, "HelloEvent", "Hello!")
  2. if err != nil {
  3. panic(err)
  4. }
  5. // You can also publish multiple messages in a single request.
  6. err = channel.PublishMultiple(ctx, []*ably.Message{
  7. {Name: "HelloEvent", Data: "Hello!"},
  8. {Name: "ByeEvent", Data: "Bye!"},
  9. })
  10. if err != nil {
  11. panic(err)
  12. }
  13. // A REST client can publish messages on behalf of another client
  14. // by providing the connection key of that client.
  15. err := channel.Publish(ctx, "temperature", "12.7", ably.PublishWithConnectionKey("connectionKeyOfAnotherClient"))
  16. if err != nil {
  17. panic(err)
  18. }

Querying the History

  1. pages, err := channel.History().Pages(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. for pages.Next(ctx) {
  6. for _, message := range pages.Items() {
  7. fmt.Println(message)
  8. }
  9. }
  10. if err := pages.Err(); err != nil {
  11. panic(err)
  12. }

Presence on a channel

  1. pages, err := channel.Presence.Get().Pages(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. for pages.Next(ctx) {
  6. for _, presence := range pages.Items() {
  7. fmt.Println(presence)
  8. }
  9. }
  10. if err := pages.Err(); err != nil {
  11. panic(err)
  12. }

Querying the Presence History

  1. pages, err := channel.Presence.History().Pages(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. for pages.Next(ctx) {
  6. for _, presence := range pages.Items() {
  7. fmt.Println(presence)
  8. }
  9. }
  10. if err := pages.Err(); err != nil {
  11. panic(err)
  12. }

Fetching your application’s stats

  1. pages, err := client.Stats().Pages(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. for pages.Next(ctx) {
  6. for _, stat := range pages.Items() {
  7. fmt.Println(stat)
  8. }
  9. }
  10. if err := pages.Err(); err != nil {
  11. panic(err)
  12. }

Getting the channel status

  1. status, err := channel.Status(ctx)
  2. if err != nil {
  3. panic(err)
  4. }
  5. fmt.Print(status, status.ChannelId)

Authentication

Initialize ably.NewREST using ABLY_KEY. Check Authentication Doc for more information types of auth and it’s server/client-side usage.

  1. restClient, err := ably.NewREST(ably.WithKey("API_KEY"))

Token requests are signed using provided API_KEY and issued by your servers.

  1. // e.g. Gin server endpoint
  2. router.GET("/token", getToken)
  3. func getToken(c *gin.Context) {
  4. token, err := restClient.Auth.CreateTokenRequest(nil)
  5. c.IndentedJSON(http.StatusOK, token)
  6. }
  • When using WithAuthURL clientOption at client side, for JWT token response, contentType header should be set to text/plain or application/jwt. For ably.TokenRequest/ ably.TokenDetails, set it as application/json.

Using the Token auth at client side

WithAuthUrl clientOption automatically decodes response based on the response contentType, WithAuthCallback needs manual decoding based on the response. See official token auth documentation for more information.

  1. // Return token of type ably.TokenRequest, ably.TokenDetails or ably.TokenString
  2. authCallback := ably.WithAuthCallback(func(ctx context.Context, tp ably.TokenParams) (ably.Tokener, error) {
  3. // HTTP client impl. to fetch token, you can pass tokenParams based on your requirement
  4. tokenReqJsonString, err := requestTokenFrom(ctx, "/token");
  5. if err != nil {
  6. return nil, err
  7. }
  8. var req ably.TokenRequest
  9. err := json.Unmarshal(tokenReqJsonString, &req)
  10. return req, err
  11. })

If JWT token is returned by server

  1. authCallback := ably.WithAuthCallback(func(ctx context.Context, tp ably.TokenParams) (ably.Tokener, error) {
  2. jwtTokenString, err := requestTokenFrom(ctx, "/jwtToken"); // jwtTokenString starts with "ey"
  3. if err != nil {
  4. return nil, err
  5. }
  6. return ably.TokenString(jwtTokenString), err
  7. })

Configure logging

  • By default, internal logger prints output to stdout with default logging level of warning.
  • You need to create a custom Logger that implements ably.Logger interface.
  • There is also an option provided to configure loglevel.
  1. type customLogger struct {
  2. *log.Logger
  3. }
  4. func (s *customLogger) Printf(level ably.LogLevel, format string, v ...interface{}) {
  5. s.Logger.Printf(fmt.Sprintf("[%s] %s", level, format), v...)
  6. }
  7. func NewCustomLogger() *customLogger {
  8. logger := &customLogger{}
  9. logger.Logger = log.New(os.Stdout, "", log.LstdFlags)
  10. return logger
  11. }
  12. client, err = ably.NewRealtime(
  13. ably.WithKey("xxx:xxx"),
  14. ably.WithLogHandler(NewCustomLogger()),
  15. ably.WithLogLevel(ably.LogWarning),
  16. )

Proxy support

The ably-go SDK doesn’t provide a direct option to set a proxy in its configuration. However, you can use standard environment variables to set up a proxy for all your HTTP and HTTPS connections. The Go programming language will automatically handle these settings.

Setting Up Proxy via Environment Variables

To configure the proxy, set the HTTP_PROXY and HTTPS_PROXY environment variables with the URL of your proxy server. Here’s an example of how to set these variables:

  1. export HTTP_PROXY=http://proxy.example.com:8080
  2. export HTTPS_PROXY=http://proxy.example.com:8080
  • proxy.example.com is the domain or IP address of your proxy server.
  • 8080 is the port number of your proxy server.

Considerations

  • Protocol: Make sure to include the protocol (http or https) in the proxy URL.
  • Authentication: If your proxy requires authentication, you can include the username and password in the URL. For example: http://username:password@proxy.example.com:8080.

After setting the environment variables, the ably-go SDK will route its traffic through the specified proxy for both Rest and Realtime clients.

For more details on environment variable configurations in Go, you can refer to the official Go documentation on http.ProxyFromEnvironment.

Setting Up Proxy via custom http client

For Rest client, you can also set proxy by providing custom http client option ably.WithHTTPClient:

  1. ably.WithHTTPClient(&http.Client{
  2. Transport: &http.Transport{
  3. Proxy: proxy // custom proxy implementation
  4. },
  5. })

Important Note - Connection reliability is totally dependent on health of proxy server and ably will not be responsible for errors introduced by proxy server.

Note on usage of ablytest package

Although the ablytest package is available as a part of ably-go, we do not recommend using it as a sandbox for your own testing, since it’s specifically intended for client library SDKs and we don’t provide any guarantees for support or that it will remain publicly accessible.
It can lead to unexpected behaviour, since some beta features may be deployed on the sandbox environment so that they can be tested before going into production.

You should rather use, ably.NewRealtime by passing the ABLY_KEY, which would be using the Ably production environment.

  1. client, err := ably.NewRealtime(ably.WithKey("xxx:xxx"))

You can also use the control api to setup a test environment using https://github.com/ably/ably-control-go/.

Resources

Demo repositories hosted at ably-labs which use ably-go.

Broaden your knowledge of realtime in Go with these useful materials:

Requirements

Supported Versions of Go

Whenever a new version of Go is released, Ably adds support for that version. The Go Release Policy supports the last two major versions. This SDK follows the same policy of supporting the last two major versions of Go.

Breaking API Changes in Version 1.2.x

Please see our Upgrade / Migration Guide for notes on changes you need to make to your code to update it to use the new API introduced by version 1.2.x.

Users updating from version 1.1.5 of this library will note that there are significant breaking changes to the API.
Our current approach to versioning is not compliant with semantic versioning, which is why these changes are breaking despite presenting only a change in the minor component of the version number.

Feature support

This library targets the Ably 1.2 client library specification. List of available features for our client library SDKs can be found on our feature support matrix page.

Known limitations

As of release 1.2.0, the following are not implemented and will be covered in future 1.2.x releases. If there are features that are currently missing that are a high priority for your use-case then please contact Ably customer support. Pull Requests are also welcomed.

REST API

Realtime API

  • Channel suspended state is partially implemented. See suspended channel state.

  • Realtime Ping function is not implemented.

  • Message Delta Compression is not implemented.

  • Push Notification Target functional is not applicable for the SDK and thus not implemented.

Support, feedback and troubleshooting

Please visit https://faqs.ably.com/ for access to our knowledgebase. If you require support, please visit https://ably.com/support to submit a support ticket.

You can also view the community reported Github issues.

Contributing

For guidance on how to contribute to this project, see CONTRIBUTING.md.