CoPilot 开发指南

CoPilot 概述

OpenNJet 是一个多进程的程序,有一个 Master 进程和多个 Worker 进程,另外还可以创建多个CoPilot 进程。

CoPilot 在 Master-Workers 进程架构的基础之上进行了扩展,提升了 OpenNJet 的控制管理和提供服务的能力。比如,可以用CoPilot运行一个Controller,对外提供访问控制;还可以用CoPilot运行一个Broker,作为一个消息中间件。这是两种典型的用法,但并不是全部,受限的是必须遵守CoPilot 插件开发规范,而不是 CoPilot 提供的功能和服务。

Master 进程创建了CoPilot进程,并在 NJet 的整个生命周期过程中管理着 CoPilot 进程,在根据控制信号停止(-s stop 或 -s quit)OpenNJet 时,会停止 CoPilot 进程;在重新加载配置(-s reload)时,是否停止并创建新的 CoPilot 进程,是由这个 CoPilot 进程的 so 决定,这一点在CoPilot 插件规范有描述;在 CoPilot 进程因为意外挂掉时,会及时创建新的 CoPilot 进程。

CoPilot 配置

CoPilot的配置,是在主配置文件中使用helper指令。

指令说明:

参数说明:

示例配置

helper ctrl modules/njt_helper_ctrl_module.so conf/njet_ctrl.conf; 
helper broker modules/njt_helper_broker_module.so conf/mqtt.conf;

helper ha modules/njt_helper_ha_module.so conf/vrrp.conf;

CoPilot 插件规范

CoPilot插件的形式是一个so文件,需实现以下接口:

1)unsigned int njt_helper_check_version(void)      注:当前版本号是 1      #define NJT_HELPER_VER          1                 2)void njt_helper_run(helper_param param)      注:helper_param是一个struc类型,定义如下

​      typedef unsigned int (*helper_check_cmd_fp)(void* ctx);      typedef struct {          size_t      conf_fn_len;          u_char     *conf_fn_data;*          *helper_check_cmd_fp check_cmd_fp;*          *void*       ctx;      } helper_param;

​      在njt_helper_run的事件循环中,需调用param.check_cmd_fp()接收命令

​      例如:      unsigned int cmd;      cmd = param.check_cmd_fp(param.ctx);

​      命令宏定义如下:      #define NJT_HELPER_CMD_NO          0      #define NJT_HELPER_CMD_STOP       1      #define *NJT_HELPER_CMD_RESTART*  2

​      

​      接收到命令后,需进行命令处理。      NJT_HELPER_CMD_STOP命令,要进行停止操作;      NJT_HELPER_CMD_RESTART 为预留命令,暂不会发送该命令,在事件处理中可以按停止操作处理该命令,或者执行自身业务逻辑的重新开始。

3)unsigned int njt_helper_ignore_reload(void)

返回1,表示该so的copilot进程,不会在reload的时候重启。

放回0,表示该so的copilot进程,会在reload的时候重启。

注1:so可以不实现该接口。若不实现,则等同于返回0。

注2:如果so实现该接口并且返回1,那么在reload的时候该so的copilot进程不会重启,但是有一点需要注意:reload的时候配置文件中需保留原helper指令,这是配置上的强制要求,不满足此要求会导致reload失败。

开发实例

开发一个demo的CoPilot插件,与开发一个module相比,有一些相似的地方,比如config文件的编写,也存在不同的地方,比如CoPilot插件不需要提像module那样实现njt_module_t中的commands和ctx等,而是需要按照CoPilot插件规范进行实现。下面的CoPilot实例,提供了参考。

在OpenNJet执行configure的时候,需要加上参数:–add-dynamic-module=./modules/njet-helper-demo-module

在make编译的时候,编译出 objs/njt_helper_demo_module.so

自带的CoPilot实例

CoPilot: broker 实例

broker的功能

broker是一个CoPilot插件,运行在一个单独的CoPilot进程中,该模块提供消息服务端功能, 使用mqtt 协议。

broker的配置使用

broker是在主配置文件中通过helper指令进行配置:

helper broker modules/njt_helper_broker_module.so conf/mqtt.conf;

配置文件必须存在,可以是空文件,未配置的配置项将使用默认配置

mqtt.conf Message Broker 配置文件

CoPilot: njet_ctrl实例

ctrl的功能

ctrl是一个CoPilot插件,运行在一个单独的CoPilot进程中,一方面它继承了所有OpenNJet静态编译的模块,另一方面可以通过load_module指令加载自己的动态模块来扩展功能,提供了强大的控制能力,成为OpenNJet的控制平面。

ctrl的配置使用

ctrl是在主配置文件中通过helper指令进行配置:

helper ctrl modules/njt_helper_ctrl_module.so conf/njet_ctrl.conf;

ctrl的配置文件是上面的helper指令中指定,配置文件中配置的指令分为两类:

1)OpenNJet中标准的指令;

注意:用 error_log 和 access_log 指令指定的log文件要不同于主配置文件中的log文件。

2)通过 load_module 指令加载的动态模块中扩展出来的指令:

ctrl配置文件配的内容示例如下:

load_module modules/njt_http_sendmsg_module.so;
load_module modules/njt_http_location_api_module.so;
load_module modules/njt_ctrl_config_api_module.so;
load_module modules/njt_http_health_check_helper.so;
load_module modules/njt_http_vtsd_module.so; 

user njet njet;
error_log logs/error-ctrl.log info;
events {
    worker_connections  1024;
}

http {
    dyn_sendmsg_conf conf/iot-ctrl.cfg;
    access_log logs/access-ctrl.log combined;

    server {
        listen       8081;
        location /kv  {
            dyn_sendmsg_kv;
        }
        location /config {
            config_api;
        }

        location /dyn_loc {
            dyn_location_api;
        }

        location /hc {
                health_check_api;
        }

        location /metrics {
            vhost_traffic_status_display;
            vhost_traffic_status_display_format html;
        }
    }
}

调用其他语言编写的CoPilot

go-copilot开发

目标

在OpenNJet架构中,sidecar agent/KIC agent都将迁移为coPilot,目前他们都实现为独立的go 程序,需要利用cgo的互操作性,提供一个go实现的coPilot POC,以方便其他go实现的独立功能向coPilot迁移。

主要思路

参照现有的njt_ctrl和njt_broker模块,用C语言实现一个简单的新helper模块,里面在初始化阶段调用go-start()函数启动go实现的服务,同时等待接收到命令,并进行命令处理。 NJT_HELPER_CMD_STOP命令,要进行停止操作,调用go-stop(); NJT_HELPER_CMD_RESTART 为预留命令,暂不会发送该命令,在事件处理中可以按停止操作处理该命令,或者执行自身业务逻辑的重新开始,这个命令先留着一个接口go-restart(),具体实现以后再考虑。

之后是在go语言实现,目前先实现启动停止一个webserver,以后再根据需要实现其他功能。

具体实现(暂时只是启动/停止一个go的webserver)

目前这个模块的功能主要是启动/停止go程序,所以比njt_ctrl和njt_broker模块的实现要简单一些。

目前将helper中指定的conf路径作为参数传入go程序,作为config文件的位置,以后可以在里面加入要加载的模块;(模块名,启动参数,配置文件位置等)。

最初尝试在c进程中通过导入*.so文件,然后调用相应的导出函数的方式启动web server,但是各种尝试都不成功。

最终方案是参考nginx-unit和njt_execute实现,通过二进制文件路径来调用,核心代码为

njt_pid_t njt_helper_go_start(njt_cycle_t *cycle, char *prefix, char *fullfn) {
    njt_exec_ctx_t ctx;
    char *n, *p;
    char *name = "gocop";
    njt_pid_t pid;

    //这里是拼接出go二进制文件的路径,install脚本会在conf-prefix路径下作了一个软链接
    n = njt_calloc(njt_strlen(prefix) + njt_strlen(name) + 1, cycle->log);
    p = (char *)njt_cpymem(n, prefix, njt_strlen(prefix));
    njt_cpystrn((u_char *)p, (u_char *)name, strlen(name) + 1);
    

    ctx.path = n;
    ctx.name = "go copilot in njt_execute";
    ctx.argv = (char* const[]){name, "--conf", fullfn, NULL}; //命令行参数,指定了配置文件位置
    ctx.envp = (char* const[]){"GIN_MODE=release",NULL}; //要传入的环境变量
    pid = njt_execute(cycle, &ctx);

    njt_free(n);
    return pid;
}

模块中会保存新生成的进程的pid,供stop命令使用,kill(pid, SIGTERM);

编译OpenNJet module

需要修改 auto/options

# 在 OPENSSL=NONE 下面增加
USE_GOCOP=NO
GOCOP_PATH=NONE

# 在--with-openssl-opt=*)            OPENSSL_OPT="$value"       ;; 之前增加
--with-gocop=*)                      GOCOP_PATH="$value"           ;;


# 在--with-openssl=DIR                 set path to OpenSSL library sources下面增加
--with-gocop=PATH                  set path to gocop binrary executable

修改auto/install,增加脚本,在指定了go文件路径后,把该文件复制到njet的相同目录下,并在配置文件目录下做了一个软链接。如果文件不存在,就什么也不做。

if test -n "$GOCOP_PATH"; then
if test -f "$GOCOP_PATH"; then

    cat << END                                                >> $NJT_MAKEFILE

        cp $GOCOP_PATH '\$(DESTDIR)`dirname "$NJT_SBIN_PATH"`/gocop'
        ln -sf '\$(DESTDIR)`dirname "$NJT_SBIN_PATH"`/gocop' '\$(DESTDIR)$NJT_CONF_PREFIX/gocop'
END

fi
fi

修改build_cc.sh

#增加
NJET_MODULES="$NJET_MODULES --add-dynamic-module=./modules/njet-helper-go-module"
#在LIB_SRC_PATH上增加
LIB_SRC_PATH=" --with-openssl=auto/lib/tassl --with-pcre=auto/lib/pcre-8.45 --with-gocop=/root/go/src/gitee.com/liismn/go-copilot/cmd/main"

其他问题

SSL的一致性

CoPilot 插件,如果使用了SSL库,需要与Njet 编译时使用的SSL库版本相同。 插件编译时使用的 Include 头文件目录( -I ),及链接时使用的 Library 目录( -L )需要指向相同版本的SSL 库路径。

使用cmake编译时,在CMakeLists.txt文件中的include_directories 及link_directories填入对应SSL库的路径

include_directories(${NJET_SSL_LIB_INCLUDE_DIR})

link_directories(${NJET_SSL_LIB_DIR})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韩未零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值