学习笔记: 我家别墅靠大海/pitaya-learn
尝试集成功能:我家别墅靠大海/pitaya-game
如果你正在看此笔记,请你左边放笔记,右边放chatdemo的代码!!
我是按代码的顺序记的笔记
和之前一样,把demo拿过来
cluster
看一下main.go
映入眼帘的就是是否是前端服务器。。第二节有记到
那我们猜着运行一个前端服务器和一个后端服务器。
估计默认参数就是前端,改了参数就是后端
前端:
后端:
go run main.go -port=3251 -type=room -frontend=false
启动完成后端以后,前端好像检测到了集群,好像启动成功了
html测试不了了,用官方cli测试一下
安装
go get github.com/topfreegames/pitaya-cli
如果出现了
在mod里面把grpc降到1.26
这个包拉完,命令就可以用了
我们先改一下demo里room.go的源码
他这个有点问题
所有人的uid都是一样的,不太好测试,改成chatdemo一样的。
这个entry类似于打开网页自动分配个uid。
只不过chatdemo集成在join里面了
别忘了你的main.go引入的还是线上的services,需要改成本地的。
重新启动一下全部服务器。
按照火龙果cli的demo链接一下
示例:https://github.com/topfreegames/pitaya-cli
我这边启动了多个cli,
测起来是可以的,好像没啥问题。
message没测试,没找到怎么传proto。反正测不测也无所谓了
可以看到,我们实际链接的服务器是前端服务器
我们跳过前端,连下后端服务器room试试
连不过去,好了,我们分析下源码
源码分析1
源码有点太乱了,我们只保留room基础的源码,后续我们再分析别的。
删除 connector.go
main.go代码如下
package main
import (
"context"
"flag"
"fmt"
"game/demo/cluster/services"
"strings"
"github.com/topfreegames/pitaya/v2"
"github.com/topfreegames/pitaya/v2/acceptor"
"github.com/topfreegames/pitaya/v2/cluster"
"github.com/topfreegames/pitaya/v2/component"
"github.com/topfreegames/pitaya/v2/config"
// "github.com/topfreegames/pitaya/v2/examples/demo/cluster/services"
"github.com/topfreegames/pitaya/v2/groups"
"github.com/topfreegames/pitaya/v2/route"
)
var app pitaya.Pitaya
func configureBackend() {
room := services.NewRoom(app)
app.Register(room,
component.WithName("room"),
component.WithNameFunc(strings.ToLower),
)
app.RegisterRemote(room,
component.WithName("room"),
component.WithNameFunc(strings.ToLower),
)
}
func configureFrontend(port int) {
err := app.AddRoute("room", func(
ctx context.Context,
route *route.Route,
payload []byte,
servers map[string]*cluster.Server,
) (*cluster.Server, error) {
// will return the first server
for k := range servers {
return servers[k], nil
}
return nil, nil
})
if err != nil {
fmt.Printf("error adding route %s\n", err.Error())
}
}
func main() {
port := flag.Int("port", 3250, "the port to listen")
svType := flag.String("type", "connector", "the server type")
isFrontend := flag.Bool("frontend", true, "if server is frontend")
flag.Parse()
builder := pitaya.NewDefaultBuilder(*isFrontend, *svType, pitaya.Cluster, map[string]string{}, *config.NewDefaultBuilderConfig())
if *isFrontend {
tcp := acceptor.NewTCPAcceptor(fmt.Sprintf(":%d", *port))
builder.AddAcceptor(tcp)
}
builder.Groups = groups.NewMemoryGroupService(*config.NewDefaultMemoryGroupConfig())
app = builder.Build()
//TODO: Oelze pitaya.SetSerializer(protobuf.NewSerializer())
defer app.Shutdown()
if !*isFrontend {
configureBackend()
} else {
configureFrontend(*port)
}
app.Start()
}
room.go 如下
package services
import (
"context"
"fmt"
"strconv"
"time"
"github.com/topfreegames/pitaya/v2"
"github.com/topfreegames/pitaya/v2/component"
"github.com/topfreegames/pitaya/v2/examples/demo/protos"
"github.com/topfreegames/pitaya/v2/timer"
)
type (
// Room represents a component that contains a bundle of room related handler
// like Join/Message
Room struct {
component.Base
timer *timer.Timer
app pitaya.Pitaya
Stats *protos.Stats
}
// UserMessage represents a message that user sent
UserMessage struct {
Name string `json:"name"`
Content string `json:"content"`
}
// Stats exports the room status
Stats struct {
outboundBytes int
inboundBytes int
}
// NewUser message will be received when new user join room
NewUser struct {
Content string `json:"content"`
}
// AllMembers contains all members uid
AllMembers struct {
Members []string `json:"members"`
}
// JoinResponse represents the result of joining room
JoinResponse struct {
Code int `json:"code"`
Result string `json:"result"`
}
)
// NewRoom returns a new room
func NewRoom(app pitaya.Pitaya) *Room {
return &Room{
app: app,
Stats: &protos.Stats{},
}
}
// Init runs on service initialization
func (r *Room) Init() {
r.app.GroupCreate(context.Background(), "room")
}
// AfterInit component lifetime callback
func (r *Room) AfterInit() {
r.timer = pitaya.NewTimer(time.Minute, func() {
count, err := r.app.GroupCountMembers(context.Background(), "room")
println("UserCount: Time=>", time.Now().String(), "Count=>", count, "Error=>", err)
println("OutboundBytes", r.Stats.OutboundBytes)
println("InboundBytes", r.Stats.OutboundBytes)
})
}
// Entry is the entrypoint
func (r *Room) Entry(ctx context.Context, msg []byte) (*protos.JoinResponse, error) {
logger := pitaya.GetDefaultLoggerFromCtx(ctx) // The default logger contains a requestId, the route being executed and the sessionId
s := r.app.GetSessionFromCtx(ctx)
// err := s.Bind(ctx, "banana")
fakeUID := s.ID() // just use s.ID as uid !!!
err := s.Bind(ctx, strconv.Itoa(int(fakeUID))) // binding session uid
if err != nil {
logger.Error("Failed to bind session")
logger.Error(err)
return nil, pitaya.Error(err, "RH-000", map[string]string{"failed": "bind"})
}
return &protos.JoinResponse{Result: "ok"}, nil
}
// Join room
func (r *Room) Join(ctx context.Context) (*protos.JoinResponse, error) {
logger := pitaya.GetDefaultLoggerFromCtx(ctx)
s := r.app.GetSessionFromCtx(ctx)
err := r.app.GroupAddMember(ctx, "room", s.UID())
if err != nil {
logger.Error("Failed to join room")
logger.Error(err)
return nil, err
}
members, err := r.app.GroupMembers(ctx, "room")
if err != nil {
logger.Error("Failed to get members")
logger.Error(err)
return nil, err
}
s.Push("onMembers", &protos.AllMembers{Members: members})
// err = r.app.GroupBroadcast(ctx, "connector", "room", "onNewUser", &protos.NewUser{Content: fmt.Sprintf("New user: %d", s.ID())})
err = r.app.GroupBroadcast(ctx, "connector", "room", "onNewUser", &protos.NewUser{Content: fmt.Sprintf("New user: %s", s.UID())})
if err != nil {
logger.Error("Failed to broadcast onNewUser")
logger.Error(err)
return nil, err
}
return &protos.JoinResponse{Result: "success"}, nil
}
// Message sync last message to all members
func (r *Room) Message(ctx context.Context, msg *protos.UserMessage) {
logger := pitaya.GetDefaultLoggerFromCtx(ctx)
err := r.app.GroupBroadcast(ctx, "connector", "room", "onMessage", msg)
if err != nil {
logger.Error("Error broadcasting message")
logger.Error(err)
}
}
代码干净了,再测一下
其实发现整套代码和之前的地方没什么不同。
先看一下配置后端
是注册模块,不同的是,分布式要注册一个远程模块。
注册方法和之前注册本地模块一样。
他这个demo是通过转发消息实现的。
后面其实是可以通过rpc实现
文档:Pitaya API — Pitaya documentation
再看看注册前端。
app有个addroute方法,添加一个分布式模块路由 ,消息由 pitaya 转发到适当的服务器类型
“room” 服务器类型
"第二个参数是路由方法"
文档:Pitaya API — Pitaya documentation
路由方法
ctx上下文
route 路由实例
payload 不知道啥玩意
cluster.Server 当前服务器的集群map
返还
一个服务器句柄
demo里面是只要在集群中找到了这个类型的服务器,就返回这个服务器
当然可以我们可以实现其他的负载算法
笔记5: