skynet源码分析(十七)ShareTable的实现

  因为 skynet 中的每一个 snlua 服务都运行在一个独立的 LuaState 中,所以就存在了数据共享的问题。因为 Lua 本身是不支持跨 LuaState 分享数据的,所以 skynet 通过修改 Lua 实现了这一功能,不过有一些局限,只能共享只读数据。本篇会尝试分析这一功能的实现。

服务

  使用 sharetable 的时候会启动一个进程唯一的服务 sharetable,它的内容在 sharetable_service 这个函数中,提供了对共享数据加载,查询,关闭的功能。

加载

  ShareTable 可以通过三种方式加载数据,文件,table 和字符串,不管是什么途径,都要传入一个 filename 来作为文件的标识。因为一般 ShareTable 都是用来存策划配置表的,所以用文件加载的时候最多。
  skynet 中把加载过的共享文件的结构叫做 matrix,在 Lua 层有两个 table 用来管理 matrix 的映射,table matrix 保存的是指针地址到文件被引用信息的映射,table files 保存的是文件名字到 matrix 的映射。
  core.matrix 是加载共享数据的最终入口函数,执行的时候会为每一份数据创建一个新的 LuaState 来加载和保存文件。文件加载用的是 Lua 自带的 luaL_loadfilex_ 进行,只是有一个关键的步骤,会调用 skynet 在 Lua 中增加的 mark_shared 函数,给加载后的 table 打上 G_SHARED 的标志,并且会递归的给 table 中所有需要 gc 的值都打上 G_SHARED 标志。所有共享的数据,在 gc 过程中都不会被标记和回收。
  core.matrix 返回的是一个 userdata,也就是 matrix 了,结构是 state_ud,可以看到 state_ud 中只有一个 lua_State 指针,指向的就是为加载共享数据创建的那个 LuaState 变量。filename 到 matrix 的映射关系会被保存在 table files 中。

1
2
3
struct state_ud {
    lua_State *L;
};

  返回值会被设置一些元表项,close 用来关闭 LuaState 停止分享,getptr 用来获取 matrix 的指针,size 用来获取 LuaState 的内存使用量。

查询

  查询通过的是 sharetable.query 进行,需要指定要查询的 filename,首先在 table files 中查找对应的 matrix,如果没有则说明没有加载过,直接返回即可。
  如果有记录的话,则尝试获取文件的被引用信息,累加文件的被引用计数,并且把引用的源服务地址加入到 table clients 中,然后通过 matrix 的 getptr 拿到其指针,将它返回给源服务,源服务拿到指针以后,会调用 clone 方法将共享的 table 的引用复制到本 LuaState 中。

更新

  要更新 sharetable 首先要重新 load 一次新文件,在 load 同一个文件时,会检查之前是否有过加载的记录,如果有的话,会清掉之前的记录,然后再加载。
  在执行过新的加载以后,还要在使用这张表的服务里调用 sharetable.update 才行。update 会首先重新 query 一次,拿到新的共享表数据。然后对新数据和旧数据进行一次递归对比,找出所有不一样的数据,然后遍历整个 LuaState 中的数据,替换掉全部的旧值。

字符串处理

  这样实现会有一个问题,就是字符串的比较问题。在新版的 Lua 中,短字符串还是内化的,也就是两个相同内容的短字符串是指向的同一份数据。根据这个实现又对字符串的比较做了一个优化,就是只比较地址即可。
  但是因为 sharetable 是在别的 LuaState 里面的,所以就会出现本 LuaState 中的某个字符串与 sharetable 中某个相同字面量的字符串不等的问题。
  为了解决这个问题,skynet 对 Lua 进行了一些改动,在 string 的类型 TString 中加了一个 id 来辅助比较。普通字符串默认都是 0,每个被共享化的字符串都会被赋予一个正整数 id。
  加上 id 以后的字符串比较方式变为了,首先比较地址,如果不同的话,比较 id,如果 id 不为 0 且相等,也可以直接判定字符串相等。如果 id 的比较也不相等的话,则需要老老实实调用 memcmp 进行一次完整比较,如果完整比较相等的话,会把较小的 id 改为较大的 id,这样下一次比较的时候就可以通过 id 直接判断出相等了。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计