关于增加skynet_fly lua服务启动的后置加载

前言

skynet lua服务默认启动加载是使用skynet/lualib/loader.lualoader.lua 会加载并执行config中配置的preload路径的lua文件,这个操作的好处是可以把所有服务的一些前置环境操作,或者一些常用工具函数写到全局变量中(我更倾向于封装成工具模块通过require引用,不去污染全局环境,尽量保持原生环境)。在skynet_fly框架中,我就利用preload做了luapath的设置来达到不用手写path的目的。在这之前我都没有想改动preload的想法,直到有一天,我萌生了想给skynet_fly日志增加钩子函数的想法,因为在工作中也在严重日志中增加了邮件推送,不过是直接修改的日志服务,不满足开闭原则,不够优雅。

增加日志钩子函数

那么如何给日志服务增加钩子函数又满足开闭原则呢?,我目前能想到的,有两种方案。

方案一

在log服务中实现钩子函数。
业务层弃用skynet logger原生日志服务,改用lua自定义日志服务并在logger服务启动后利用消息的方式通知logger服务需要加载哪些钩子函数文件实现。

  • 优势

    1. 钩子函数由日志服务执行,业务逻辑不用处理,不影响业务服务吞吐量。
  • 劣势

    1. 用lua写日志服务性能远远低于c层的日志服务,c比lua快10倍。
    2. skynet原生大量代码依赖原生日志服务,如果自定义要么保留原生日志,要么修改大量源码。
    3. 想要利用signal_hup信号来切割日志也需要修改源码。

方案二

在log模块中实现钩子函数。
log模块中简单的调用增加钩子函数的API,在preload中设置钩子函数,这样每个启动lua服务都挂上钩子函数。

  • 优势

    1. 最小改动。
  • 劣势

    1. 增加了业务层log的调用开销。

通过对比两种方案,我选用了方案二,原因如下:
1. 尽量保持skynet源码不改动。
2. c实现的日志服务速度更快。

原因

在使用preload去加载钩子函数的时候,想到这个是偏业务的实现,并不是所有项目都要在log日志外挂一个钩子函数,也可能每个项目钩子函数实现不同,这时候有人可能会想到,skynet中的preload不是可以配置的吗,但是我想大部分项目都依赖一个preload文件,突然有一个项目不同,岂不是要拷贝一份再新增不同,或者全部堆叠在一个文件中,我觉得这是很糟糕的设计。不满足开闭原则,本身skynet_fly项目就需要依赖框架的preload.lua文件,当然有个解决方案就是在自定义的preload中再次load preload.lua,手动链式依赖。但是我不想这样设计,我希望我用代码生成config能不手改就不手改,况且这个还是路径,手写挺麻烦的。解决这个问题很简单,把前置加载单个文件,改为前置加载多个文件,skynet_fly默认加载框架的preload.lua,其他项目需要额外加载只需要在main.lua中显示增加。这里又想到了preload有个不好的地方,preload中require的文件是不能热更的,应该在这之前还没有调用skynet.codecache off。所以就想着增加afterload,afterload是lua服务最后加载的,如果是可热更模块已经执行了skynet.codecache off

使用注意点

加载顺序是preload-main-afterload
可想而知afterload执行代码,不可能在mainrequire期间生效。

改动

后续发现在loader中就关闭cache,这样preload中的require也能检测热更了。

示例

skynet_fly/examples/log_hook
skynet_fly/examples/pre_after_load

总结

修改了preload和afterload能让skynet_fly在微服务多项目架构增加更多的灵活性和扩展性。
skynetfly源码地址


关于增加skynet_fly lua服务启动的后置加载
https://huahua132.github.io/2023/08/20/skynet_fly_ss/after_load/
作者
huahua132
发布于
2023年8月20日
许可协议