leaf源码分析(一)----chanrpc

本文公众号链接为:https://mp.weixin.qq.com/s/bzh3jZJkz96WxbPX4zF75A



Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏服务器框架。Leaf 适用于各类游戏服务器的开发,包括 H5(HTML5)游戏服务器。


阅读中文文档,其中介绍的是


leaf/chanrpc 提供了一套基于 channel 的 RPC 机制,用于游戏服务器模块间通讯。chanrpc提供了三种模式

  1. 同步模式,调用并等待 ChanRPC 返回

  2. 异步模式,调用并提供回调函数,回调函数会在 ChanRPC 返回后被调用

  3. Go 模式,调用并立即返回,忽略任何返回值和错误


那么下面看源码

源码目录为:




先看看github.com/name5566/leaf/chanrpc/example_test.go,里面演示了chanrpc的具体用法

chanrpc分为server和client两个部分



以上代码中是server的一个过程

1、初始化了一个server。

2、接着,注册了server提供的rpc函数。rpc支持三种类型

func([]interface{})                           参数为[]interface,无返回值
func([]interface{}) interface{} 参数为[]interface,返回值为interface
func([]interface{}) []interface{} 参数为[]interface,返回值为[]interface

3、最后server开启Exec执行rpc函数



以上为client过程

1、创建了client

2、同步调用。提供了集中调用方法。可以根据返回值分类来记忆:

Call0,Call1,CallN



3、异步调用。异步与同步的区别在于,异步执行调用完就会直接返回,但其注册了cb(call back)回调函数。在最后会等待异步执行完毕后,执行cb

4、Go模式。和异步调用的区别就是无cb。也是立即返回的。


用法分析完后,看具体的源码


github.com/name5566/leaf/chanrpc/chanrpc.go

基本数据结构RetInfo




ret interface{} 记录返回值,有三种类型

err error              记录err
cb interface{} 保存上下文中的Call back函数


基本数据结构CallInfo



f       interface{}                rpc调用的函数
args    []interface{} 参数
chanRet chan *RetInfo       传递ret的chan
cb      interface{} 保存上下文中的Call back函数


Server



Server数据结构

functions map[interface{}]interface{}           用于保存注册rpc函数
ChanCall  chan *CallInfo                            用于接收rpc调用chan


以上是Server初始化。注意其中l,是chancall的容量,意味着,异步调用时候能够异步传递多少函数。

Register



以上是注册接口(注意:此处并非线程安全的)

1、通过switch来对类型做一个判断。default中,则是不支持的类型,将会panic

2、判断是否重复注册

3、注册,其实就是放到map中。


Exec

回顾下用法

在github.com/name5566/leaf/chanrpc/example_test.go中





从以上代码中可以看出,就是从ChanCall中,取出CallInfo信息,然后执行。

继续跟踪



根据不同的rpc函数类型,封装不同的RetInfo。继续跟踪



1、当client调用的时候,没有注册chanret时,也就是为nil,不需要传递返回值,则直接返回不处理。否则继续往下处理

2、将client调用rpc时候,注册的cb,传递到RetInfo中。并将RetInfo发送到CallInfo中的chanRet中,将ret信息和cb传递给client。


到此Server所处理的流程都已经完了


Client



s               *Server                            对应的Server
chanSyncRet     chan *RetInfo             同步调用时候,用于接收ret的chan
ChanAsynRet     chan *RetInfo            异步调用的时候,用于接收ret和cb的chan
pendingAsynCall int                           异步调用的计数器


初始化

两种模式



1、先初始化client结构体。这里要注意的是

同步chanSyncRet容量为1

异步cahnAsynRet的容量是初始化为参数l的。

2、将其Attach,附属到server中。其实就是将s初始化到client中的成员变量s

这种模式需要调用两次函数



这种模式,是将初始化和Attach封装到了一起。


同步调用



步骤是一样的

1、查找函数f

2、远程调用call。其中chanRet赋值的是chanSyncRet,block设置为true。这个是区别于异步调用模式的。

3、从chanSyncRet中接收ret,并将ret返回给上一层



f函数,功能就是一个,从server的注册函数中,查找到对应id的函数,将其返回。

其中做了一些函数的检查。



call函数:将CallInfo信息通过server的chancall发送给server。

1、同步模式,会阻塞等待

2、异步模式,会直接发送。如果chancall的容量不够,则会直接执行default。

在NewServer的时候,有个参数l,就是用来设置chancall的容量的。


异步调用


上述步骤

1、参数分割,最后一个参数是cb

2、对cb函数,类型检查,不支持的函数的话,则panic

3、判断异步调用是不是超出了异步chanAsynRet的容量,如果是,则直接将err返回

4、调用asynCall,并计数pendingAsynCall



以上步骤

1、从f中查找对应id的rpc函数。如果未查到,则直接通过chanAsynRet将err返回

2、call,这里面chanRet赋值的是chanAsynRet。blcok设置为false。这是区别于同步调用的。


异步执行结果





通过RetInfo中获取到cb函数,通过不同类型,进行cb调用。三种类型如下


ri.cb.(func(error))(ri.err)

ri.cb.(func(interface{}, error))(ri.ret, ri.err)

ri.cb.(func([]interface{}, error))(assert(ri.ret), ri.err)


Go模式


其实这个模式是最简单的

1、查找对应id的rpc函数

2、构建CallInfo发送到chancall中

不需要构建返回,也没有cb


close

还有一个没讲解的那就是close



server close

1、会先将chancall关闭掉

2、处理chancall中剩余的rpc调用。并不会执行,而是执行返回错误



client close

同步的是不用处理的,肯定会处理结束

主要的是异步的,异步有个计数器,会一直处理等待计数器为0




龚浩华

月牙寂道长

QQ 29185807

2018年04月17日

如果你觉得本文对你有帮助,可以转发分享到你的朋友圈,让更多人一起学习。

第一时间获取文章,可以关注本人公众号:月牙寂道长,也可以扫码关注



猜你喜欢

转载自blog.csdn.net/screscent/article/details/79976928