zoukankan      html  css  js  c++  java
  • skynet1.0阅读笔记_skynet的启动


    首先看skynet的启动,函数入口在 skynet_main.c 的main(),其中最重要的是:

            skynet_start(&config);

    在skynet_start中做了两个启动:

            //启动了snlau服务,然后加载launch服务
            bootstrap(ctx, config->bootstrap);
            //创建monitor,timer,socket,worker线程等
            start(config->thread);


    下面我们逐步跟进函数

        static void
        bootstrap(struct skynet_context * logger, const char * cmdline) {
            ...
            sscanf(cmdline, "%s %s", name, args);
            struct skynet_context *ctx = skynet_context_new(name, args);
            ...
        }

    这里的cmdline就是 config配置里面的bootstrap那行,默认是为 "snlua bootstrap"


    所以实际执行的是 skynet_context_new("snlua","bootstrap")
    这里的skynet_context_new是很重要的一个函数,skynet每个lua服务创建,都是使用它来执行的。

        skynet_context_new(const char * name="snlua", const char *param="bootstrap") {
            //查询mod对象是否已存在,不存在就根据name查找文件加载创建
            struct skynet_module * mod = skynet_module_query(name="snlua");
            ...
            //调用mod的create()方法,这里调用了 snlua_create
            void *inst = skynet_module_instance_create(mod);
            //为lua服务new一个skynet_context的c对象
            struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
            ...
            //为服务的ctx创建一个message_queue
            struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
            //调用对象的init方法,这里是 snlua_init(snlua_create(),ctx,"bootstrap")
            int r = skynet_module_instance_init(mod, inst, ctx, param);
            if(r==0){        //0表示成功
                //成功以后,把服务的message_queue放入global_queue全局队列中
                skynet_globalmq_push(queue);
            }
        }

    就是说,bootstrap的启动请求 现在交到service_snlua.c的snlua_init里面了,所以接着我们来看snlua_init的实现

        snlua_init(struct snlua *l,struct skynet_context *ctx,args="bootstrap"){
            ...
            //把ctx->cb=_launch, ctx->cb是worker线程取出skynet_message时的处理函数
            skynet_callback(ctx, l , _launch);
            //查询自己的handle id
            const char * self = skynet_command(ctx, "REG", NULL);
            uint32_t handle_id = strtoul(self+1, NULL, 16);
            ...
            memcpy(tmp, args, sz);
            // it must be first message ,然后在这里立即投递第一个请求
            skynet_send(ctx, ... , tmp="bootstrap", sz);
            ...
        }

    这里需要注意的是,skynet_command这里,实际上通过遍历查找,最终调用了cmd_reg,由于传入的param是NULL,实际是

         skynet_command(ctx, "REG", NULL){
            sprintf(context->result, ":%x", context->handle);
            return context->result;
         }

    //返回的是自己的ctx的handle id.

    也就是下面的skynet_send:

            skynet_send(ctx, ... , tmp="bootstrap", sz);

    是往自己的队列中投递了一个消息,worker线程拿到这个消息后,根据ctx->cb,调用callback函数: _launch

    跟进 _launch:

        _launch(..., const void* msg="bootstrap"){
            //把context->cb设为NULL了
            skynet_callback(context, NULL, NULL);
            //_init这里面实际上就是找到要加载的lua文件,然后加载到lua虚拟机中
            //在这里就是把 bootstrap.lua文件加在进来
            _init(l, context, msg, sz);
            ...
        }

    那我们来看看 bootstrap.lua 这个文件

    local skynet = require "skynet"
    local harbor = require "skynet.harbor"
    require "skynet.manager"    -- import skynet.launch, ...
    local memory = require "memory"
    
    skynet.start(function()
            ...
            -- 这里面,使用skynet.newservice 启动了很多个服务
    
        local launcher = assert(skynet.launch("snlua","launcher"))
        skynet.name(".launcher", launcher)
    
            ...
    
        if harbor_id == 0 then
            local ok, slave = pcall(skynet.newservice, "cdummy")
            skynet.name(".cslave", slave)
        else
            local ok, slave = pcall(skynet.newservice, "cslave")
            skynet.name(".cslave", slave)
        end
            ...
    
        if standalone then
            local datacenter = skynet.newservice "datacenterd"
            skynet.name("DATACENTER", datacenter)
        end
            ...
    end)

    这里有两点要注意:
    1. skynet.start 的function里面,用snlua启动了launcher服务,并用skynet.name把自己注册名字为".launcher"的服务
    ".launcher" 的调用 skynet.call(".launcher",...)
    在后面将经常看到。".launcher"服务就是在这里注册的。
    以.开头的名字,是表示这个服务只在当前skynet节点下有效,如果不带点,需要支持跨节点的额外开销,没必要都会带上它。

    2.skynet.start 函数

        function skynet.start(start_func)
            //把回调函数注册为 skynet.dispatch_message
            c.callback(skynet.dispatch_message)
            //调用 skynet.init_service 调用了外面定义的 function 进行启动
            skynet.timeout(0, function()
                skynet.init_service(start_func)
            end)
        end

    到这里,就能明白,通过skynet_context_new 中的 snlua_init 和
    _launch,skynet把请求的处理权从 c交到了 lua手上。

  • 相关阅读:
    java.lang.AbstractMethodError: com.microsoft.jdbc.base.BaseDatabaseMetaData.supportsGetGeneratedKeys()Z
    安装oracle后java -version命令显示 jdk version "1.3.1"的原因
    jquery 获取和设置Select选项常用方法总结
    select动态增加option
    Jquery detect page refresh
    HibernateTemplate 查询
    The dialect was not set. Set the property hibernate.dialect
    hibernate的异常 Session was already closed
    <c:forEach>取得集合数量
    Jstl标签<c:forEach>的用法
  • 原文地址:https://www.cnblogs.com/sixbeauty/p/4978404.html
Copyright © 2011-2022 走看看