如何使用redis pub/sub在多个实例上处理socket.io会话?
#go #redis #socketio #pubsub

当我们使用socket.io时,我们通常正在尝试在应用程序中向所有工作区的所有合作者发布消息,即使消息的来源是协作应用程序中的合作者之一。

在这种情况下,服务器必须将其消息传达给多个用户,插座客户端会创建一个房间并将用户添加到此房间中。当任何更改(例如应用程序更新,新评论)发生在此房间中,必须将特定的更改传达给房间的所有成员。

信息,有一个名为xyz的房间,有一些合作者说房间中的a,b,c在特定服务器实例上存储。

创建了应用程序或服务器的多个实例时,还为应用程序的新实例创建了套接字的新实例。现在,当房间的更改到达此服务器时,此实例不知道上一个实例创建的房间,也不知道房间的成员 /协作者。因此,新的服务器实例可以将更改发送给连接到现有服务器的更改。我试图在此处视觉上解释问题。

The problem

在解决问题的解决方案之前,让我们了解我使用的技术以及实际code的一些片段。我已经在golang中编写了服务器代码,并且使用Postman作为客户端。我想向大家简介的两种技术是socket.ioRedis pub/sub
如果您已经熟悉这些概念,只是通过我的描述来更好地了解它提供的博客和解决方案。

socket.io的简短说明

socket.io有助于实时向客户传递消息。场景后面的socket.io在服务器和客户端之间创建一个双向通道,因此可以在Web客户端和服务器之间进行双向通信。对于demo code,我使用了golang服务器,因此我使用了一个名为go-socket.io的Golang库。您应该知道的一些Advance Socket.io概念是渠道,事件,名称空间和房间。

golang代码段用于连接到socket.io-查找完整代码here

c.SocketServer.OnConnect("/", func(s socketio.Conn) error {
        log.Println("connected:", s.ID())
        return nil
    })

golang代码段用于与插座断开连接。IO

c.SocketServer.OnDisconnect("/", func(s socketio.Conn, reason string) {
        fmt.Println("Disconnected ", s.ID(), "reason", reason)
        s.Leave(reason)
        fmt.Println("Disconnected ", s.ID())
    })

socket.io房间的简要说明

房间是socket.io功能。通过使用此服务器可以向特定的组/订户发送消息。为了使客户接收特定组的消息,他们已被订阅/连接到一个名为Room的特定名称空间。然后socket.io使用套接字对象的广播方法向所有订阅客户端发送消息。

socket.io房间可用于使用工作区和协作器架构的聊天应用程序和应用程序。在聊天应用程序中,该消息将广播给组,并且组的所有用户实时接收消息。在协作应用程序中,消息将广播到工作空间,以便合作者可以接收它们。这是绘画视图,解释了socket.io中的房间的概念。

socket.io room

golang代码段解释房间联接功能

c.SocketServer.OnEvent("/", "application",
        c.authenticate(c.authorize(func(s socketio.Conn, payload ApplicationSubscriptionPayload) error {
            log.Println("roomName : ", getRoomName(payload.Payload.WorkspaceId, payload.Payload.AppId),
                "payload : ", payload.Event)
            roomName := getRoomName(payload.Payload.WorkspaceId, payload.Payload.AppId)
            if payload.Event == Subscribe {
                sessionContext := getSessionContext(s)
                s.Join(roomName)
                log.Printf("subscribed : (%s), sessionId : %s, roomName : %s , allRooms : %v ",
                    sessionContext, s.ID(),
                    roomName, s.Rooms())
                newApplicationSubscriptionResponse := NewApplicationSubscriptionResponse("SUCCESS",
                    payload.Event, payload.Payload)
                s.Emit("application_response", newApplicationSubscriptionResponse)
                return nil
            }
            log.Printf("%s un subscribed to roomName : %s , allRooms : %v ", s.ID(),
                roomName, s.Rooms())
            s.Leave(getRoomName(payload.Payload.WorkspaceId, payload.Payload.AppId))
            newNodezapSubscriptionResponse := NewApplicationSubscriptionResponse("SUCCESS",
                payload.Event, payload.Payload)
            s.Emit("application_response", newNodezapSubscriptionResponse)
            return nil
        })))

在给定的代码段中验证,授权功能是可选的。这些功能是中间件,可用于为事件添加更多上下文。 s.Join(roomName)功能用于连接给定的房间名称。

可用于加入房间的样本有效载荷如下所示,是我写的申请的特定于。

{
    "event":"SUBSCRIBE",
    "payload":{
        "appId" :"test_app_id",
        "workspaceId" :"test_workspace_id"
    }
}

让我解释一下Redis pub/sub,我将其用作上述问题的解决方案之一。

关于Redis Pub/Sub的简要说明

首先,让我们了解Redis。它是一种内存数据结构存储,用作数据库,缓存,消息代理和流引擎。 Redis Pub/sub是Redis的特征之一。您可以获取Redis Pub/sub here的官方文件。让我简要介绍Redis Pub/Sub的一些概念。 Redis Pub/Sub First中有两个组件是发布者。它的功能是将消息有效载荷发送到给定的频道/主题。订阅者,这些订阅/取消订阅特定主题。所有订阅该主题的订阅者都会收到发布者发送给主题的消息。给定的图解释了redis pub/sub。

的原理

Redis pub/sub

golang代码摘要解释了频道
的订阅

func (c *CacheStore) SubscribeSocketChannel(socketClient *Client) {
    ctx := context.Background()
    redisPubsub := c.RedisClient.Subscribe(ctx, storePubsubChannel)
    go func() {
        for msg := range redisPubsub.Channel() {
            switch msg.Channel {
            case storePubsubChannel:
                fmt.Println("received pubsub message:", msg.Payload)
                publishToSocketSubscribers(socketClient, msg.Payload)
            }
        }
    }()
}

golang代码段,向频道解释出版

func Publish(ctx context.Context, redisClient *redis.Client,
    workspaceId, appId string, payload interface{}) error {
    publishableData, err := getPublishPayload(workspaceId, appId, payload)
    if err != nil {
        return err
    }
    redisStore := NewCacheStore(ctx, redisClient)
    err = redisStore.PublishToSocketChannel(publishableData)
    if err != nil {
        return err
    }
    return nil
}

现在,大家都有一些我使用的技术的想法,我们可以得出上述问题的可行解决方案。简单地说,问题是所有服务器实例都没有副作用 /更改,因此它们无法将更改 /副作用传达给套接字房间订阅者。通过将从Origin服务器发送到服务器的所有实例的更改可以轻松解决。

在这种情况下,REDIS实时数据库存储很容易拟合,以解决此问题。它可以帮助我们在服务器实例上发布更改的数据,然后每个服务器上的订户可以接收更改,然后使用套接字,可以将此更改发送到所有订阅的协作者/客户端。因此,使用redis pub/sub,我们可以在多个应用程序中处理插座。

The solution

我已上传Golang Server代码和API的Postman集合,以及有关如何使用Postman用作github -here

使用邮递员的Web套接字的文档。

这篇文章与Redis合作。

了解更多: