centrifugal / centrifuge-go

Go client SDK for bidirectional communication with Centrifugo and Centrifuge-based server over WebSocket
MIT License
209 stars 49 forks source link

Handle re-connect to new URI #52

Closed theflyingcodr closed 3 years ago

theflyingcodr commented 3 years ago

I'm wondering what would be the best way to handle a disconnect and then within that, re-connect but to a different URI.

My use case is hitting a server with a param where that param is an integer, this increments as I sync events.

On disconnect, I then want to re-connect but with a specific param with the last value of the integer. So say i initially connect to wss://domain.com?param=0. Then on disconnect I have synced 100000 so I want to connect to wss://domain.com?param=100000.

What's the best way to handle this with this library?

The reason I've to handle these disconnects is I get many many "client too slow" disconnect events.

FZambia commented 3 years ago

Hello, to be fair I don't understand a use case. How do you handle this param?

theflyingcodr commented 3 years ago

So what this is essentially is a sync client service that receives a load of messages from the server, I can request where these messages start by passing in the query string so wss://domain.com?param=0 will start from the start.

As I process messages I add them to a db, however, the client is always disconnecting because it can't cope with the load from the server. If I simply allow it to re-connect using default behaviour it will start again at 0, re-syncing messages already handled. It then goes through a cycle of sync -> drop connection -> re-connect -> re-sync same messages over and over.

What i'd like to be able to do is on disconnect / dial, is to get the current value from the data store and rather than re-connect to wss://domain.com?param=0 it would then connect to the current value X, so wss://domain.com?param=X, and on any further re-connects it would check the current value and use that.

Does this make any more sense?

I have a feeling I could implement a custom NetDialContext which could then on connect contain the logic to build the correct url?

FZambia commented 3 years ago

I feel that you are using wrong instrument for a task. Maybe you should not sync messages this way and consider alternative - just issue a request without websocket involved. But maybe thats just my limited understanding.

Take a look at https://github.com/centrifugal/centrifuge-go/blob/master/client.go#L136 - you can set custom connect data which will be appended to connect command and available on server side in OnConnecting handler (I suppose you are using centrifuge library on server).

theflyingcodr commented 3 years ago

I agree, ideally I would just read the data but the server is well out of my control and the only way it will sync is via sockets. Just FYI it's this service https://developers.whatsonchain.com/#block-headers-history.

I'll take a look at that, ta!

theflyingcodr commented 3 years ago

I have found that if I make client.URL public and I alter this on disconnect ie c.URL = wss://domain.com?param=X, this works really well. Data isn't an option as it'd need a server change from another company I have no control over, I can suggest they add support for handling a body but I've no guarantees they will so need a client side solution.

Would you accept this if I opened a PR - just doing some testing here first.

func (h *myClient) OnDisconnect(c *centrifuge.Client, e centrifuge.DisconnectEvent) {
    height, _ := h.svc.Height(context.Background())
    h.ws = nil
    c.URL = fmt.Sprintf("%s%d", h.cfg.URL, height.Height)
}
FZambia commented 3 years ago

No, I won't accept this change since the use case seems odd to me. In Centrifugal we are trying to keep all clients in sync and reduce supported features to the required minimum.

As a workaround you can create new Client instance every time.

theflyingcodr commented 3 years ago

What would be your recommended way of creating the new client?

My setup is like:

type myClient struct{
  ws  *centrifuge.Client
}

func NewMyClient(ws *centrifuge.Client) *myClient {
    h := &headersSocket{ws: ws}
    h.setup()
        h.ws.OnDisconnect(h)
       h.ws.OnServerPublish(h)
    return h
}

func (h *headersSocket) OnDisconnect(c *centrifuge.Client, e centrifuge.DisconnectEvent) {
    height, _ := h.svc.Height(context.Background())
         c = centrifuge.New(fmt.Sprintf("%s%d", cfg.Woc.URL, height), centrifuge.DefaultConfig())
         c.Connect()
         h.ws = c
}

But isn't working quite right.

FZambia commented 3 years ago

Looks like you are not setting event handlers after disconnect. Just refactor a code to create a client and set all necessary event handlers. I also suggest contacting server authors to communicate for a better solution/approach on how to handle this story.