session
是个啥?参考官方文档: https://pitaya.readthedocs.io/en/latest/features.html?highlight=session#sessions
本文中实际上我们主要用的函数:
1,Bind(ctx context.Context, uid string) error 绑定会话:通常用在处理登录业务的函数内:
例如客户端传来用户id,然后就将其作为session的uid,传入该函数即可。
后续客户端发来任意的消息时,session里就都会带上该id。其他业务就可以取出来,然后用用户id来进行业务处理,比如查询数据库。
2,Set(key string, value interface{}) 根据键值 设置session存储的一条数据。该数据永远存在,直到客户端断开tcp连接。
Get(key string) interface{} 根据键值 返回数据
3,GetSessionFromCtx 通过上下文即可获得客户端和服务器的会话:
因为ctx(context.Context)基本上在任何业务处理函数获取到,再通过该函数就能直接获取到session,获取到session就能通过get函数获取到我们存入到里面的任意类型的数据
中间件
参考博客: go-pitaya学习笔记(8)-pipeline demo分析_我是大彩笔的博客-CSDN博客
得知一个很重要的点:pitaya框架的中间件函数是格式固定的。
中间件的使用:参考博客: go-pitaya学习笔记(8)-pipeline demo分析_我是大彩笔的博客-CSDN博客
假设这里有个业务函数:
func (r *Room) SetSessionData(ctx context.Context, data *SessionData) ([]byte, error) {
前置中间件函数格式:
HandlerTempl func(ctx context.Context, in interface{}) (c context.Context, out interface{}, err error)
红=框架传来 或=上一个前置中间件的返回值(绿色部分)
绿=传递给业务函数的入参(红) 或下一个前置中间件的入参(红)
后置中间件函数格式:
AfterHandlerTempl func(ctx context.Context, out interface{}, err error) (interface{}, error)
红=业务函数的返回值(绿) 或=上一个后置中间件的返回值(绿)
绿=传递给业务函数的入参(红)或下一个后置中间件的入参(红)
注意:
1,它传入的参数有ctx,这意味着我们能直接通过它获取到session,进而获取到我们存入的任何数据
2,响应客户端的业务函数(handler)格式也是固定的 ,参考:
https://pitaya.readthedocs.io/en/latest/API.html?highlight=component#handler-example
这意味着我们可以合理的在中间件包装数据或error变为自定义的结构体
实验
为了方便后续演示,这里将官方的源码的示例demo中的cluster项目( pitaya/examples/demo/cluster at main · topfreegames/pitaya · GitHub )
单独提取出来为一个项目,并在其基础上增加中间件与业务代码,同时改动的地方加上注释//new。
本文演示项目地址 :Aikxia alvis / pitayaServerDemo · GitLab
根据前面一篇博客 pitaya学习实践笔记(1)-跑通demo_蹦蹦八哥弹的博客-CSDN博客 。得知前端服务器=connector 后端=room
假设前端服务器connector不处理任何业务请求,所有的业务请求都交给后端服务器room
假设现在有这么个需求:
1,前置中间件检查用户任意一个请求的信息,如果用户未登录,则要拦截下来,并直接返回给客户端错误提示信息,不进行接下来的业务函数处理
2,并且要求登录本身这个请求不用验证,直接放行。
实现:
D:\Project\VSCode\pitayaServerDemo\cluster\middleware\before.go
// 未登录则要拦截下来,部分请求不用验证是否登录
func BeforeHandler_LoginCheck(ctx context.Context, in interface{}) (rsCtx context.Context, out interface{}, outErr error) {
rsCtx = ctx
out = in
session := app.GetSessionFromCtx(ctx)
route := session.Get("route")
for _, rIn := range exceptInRoute { // 这些路径不用验证是否登录
if rIn == route {
return
}
}
if session.UID() == "" {
outErr = fmt.Errorf("未登录")
return
}
return
}
D:\Project\VSCode\pitayaServerDemo\cluster\services\room.go
// new
func (r *Room) Login(ctx context.Context, data []byte) ([]byte, error) {
s := r.app.GetSessionFromCtx(ctx)
s.Bind(ctx, string(data))
return []byte("Login success"), nil
}
要完成这个功能,那么我们首先需要知道请求的路径(route:对应到具体处理业务函数的位置;格式=服务器类型.组件名.函数名),如果是登录请求(route=room.room.login),那么直接放行让它进入到函数func (r *Room) Login(ctx context.Context, data []byte) 执行。如果是其他请求(比如 route=room.room.join)那么就检查用户是否登录了(session的uid默认为"",这里登录成功后执行Bind函数,然后uid就不为空了),否则直接返回给客户端错误提示信息,是则放行,让它可以进入到函数func (r *Room) Join(ctx context.Context)进行处理。
那么业务请求跑到中间件时,ctx是否自己带route之类的信息呢?
为了测试方便,将main.go改为启动后端,run.bat运行前端服务器,同时加入我们才写的前置中间件。
通过pitaya-cli发条消息,结果显示ctx自带的数据只有ipversion,以及日志组件,并不带我们想要的route。
那我们怎么给每个到达后端服务器中间件的请求,在这之前加上route呢?
参考 : https://pitaya.readthedocs.io/en/latest/API.html?highlight=addroute#routing-messages
发现一个叫addRoute()的函数,同时知道了所有到达后端服务器的请求都要经过该函数,同时发现其入参正好带着
route参数,还要至关重要的ctx参数(我们可以直接通过GetSessionFromCtx 拿到session,然后将route存入session里面;或直接存入ctx里)。而请求到达中间件后,就可以通过ctx取出存入的数据。
路由方法:ctx上下文,route 路由实例,payload(载荷) 请求的参数和数据,cluster.Server 当前服务器的集群map
调换一下main.go和run.bat启动的服务器,并调试:
往addRoute()加上session存入ip,请求参数数据payload。
后置中间件将完整请求信息和结果记录到log里:
通过pitaya_cli发送请求。最终效果
注:
sv->{"code":"PIT-000","msg":"nats: no responders available for request"}
是因为前端服务器一直开着,重启了后端服务器后立马客户端发请求,这时nat还没服务发现后端
嫌pitaya的日志打印(红色部分吗,大多是level=debug,warning)的太多的话,可以重写框架logger:
logger.SetLogger(initLogger()) //写到main函数内
func initLogger() interfaces.Logger {
plog := logrus.New()
plog.Formatter = new(logrus.TextFormatter)
plog.Level = logrus.ErrorLevel//只显示err
log := plog.WithFields(logrus.Fields{
"source": "pitaya",
})
return logruswrapper.NewWithFieldLogger(log)
}