Real-time messaging library for Go with scalability in mind.

Overview

Join the chat at https://t.me/joinchat/ABFVWBE0AhkyyhREoaboXQ Build Status codecov.io GoDoc

This library has no v1 release, API still evolves. Use with strict versioning. Before v1 release patch version updates only have backwards compatible changes and fixes, minor version updates can have backwards-incompatible API changes. For all breaking changes we provide a detailed changelog. Master branch can have unreleased code.

Centrifuge library is a real-time core of Centrifugo server. It's also supposed to be a general purpose real-time messaging library for Go programming language. The library built on top of strict client-server protocol schema and exposes various real-time oriented primitives for a developer. Centrifuge solves several problems a developer may come across when building complex real-time applications – like scalability (millions of connections), proper persistent connection management and invalidation, fast reconnect with message recovery, WebSocket fallback option.

Library highlights:

  • Fast and optimized for low-latency communication with millions of client connections. See test stand with 1 million connections in Kubernetes
  • Builtin bidirectional transports: WebSocket (JSON or binary Protobuf) and SockJS (JSON only)
  • Possibility to use unidirectional transports without using custom Centrifuge client library: see examples for GRPC, EventSource(SSE), Fetch Streams, Unidirectional WebSocket
  • Built-in horizontal scalability with Redis PUB/SUB, consistent Redis sharding, Sentinel and Redis Cluster for HA
  • Native authentication over HTTP middleware or custom token-based
  • Channel concept to broadcast message to all active subscribers
  • Client-side and server-side channel subscriptions
  • Bidirectional asynchronous message communication and RPC calls
  • Presence information for channels (show all active clients in a channel)
  • History information for channels (ephemeral streams with size and TTL retention)
  • Join/leave events for channels (aka client goes online/offline)
  • Possibility to register a custom PUB/SUB Broker and Presence Manager implementations
  • Message recovery mechanism for channels to survive PUB/SUB delivery problems, short network disconnects or node restart
  • Prometheus instrumentation
  • Client libraries for main application environments (see below)

For bidirectional communication between a client and a Centrifuge-based server we have a bunch of client libraries:

If you opt for a unidirectional communication then you may leverage Centrifuge possibilities without any specific library on client-side - simply by using native browser API or GRPC-generated code. See examples of unidirectional communication over GRPC, EventSource(SSE), Fetch Streams, WebSocket.

Explore Centrifuge

Installation

To install use:

go get github.com/centrifugal/centrifuge

go mod is a recommended way of adding this library to your project dependencies.

Quick example

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

Create file main.go with the following code:

package main

import (
	"log"
	"net/http"

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

// Authentication middleware example. Centrifuge expects Credentials
// with current user ID set. Without provided Credentials client
// connection won't be accepted. Another way to authenticate connection
// is reacting to node.OnConnecting event where you may authenticate
// connection based on a custom token sent by a client in first protocol
// frame. See _examples folder in repo to find real-life auth samples
// (OAuth2, Gin sessions, JWT etc).
func auth(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		// Put authentication Credentials into request Context.
		// Since we don't have any session backend here we simply
		// set user ID as empty string. Users with empty ID called
		// anonymous users, in real app you should decide whether
		// anonymous users allowed to connect to your server or not.
		cred := &centrifuge.Credentials{
			UserID: "",
		}
		newCtx := centrifuge.SetCredentials(ctx, cred)
		r = r.WithContext(newCtx)
		h.ServeHTTP(w, r)
	})
}

func main() {
	// We use default config here as a starting point. Default config
	// contains reasonable values for available options.
	cfg := centrifuge.DefaultConfig

	// Node is the core object in Centrifuge library responsible for
	// many useful things. For example Node allows to publish messages
	// to channels with its Publish method.
	node, err := centrifuge.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	// Set ConnectHandler called when client successfully connected to Node.
	// Your code inside a handler must be synchronized since it will be called
	// concurrently from different goroutines (belonging to different client
	// connections). See information about connection life cycle in library readme.
	// This handler should not block – so do minimal work here, set required
	// connection event handlers and return.
	node.OnConnect(func(client *centrifuge.Client) {
		// 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.
		transportProto := client.Transport().Protocol()
		log.Printf("client connected via %s (%s)", transportName, transportProto)

		// Set SubscribeHandler to react on every channel subscription attempt
		// initiated by a client. Here you can theoretically return an error or
		// disconnect a client from a server if needed. But here we just accept
		// all subscriptions to all channels. In real life you may use a more
		// complex permission check here. The reason why we use callback style
		// inside client event handlers is that it gives a possibility to control
		// operation concurrency to developer and still control order of events.
		client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
			log.Printf("client subscribes on channel %s", e.Channel)
			cb(centrifuge.SubscribeReply{}, nil)
		})

		// By default, clients can not publish messages into channels. By setting
		// PublishHandler we tell Centrifuge that publish from a client-side is
		// possible. Now each time client calls publish method this handler will be
		// called and you have a possibility to validate publication request. After
		// returning from this handler Publication will be published to a channel and
		// reach active subscribers with at most once delivery guarantee. In our simple
		// chat app we allow everyone to publish into any channel but in real case
		// you may have more validation.
		client.OnPublish(func(e centrifuge.PublishEvent, cb centrifuge.PublishCallback) {
			log.Printf("client publishes into channel %s: %s", e.Channel, string(e.Data))
			cb(centrifuge.PublishReply{}, nil)
		})

		// Set Disconnect handler to react on client disconnect events.
		client.OnDisconnect(func(e centrifuge.DisconnectEvent) {
			log.Printf("client disconnected")
		})
	})

	// Run node. This method does not block. See also node.Shutdown method
	// to finish application gracefully.
	if err := node.Run(); err != nil {
		log.Fatal(err)
	}

	// Now configure HTTP routes.

	// Serve Websocket connections using WebsocketHandler.
	wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{})
	http.Handle("/connection/websocket", auth(wsHandler))

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

	log.Printf("Starting server, visit http://localhost:8000")
	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <script type="text/javascript" src="https://rawgit.com/centrifugal/centrifuge-js/master/dist/centrifuge.min.js"></script>
        <title>Centrifuge library chat example</title>
    </head>
    <body>
        <input type="text" id="input" />
        <script type="text/javascript">
            // Create Centrifuge object with Websocket endpoint address set in main.go
            const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
            function drawText(text) {
                const div = document.createElement('div');
                div.innerHTML = text + '<br>';
                document.body.appendChild(div);
            }
            centrifuge.on('connect', function(ctx){
                drawText('Connected over ' + ctx.transport);
            });
            centrifuge.on('disconnect', function(ctx){
                drawText('Disconnected: ' + ctx.reason);
            });
            const sub = centrifuge.subscribe("chat", function(ctx) {
                drawText(JSON.stringify(ctx.data));
            })
            const input = document.getElementById("input");
            input.addEventListener('keyup', function(e) {
                if (e.keyCode === 13) {
                    sub.publish(this.value);
                    input.value = '';
                }
            });
            // After setting event handlers – initiate actual connection with server.
            centrifuge.connect();
        </script>
    </body>
</html>

Then run as usual:

go run main.go

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

While this example is only the top of an iceberg, it should give you a good insight on library API. Check out examples folder for more.

Keep in mind that Centrifuge library is not a framework to build chat applications. 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.

Tips and tricks

Some useful advices about library here.

Connection life cycle

Let's describe some aspects related to connection life cycle and event handling in Centrifuge:

  • If you set middleware for transport handlers (WebsocketHandler, SockjsHandler) – then it will be called first before a client sent any command to a server and handler had a chance to start working. Just like a regular HTTP middleware. You can put Credentials to Context to authenticate connection.
  • node.OnConnecting called as soon as client sent Connect command to server. At this point no Client instance exists. You have incoming Context and Transport information. You still can authenticate Client at this point (based on string token sent from client side or any other way). Also, you can add extra data to context and return modified context to Centrifuge. Context cancelled as soon as client connection closes. This handler is synchronous and connection read loop can't proceed until you return ConnectReply.
  • node.OnConnect then called (after a reply to Connect command already written to connection). Inside OnConnect closure you have a possibility to define per-connection event handlers. If particular handler not set then client will get ErrorNotAvailable errors requesting it. Remember that none of event handlers available in Centrifuge should block forever – do minimal work, start separate goroutines if you need blocking code.
  • Client initiated request handlers called one by one from connection reading goroutine. This includes OnSubscribe, OnPublish, OnPresence, OnPresenceStats, OnHistory, client-side OnRefresh, client-side OnSubRefresh.
  • Other handlers like OnAlive, OnDisconnect, server-side OnSubRefresh, server-side OnRefresh called from separate internal goroutines.
  • OnAlive handler must not be called after OnDisconnect.
  • Client initiated request handlers can be processed asynchronously in goroutines to manage operation concurrency. This is achieved using callback functions. See concurrency example for more details.

Channel history stream

Centrifuge Broker interface supports saving Publication to history stream on publish. Depending on Broker implementation this feature can be missing though. Builtin Memory and Redis brokers support keeping Publication stream.

When using default MemoryBroker Publication stream kept in process memory and lost as soon as process restarts. RedisBroker keeps Publication stream in Redis LIST or STREAM data structures – reliability inherited from Redis configuration in this case.

Centrifuge library publication stream not meant to be used as the only source of missed Publications for a client. It mostly exists to help many clients reconnect at once (load balancer reload, application deploy) without creating a massive spike in load on your main application database. So application database still required in idiomatic use case.

Centrifuge message recovery protocol feature designed to be used together with reasonably small Publication stream size as all missed publications sent towards client in one protocol frame on resubscribe to channel.

Logging

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.

// Function to handle Centrifuge internal logs.
func handleLog(e centrifuge.LogEntry) {
	log.Printf("%s: %v", e.Message, e.Fields)
}

cfg := centrifuge.DefaultConfig
cfg.LogLevel = centrifuge.LogLevelDebug
cfg.LogHandler = handleLog
Issues
  • Add Node.Send() method

    Add Node.Send() method

    The method is required to send personalized messages to channel subscribers when certain channel events occur. For example:

    
    	node, _ := centrifuge.New(cfg)
    	node.On().ClientConnected(func(ctx context.Context, client *centrifuge.Client) {
    		client.On().Message(func(e centrifuge.MessageEvent) centrifuge.MessageReply {
    
    			ci, _ := node.Presence("channel")
    			for uid := range ci {
    				node.Send(uid, []byte(uid))
    			}
    
    			return centrifuge.MessageReply{}
    		})
    	})
    
    
    opened by jekaspekas 30
  • History

    History

    Hi,A client when subscribing to a channel needs to get last n messages published in that channel. How can I handle this use case?Any reference for that? centrifuge-js is the client library I am using.

    opened by rakkeshravismile 18
  • Access client's subscribed channels in DisconnectEvent

    Access client's subscribed channels in DisconnectEvent

    We are looking to implement behaviour on client Disconnect based on the channels that the client was subscribed to. However, the client.close() function unsubscribes the client from all channels before calling the disconnect handler.

    Is there any way the client's channels can be passed into the DisconnectEvent?

    opened by EmandM 15
  • Swappable Persistence Backends?

    Swappable Persistence Backends?

    I'm curious if making the persistence backends swappable is on the roadmap for this library.

    In my specific use case, it would be nice to use the lightweight Bolt for persistence while still utilizing centrifuge's pub sub system, unlike the way you have it set up with Redis now which seems to require using Redis's pub/sub system if you want persistence.

    If there was an API for writing pluggable backends, I'd happily get started on one for bolt.

    opened by stemcc 12
  • Does it safe to put handleCommand in goroutine? (Manage client operations concurrency)

    Does it safe to put handleCommand in goroutine? (Manage client operations concurrency)

    In https://github.com/centrifugal/centrifuge/blob/master/client.go#L651 image

    Can I put it in to goroutine? Because slow rpc command can effect to all of application. I think it need to put to goroutine.

    P/s: sorry for my English. Thanks.

    opened by thanhps42 9
  • Multi tenant support

    Multi tenant support

    Hi!

    We have been using centrifuge in production for more than a year now, without any issues, so thanks for the awesome project. A couple weeks ago we started launching multiple websites using our api, and we need to separate them completely. Our api was desigend for this, so it was no issue, but we need to separate centrifuge channels as well.

    My first idea was:

    • user subscribes to some:channel
    • in centrifuge, I add support for multiple site, and modify the channel name to some:<somesite>.channel (eg: based on a header, or some values encoded in the jwt token)
    • when messages arrive on the some:<somesite>.channel, i send them out as some:channel

    and although it would be best for us, I found no mechanism in centrifuge where a channel name is rewritten, so this approach would be very difficult for me to implement. If you think this is a good idea I would still love for it to happen, but until then I found a different approach.

    My second idea:

    • user subscibes to some:<somesite>.channel
    • I validate that the user belongs to the site (eg: based on some values encoded in the jwt token)

    I could almost implement this using centrifuge as a library (instead of using centrifugo server). I made a handler for the client.Subscribed event. The only thing I miss is to be able to access the JWT token info in the client.On().Subscribe handler. I made a fork exposing the client.info, with that it works perfectly.

    opened by masterada 9
  • reporting node health

    reporting node health

    How is node health reported? Looked around in the code and couldn't see anything obvious.

    centrifugo's api has no healthcheck endpoint, while investigating how to create one I noticed that health would need to be reported by each node. Is that correct?

    To start simple we could just ping the configured engine, then report "ok" or "not ok".

    I'm willing to send a PR, but would probably need a little guidance to get me started.

    Great project btw. Thanks!

    opened by flavianmissi 9
  • Set Client.user w/o Disconnect/Connect events

    Set Client.user w/o Disconnect/Connect events

    Hello! I implement the user authentication process completely with WebSockets. As I understand it, changing Client.user now is possible only at node.On().ClientConnecting method (by return Credentials at ConnectReply struct). But Singin reguest is possible only after the Connecting event. And for add UserID to Client.user i have emit Disconnect/Connect events. I would like to avoid this. Is it possible to add Client.SetUserID method like this:

    // SetUserID set user ID.
    func (c *Client) SetUserID(userID string) {
    	c.mu.Lock()
    	c.user = userID
    	c.mu.Unlock()
    }
    

    P.S.: I have studied the source code and did not see difficulty when applying sharding with Client.SetUserID method.

    opened by jekaspekas 6
  • How to publish and subscribe to multiple channels?

    How to publish and subscribe to multiple channels?

    Hello, I'm currently working on a use-case wherein:

    1. a client should be able to publish to one or more channels (at the same time) and a subscriber should be able to subscribe to one or many channels (at the same time).
    2. publish to all subscribers without a client publish. Something like a PUT call with the channel name and the message to publish.

    Is this possible? Any directions for this use-case?

    Also, I see two handers NewWebsocketHandler and NewSockjsHandler. is WebsocketHandler a pure websocket handler with no means to downgrade to other methods where WS is not supported? So, if thats the case do you suggest using SockjsHandler in these scenarios?

    opened by nitin302 6
  • How to use protobuf bytes publish

    How to use protobuf bytes publish

    thanks all guys.

    image

    when im try to publish a protobuf data json: error calling MarshalJSON for type protocol.Raw: invalid character '$' looking for beginning of value /

    opened by godtoy 5
  • Decouple Centrifugo specific channel namespace configuration from library core

    Decouple Centrifugo specific channel namespace configuration from library core

    At moment Centrifuge library follows Centrifugo in terms of subscribe and publish permissions. While this is fine for Centrifugo - having namespaces and predefined channel rules inside a library can be a bit confusing for users.

    What I want to achieve with this library is make it only a reasonable wrapper over client-server protocol without many opinionated logic on top of it. I.e. give developers a possibility to define their own channel rules and permission checks.

    Another confusing thing in my opinion is that if we don't define On().Subscribe() handler then client will be able to subscribe on all channels. I believe the default behaviour should be: not allow subscription at all if subscribe handler not set for connection.

    That said I'd want to decouple namespace and channel permission logic from Centrifuge library core since it adds a very opinionated layer to library. Maybe this process will also require decoupling JWT parsing logic too.

    opened by FZambia 5
  • Support Redis 7 sharded PUB/SUB

    Support Redis 7 sharded PUB/SUB

    Our team are looking into solutions for automating adding redis capacity with no downtime (we are less concerned with automatic downscaling, but that would also be nice to have). We are wondering if this is something anyone has experience or thoughts on? All the examples in this repository seem to assume all redis capacity is known on centrifuge startup.

    We are also wondering if you have run benchmarks with the different configurations of redis broker found here https://centrifugal.github.io/centrifugo/server/engines/#redis-engine or if you have any opinions on which performs better.

    opened by EmandM 9
  • WebTransport support

    WebTransport support

    An issue to track WebTransport support:

    • [x] Make a POC based on quic-go Webtransport pull request
    • [ ] Make sure Disconnect logic matches ours (i.e. WT can be closed with code and string reason)
    • [ ] Add StreamCommandDecoder which accepts io.Reader and exposes Commands
    • [ ] Modify DataEncoder to always append \n to JSON, for single WebSocket messages we have fast path which does not use DataEncoder – so for single messages \n won't be included into payload sent.
    • [ ] Expose handleCommand to Client's public API. Maybe remove Client.Handle and move command decoding to Transport.
    • [ ] With the base above introduce an example with NewWebtransportHandler and WebtransportConfig which uses quic-go for WT stuff.
    opened by FZambia 0
  • Investigate possible migration to github.com/go-redis/redis

    Investigate possible migration to github.com/go-redis/redis

    My initial quick measurements for asking stream top showed that we can get ~25% performance boost in Redis benchmarks if migrating from Redigo to github.com/go-redis/redis/v8. But this requires more effort to measure in various scenarios.

    github.com/go-redis/redis also supports Redis Cluster and Sentinel so I believe we can migrate related parts too. The good thing is that those are supported by github.com/go-redis/redis natively while currently with redigo we are using 2 additional libs for this: github.com/mna/redisc and github.com/FZambia/sentinel.

    Since we have pretty big Redis related code base we may want to migrate step-by-step.

    opened by FZambia 1
Releases(v0.23.0)
  • v0.23.0(Jun 28, 2022)

    This release is a work concentrated around two main things:

    • More work on client protocol v2. This should become part of Centrifugo v4. New SDKs which work over new protocol and have new API will be soon released. SDKs will behave according to client SDK API spec. Probably in next major release of Centrifuge we will switch using protocol v2 by default. For now things should be backwards compatible with current protocol.
    • Introducing our own EXPERIMENTAL bidirectional emulation layer using HTTP-streaming and EventSource transports. There are a couple of examples which demonstrate how to use it. Examples located in _examples/experimental directory (they require new centrifuge-js SDK served locally from v3_dev branch). The important thing about our emulation implementation is that it does not require sticky sessions on load balancer in distributed case. This should be a more lightweight alternative to SockJS, and the cool thing is that our new Javascript SDK will be able to automatically fallback to HTTP-based transports in case of problems with WebSocket connection. More information will be available soon upon Centrifugo v4 release.

    Lots of changes here but in most cases it should be straightforward to adapt. Don't hesitate to reach out with questions in community chat rooms.

    Some important release highlights:

    • DefaultConfig removed, use Config{} as a starting point. I.e. centrifuge.New(centrifuge.Config{}) is equivalent to centrifuge.New(centrifuge.DefaultConfig).
    • We are avoiding using pointers for Disconnects. We got rid of nil Disconnect inside OnDisconnect callback when connection closing was not forced by a server. Where we previously had nil we now always have DisconnectConnectionClosed. This should make library usage more safe and better describes the reason of disconnection.
    • Refactor unsubscribe reasons, make unsubscribe first-class citizen with unsubscribe codes.
    • Introducing Temporary flag for Error to indicate temporary errors to a client. This allows making Subscriptions more resilient in client protocol v2 - subscriptions will re-subscribe automatically upon receiving temporary errors.
    • There are updated rules in code numbers used for errors, unsubscribes and disconnects. This allows splitting code number space and avoid code overlap. While these rules may be tricky to follow – we believe that in most cases library users do not deal with them a lot in application code:
      • For errors: error codes must be in range [0, 1999]. Codes [0, 99] are reserved for client-side errors. Codes [100, 399] are reserved for Centrifuge library internal usage. So applications must use codes in range [400, 1999] when creating custom errors.
      • For unsubscribe codes: codes must be in range [2000, 2999]. Unsubscribe codes >= 2500 coming from server to client result into resubscribe attempt in client protocol V2. Codes [2000, 2099] and [2500, 2599] are reserved for Centrifuge library internal usage. In client protocol v2 we are making Subscriptions to behave isolated from Connection. For example, some individual subscriptions can expire, but it does not result into connection close, only that individual Subscription will re-subscribe if required.
      • For disconnect codes: codes must be in range [3000, 4999]. Codes [3000, 3999] are reserved for Centrifuge library internal usage. Upon receiving disconnect code in range [3000, 3499] or [4000, 4499] client won't reconnect to a server. Splitting disconnect codes to ranges allows getting rid of sending JSON-encoded data in WebSocket CLOSE frame in client protocol v2. Thus – less network traffic and more lightweight disconnection process.
    • OnStateSnapshot callback for connection to return Client current state to the external code, useful for connection introspection. This is EXPERIMENTAL and a subject to change.
    • Remove an unnecessary lock - #230

    As you can see many changes in this release are concentrated around making library more strict in some aspects, this is a part of standardization and unifying client protocol and SDK API/behavior we want to achieve.

    ❯ gorelease -base v0.22.2 -version v0.23.0
    # github.com/centrifugal/centrifuge
    ## incompatible changes
    (*Client).Disconnect: changed from func(*Disconnect) to func(...Disconnect)
    (*Client).Unsubscribe: changed from func(string) error to func(string, ...Unsubscribe)
    DefaultConfig: removed
    DisconnectBadRequest: changed from *Disconnect to Disconnect
    DisconnectChannelLimit: changed from *Disconnect to Disconnect
    DisconnectConnectionLimit: changed from *Disconnect to Disconnect
    DisconnectEvent.Disconnect: changed from *Disconnect to Disconnect
    DisconnectExpired: changed from *Disconnect to Disconnect
    DisconnectForceNoReconnect: changed from *Disconnect to Disconnect
    DisconnectForceReconnect: changed from *Disconnect to Disconnect
    DisconnectInsufficientState: changed from *Disconnect to Disconnect
    DisconnectInvalidToken: changed from *Disconnect to Disconnect
    DisconnectNoPong: changed from *Disconnect to Disconnect
    DisconnectNormal: removed
    DisconnectServerError: changed from *Disconnect to Disconnect
    DisconnectShutdown: changed from *Disconnect to Disconnect
    DisconnectSlow: changed from *Disconnect to Disconnect
    DisconnectStale: changed from *Disconnect to Disconnect
    DisconnectSubExpired: changed from *Disconnect to Disconnect
    DisconnectWriteError: changed from *Disconnect to Disconnect
    Error.Error: removed
    Hub: old is comparable, new is not
    Transport.Close: changed from func(*Disconnect) error to func(Disconnect) error
    TransportInfo.Emulation: added
    UnsubscribeEvent.Reason: removed
    UnsubscribeReason: removed
    UnsubscribeReasonClient: removed
    UnsubscribeReasonDisconnect: removed
    UnsubscribeReasonServer: removed
    WithDisconnect: removed
    ## compatible changes
    (*Client).OnStateSnapshot: added
    (*Client).StateSnapshot: added
    (*Disconnect).CloseText: added
    (*Hub).Connections: added
    Disconnect.Error: added
    Disconnect.String: added
    DisconnectConnectionClosed: added
    DisconnectEvent.Code: added
    DisconnectEvent.Reason: added
    DisconnectEvent.Reconnect: added
    EmulationConfig: added
    EmulationHandler: added
    Error.Temporary: added
    HTTPStreamConfig: added
    HTTPStreamHandler: added
    NewEmulationHandler: added
    NewHTTPStreamHandler: added
    NewSSEHandler: added
    SSEConfig: added
    SSEHandler: added
    StateSnapshotHandler: added
    SubscribeEvent.Positioned: added
    SubscribeEvent.Recoverable: added
    Unsubscribe.String: added
    Unsubscribe: added
    UnsubscribeCodeClient: added
    UnsubscribeCodeDisconnect: added
    UnsubscribeCodeExpired: added
    UnsubscribeCodeInsufficient: added
    UnsubscribeCodeServer: added
    UnsubscribeEvent.Code: added
    UnsubscribeEvent.Unsubscribe: added
    WithCustomDisconnect: added
    WithCustomUnsubscribe: added
    
    # summary
    v0.23.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.22.2(Apr 25, 2022)

  • v0.21.1(Feb 25, 2022)

  • v0.21.0(Feb 10, 2022)

    • It's now possible to use Config directly when creating new Centrifuge Node, without using DefaultConfig which is now deprecated.
    • Removed some constants with default values, added better comments which reflect zero value behavior - no need to jump to const definition when reading code/docs.
    • Some allocation optimizations in WebSocket disconnect process.
    • Continue working on ProtocolVersion2 – introducing application-level server-to-client pings. This is still EXPERIMENTAL at the moment and may be changed in later releases. #224
    gorelease -base v0.20.0 -version v0.21.0
    # github.com/centrifugal/centrifuge
    ## incompatible changes
    DefaultWebsocketMessageSizeLimit: removed
    DefaultWebsocketPingInterval: removed
    DefaultWebsocketWriteTimeout: removed
    TransportInfo.AppLevelPing: added
    ## compatible changes
    AppLevelPing: added
    DisconnectNoPong: added
    SockjsConfig.AppLevelPingInterval: added
    SockjsConfig.AppLevelPongTimeout: added
    WebsocketConfig.AppLevelPingInterval: added
    WebsocketConfig.AppLevelPongTimeout: added
    WebsocketConfig.PongTimeout: added
    
    # summary
    v0.21.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.20.0(Jan 26, 2022)

    • Support client protocol v2. As of v0.20.0 it's considered experimental and can have some adjustments in the following releases. But the plan is to make it default at some point. The initial motivation described in #217 and implementation is in #218. Client connectors which support client protocol v2 will be released soon. Both WebsocketConfig and SockjsConfig now have an option to set default protocol version handler will expect from connecting clients. It's also possible to override that option by using cf_protocol_version URL parameter (v1 or v2) when connecting to the server. This should provide a way to migrate to new protocol gradually.
    • Refactor disconnect semantics for client protocol v2. We are getting rid of JSON in close reason by introducing strict ranges for disconnect codes - see #221. Client connectors will expose disconnect codes when working with client protocol v2. Client-side disconnect reasons will also have its own codes – according to this comment.
    • Various optimizations in message broadcast, client command handling, client initial connect – fewer things now escape to the heap.
    • TransportWriteEvent.IsPush field is removed (we can discuss putting it back later if required).
    • Node Survey API now allows choosing the node to which we want to send survey request.
    • Warn log level introduced between Info and Error.
    • Publication now has Tags field (map[string]string) – this may help to put some useful info into publication without modifying payload. It can help to avoid processing payload in some scenarios.
    • Support for setting auth user in Redis shard configuration – for Redis itself and for Sentinel. This is useful if ACL-based auth used on Redis side.
    gorelease -base v0.19.0 -version v0.20.0
    # github.com/centrifugal/centrifuge
    ## incompatible changes
    (*Disconnect).CloseText: changed from func() string to func(ProtocolVersion) string
    (*Node).Survey: changed from func(context.Context, string, []byte) (map[string]SurveyResult, error) to func(context.Context, string, []byte, string) (map[string]SurveyResult, error)
    LogLevelError: value changed from 4 to 5
    PublishOptions: old is comparable, new is not
    PublishReply: old is comparable, new is not
    TransportInfo.ProtocolVersion: added
    TransportWriteEvent.IsPush: removed
    ## compatible changes
    LogLevelWarn: added
    ProtocolVersion1: added
    ProtocolVersion2: added
    ProtocolVersion: added
    Publication.Tags: added
    PublishOptions.Tags: added
    RedisShardConfig.SentinelUser: added
    RedisShardConfig.User: added
    SockjsConfig.ProtocolVersion: added
    WebsocketConfig.ProtocolVersion: added
    WithTags: added
    
    # summary
    v0.20.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.19.0(Dec 3, 2021)

    • JSON protocol performance improvements. See #215 for details. We are now more strict in parsing multiple command frames: in a multiple command JSON frame individual Commands must be separated by exactly one new line symbol and have an optional new line after the last command. This was always this way and current client connectors work according to these requirements – but since the parser becoming more strict this can theoretically cause some problems with third-party connector implementations.
    • Support custom data from a client passed in a subscribe command, this data is then available in SubscribeEvent.
    gorelease -base v0.18.9 -version v0.19.0
    # github.com/centrifugal/centrifuge
    ## incompatible changes
    SubscribeEvent: old is comparable, new is not
    ## compatible changes
    SubscribeEvent.Data: added
    
    # summary
    v0.19.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.18.9(Nov 23, 2021)

    • Add unsubscribe Reason and optional Disconnect to UnsubscribeEvent. See issue #211 and pr #213.
    gorelease -base v0.18.8 -version v0.18.9
    # github.com/centrifugal/centrifuge
    ## compatible changes
    UnsubscribeEvent.Disconnect: added
    UnsubscribeEvent.Reason: added
    UnsubscribeReason: added
    UnsubscribeReasonClient: added
    UnsubscribeReasonDisconnect: added
    UnsubscribeReasonServer: added
    
    # summary
    v0.18.9 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.18.8(Oct 18, 2021)

  • v0.18.7(Oct 16, 2021)

  • v0.18.6(Oct 6, 2021)

  • v0.18.5(Oct 2, 2021)

  • v0.18.4(Sep 11, 2021)

  • v0.18.3(Sep 10, 2021)

    • Fix unexpected end of JSON input errors in Javascript client with Centrifuge >= v0.18.0 when publishing formatted JSON (with new lines). See centrifugal/protocol#10. Pull request also removes one extra allocation (data copy) during JSON and Protobuf protocol Push message encoding. As the result JSON without new lines will be encoded even faster, Protobuf messages will be encoded faster regardless new lines in payloads. JSON encoding of payload with new lines will require additional allocation since we are stripping new lines now. The penalty is not critical though - see benchmarks in mentioned pull request.
    Source code(tar.gz)
    Source code(zip)
  • v0.18.2(Aug 30, 2021)

  • v0.18.1(Aug 28, 2021)

  • v0.18.0(Aug 28, 2021)

    This release has several backward incompatible changes. Changes should affect the small number of Centrifuge library users, and it should be possible to adapt only by slightly modifying server-side code. Follow release notes carefully.

    • Breaking change. Client history API behavior changed. Now history call does not return all publications by default (#196). See an advice how you can handle this change in a backwards compatible way below.
    • Breaking change. Redis STREAM data structure now used by default to keep a publication history in Redis Engine (#195). Previously Centrifuge Redis Engine used LIST data structure by default. See RedisBrokerConfig.UseLists option to turn on previous behavior. Redis streams is a recommended way though, support for LIST data structure may be eventually removed.
    • Breaking change. Transport.Encoding removed. It turns out that this option is not really useful for the Centrifuge library and can only cause confusion.
    • Breaking change. Change Node.History behavior – Unrecoverable Position error now returned based on the wrong epoch only.
    • Breaking change. Remove deprecated seq/gen fields - see #197. Those were deprecated for a long time.
    • Breaking change. Client.Connect now does not return an error – this allows Centrifuge to automatically send a proper Disconnect push towards the connection.
    • Breaking change. WithClientWhitelist renamed to WithDisconnectClientWhitelist.
    • Much faster JSON client protocol. Expect at least 4x speedup for small messages JSON encoding/decoding. For large messages the difference can be even bigger. This is possible due to using code generation for encoding and a faster library for JSON decoding in centrifugal/protocol package. See centrifugal/protocol#8.
    • Message broadcast allocates less - see #193. Can be noticeable when broadcasting messages to large number of active subscribers. The side effect of this change is that Transport implementations should now have Write and WriteMany methods.
    • Centrifuge now uses official Protobuf library for Go with planetscale/vtprotobuf code generator instead of gogo/protobuf library which is not maintained these days anymore. The performance of Protobuf marshaling/unmarshaling is comparable.
    • New Config.UseSingleFlight option added. The option can help to reduce the load on Broker and Presence manager during massive reconnect and history synchronization scenarios.
    • WebSocket subprotocol is now can be used for switching to Protobuf protocol (#194). This will help to avoid adding ?format=protobuf in WebSocket connection URL.
    • OnTransportWrite callback added to inject custom logic before actual write to a client connection.
    • OnNodeInfoSend callback added to attach custom data to Node control frame.
    • Client.Info method which returns a copy of the connection info (set by Credentials).
    • Node.History now supports iteration in reversed order (#201).
    • Client.Refresh and Node.Refresh methods added to prolong/expire connections (useful for unidirectional transports).
    • GRPC unidirectional transport example improvements

    Regarding client history API change. So previously when a client called history it received all publications in a stream by default. In Centrifuge v0.18.0 it will only receive current stream top position (offset and epoch) without any publications.

    To mimic previous behavior you can use code like this:

    node.OnConnect(func(client *centrifuge.Client) {
        client.OnHistory(func(e centrifuge.HistoryEvent, cb centrifuge.HistoryCallback) {
            if e.Filter.Limit == 0 {
                result, err := node.History(e.Channel,
                    centrifuge.WithSince(e.Filter.Since),
                    centrifuge.WithLimit(centrifuge.NoLimit),
                )
                if err != nil {
                    cb(centrifuge.HistoryReply{}, err)
                    return
                }
                cb(centrifuge.HistoryReply{Result: &result}, nil)
                return
            }
            cb(centrifuge.HistoryReply{}, nil)
        })
    })
    

    I.e. explicitly handle zero limit and return all publications in response (using centrifuge.NoLimit). Then upgrade clients to use recent Centrifuge clients (will be released soon after Centrifuge v0.18.0) which allow setting limit explicitly and remove this custom logic eventually from the server code.

    gorelease -base v0.17.1 -version v0.18.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*Client).Connect: changed from func(ConnectRequest) error to func(ConnectRequest)
    - CompatibilityFlags: removed
    - EncodingType: removed
    - EncodingTypeBinary: removed
    - EncodingTypeJSON: removed
    - NodeInfo: old is comparable, new is not
    - RedisBrokerConfig.UseStreams: removed
    - Transport.Write: changed from func(...[]byte) error to func([]byte) error
    - Transport.WriteMany: added
    - TransportInfo.Encoding, method set of Transport: removed
    - TransportInfo.Encoding: removed
    - UseSeqGen: removed
    - WithClientWhitelist: removed
    Compatible changes:
    - (*Client).Info: added
    - (*Client).Refresh: added
    - (*Hub).NumSubscriptions: added
    - (*Hub).UserConnections: added
    - (*Node).OnNodeInfoSend: added
    - (*Node).OnTransportWrite: added
    - (*Node).Refresh: added
    - Config.UseSingleFlight: added
    - ConnectEvent.Channels: added
    - DisconnectChannelLimit: added
    - HistoryFilter.Reverse: added
    - HistoryOptions.Reverse: added
    - NodeInfo.Data: added
    - NodeInfo.NumSubs: added
    - NodeInfoSendHandler: added
    - NodeInfoSendReply: added
    - RedisBrokerConfig.UseLists: added
    - RefreshOption: added
    - RefreshOptions: added
    - SubscribeOptions.RecoverSince: added
    - TransportWriteEvent: added
    - TransportWriteHandler: added
    - WithDisconnectClient: added
    - WithDisconnectClientWhitelist: added
    - WithRecoverSince: added
    - WithRefreshClient: added
    - WithRefreshExpireAt: added
    - WithRefreshExpired: added
    - WithRefreshInfo: added
    - WithReverse: added
    
    v0.18.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.17.1(Jun 23, 2021)

  • v0.17.0(Apr 10, 2021)

    • Remove unidirectional WebSocket support from builtin WebSocket transport. This was done to not force transport implementation details and to reduce code in a library core. It's still possible to build unidirectional WebSocket transport - just not with a builtin WebsocketHandler, see example.
    • Introduce node.Notify method. Notify allows sending an asynchronous notification to all other node (or to a single specific node). Unlike Survey it does not wait for any response
    • Since option of history call renamed to WithSince for consistency with other option names
    gorelease -base v0.16.0 -version v0.17.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - Since: removed
    - WebsocketConfig.Unidirectional: removed
    Compatible changes:
    - (*Node).Notify: added
    - (*Node).OnNotification: added
    - NotificationEvent: added
    - NotificationHandler: added
    - WithSince: added
    
    Source code(tar.gz)
    Source code(zip)
  • v0.16.0(Mar 22, 2021)

    This release is huge. The list of changes may look scary - but most changes should be pretty straightforward to adopt.

    Highlights:

    • Support for unidirectional clients, this opens a road to more adoption of Centrifuge for cases where bidirectional communication is not really needed. Unidirectional support is still a subject to change in future versions as soon as more feedback appears – for now Centrifuge has examples for GRPC, EventSource(SSE), Fetch Streams, Unidirectional WebSocket transports. The beauty here is that you don't need to use any Centrifuge client library to receive real-time updates - just use native browser APIs or GRPC generated code with simple decoding step.
    • The introduction of unidirectional transport required to change Transport interface a bit. The important thing is that it's now a responsibility of Transport.Write to properly encode data to JSON-streaming or Protobuf length-delimited format
    • Centrifuge now uses same-origin policy by default when checking incoming WebSocket or SockJS request due to security considerations (prevent CSRF attack), previously default check allowed all connections. If you want to mimic previous behavior then pass custom check functions to handler configurations – see example below.
    • New Subscribe method of Node - to subscribe user to server-side channels cluster-wide - see example that demonstrates new API
    • Engine interface removed - now Centrifuge only has separate Broker and PresenceManager entities. This changes how you should set up Redis to scale Nodes - see updated Redis example - it's now required to provide Redis Broker and Redis Presence Manager separately
    • Trace level logging (to see all protocol frames, obviously mostly suitable for development)
    • WithResubscribe option of unsubscribe removed - it never worked properly in client libraries and had no clearly defined semantics
    • It is now possible to return custom data in Subscribe result or in Subscribe Push
    • Broker.PublishControl method signature changed - it now accepts shardKey string argument, this is not used at the moment but can be used later if we will need an order of control messages
    • PresenceManager.AddPresence signature changed - now presence expiration time is an option of PresenceManager itself
    • Field version of ConnectResult is now omitted from JSON if empty
    • Server-side subscriptions now trigger unsubscribe event (with ServerSide boolean flag set to true)
    • Slightly faster Redis history decoding - commit
    • Hub container now shards connections and subscriptions - this can reduce lock contention significantly in some workloads thus reducing operation latency. See #184
    • Various example improvements

    That's what you have to do if you want to accept all connections without same-origin check introduced in v0.16.0 (can lead to CSRF vulnerability in some situations):

    wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    })
    
    sockjsHandler := centrifuge.NewSockjsHandler(node, centrifuge.SockjsConfig{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
        WebsocketCheckOrigin: func(r *http.Request) bool {
            return true
        },  
    })
    

    All changes:

    $ gorelease -base v0.15.0 -version v0.16.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*Client).Subscribe: changed from func(string) error to func(string, ...SubscribeOption) error
    - (*Client).Unsubscribe: changed from func(string, ...UnsubscribeOption) error to func(string) error
    - (*Node).SetEngine: removed
    - Broker.PublishControl: changed from func([]byte, string) error to func([]byte, string, string) error
    - Config.ClientPresenceExpireInterval: removed
    - Engine: removed
    - LogLevelDebug: value changed from 1 to 2
    - LogLevelError: value changed from 3 to 4
    - LogLevelInfo: value changed from 2 to 3
    - MemoryEngine: removed
    - MemoryEngineConfig: removed
    - NewMemoryEngine: removed
    - NewRedisEngine: removed
    - PresenceManager.AddPresence: changed from func(string, string, *ClientInfo, time.Duration) error to func(string, string, *ClientInfo) error
    - RedisEngine: removed
    - RedisEngineConfig: removed
    - RedisShardConfig.ClusterAddrs: removed
    - RedisShardConfig.Host: removed
    - RedisShardConfig.Port: removed
    - RedisShardConfig.Prefix: removed
    - RedisShardConfig.PubSubNumWorkers: removed
    - RedisShardConfig.SentinelAddrs: removed
    - Transport.Write: changed from func([]byte) error to func(...[]byte) error
    - TransportInfo.DisabledPushFlags: added
    - TransportInfo.Unidirectional: added
    - UnsubscribeOptions.Resubscribe: removed
    - WithResubscribe: removed
    Compatible changes:
    - (*Client).Connect: added
    - (*Node).Subscribe: added
    - ConnectRequest: added
    - DefaultRedisBrokerPrefix: added
    - DefaultRedisConnectTimeout: added
    - DefaultRedisPresenceManagerPrefix: added
    - DefaultRedisPresenceTTL: added
    - DefaultRedisReadTimeout: added
    - DefaultRedisWriteTimeout: added
    - LogLevelTrace: added
    - MemoryBroker: added
    - MemoryBrokerConfig: added
    - MemoryPresenceManager: added
    - MemoryPresenceManagerConfig: added
    - NewMemoryBroker: added
    - NewMemoryPresenceManager: added
    - NewRedisBroker: added
    - NewRedisPresenceManager: added
    - NewRedisShard: added
    - PushFlagConnect: added
    - PushFlagDisconnect: added
    - PushFlagJoin: added
    - PushFlagLeave: added
    - PushFlagMessage: added
    - PushFlagPublication: added
    - PushFlagSubscribe: added
    - PushFlagUnsubscribe: added
    - RedisBroker: added
    - RedisBrokerConfig: added
    - RedisPresenceManager: added
    - RedisPresenceManagerConfig: added
    - RedisShard: added
    - RedisShardConfig.Address: added
    - RedisShardConfig.ClusterAddresses: added
    - RedisShardConfig.SentinelAddresses: added
    - SubscribeOption: added
    - SubscribeOptions.Data: added
    - SubscribeRequest: added
    - UnsubscribeEvent.ServerSide: added
    - WebsocketConfig.Unidirectional: added
    - WithChannelInfo: added
    - WithExpireAt: added
    - WithJoinLeave: added
    - WithPosition: added
    - WithPresence: added
    - WithRecover: added
    - WithSubscribeClient: added
    - WithSubscribeData: added
    - WithUnsubscribeClient: added
    
    v0.16.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.15.0(Jan 25, 2021)

    • Add Node.Survey method – it allows gathering results from all running nodes. It's possible to define your own survey handlers. See example. Keep in mind that Survey does not scale very well as number of Centrifuge Nodes grows. Though it has reasonably good performance to perform rare tasks even with relatively large number of nodes – see benchmark in pull request
    • The main motivation of adding Node.Survey was attempt to remove Broker.Channels method – which is not supported by most of existing PUB/SUB brokers and does not work in Redis cluster. Broker.Channels now removed, but it can be replaced with survey if needed
    • Improve clustering - node will now send a SHUTDOWN message so other nodes have a chance to realize that node left cluster almost immediately
    • Signature of Since history call option changed – it now accepts a pointer to StreamPosition. This change simplifies a code to construct history call
    • Added SubscribeOptions.Position boolean flag to enable positioning in channel stream. Positioning means that Centrifuge will check that the client did not miss any message from PUB/SUB system, as soon as loss detected client will be disconnected with Insufficient State reason. This is very similar to what Recover: true option did, but Position: true does not enable recovery. As soon as Position flag enabled Centrifuge will expose top stream StreamPosition information to a client in Subscribe Reply
    • Added possibility to iterate over a channel history stream from client side. See an example that demonstrates this
    • New Config options: HistoryMaxPublicationLimit and RecoveryMaxPublicationLimit to control maximum number of publications to return during history call or recovery process. See Centrifuge documentation for detailed description
    • New example that shows Centrifuge integration with Tarantool. Tarantool engine implementation can outperform Redis (up to 5-10x for presence and history operations), though while example contains a full-featured fast Engine implementation – it's still just an example at the moment and have not been tested in production environment
    • New blog post in Centrifugo blog where we introduce Centrifuge library
    • Most examples now do not use jQuery which was replaced by vanilla JS
    $ gorelease -base v0.14.2 -version v0.15.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*MemoryEngine).Channels: removed
    - (*MemoryEngine).PublishControl: changed from func([]byte) error to func([]byte, string) error
    - (*Node).Channels: removed
    - (*RedisEngine).Channels: removed
    - (*RedisEngine).PublishControl: changed from func([]byte) error to func([]byte, string) error
    - Broker.Channels, method set of Engine: removed
    - Broker.Channels: removed
    - Broker.PublishControl: changed from func([]byte) error to func([]byte, string) error
    - BrokerEventHandler.HandlePublication: changed from func(string, *Publication) error to func(string, *Publication, StreamPosition) error
    - Since: changed from func(StreamPosition) HistoryOption to func(*StreamPosition) HistoryOption
    Compatible changes:
    - (*Node).ID: added
    - (*Node).OnSurvey: added
    - (*Node).Survey: added
    - Config.HistoryMaxPublicationLimit: added
    - Config.RecoveryMaxPublicationLimit: added
    - ErrorUnrecoverablePosition: added
    - HistoryEvent.Filter: added
    - SubscribeOptions.Position: added
    - SurveyCallback: added
    - SurveyEvent: added
    - SurveyHandler: added
    - SurveyReply: added
    - SurveyResult: added
    
    v0.15.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.14.2(Dec 28, 2020)

  • v0.14.1(Nov 13, 2020)

  • v0.14.0(Nov 7, 2020)

    • Add possibility to disconnect user with custom Disconnect object, and with client ID whitelist.
    • Thus fixing non-working WithReconnect option when calling node.Disconnect method.
    • No error returned from client.Disconnect method anymore. It was always nil before.

    Here is what changed since v0.13.0:

    gorelease -base v0.13.0 -version v0.14.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*Client).Disconnect: changed from func(*Disconnect) error to func(*Disconnect)
    - DisconnectOptions.Reconnect: removed
    - DisconnectOptions: old is comparable, new is not
    - WithReconnect: removed
    Compatible changes:
    - DisconnectOptions.ClientWhitelist: added
    - DisconnectOptions.Disconnect: added
    - WithClientWhitelist: added
    - WithDisconnect: added
    
    v0.14.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.13.0(Oct 30, 2020)

    This release solves two important issues from v1.0.0 library milestone. It has API changes, though as always it's possible to implement the same as before, and adapting new version should be pretty straightforward.

    • #163 Provide a way to add concurrent processing of protocol commands. Before this change protocol commands could only be processed one by one. The obvious drawback in this case is that one slow RPC could result into stopping other requests from being processed thus affecting overall latency. This required changing client handler API and use asynchronous callback style API for returning replies from event handlers. This approach while not being very idiomatic allows using whatever concurrency strategy developer wants without losing the possibility to control event order.
    • #161 Eliminating ChannelOptionsFunc – now all channel options can be provided when calling Publish operation (history size and TTL) or by returning from event handlers inside SubscribeReply (enabling channel presence, join/leave messages, recovery in a channel). This means that channel options can now be controlled per-connection (not only per channel as before). For example if you need admin connection to subscribe to channel but not participate in channel presence – you are able to not enable presence for that connection.
    • Server-side subscriptions now set over Subscriptions map (instead of Channels). Again – subscribe options can be set with per-connection resolution.
    • Change signature of Publish method in Broker interface – method now accepts []byte data instead of *Publication.
    • Function options for Unsubbscribe and Disconnect methods now have boolean argument.
    • History functional option WithNoLimit removed – use WithLimit(centrifuge.NoLimit) instead.
    • Config option ClientUserConnectionLimit renamed to UserConnectionLimit. If UserConnectionLimit set then now connection will be disconnected with DisconnectConnectionLimit instead of returning a LimitExceeded error.

    Since API changes are pretty big, let's look at example program and how to adapt it from v0.12.0 to v0.13.0.

    The program based on v0.12.0 API:

    package main
    
    import (
    	"context"
    
    	"github.com/centrifugal/centrifuge"
    )
    
    func main() {
    	cfg := centrifuge.DefaultConfig
    	cfg.ChannelOptionsFunc = func(channel string) (centrifuge.ChannelOptions, bool, error) {
    		return centrifuge.ChannelOptions{
    			Presence:        true,
    			JoinLeave:       true,
    			HistorySize:     100,
    			HistoryLifetime: 300,
    			HistoryRecover:  true,
    		}, true, nil
    	}
    
    	node, _ := centrifuge.New(cfg)
    
    	node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) {
    		return centrifuge.ConnectReply{
    			Credentials: &centrifuge.Credentials{UserID: "42"},
    			// Subscribe to a server-side channel.
    			Channels: []string{"news"},
    		}, nil
    	})
    
    	node.OnConnect(func(c *centrifuge.Client) {
    		println("client connected")
    	})
    
    	node.OnSubscribe(func(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
    		return centrifuge.SubscribeReply{}, nil
    	})
    
    	node.OnPublish(func(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
    		return centrifuge.PublishReply{}, nil
    	})
    
    	node.OnDisconnect(func(c *centrifuge.Client, e centrifuge.DisconnectEvent) {
    		println("client disconnected")
    	})
    
    	_ = node.Run()
    }
    

    With v0.13.0 the same program becomes:

    package main
    
    import (
    	"context"
    	"time"
    
    	"github.com/centrifugal/centrifuge"
    )
    
    func main() {
    	node, _ := centrifuge.New(centrifuge.DefaultConfig)
    
    	node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) {
    		return centrifuge.ConnectReply{
    			Credentials: &centrifuge.Credentials{UserID: "42"},
    			// Subscribe to a server-side channel.
    			Subscriptions: map[string]centrifuge.SubscribeOptions{
    				"news": {Presence: true, JoinLeave: true, Recover: true},
    			},
    		}, nil
    	})
    
    	node.OnConnect(func(client *centrifuge.Client) {
    		println("client connected")
    
    		client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
    			cb(centrifuge.SubscribeReply{
    				Options: centrifuge.SubscribeOptions{
    					Presence:  true,
    					JoinLeave: true,
    					Recover:   true,
    				},
    			}, nil)
    		})
    
    		client.OnPublish(func(e centrifuge.PublishEvent, cb centrifuge.PublishCallback) {
    			// BTW you can publish here explicitly using node.Publish method – see Result
    			// field of PublishReply and chat_json example.
    			cb(centrifuge.PublishReply{
    				Options: centrifuge.PublishOptions{
    					HistorySize: 100,
    					HistoryTTL:  5 * time.Minute,
    				},
    			}, nil)
    		})
    
    		client.OnDisconnect(func(e centrifuge.DisconnectEvent) {
    			println("client disconnected")
    		})
    	})
    
    	_ = node.Run()
    }
    

    As you can see there are three important changes:

    1. You should now set up event handlers inside node.OnConnect closure
    2. Event handlers now have callback argument that you should call with corresponding Reply as soon as you have it
    3. For server-side subscriptions you should now return Subscriptions field in ConnectReply which is map[string]SubscribeOptions instead of []string slice.

    See new example that demonstrates concurrency using bounded semaphore.

    Note that every feature enabled for a channel increases resource usage on a server. You should only enable presence, recovery, join/leave features and maintaining history in channels where this is necessary.

    See also updated Tips and tricks section in a README – it now contains information about connection life cycle and event handler concurrency.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Oct 1, 2020)

    This release is a step back in Engine separation and has some important fixes and improvements. Backwards incompatible changes are all about Engine interfaces so if you are using built-in Memory or Redis engines you should be fine to upgrade. Otherwise, take a closer look on first and second points below.

    • HistoryManager interface removed and its methods now part of Broker interface{}. The reason behind this is that Broker should be responsible for an atomicity of saving message into history stream and publish to PUB/SUB. More details in #158
    • Cleaner Broker interface methods without ChannelOptions
    • Fix reconnects due to InsufficientState errors in channels with HistoryRecover option on when using Memory Engine and frequently publishing in parallel (from different goroutines)
    • Fix reconnects due to InsufficientState errors when using legacy seq, gen fields - #157
    • Fix returning custom disconnect for SockJS transport
    • Possibility to define history stream options in Publish call
    • Deprecate Broker/Engine Channels method – see #147
    • Increase test coverage up to 83% so #106 is finally closed
    • Test Sentinel scenario in CI
    • Refactor queue writer to prevent possible message loss on connection close - 160
    • Fix inconsistent tests of Redis Cluster recovery due to PUB/SUB buffering
    • Minor improvements in Gin auth example - #154

    I have a plan for future library versions to remove ChannelOptionFunc completely (but still have a control over channel feature set). This is still in research – if you are interested welcome to #161.

    $ gorelease -base v0.11.2 -version v0.12.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*MemoryEngine).AddHistory: removed
    - (*MemoryEngine).Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
    - (*MemoryEngine).PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - (*MemoryEngine).PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - (*Node).SetHistoryManager: removed
    - (*RedisEngine).AddHistory: removed
    - (*RedisEngine).Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
    - (*RedisEngine).PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - (*RedisEngine).PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - Broker.History: added
    - Broker.Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
    - Broker.PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - Broker.PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
    - Broker.RemoveHistory: added
    - HistoryManager.AddHistory, method set of Engine: removed
    - HistoryManager: removed
    - MemoryEngine: old is comparable, new is not
    - PublishOptions.SkipHistory: removed
    - RedisEngineConfig.PublishOnHistoryAdd: removed
    Compatible changes:
    - PublishOptions.HistorySize: added
    - PublishOptions.HistoryTTL: added
    - WithHistory: added
    
    v0.12.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.11.2(Sep 24, 2020)

  • v0.11.1(Sep 19, 2020)

    • Added MetricsNamespace field of Config to configure Prometheus metrics namespace used by Centrifuge library internal metrics
    • Fix messages_sent_counter – it now correctly counts Control, Join and Leave messages
    • Redis cluster integration now tested in CI
    $ gorelease -base v0.11.0 -version v0.11.1
    github.com/centrifugal/centrifuge
    ---------------------------------
    Compatible changes:
    - Config.MetricsNamespace: added
    
    v0.11.1 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Aug 27, 2020)

    • Refactor client channels API – see detailed changes below, #146
    • Fix atomic alignment in struct for 32-bit builds, commit
    • Field Code of Disconnect has uint32 type now instead of int, commit
    • Refactor WebSocket graceful close – do not use a new goroutine for every read, #144
    • Support client name and version fields of Connect command which will be available in ConnectEvent struct (if set on client side), #145
    $ gorelease -base v0.10.1 -version v0.11.0
    github.com/centrifugal/centrifuge
    ---------------------------------
    Incompatible changes:
    - (*Client).Channels: changed from func() map[string]ChannelContext to func() []string
    - ChannelContext: removed
    - Disconnect.Code: changed from int to uint32
    Compatible changes:
    - (*Client).IsSubscribed: added
    - ConnectEvent.Name: added
    - ConnectEvent.Version: added
    - ErrorTooManyRequests: added
    
    v0.11.0 is a valid semantic version for this release.
    
    Source code(tar.gz)
    Source code(zip)
  • v0.10.1(Aug 7, 2020)

  • v0.10.0(Jul 10, 2020)

    This release is a massive rewrite of Centrifuge library (actually of some part of it) which should make library a more generic solution. Several opinionated and restrictive parts removed to make Centrifuge feel as a reasonably thin wrapper on top of strict client-server protocol.

    Most work done inside #129 pr and relates to #128 issue.

    Release highlights:

    • Layer with namespace configuration and channel rules removed. Now developer is responsible for all permission checks and channel rules.
    • Hard dependency on JWT and predefined claims removed. Users are now free to use any token implementation – like Paceto tokens for example, use any custom claims etc.
    • Event handlers that not set now always lead to Not available error returned to client.
    • All event handlers now should be set to Node before calling its Run method.
    • Centrifuge still needs to know some core options for channels to understand whether to use presence inside channels, keep Publication history stream or not. It's now done over user-defined callback function in Node Config called ChannelOptionsFunc. See its detailed description in library docs.
    • More idiomatic error handling in event handlers, see #134.
    • Aliases to Raw, Publication and ClientInfo Protobuf types removed from library public API, see #136
    • Support Redis Sentinel password option

    Look at updated example in README and examples folder to find out more.

    I hope to provide more guidance about library concepts in the future. I feel sorry for breaking things here but since we don't have v1 release yet, I believe this is acceptable. An important note is that while this release has lots of removed parts it's still possible (and not too hard) to implement the same functionality as before on top of this library. Feel free to ask any questions in our community chats.

    Source code(tar.gz)
    Source code(zip)
Owner
Centrifugal
Real-time messaging server and its friends
Centrifugal
Minimal and idiomatic WebSocket library for Go

websocket websocket is a minimal and idiomatic WebSocket library for Go. Install go get nhooyr.io/websocket Highlights Minimal and idiomatic API First

Anmol Sethi 2.2k Jun 20, 2022
go-socket.io is library an implementation of Socket.IO in Golang

go-socket.io is library an implementation of Socket.IO in Golang, which is a realtime application framework.

Googol Lee 4.7k Jun 25, 2022
A go library for consuming Binance Websocket Market Streams

binancestream A go library for consuming Binance Websocket Market Streams This library handles network failures by automatically reconnecting to the w

Yasin Özel 1 Nov 1, 2021
Real-time Map displays real-time positions of public transport vehicles in Helsinki

Real-time Map Real-time Map displays real-time positions of public transport vehicles in Helsinki. It's a showcase for Proto.Actor - an ultra-fast dis

ASYNKRON 27 Jun 8, 2022
LinDB is an open-source Time Series Database which provides high performance, high availability and horizontal scalability.

LinDB is an open-source Time Series Database which provides high performance, high availability and horizontal scalability. LinDB stores all monitoring data of ELEME Inc, there is 88TB incremental writes per day and 2.7PB total raw data.

LinDB 2.3k Jun 29, 2022
💨 A real time messaging system to build a scalable in-app notifications, multiplayer games, chat apps in web and mobile apps.

Beaver A Real Time Messaging Server. Beaver is a real-time messaging server. With beaver you can easily build scalable in-app notifications, realtime

Ahmed 1.3k Jun 27, 2022
Scalable real-time messaging server in language-agnostic way

Centrifugo is a scalable real-time messaging server in language-agnostic way. Centrifugo works in conjunction with application backend written in any

Centrifugal 6.1k Jun 28, 2022
Scalable real-time messaging server in language-agnostic way

Centrifugo is a scalable real-time messaging server in language-agnostic way. Centrifugo works in conjunction with application backend written in any

Centrifugal 6.1k Jun 24, 2022
A LoRaWAN nodes' and network simulator that works with a real LoRaWAN environment (such as Chirpstack) and equipped with a web interface for real-time interaction.

LWN Simulator A LoRaWAN nodes' simulator to simulate a LoRaWAN Network. Table of Contents General Info Requirements Installation General Info LWN Simu

ARSLab 24 May 17, 2022
Kitex byte-dance internal Golang microservice RPC framework with high performance and strong scalability, customized extensions for byte internal.

Kitex 字节跳动内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的特点,针对字节内部做了定制扩展。

CloudWeGo 4.5k Jun 20, 2022
A web application framework with complete functions and good scalability

English | 中文 Abuout Goravel Goravel is a web application framework with complete

null 14 Jun 17, 2022
Instant messaging server for the Extensible Messaging and Presence Protocol (XMPP).

Instant messaging server for the Extensible Messaging and Presence Protocol (XMPP).

Miguel Ángel Ortuño 1.3k Jun 23, 2022
Simple-messaging - Brokerless messaging. Pub/Sub. Producer/Consumer. Pure Go. No C.

Simple Messaging Simple messaging for pub/sub and producer/consumer. Pure Go! Usage Request-Response Producer: consumerAddr, err := net.ResolveTCPAddr

IchHabeKeineNamen 1 Jan 20, 2022
Fast specialized time-series database for IoT, real-time internet connected devices and AI analytics.

unitdb Unitdb is blazing fast specialized time-series database for microservices, IoT, and realtime internet connected devices. As Unitdb satisfy the

Saffat Technologies 91 Jun 9, 2022
Time-Based One-Time Password (TOTP) and HMAC-Based One-Time Password (HOTP) library for Go.

otpgo HMAC-Based and Time-Based One-Time Password (HOTP and TOTP) library for Go. Implements RFC 4226 and RFC 6238. Contents Supported Operations Read

Jose Torres 35 Jun 23, 2022
GO2P is a P2P framework, designed with flexibility and simplicity in mind

go2p golang p2p framework By v-braun - viktor-braun.de. Description GO2P is a P2P framework, designed with flexibility and simplicity in mind. You can

Viktor Braun 89 Jun 22, 2022
Turn asterisk-indented text lines into mind maps

Crumbs Turn asterisk-indented text lines into mind maps. Organize your notes in a hierarchical tree structure, using a simple text editor. an asterisk

Luca Sepe 94 Mar 19, 2022
🏮Blazing fast URL shortener made with simplicity in mind

klein Blazing fast URL shortener made with simplicity in mind Structures The project is what people would call a "monolith".

sach1 19 Feb 16, 2022
🏮 ― Blazing fast URL shortener made with simplicity in mind

klein Blazing fast URL shortener made with simplicity in mind Run As easy as filling out config/config.yaml and running make. Of course, you need to h

Pink Cigarette 19 Feb 16, 2022
Oogway is a simple web server with dynamic content generation and extendability in mind supporting a Git based workflow.

Oogway Oogway is a simple web server with dynamic content generation and extendability in mind supporting a Git based workflow. It's somewhere in betw

Emvi 5 Mar 9, 2022
KinK is a helper CLI that facilitates to manage KinD clusters as Kubernetes pods. Designed to ease clusters up for fast testing with batteries included in mind.

kink A helper CLI that facilitates to manage KinD clusters as Kubernetes pods. Table of Contents kink (KinD in Kubernetes) Introduction How it works ?

Trendyol Open Source 353 May 8, 2022
A pure Go collection of Base58, Base91, Base92… with safety, rigor and performance in mind

BaseXX Go modules:           import "github.com/teal-finance/BaseXX/base58" import "github.com/teal-finance/BaseXX/base62" import "github.com/teal-fin

Teal.Finance 2 May 8, 2022
Time struct in Go that uses 4 bytes of memory vs the 24 bytes of time.Time

A tiny time object in Go. Tinytime uses 4 bytes of memory vs the 24 bytes of a standard time.Time{}

Lane Wagner 57 May 2, 2022
Scalable datastore for metrics, events, and real-time analytics

InfluxDB InfluxDB is an open source time series platform. This includes APIs for storing and querying data, processing it in the background for ETL or

InfluxData 23.7k Jun 26, 2022
Simple and easy to use client for stock market, forex and crypto data from finnhub.io written in Go. Access real-time financial market data from 60+ stock exchanges, 10 forex brokers, and 15+ crypto exchanges

go-finnhub Simple and easy to use client for stock, forex and crpyto data from finnhub.io written in Go. Access real-time market data from 60+ stock e

Miles Croxford 71 Jun 8, 2022
Real-time Geospatial and Geofencing

Tile38 is an open source (MIT licensed), in-memory geolocation data store, spatial index, and realtime geofence. It supports a variety of object types

Josh Baker 8.2k Jun 24, 2022
A Realtime API Gateway used with NATS to build REST, real time, and RPC APIs, where all your clients are synchronized seamlessly.

Realtime API Gateway Synchronize Your Clients Visit Resgate.io for guides, live demos, and resources. Resgate is a Go project implementing a realtime

Resgate.io - Synchronize Your Clients 578 Jun 29, 2022
Visualise Go program GC trace data in real time

This project is no longer maintained I'm sorry but I do not have the bandwidth to maintain this tool. Please do not send issues or PRs. Thank you. gcv

Dave Cheney 1.1k Jun 28, 2022
Real-time Charging System for Telecom & ISP environments

Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Features Real-time Online/Offline Charging System (OCS). Account Balance

null 353 Jun 23, 2022