• 1

  • 504

从0到1的弹幕系统--多对多服务

机器猫

机器学习

1星期前

前言

之前我们做的都是单机服务,即一台服务器为所有房间提供服务。实际中,一个大主播的观看人数就几十、上百万了,单台服务器不可能支撑住这样的用户量。遇到这样的情况,往往都是很豪横的说加机器,但怎么加呢?之前我们都是按照单机实现的,如果只是冒然的加的话,可能会出现下面的情况:

Server 1Server 2同时为 Room ARoom B提供服务,这样 Room A房间用户的websocket连接,有些可能在 Server 1上,有些可能在 Server 2上,同样, Room B房间用户的websocket连接,有些可能在 Server 1上,有些可能在 Server 2上。

假设用户张三在Room A房间看直播, 他的websocket连接在Server 1上,这时张三发送了一条弹幕,弹幕被发送到Server 1上,然后Server 1将弹幕发送给Room A的所有用户。但是问题来了,Room A的用户,并不是都连接在Server 1上,有些连接在Server 2上,这些用户将接受不到弹幕。同理,假如李四也在观看Room A房间直播,他的websocket连接在Server 2上,他发送的弹幕,只有Room A中websocket连接在Room 2房间的用户能接受到。

造成这个问题的原因是Server 1Server 2之间没有通信。张三发送了一条弹幕,然后Server 1将这条弹幕发送给Server 2,这样李四就也能接受到张三发送的弹幕了。

Server 1Server 2之间怎么通信呢?是用RPC或HTTP接口同步通信,还是使用消息队列异步通信呢?由于异步通信的各种好处,解耦、降低负载等,我们使用异步通信。所以我们需要一个消息队列,开源的消息队列服务有很多,kafka、rabbitMQ等等,我们使用go语言实现的NSQ做消息队列。

NSQ

NSQ的安装和接入都非常简单,官方文档上已经详细介绍怎么安装了,我们只需选择一种自己喜欢的安装方式,然后启动相关服务。

$ nsqlookupd
复制代码

再开启一个终端:

$ nsqd --lookupd-tcp-address=127.0.0.1:4160
复制代码

再开启第二个终端:

nsqadmin --lookupd-http-address=127.0.0.1:4161
复制代码

安装客户端SDK,我们使用官方的SDK

$ go get github.com/nsqio/go-nsq
复制代码

官方已经给出了demo,我们只需拿来用就行了。

收到客户端发送的弹幕后,之前我们是直接将弹幕转发给房间中的其他用户,现在不这样做了,现在我们将弹幕发送到NSQ,然后从NSQ中获取弹幕,再转发给用户。

nsqData := NsqMsg{RoomId: c.room.id, Msg: byteMsg}
writeByte, _ := json.Marshal(nsqData)
NsqSend(roomDanMuMsg, writeByte)
复制代码

NSQ中获取弹幕

func NewConsumer() {
    config := nsq.NewConfig()
    consumer, err := nsq.NewConsumer(roomDanMuMsg, "channel", config)
    if err != nil {
        log.Fatal(err)
    }

    consumer.AddHandler(&myMessageHandler{})

    err = consumer.ConnectToNSQD("127.0.0.1:4150")
    if err != nil {
        log.Fatal(err)
    }
}

type myMessageHandler struct {
}

// HandleMessage implements the Handler interface.
func (h *myMessageHandler) HandleMessage(m *nsq.Message) error {
    if len(m.Body) == 0 {
        return nil
    }

    nsqData := NsqMsg{}
    json.Unmarshal(m.Body, &nsqData)

    room, ok := rooms.rooms[nsqData.RoomId]
    if ok {
        room.broadcastMsg <- nsqData.Msg
    }

    return nil
}
复制代码

然后在入口中调用一下就行了

func main() {
    NewConsumer()
    ...
}
复制代码

好了,完成了。

后记

到这里,一个弹幕系统的基本功能就实现了。后续只需要根据产品需求,加业务功能就行了,都是一些业务代码了,我们就不在继续写了。而且还得考虑架构的问题,比如怎么处理单房间百万用户连接?弹幕的丢失问题,怎么保证一定能看到自己发送的弹幕?在一些大主播的房间中,用户不可能接收到所有弹幕,弹幕太多了,得要有所取舍,具体怎么取舍呢?。。。

免责声明:文章版权归原作者所有,其内容与观点不代表Unitimes立场,亦不构成任何投资意见或建议。

机器学习

504

相关文章推荐

未登录头像

暂无评论