skynet学习笔记之http服务搭建

文章目录

    • 前言
    • 环境准备
    • http 服务搭建流程
    • skynet 日志服务介绍
    • 给日志输出添加颜色
    • 结语

前言

今天尝试了下 skynet 提供的 http 服务,服务代码全部包含在 examples/simpleweb.lua 文件中,服务介绍见 skynet_wiki-http。
skynet 通过 daemon = “./skynet.pid” 设置进程 id 的保存文件名,开启后台模式,我用的前台模式,所以注释掉这个配置项即可。

环境准备

  1. centos 7 服务器,我是笔记本里装了个 centos 7 镜像的虚拟机。
  2. 在 github 上 fork 了 skynet 官方库。
  3. 为了能提交代码,需要在自己账户下添加 ssh 公钥,密钥放在自己本地服务器上,通过密钥拉取,提交代码都很方便且安全。
  4. git clone 自己 github 账户下的 skynet 库,步骤2 fork 出来的库。
  5. gcc -v 查看 gcc 的版本,如果只有 4.几,建议升级,最新的 skynet 引用了 stdatomic.h 等头文件,需要 gcc 的较高版本的支持。我目前是 gcc 9.3.1。
  6. yum install -y autoconf 安装 autoconf 程序,jemalloc 库编译前需要它来自动生成 makefile。
  7. 准备就绪,执行 make linux 编译 skynet,喝口水等它编译好就可以搭建 http 服务了。

http 服务搭建流程

examples 这个目录下是 skynet 的一些示例文件,很多很杂,涉及不同的服务和配置,对我们新手来说,掌握 skynet 的运行轨迹,知道启动了什么服务,了解实际关联了哪些脚本,是很重要的,所以我创建了一个 game 目录,用来存放我实际启用的脚本,如果我启动失败或者有报错,那么肯定是我缺少了某些脚本,我可以再把他们放到我的 game 目录下,这样做到心里有数。我给这个目录下的文件定位是配置 + 非 skynet 默认支持的服务外的内容,这部分内容要么是我们新加的文件,要么是 examples 目录下的文件。对于搭建 http 服务,game 下面的内容如下:
skynet学习笔记之http服务搭建_第1张图片
正式搭建流程:

  1. 编辑 game/etc/config 将默认的 start = “main” 设置为我们要启动的服务 start = “service_web/simpleweb”

  2. 编辑 game/etc/config.path 从 luaservice 表示的服务路径中删除 test、example 相关路径,保证 skynet 不会加载我们确定的服务内容以外的服务。然后加上我们新创建的服务路径。

    	luaservice = root.."service/?.lua;"..root.."game/myservice/?.lua"
    
  3. 注释掉 config.path 的 snax = xxx 配置项,snax 服务貌似是一个推荐的 lua 业务框架,我们这里用不到它,就不要被它干扰到。

  4. 启动服务试试看 ./skynet game/etc/config
    skynet学习笔记之http服务搭建_第2张图片
    通过访问虚拟机的 ip:8001 成功收到服务器的响应
    skynet学习笔记之http服务搭建_第3张图片
    服务器也输出了我在 agent 服务解析消息后加的日志
    skynet学习笔记之http服务搭建_第4张图片
    进行到这里,我们 http 服务已经搭建完成,我尝试用我的微信小程序发起了 http 请求,也能成功响应。

skynet 日志服务介绍

日志服务 logger 是一个基础的 c 服务,skynet 启动的时候会自动启动 logger 服务。

  1. 源文件:service-src/service_logger.c
  2. 编译之后的库文件:cservice/logger.so
  3. 启动 logger 服务的代码位置:
    skynet学习笔记之http服务搭建_第5张图片
  4. lua-skynet.c 提供的日志输出接口 error,其实就是从当前服务往 logger 服务发送一条类型为 PTYPE_TEXT 的服务消息,logger 服务处理该消息,往控制台(前台模式启动,且 config 中没有配置 logger 的话 )和指定文件(config 中配置了 logger 的话)输出日志信息。
    skynet学习笔记之http服务搭建_第6张图片
  5. 该日志接口在 lua 层的使用方式为 lualib/skynet.lua:
local c = require "skynet.core"  // 加载 luaclib/skynet.so 库,执行 luaopen_skynet_core api 注册该库提供的 c 接口。
...
skynet.error = c.error     
  1. 这样,你要使用的时候:
local skynet = require "skynet"  // 加载 lualib/skynet.lua,其中封装了不少 c 层的 api。
skynet.error("日志内容1","日志内容2","...")
  1. 最后日志的输出格式为:
[:打印日志的服务id] 日志内容1 日志内容2 ...

给日志输出添加颜色

skynet 的日志单独由一个服务来非阻塞式输出日志,性能上没有什么问题,但是日志输出不分级别,且输出模式不是 printf 的风格,我不习惯。所以我在 lua 层添加了个日志处理工具脚本,来封装 skynet.error 实现我习惯的 LOG/INFO/WARN/ERROR 接口,且修改 service-src/service_logger.c 服务代码,实现日志输出到前台的时候根据日志级别不同具有不同的颜色。

  1. 在 game 下面创建一个 tools 目录,作为工具脚本的存放位置;创建 game/tools/tool_log.lua 来封装 skynet.error。

    --==============================================================================
    -- 工具函数库
    -- Author: toby
    -- Desc:
    --	   在 preload 中加载到全局表
    -- History:
    --     2022-03-31 11:35:54 Updated
    -- Copyright © 2022 https://github.com/tobybo All rights reserved.
    --==============================================================================
    
    local c = require "skynet.core"
    
    local log = c.error
    
    local base_str = ")%s%s [%s], %s" -- 用日志内容第一个字符为 ')' 标识后两个字符为颜色代码
    
    local COLOR = {
        BLACK   = 30,
        RED     = 31,
        GREEN   = 32,
        YELLOW  = 33,
        BLUE    = 34,
        FUCHSIN = 35, -- 品红
        CYAN    = 36, -- 青色 蓝绿色
    }
    
    LOG = function(format_str, ...)
        local str = string.format(base_str, COLOR.CYAN, "LOG", SERVICE_NAME, format_str)
        if select("#", ...) > 0 then
            str = string.format(str, ...)
        end
        log(str)
    end
    
    INFO = function(format_str, ...)
        local str = string.format(base_str, COLOR.GREEN, "INFO", SERVICE_NAME, format_str)
        if select("#", ...) > 0 then
            str = string.format(str, ...)
        end
        log(str)
    end
    
    WARN = function(format_str, ...)
        local str = string.format(base_str, COLOR.YELLOW, "WARN", SERVICE_NAME, format_str)
        if select("#", ...) > 0 then
            str = string.format(str, ...)
        end
        log(str)
    end
    
    ERROR = function(format_str, ...)
        local str = string.format(base_str, COLOR.RED, "ERROR", SERVICE_NAME, format_str)
        if select("#", ...) > 0 then
            str = string.format(str, ...)
        end
        log(str)
    end
    
  2. 修改 service-src/service_logger.c 代码,实现日志输出到前台模式的时候具有颜色。

    static int
    logger_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
    	struct logger * inst = ud;
    	switch (type) {
    	case PTYPE_SYSTEM:
    		if (inst->filename) {
    			inst->handle = freopen(inst->filename, "a", inst->handle);
    		}
    		break;
    	case PTYPE_TEXT:
            {
                int offset = 0;
                const char* _msg = msg;
                if (sz > 3 && _msg[0] == ')') {
                    offset = 3;
                }
                if (inst->filename) {
                    char tmp[SIZETIMEFMT];
                    int csec = timestring(ud, tmp);
                    fprintf(inst->handle, "%s.%02d ", tmp, csec);
    
                    fprintf(inst->handle, "[:%08x] ", source);
                    fwrite(msg + offset, sz - offset, 1, inst->handle);
                    fprintf(inst->handle, "\n");
                    fflush(inst->handle);
                } else {
                    /* toby@2022-03-31): 输出到控制台 */
                    if (offset) {
                        int color = (_msg[1]-'0')*10 + _msg[2]-'0';
                        fprintf(inst->handle, "\e[%dm", color);
                    }
                    fprintf(inst->handle, "[:%08x] ", source);
                    fwrite(msg + offset, sz - offset, 1, inst->handle);
                    fprintf(inst->handle, "\n");
                    if (offset) {
                        fprintf(inst->handle, "\e[0m");
                    }
                    fflush(inst->handle);
                }
                break;
            }
    	}
    
    	return 0;
    }
    
  3. 在 game 目录下添加 preload.lua 脚本,在 game/etc/config 中配置 preload = “./game/preload.lua”,这样任何一个 lua 服务启动的时候,都会预先加载 game/preload.lua。

    -- game/preload.lua
    --print("PRELOAD", ...)
    package.path = "./game/?.lua;"..package.path
    require("tools/tool_log")
    
  4. 下面介绍使用方式,类似 printf 的使用。
    skynet学习笔记之http服务搭建_第7张图片

  5. 前台输出表现如下,默认添加了当前服务名称 SERVICE_NAME。
    skynet学习笔记之http服务搭建_第8张图片

结语

目前日志级别只在前台显示的颜色上有所区别,后续会在后台模式下,根据日志级别不同,将日志输出到不同的文件中。

你可能感兴趣的:(skynet,lua,解决方案,lua,c语言,http)