Real-time messaging library for Go (Websocket and SockJS). WIP
Clone or download
Permalink
Failed to load latest commit information.
examples use js client from master branch Sep 17, 2018
internal Reliable recover based of global sequence identificator (#19) Sep 11, 2018
misc/proto Reliable recover based of global sequence identificator (#19) Sep 11, 2018
.gitignore oauth2 example Jul 7, 2018
.travis.yml disconnect if can not get last message id, go1.11 Aug 29, 2018
Gopkg.lock Reliable recover based of global sequence identificator (#19) Sep 11, 2018
Gopkg.toml Reliable recover based of global sequence identificator (#19) Sep 11, 2018
LICENSE Initial commit Feb 21, 2018
README.md update readme Sep 16, 2018
alias.go comments, tests, readme updates Jul 14, 2018
channel.go remove history_drop_inactive option (#21) Sep 17, 2018
client.go clean ups and comments, engine shutdown method, combine recovery meth… Sep 14, 2018
client_test.go clean ups and comments, engine shutdown method, combine recovery meth… Sep 14, 2018
config.go allow dot in namespace name Sep 30, 2018
disconnect.go stick with disconnect in case of expired sub Aug 2, 2018
engine.go clean ups and comments, engine shutdown method, combine recovery meth… Sep 14, 2018
engine_memory.go remove history_drop_inactive option (#21) Sep 17, 2018
engine_memory_test.go remove history_drop_inactive option (#21) Sep 17, 2018
engine_redis.go improve redis engine pubsub - fix race on error, performance (#22) Oct 5, 2018
engine_redis_test.go improve redis engine pubsub - fix race on error, performance (#22) Oct 5, 2018
engine_test.go Reliable recover based of global sequence identificator (#19) Sep 11, 2018
errors.go C2 refactor public api, remove api handlers (#11) Aug 1, 2018
events.go Subscription refresh support (#7) Jul 3, 2018
handler_sockjs.go Reliable recover based of global sequence identificator (#19) Sep 11, 2018
handler_sockjs_test.go exclude examples from travis tests, small code style improvements Jul 7, 2018
handler_websocket.go Reliable recover based of global sequence identificator (#19) Sep 11, 2018
handler_websocket_test.go add handler tests Jun 12, 2018
hub.go Reliable recover based of global sequence identificator (#19) Sep 11, 2018
hub_test.go expose original http request in transport Jul 22, 2018
logging.go C2 refactor public api, remove api handlers (#11) Aug 1, 2018
logging_test.go change names, gometalinter warning fixes Apr 10, 2018
metrics.go client testsuite prototype, recover metrics Aug 19, 2018
node.go improve redis engine pubsub - fix race on error, performance (#22) Oct 5, 2018
node_test.go clean ups and comments, engine shutdown method, combine recovery meth… Sep 14, 2018
transport.go expose original http request in transport Jul 22, 2018
transport_test.go more tests Apr 8, 2018

README.md

Join the chat at https://gitter.im/centrifugal/centrifuge Build Status GoDoc

Work in progress. Library has all features working but in active development stage so API is not stable at all. It has not been tested in production yet so use with caution. Feedback is highly appreciated.

Centrifuge library represents real-time core for Centrifugo server. It's also aimed to be a general purpose real-time messaging library for Go programming language.

Message transports:

  • Websocket transport with JSON or binary Protobuf protocol
  • SockJS polyfill library support (JSON only)

Features:

  • Fast and optimized for low-latency communication with thousands of client connections
  • Scaling to many nodes with Redis PUB/SUB, built-in Redis sharding, Sentinel for HA
  • Bidirectional asynchronous message communication, RPC calls
  • Channel (room) concept to broadcast message to all channel subscribers
  • Presence information for channels (show all active clients in channel)
  • History information for channels (last messages published into channel)
  • Join/leave events for channels (aka client goes online/offline)
  • Message recovery mechanism for channels to survive short network disconnects
  • MIT license

Clients (also work in progress but with most features already supported):

Godoc and examples

Installation

To install globally into $GOPATH use:

go get -u github.com/centrifugal/centrifuge

But recommended way is using tools like dep or go mod to add this library as dependency to your project.

Quick example

Let's take a look on how to build the simplest real-time chat ever with Centrifuge library. Clients will be able to open page in browser, connect to server over Websocket, send message into channel and this message will be instantly delivered to all active channel subscribers. On server side we will accept all connections and will work as simple PUB/SUB proxy without worrying too much about permissions. In this example we will use Centrifuge Javascript client on frontend.

Create file main.go with the following code:

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	// Import this library.
	"github.com/centrifugal/centrifuge"
)

func handleLog(e centrifuge.LogEntry) {
	log.Printf("%s: %v", e.Message, e.Fields)
}

// Wait until program interrupted. When interrupted gracefully shutdown Node.
func waitExitSignal(n *centrifuge.Node) {
	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sigs
		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
		defer cancel()
		n.Shutdown(ctx)
		done <- true
	}()
	<-done
}

func main() {
	// We use default config here as starting point. Default config contains
	// reasonable values for available options.
	cfg := centrifuge.DefaultConfig
	// In this example we want client to do all possible actions with server
	// without any authentication and authorization. Insecure flag DISABLES
	// many security related checks in library. This is only to make example
	// short. In real app you most probably want authenticate and authorize
	// access to server. See godoc and examples in repo for more details.
	cfg.ClientInsecure = true
	// By default clients can not publish messages into channels. Setting this
	// option to true we allow them to publish.
	cfg.Publish = true

	// Node is the core object in Centrifuge library responsible for many useful
	// things. Here we initialize new Node instance and pass config to it.
	node, _ := centrifuge.New(cfg)

	// On().Connect() method is a point where you create a binding between
	// Centrifuge and your app business logic. Callback function you pass
	// to On().Connect will be called every time new connection established
	// with server. Inside this callback function you can set various event
	// handlers for incoming client connection.
	node.On().Connect(func(ctx context.Context, client *centrifuge.Client, e centrifuge.ConnectEvent) centrifuge.ConnectReply {
		// Set Subscribe Handler to react on every channel subscribtion attempt
		// initiated by client. Here you can theoretically return an error or
		// disconnect client from server if needed. But now we just accept
		// all subscriptions.
		client.On().Subscribe(func(e centrifuge.SubscribeEvent) centrifuge.SubscribeReply {
			log.Printf("client subscribes on channel %s", e.Channel)
			return centrifuge.SubscribeReply{}
		})

		// Set Publish Handler to react on every channel Publication sent by client.
		// Inside this method you can validate client permissions to publish into
		// channel. But in our simple chat app we allow everyone to publish into
		// any channel.
		client.On().Publish(func(e centrifuge.PublishEvent) centrifuge.PublishReply {
			log.Printf("client publishes into channel %s: %s", e.Channel, string(e.Data))
			return centrifuge.PublishReply{}
		})

		// Set Disconnect Handler to react on client disconnect events.
		client.On().Disconnect(func(e centrifuge.DisconnectEvent) centrifuge.DisconnectReply {
			log.Printf("client disconnected")
			return centrifuge.DisconnectReply{}
		})

		// In our example transport will always be Websocket but it can also be SockJS.
		transportName := client.Transport().Name()
		// In our example clients connect with JSON protocol but it can also be Protobuf.
		transportEncoding := client.Transport().Encoding()

		log.Printf("client connected via %s (%s)", transportName, transportEncoding)
		return centrifuge.ConnectReply{}
	})

	// Centrifuge library exposes logs with different log level. In your app
	// you can set special function to handle these log entries in a way you want.
	node.SetLogHandler(centrifuge.LogLevelDebug, handleLog)

	// Run node will start node's underlying Engine, launch several
	// internal goroutines.
	if err := node.Run(); err != nil {
		panic(err)
	}

	// Configure http routes.

	// The first route is for handling Websocket connections.
	http.Handle("/connection/websocket", centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{}))

	// The second route is for serving index.html file.
	http.Handle("/", http.FileServer(http.Dir("./")))

	// Start HTTP server.
	go func() {
		if err := http.ListenAndServe(":8000", nil); err != nil {
			panic(err)
		}
	}()

	// Run program until interrupted.
	waitExitSignal(node)
}

Also create file index.html near main.go with content:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <!-- Note that we use client from c2 branch of centrifuge-js because its not released yet -->
        <!-- TODO: use client from master branch after client release -->
        <script type="text/javascript" src="https://rawgit.com/centrifugal/centrifuge-js/c2/dist/centrifuge.min.js"></script>
    </head>
    <body>
        <input type="text" id="input" />
        <script type="text/javascript">
            // Create Centrifuge object with Websocket endpoint address set in main.go
            var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
            function drawText(text) {
                var div = document.createElement('div');
                div.innerHTML = text;
                document.body.appendChild(div);
            }
            centrifuge.on('connect', function(ctx){
                drawText('Connected over ' + ctx.transport + '<br>');
            });
            centrifuge.on('disconnect', function(ctx){
                drawText('Disconnected: ' + ctx.reason + '<br>');
            });
            var sub = centrifuge.subscribe("chat", function(message) {
                drawText(JSON.stringify(message) + '<br>');
            })
            var input = document.getElementById("input");
            input.addEventListener('keyup', function(e) {
                if (e.keyCode == 13) { // ENTER key pressed
                    sub.publish(this.value);
                    input.value = '';
                }
            });
            // After setting event handlers – initiate actual connection with server.
            centrifuge.connect();
        </script>
    </body>
</html>

Then run Go program as usual:

go run main.go

Open several browser tabs with http://localhost:8000 and see chat in action.

This example is only the top of an iceberg. But it should give you an insight on library API.

Keep in mind that Centrifuge library is not a framework to build chat apps. It's a general purpose real-time transport for your messages with some helpful primitives. You can build many kinds of real-time apps on top of this library including chats but depending on application you may need to write business logic yourself.

For contributors

Currently library uses dep to manage dependencies. This is how you can clone library and install all required dependencies locally:

mkdir -p $GOPATH/src/github.com/centrifugal
git clone https://github.com/centrifugal/centrifuge.git $GOPATH/src/github.com/centrifugal/centrifuge
cd $GOPATH/src/github.com/centrifugal/centrifuge
dep ensure

Another way is using go get but all dependencies will be downloaded into your global $GOPATH in this case:

go get -u github.com/centrifugal/centrifuge