进入游戏后的消息处理与远程调用
1. 消息处理逻辑
进入游戏后,客户端会收到一条消息,内容如下:
{
"cid": "客户端ID",
"body": {
"uid": "用户ID",
"location": {
"latitude": "纬度",
"longitude": "经度",
"province": "省份",
"city": "城市"
}
},
"src": "来源服务ID",
"dst": "目标服务ID",
"router": "user_handler.update_user_address",
"type": "normal"
}
这条消息的路由信息表明,它需要访问 user_handler
中的 update_user_address
方法,用于更新用户的地理位置信息(如经纬度、省份、城市等)。我们需要在后台处理这条消息,更新 user
表中的相关信息。
2. 业务逻辑回顾
在前面的业务逻辑中,用户访问 connect
服务并进入游戏时,会完成以下操作:
-
操作验证:验证用户的身份(通过
TOKEN
)。 -
用户信息返回:返回用户的相关信息(从
user
表中获取)。 -
配置信息返回:返回游戏配置信息(供客户端使用)。
此外,我们还需要将用户的 UID
保存到 Session
中,以便维护用户的连接状态和上下文信息。
3. 远程调用(RPC)逻辑
接下来,我们需要处理访问 auth
服务器的请求。由于 auth
服务器和 connect
服务器是不同的服务,我们需要通过网络进行跨服务调用,即远程过程调用(RPC)。我们选用的通讯框架是 NATS。
NATS 是一个高性能的分布式消息系统,适用于微服务架构中的服务间通信。我们已经在 Docker 中启动了 NATS 服务,并在 connect
服务启动时连接到 NATS 服务器并进行订阅。
4. NATS 通讯框架的实现
4.1 定义 NATS 客户端接口
在项目中创建一个 remote
目录,用于存放远程调用相关的代码。在 remote
目录中定义一个 NATS 客户端接口:
remote/client.go
:
package remote
type NATSClient interface {
Run() error
Close() error
SendMessage(message []byte) error
}
4.2 实现 NATS 客户端
接下来,实现 NATS 客户端的具体逻辑:
remote/nats_client.go
:
package remote
import (
"context"
"log"
"github.com/nats-io/nats.go"
)
type NATSClientImpl struct {
nc *nats.Conn
subject string
}
func NewNATSClient(serverURL, subject string) NATSClient {
return &NATSClientImpl{
subject: subject,
}
}
func (c *NATSClientImpl) Run() error {
var err error
c.nc, err = nats.Connect("nats://localhost:4222") // 连接到 NATS 服务器
if err != nil {
return err
}
// 订阅消息
_, err = c.nc.Subscribe(c.subject, func(msg *nats.Msg) {
log.Printf("Received message: %s", msg.Data)
// 处理接收到的消息
})
if err != nil {
return err
}
return nil
}
func (c *NATSClientImpl) Close() error {
if c.nc != nil {
return c.nc.Close()
}
return nil
}
func (c *NATSClientImpl) SendMessage(message []byte) error {
if c.nc != nil {
return c.nc.Publish(c.subject, message)
}
return nil
}
4.3 集成 NATS 客户端到业务逻辑
在 connect
服务中,集成 NATS 客户端并处理消息:
connect/service.go
:
package connect
import (
"context"
"log"
"your_project/remote"
)
type ConnectService struct {
NATSClient remote.NATSClient
}
func NewConnectService() *ConnectService {
natsClient := remote.NewNATSClient("nats://localhost:4222", "auth.update_user_address")
return &ConnectService{
NATSClient: natsClient,
}
}
func (s *ConnectService) Run() error {
if err := s.NATSClient.Run(); err != nil {
return err
}
return nil
}
func (s *ConnectService) Close() error {
return s.NATSClient.Close()
}
func (s *ConnectService) HandleMessage(message []byte) error {
// 处理接收到的消息
log.Printf("Handling message: %s", message)
return nil
}
func (s *ConnectService) SendMessage(message []byte) error {
return s.NATSClient.SendMessage(message)
}
4.4 启动和使用 NATS 客户端
在 connect
服务的主函数中,启动 NATS 客户端并处理消息:
connect/main.go
:
package main
import (
"context"
"log"
"your_project/connect"
)
func main() {
service := connect.NewConnectService()
if err := service.Run(); err != nil {
log.Fatalf("Failed to start connect service: %v", err)
}
// 模拟发送消息
message := []byte(`{"cid":"client123","body":{"uid":"user123","location":{"latitude":"34.0522","longitude":"-118.2437","province":"CA","city":"Los Angeles"}},"src":"connect","dst":"auth","router":"user_handler.update_user_address","type":"normal"}`)
if err := service.SendMessage(message); err != nil {
log.Fatalf("Failed to send message: %v", err)
}
// 等待一段时间后关闭服务
<-context.Background().Done()
if err := service.Close(); err != nil {
log.Fatalf("Failed to close connect service: %v", err)
}
}
5. 测试与调试
在测试过程中,确保以下几点:
-
NATS 服务已启动:确保 NATS 服务已在 Docker 中启动并运行。
-
服务连接正确:确保
connect
服务能够成功连接到 NATS 服务。 -
消息处理逻辑正确:确保消息能够被正确发送和接收,并且逻辑处理正确。
测试步骤如下:
-
启动 NATS 服务。
-
启动
connect
服务。 -
模拟发送消息,观察日志输出,确保消息被正确处理。
-
检查
auth
服务是否收到消息并正确处理。
如果在测试过程中遇到问题,可以通过以下方式排查:
-
检查 NATS 服务的日志,确保服务正常运行。
-
检查
connect
服务的日志,确保连接和消息发送逻辑正确。 -
使用调试工具(如 VS Code 的调试功能)设置断点,逐步排查问题。