Git Product home page Git Product logo

go's Introduction

PubNub Go SDK

GoDoc Build Status codecov.io Go Report Card

This is the official PubNub Go SDK repository.

PubNub takes care of the infrastructure and APIs needed for the realtime communication layer of your application. Work on your app's logic and let PubNub handle sending and receiving data across the world in less than 100ms.

Requirements

  • Go (1.9+)

Get keys

You will need the publish and subscribe keys to authenticate your app. Get your keys from the Admin Portal.

Configure PubNub

  1. Integrate PubNub into your project using the go command:

    go get github.com/pubnub/go

    If you encounter dependency issues, use the dep ensure command to resolve them.

  2. Create a new file and add the following code:

    func main() {
        config := pubnub.NewConfigWithUserId("userId")
        config.SubscribeKey = "mySubscribeKey"
        config.PublishKey = "myPublishKey"
    
        pn := pubnub.NewPubNub(config)
    }

Add event listeners

listener := pubnub.NewListener()
doneConnect := make(chan bool)
donePublish := make(chan bool)

msg := map[string]interface{}{
    "msg": "Hello world",
}
go func() {
    for {
        select {
        case status := <-listener.Status:
            switch status.Category {
            case pubnub.PNDisconnectedCategory:
                // This event happens when radio / connectivity is lost
            case pubnub.PNConnectedCategory:
                // Connect event. You can do stuff like publish, and know you'll get it.
                // Or just use the connected event to confirm you are subscribed for
                // UI / internal notifications, etc
                doneConnect <- true
            case pubnub.PNReconnectedCategory:
                // Happens as part of our regular operation. This event happens when
                // radio / connectivity is lost, then regained.
            }
        case message := <-listener.Message:
            // Handle new message stored in message.message
            if message.Channel != "" {
                // Message has been received on channel group stored in
                // message.Channel
            } else {
                // Message has been received on channel stored in
                // message.Subscription
            }
            if msg, ok := message.Message.(map[string]interface{}); ok {
                fmt.Println(msg["msg"])
            }
            /*
                log the following items with your favorite logger
                    - message.Message
                    - message.Subscription
                    - message.Timetoken
            */

            donePublish <- true
        case <-listener.Presence:
            // handle presence
        }
    }
}()

pn.AddListener(listener)

Publish and subscribe

In this code, publishing a message is triggered when the subscribe call is finished successfully. The Publish() method uses the msg variable that is used in the following code.

msg := map[string]interface{}{
        "msg": "Hello world!"
}

pn.Subscribe().
    Channels([]string{"hello_world"}).
    Execute()

<-doneConnect

response, status, err := pn.Publish().
    Channel("hello_world").Message(msg).Execute()

if err != nil {
     // Request processing failed.
     // Handle message publish error
}

Documentation

API reference for Go

Support

If you need help or have a general question, contact [email protected].

go's People

Contributors

anovikov1984 avatar chandler767 avatar client-engineering-bot avatar crimsonred avatar davidnub avatar drichelson avatar edaniels avatar enobufs avatar jeffgreen7 avatar kleewho avatar maxpresman avatar michaljolender avatar parfeon avatar pedromg avatar pkaeding avatar sahroshan avatar seba-aln avatar sergeylanzman avatar technosophos avatar xavrax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go's Issues

Log file breaks production builds

Commit 399e5d1 is breaking our builds because it requires the process to have write access to the directory where the binary is executed (which in our case is an init script).

Could you either use the built-in log package (or log/syslog) or make the path to the log file configurable so we can put it in /var/log?

Idiomatic Go

I'm frustrated with the Pubnub Go API because it does not follow the guidelines common to all Go libraries. Would it be possible to fix the library to act more like a normal Go library?

While not an exhaustive list, the following issues violate the Go coding guidelines.

Package Name:

"By convention, packages are given lower case, single-word names; there should be no need for underscores or mixedCaps." http://golang.org/doc/effective_go.html#package-names

pubnubMessaging violates this idiom. Naming the package pubnub would be great. Even putting it in pubnub/messaging with the package name messaging would be better than what is currently here.

PubNubInit()

In idomatic Go, a constructor-like function should be called New() or NewXXX(), depending on whether there is more than one constructor per package.

Calling pubnubMessaging.PubnubInit() is not idiomatic Go code. pubnub.New() is. Using Init() is actually more confusing, as it is also idiomatic to call New().Init(), but never just Init().

Variable/Constant Names

In go, public variables begin with an uppercase letter, and private variables begin with a lowercase letter. Variables should not begin with an underscore. Typically, the naming convention for constants is that they always begin with an uppercase letter.

Race conditions in message sending

Running a Go app with go run -race is showing me a few race conditions when sending messages.

==================
WARNING: DATA RACE
Write by goroutine 14:
  github.com/pubnub/go/messaging.func·001()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:1978 +0x36e
  net/http.(*Transport).dial()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:442 +0xcc
  net/http.(*Transport).dialConn()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:496 +0xab
  net/http.func·018()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:472 +0x8c

Previous write by goroutine 17:
  github.com/pubnub/go/messaging.func·001()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:1978 +0x36e
  net/http.(*Transport).dial()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:442 +0xcc
  net/http.(*Transport).dialConn()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:496 +0xab
  net/http.func·018()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:472 +0x8c

Goroutine 14 (running) created at:
  net/http.(*Transport).getConn()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:474 +0x347
  net/http.(*Transport).RoundTrip()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:201 +0x584
  net/http.send()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:195 +0x62d
  net/http.(*Client).send()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:118 +0x200
  net/http.(*Client).doFollowingRedirects()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:343 +0xd31
  net/http.(*Client).Do()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:150 +0xc2
  github.com/pubnub/go/messaging.connect()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:2056 +0x154
  github.com/pubnub/go/messaging.(*Pubnub).httpRequest()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:1936 +0xe7
  github.com/pubnub/go/messaging.(*Pubnub).sendPublishRequest()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:590 +0x430
  github.com/pubnub/go/messaging.(*Pubnub).Publish()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:733 +0xddc

Goroutine 17 (running) created at:
  net/http.(*Transport).getConn()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:474 +0x347
  net/http.(*Transport).RoundTrip()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/transport.go:201 +0x584
  net/http.send()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:195 +0x62d
  net/http.(*Client).send()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:118 +0x200
  net/http.(*Client).doFollowingRedirects()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:343 +0xd31
  net/http.(*Client).Do()
      /usr/local/Cellar/go/1.3/libexec/src/pkg/net/http/client.go:150 +0xc2
  github.com/pubnub/go/messaging.connect()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:2056 +0x154
  github.com/pubnub/go/messaging.(*Pubnub).httpRequest()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:1936 +0xe7
  github.com/pubnub/go/messaging.(*Pubnub).sendPublishRequest()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:590 +0x430
  github.com/pubnub/go/messaging.(*Pubnub).Publish()
      /Users/mbutcher/Code/sugarmill/.godeps/src/github.com/pubnub/go/messaging/pubnub.go:733 +0xddc
==================

Subscription manager should respond with complete PNStatus

PNStatus not populated completely

res, _, err := executeRequest(opts)

Why are we ignoring the PNStatus returned by executeRequest here?

executeRequest has full context of the API response and returns a fully populated PNStatus which is ignored and it is then being recreated minimally with only PNStatus.Category information.

Solution

We should either be passing the returned response from executeRequest or construct the PNStatus fully.

Goroutine leaks?

The documentation suggests doing this:

go pubInstance.Publish(<pubnub channel>, <message to publish>, callbackChannel, errorChannel)
go ParseResponse(callbackChannel)
go ParseErrorResponse(errorChannel) 

It appears that Publish() will only send messages on one of the two channels. And (like a good Go receiver) never closes the channels that it receives.

Won't this lead to leaking at least one goroutine for every single Publish?

See, at least one of those two goroutines will block on <-myChannel, which means that goroutine will never be freed.

Wouldn't it make more sense to use a select{} on those two channels and react to whichever one returns first? And probably having a time.Timer to timeout long requests would be a good idea, too, right?

publishBuilder is not exported and annoying to use

A publishBuilder can be used like this:

publishBuilder := pn.Publish()

publishBuilder.Channel("hello_world").Message(msg).Execute()

But looking in go docs for the package there is no entry for Channel(), Message(), or Execute().
This happens because publishBuilder is not exported but has exported methods.

golang/lint#210 explains why there are lint rules for situations like this.

Panic when immediately calling Destroy

There are some cases where we want to publish messages before calling the Destroy function on the pubnub object, and other cases where there is nothing to publish and we call Destroy immediately.

This causes a panic on version github.com/pubnub/go/v7 v7.2.1, which is easily reproducible with a minimal setup

package main

import (
	pn "github.com/pubnub/go/v7"
)

func main() {
	config := pn.NewConfigWithUserId(pn.UserId("userid"))
	config.AuthKey = "xxxx"
	config.PublishKey = "yyyy"
	config.SubscribeKey = "zzzz"

	pubnub := pn.NewPubNub(config)
	// pubnub.GetClient()
	pubnub.Destroy()
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x5ec94e]

goroutine 1 [running]:
net/http.(*Client).transport(...)
	/usr/lib/go/src/net/http/client.go:201
net/http.(*Client).CloseIdleConnections(0xc0000a92c0?)
	/usr/lib/go/src/net/http/client.go:947 +0xe
github.com/pubnub/go/v7.(*PubNub).Destroy(0xc0000c4420)
	.../go/pkg/mod/github.com/pubnub/go/[email protected]/pubnub.go:748 +0x67f
main.main()
	.../projects/masv/misc/pubnub_panic_test/main.go:16 +0x13d

Calling GetClient() on the pubnub object before calling Destroy properly sets up the client/transport, which prevents the panic from ocurring.

Unless this is documented somewhere that I missed, I believe that pn.NewPubNub(config) should properly setup the client.

All functions are public, so which ones are part of the external API?

As the title says all functions in https://github.com/pubnub/go/blob/master/pubnubMessaging/pubnub.go are public making it impossible to determine which ones are part of the internal workings of the Go client implementation and which are part of the API. In Go public functions are ones that start with an uppercase letter, private ones are those that start with a lowercase one.

Looking at the code, it could do with a rewrite to make it more Go idiomatic. I would advise removing any API dependence on Go channels and doing something like the websocket implementation http://godoc.org/code.google.com/p/go.net/websocket

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.