大厅服务器概述
大厅服务器负责玩家进入游戏的接入、发送公告等功能。当玩家登陆完毕、游戏结束后将会进入大厅服务器。
大厅服务器需求
- 公告
- 对玩家进行匹配
- 创建房间
- 加入房间
大厅服务器结构
大厅服务器流程
大厅服务器细节
由于使用了Redis当作全局内存,其实游戏大厅只是一个使用Redis进行业务操作的模块。在线匹配功能后期可以划分为匹配服务器,降低游戏大厅负载。
在创建房间过程中,大厅服务器对在线的游戏服务进行遍历,负载低的服务器优先创建房间。
由于使用了消息队列,因此并不能同步发送信息,因此写了组件专门处理使异步操作使其变为同步操作,代码如下:
type BlockVisit struct {
Message map[string]chan grpc_common.Message
sync.RWMutex
}
func (block *BlockVisit) Get(name string) (chan grpc_common.Message, error) {
block.RLock()
defer block.RUnlock()
message, exit := block.Message[name]
if !exit {
return nil, errors.New("error messageid")
}
return message, nil
}
func (block *BlockVisit) GrpcSend(client *Grpc_client, Message grpc_common.MessageRouter, timeout int) (grpc_common.Message, error) {
// 设置信息为发送出去处理
Message.RouteMessage.DealMessage = true
message := make(chan grpc_common.Message, 1)
block.Lock()
MessageId := block.NewId()
block.Message[MessageId] = message
Message.RouteMessage.MessageId = MessageId
sendmessage, err := grpc_common.Encode(&Message)
if err != nil {
return grpc_common.Message{}, err
}
err = client.WriteMessage(sendmessage)
if err != nil {
delete(block.Message, MessageId)
block.Unlock()
return grpc_common.Message{}, err
}
block.Unlock()
timer := time.NewTicker(time.Duration(timeout) * time.Second)
select {
case <-timer.C:
block.Lock()
delete(block.Message, MessageId)
block.Unlock()
return grpc_common.Message{}, errors.New("Time out")
case m := <-message:
block.Lock()
delete(block.Message, MessageId)
block.Unlock()
if m.GetName() == MessageNotFindItem {
return grpc_common.Message{}, errors.New(MessageNotFindItem)
}
return m, nil
}
}
func (block *BlockVisit) RabbitmqSend(client *RabbitMq,Message grpc_common.Message,Item string,timeout int)(*grpc_common.Message,error){
Message.DealMessage = true
MessageChan := make(chan grpc_common.Message, 1)
block.Lock()
MessageId := block.NewId()
block.Message[MessageId] = MessageChan
Message.MessageId = MessageId
block.Unlock()
MessageData,err:= proto.Marshal(&Message)
if err!=nil{
return nil,err
}
client.Publish(Item,MessageData)
timer := time.NewTicker(time.Duration(timeout) * time.Second)
select {
case <-timer.C:
block.Lock()
delete(block.Message, MessageId)
block.Unlock()
return nil, errors.New("Time out")
case m := <-MessageChan:
block.Lock()
delete(block.Message, MessageId)
block.Unlock()
return &m,nil
}
}
// 使用需上锁
func (block *BlockVisit) NewId() string {
var ID string
for true {
ID = GetRandomString(10)
_, exit := block.Message[ID]
if !exit {
return ID
}
}
return ""
}
func GetRandomString(l int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}
原理很简单,为发送的信息生成唯一ID,并将其ID对应一个channel。当发送出去时启动一个定时器,当定时器超时返回错误。如果接收到对应ID,则将其信息通过channel发送给正处于定时状态的函数,接收到信息的函数将结果返回。