zishang520 / socket.io-go-redis

Not fully ready yet, there may be many problems
MIT License
2 stars 4 forks source link

Redis Cluster support? #6

Closed bneigher closed 1 week ago

bneigher commented 2 months ago

hey @zishang520 - got up and running with your golang socketio server library super quick. awesome work.

I'm trying to set up is redis adapter, but unfortunately it looks like Redis can only be of type RedisClient, not a ClusterClient

type RedisAdapterBuilder struct {
    socket.AdapterConstructor

    // a Redis client
    Redis *_types.RedisClient
    // additional options
    Opts *RedisAdapterOptions
}

which is created by *redis.NewClusterClient(opt *redis.ClusterOptions) in "github.com/redis/go-redis/v9"

do you see any reason to accomplish this as-is or is this a larger change for the socket.io-go-redis?

zishang520 commented 2 months ago

The Redis adapter is not ready yet, I try to keep the cluster consistent with the standalone machine as much as possible (:

bneigher commented 1 week ago

hey @zishang520, thanks for getting this in! can you show how I can change my impl to work with the new change?

bneigher commented 1 week ago

impl:

import (
    "fmt"
    "log/slog"
    "regexp"

    "github.com/redis/go-redis/v9"
    "github.com/zishang520/engine.io/v2/types"
    "github.com/zishang520/engine.io/v2/utils"
    "github.com/zishang520/socket.io-go-redis/adapter"
    "github.com/zishang520/socket.io/v2/socket"
)

type Instance struct {
    Server *socket.Server
}

func SetupSocketServer(redisClient *redis.ClusterClient) *Instance {
    opts := socket.DefaultServerOptions()
    opts.SetAllowEIO3(true)
    opts.SetCors(&types.Cors{
        Origin:      "*",
        Credentials: true,
    })
    opts.SetAdapter(&adapter.RedisAdapterBuilder{
        Redis: redisClient,
        Opts:  &adapter.RedisAdapterOptions{},
    })
    server := socket.NewServer(nil, opts)
    return &Instance{
        Server: server,
    }
}
zishang520 commented 1 week ago

Sorry I haven't had time to test the PR request yet, there may be some breaking updates, I will update an example later.

zishang520 commented 1 week ago

You can use Cluster and Failover directly like Simple:

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    redis "github.com/redis/go-redis/v9"
    "github.com/zishang520/engine.io/v2/log"
    "github.com/zishang520/engine.io/v2/types"
    "github.com/zishang520/socket.io-go-redis/adapter"
    r_type "github.com/zishang520/socket.io-go-redis/types"
    "github.com/zishang520/socket.io/v2/socket"
)

func main() {
    log.DEBUG = true
    c := socket.DefaultServerOptions()
    // c.SetAllowEIO3(true)
    // c.SetConnectionStateRecovery(&socket.ConnectionStateRecovery{})
    // c.SetAllowEIO3(true)
    // c.SetPingInterval(300 * time.Millisecond)
    // c.SetPingTimeout(200 * time.Millisecond)
    // c.SetMaxHttpBufferSize(1000000)
    // c.SetConnectTimeout(1000 * time.Millisecond)
    c.SetCors(&types.Cors{
        Origin:      "*",
        Credentials: true,
    })
    // Simple
    // redisClient := r_type.NewRedisClient(context.Background(), redis.NewClient(&redis.Options{
    //  Addr:     "localhost:6379",
    //  Username: "",
    //  Password: "",
    //  DB:       0,
    // }))

    // Cluster
    redisClient := r_type.NewRedisClient(context.Background(), redis.NewClusterClient(&redis.ClusterOptions{
        Addrs: []string{"127.0.0.1:6379", "127.0.0.1:7001"},
        // more opts...
    }))
    redisClient.On("error", func(a ...any) {
        fmt.Println(a)
    })
    c.SetAdapter(&adapter.RedisAdapterBuilder{
        Redis: redisClient,
        Opts:  &adapter.RedisAdapterOptions{},
    })
    httpServer := types.NewWebServer(nil)
    io := socket.NewServer(httpServer, c)
    io.On("connection", func(clients ...interface{}) {
        client := clients[0].(*socket.Socket)
        client.Emit("auth", client.Handshake().Auth)

        client.On("message", func(args ...interface{}) {
            client.Emit("message-back", args...)
        })

        client.On("message-with-ack", func(args ...interface{}) {
            ack := args[len(args)-1].(func([]any, error))
            ack(args[:len(args)-1], nil)
        })
    })

    io.Of("/custom", nil).On("connection", func(clients ...interface{}) {
        client := clients[0].(*socket.Socket)
        client.Emit("auth", client.Handshake().Auth)
    })

    httpServer.Listen(":3000", nil)

    exit := make(chan struct{})
    SignalC := make(chan os.Signal)

    signal.Notify(SignalC, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    go func() {
        for s := range SignalC {
            switch s {
            case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
                close(exit)
                return
            }
        }
    }()

    <-exit
    io.Close(nil)
    os.Exit(0)
}

We would like to thank @alxeg for his contribution.

bneigher commented 1 week ago

ah I see, so we need to specifically create this client with the github.com/zishang520/socket.io-go-redis/types NewRedisClient method. thanks!