Skynet环境搭建

Ubuntu安装Skynet

  • 系统环境:Ubuntu
  • 框架地址:https://github.com/cloudwu/skynet
# 安装git
$ sudo apt-get update
$ sudo apt-get install git

# 安装支持库
$ sudo apt-get install build-essential libssl-dev libcurl4-gnutls-dev libexpat1-dev gettext unzip

# git克隆项目
$ git clone https://github.com/cloudwu/skynet.git

# 安装 autoconf
$ sudo apt-get install autoconf

# 安装 realine-devel
$ sudo apt-get install libreadline-dev

# 编译源码
$ cd skynet
$ sudo make linux

运行Skynet服务器

# 启动skynet服务节点
$ ./skynet example/config

运行Skynet客户端

# 终端下编译Lua虚拟机成功后生成lua可执行文件
$ cd ./3rd/lua/
$ make linux

# 返回skynet根目录下运行客户端脚本
$ cd ../../
$ ./3rd/lua/lua example/client.lua

CentOS配置Skynet

安装Lua5.3

  • http://www.lua.org/ftp
$ lua -v
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio

$ which lua luac
/usr/bin/lua
/usr/bin/luac

$ wget http://www.lua.org/ftp/lua-5.3.5.tar.gz
$ tar -zxvf lua-5.3.5.tar.gz
$ cd lua-5.3.5
$ make linux test
$ make install

$ yum install libtermcap-devel ncurses-devel libevent-devel readline-devel
$ make linux
$ make linux install

配置Lua环境变量

$ sudo rm -rf /usr/bin/lua*
$ sudo ln -s /home/jc/projects/lua/lua-5.3.5/src/luac /usr/bin/luac
$ sudo ln -s /home/jc/projects/lua/lua-5.3.5/src/lua /usr/bin/lua
$ lua -v
Lua 5.3.5  Copyright (C) 1994-2018 Lua.org, PUC-Rio

安装Skynet

$ sudo yum -y install curl curl-devel readline-devel ncurses-devel autoconf gcc-c++ git
$ git clone [https://github.com/cloudwu/skynet.git](https://github.com/cloudwu/skynet.git)
$ cd skynet
$ make install
$ ./skynet example/config

目录结构

Skynet目录结构

  • 3rd 第三方代码,包括lua和jemalloc等。
  • lualib 使用Lua编写的库
  • lualib-src 使用C编写并封装给Lua使用的库
  • service 使用Lua编写的服务模块
  • service-src 使用C编写的服务模块
  • skynet-src Skynet核心代码
Skynet环境搭建_第1张图片
Skynet目录结构

Skynet启动

Skynet由一个或多个进程构成,每个进程又称为Skynet节点。Skynet节点的启动首先需要配置文件config支持,分析实例example文件夹下提供的config配置文件会发现它是Lua代码,并以key-value形式进行赋值。Skynet启动时会读取必要配置项,其他配置项以字符串的形式存入env环境表中,所有配置项可通过skynet.getenv的方式获取。

Skynet启动流程大体如下:

  1. 加载配置文件
  2. 配置文件存入Lua的全局变量env
  3. 创建和启动C服务的日志logger
  4. 启动引导模块并开启第一个Lua服务(bootstrap)

第一个启动的Lua服务其实都会由config配置文件中的bootstrap配置项所决定,实际项目会根据情况进行修改,保持使用bootstrap作为第一个Lua服务,会直接或间接地去启动其他的Lua服务。

  1. 配置文件
$ vim example/config
include "config.path"

-- preload = "./examples/preload.lua"   -- run preload.lua before every lua service run
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main"  -- main script
bootstrap = "snlua bootstrap"   -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"

Skynet节点启动的必要配置项包括

-- 启动多少个工作线程,通常不要超过实际拥有的CPU核心数量。
thread = 8
-- 启动的第一服务及参数,默认为snlua bootstrap表示启动名为bootstrap的Lua服务,即service/bootstrap.lua。
bootstrap = "snlua bootstrap"
-- 使用C语言编写的服务模块的位置,通常是cservice文件下.so文件。若系统的动态库不是以.so作为后缀需修改,此路径可配置多项以;分号分隔。
cpath = root.."cservice/?.so"
  1. 配置信息结构体

skynet启动时必要的数据被定义在skynet-src/skynet_imp.h文件中的skynet_config结构体中。

$ vim syket-src/skynet_imp.h
#ifndef SKYNET_IMP_H
#define SKYNET_IMP_H
//skynet配置信息结构体
struct skynet_config {
    //启动工作线程数量,建议不要设置超过实际拥有的CPU的核心数。
    int thread;
    //skynet网络节点的唯一编号,1到255之间的任意整数。一个skynet网络最多支持255个节点。
    int harbor;
    //是否开启统计功能,统计每个服务使用了多少CPU时间,默认为开启。
    int profile;
    //后台模式:daemon="./skynet.pid"以后台模式启动skynet,同时需配置logger日志项中输出的log。
    const char * daemon;
    //设置使用C语言编写的服务模块的位置,通常指的是cservice目录下的.so文件。
    const char * module_path;
    //设置skynet启动的第一个服务以及启动参数,默认配置为snlua bootstrap,即启动一个名为bootstrap的Lua服务,通常指的是service/bootstrap.lua文件。
    const char * bootstrap;
    //设置skynet内置skynet_error这个C API将日志信息输出文件的路径,如果配置为nil将输出到标准输出,可配置一个文件名将信息保存在特定的文件中。
    const char * logger;
    //自定义log服务,默认为logger,可参考service_logger.c来实现。若希望使用LUa来实现log服务则填写snlua,并在logger中配置具体的Lua服务的名字。在example配置中有config.userlog范例可供参考。
    const char * logservice;
};

#define THREAD_WORKER 0
#define THREAD_MAIN 1
#define THREAD_SOCKET 2
#define THREAD_TIMER 3
#define THREAD_MONITOR 4

void skynet_start(struct skynet_config * config);

#endif
  1. 入口函数和初始化

启动Skynet所使用的指令./skynet example/config实际上是调用 skynet-src/skynet_main.c脚本的入口函数main,调用时将config配置文件地址作为参数传入函数中,在主函数中主要完成设置环境和加载配置文件两项工作。

$ vim skynet-src/skynet_main.c

//配置信息
#include skynet_imp.h

// 入口主函数
int
main(int argc, char *argv[]) {
    //定义保存配置文件地址的变量
    const char * config_file = NULL ;
    //若传入配置文件地址作为参数
    if (argc > 1) {
        //读取配置文件的地址并保存到配置文件变量中
        config_file = argv[1];
    } else {
        //若没有传入配置文件地址则提示错误并结束程序
        fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
            "usage: skynet configfilename\n");
        return 1;
    }
    //初始化:todo
    luaS_initshr();
    //全局初始化,为线程特有数据使用pthread_key_create()函数创建一个key,然后使用pthread_setspecific()函数为这个key设置value值。
    skynet_globalinit();
    //初始化Lua环境,创建一个全局数据结构struct skynet_env *E,并初始化结构的值。
    skynet_env_init();
    //设置信号处理函数,用于忽略sigpipe信号的处理
    sigign();
    //创建启动skynet所必须的配置信息结构数据
    struct skynet_config config;
    //申请一个Lua虚拟机
    struct lua_State *L = luaL_newstate();
    //连接到必要的Lua库到上面申请的Lua虚拟机中
    luaL_openlibs(L);   // link lua lib
    //执行配置文件并在Lua中进行读取
    int err =  luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
    assert(err == LUA_OK);
    //将C读取的配置文件内容串压入栈顶
    lua_pushstring(L, config_file);
    //执行栈顶的程序块(chunk),实际是加载配置Lua脚本字符串的内容
    err = lua_pcall(L, 1, 1, 0);
    if (err) {
        fprintf(stderr,"%s\n",lua_tostring(L,-1));
        lua_close(L);
        return 1;
    }
    //初始化保存配置信息的环境变量env
    _init_env(L);
    //通过skynet_getenv()接口从env中获取配置文件的信息,其实内部机制是通过lua_setglobal将之前压入栈顶的配置文件config_file转换为Lua中的全局变量
    config.thread =  optint("thread",8);
    config.module_path = optstring("cpath","./cservice/?.so");
    config.harbor = optint("harbor", 1);
    config.bootstrap = optstring("bootstrap","snlua bootstrap");
    config.daemon = optstring("daemon", NULL);
    config.logger = optstring("logger", NULL);
    config.logservice = optstring("logservice", "logger");
    config.profile = optboolean("profile", 1);
    //关闭上面创建的Lua虚拟机L
    lua_close(L);
    //开始执行skynet,正式启动skynet服务程序的操作
    skynet_start(&config);
    //删除线程存储的key,对应skynet_globalinit()
    skynet_globalexit();
    //初始化结束,对应luaS_initshr()
    luaS_exitshr();

    return 0;
}
  1. 启动skynet服务程序

skynet-src/skynet_main.cmain函数末尾,也就是完成环境设置和配置加载后,会调用skynet_start(&config);skynet_start函数和是在skynet-src/skynet_start.c中定义的,作用是开始执行skynet。

$ vim skynet-src/skynet_start.c
void 
skynet_start(struct skynet_config * config) {
    // register SIGHUP for log file reopen
    struct sigaction sa;
    sa.sa_handler = &handle_hup;
    sa.sa_flags = SA_RESTART;
    sigfillset(&sa.sa_mask);
    sigaction(SIGHUP, &sa, NULL);
    //根据配置进行初始化
    if (config->daemon) {
        //初始化守护进程
        if (daemon_init(config->daemon)) {
            exit(1);
        }
    }
    //初始化节点模块,用于集群并转发远程节点的消息
    skynet_harbor_init(config->harbor);
    //初始化句柄模块,用于给每个skynet服务创建一个全局唯一的句柄值。
    skynet_handle_init(config->harbor);
    //初始化消息队列模块(skynet的主要数据结构)
    skynet_mq_init();
    //初始化服务动态库加载模块,用于加载符合skynet服务模块接口的动态链结库(.so)。
    skynet_module_init(config->module_path);
    //初始化定时器模块
    skynet_timer_init();
    //初始化网络模块
    skynet_socket_init();
    //加载日志模块
    skynet_profile_enable(config->profile);
    //创建第一个模块logger服务的实例并启动
    struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
    if (ctx == NULL) {
        fprintf(stderr, "Can't launch %s service\n", config->logservice);
        exit(1);
    }
    skynet_handle_namehandle(skynet_context_handle(ctx), "logger");
    //加载bootstrap引导模块
    bootstrap(ctx, config->bootstrap);
    //启动Lua服务
    start(config->thread);

    // harbor_exit may call socket send, so it should exit before socket_free
    skynet_harbor_exit();
    skynet_socket_free();
    if (config->daemon) {
        daemon_exit(config->daemon);
    }
}

使用Docker安装配置Skynet

关于Docker的安装配置,这里不做过多说明,详细请查看相关文档。

操作步骤

  1. 使用SSH登录Docker虚拟机
  2. 在DockerHub中查找Skynet文件
  3. 使用Docker拉取Skynet镜像
  4. 运行Skynet镜像
$ docker-machine ls

$ docker-machine ssh default

docker@default $ docker pull skynet/skynet

docker@default $ 

你可能感兴趣的:(Skynet环境搭建)