【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
前面我们说过,在工业系统上面一般都是使用多进程来代替多线程。这后面,主要的原因还是基于安全的考虑。毕竟一个系统里面,如果很多的功能都focus在一个进程里面,这本身比较危险。因为这意味着,如果一个线程里面的code不是很健壮,那么整个系统就无法正常运行了。所以大家普遍采用的方法还是多进程。
当然多进程就会涉及到通信的问题。很多同学都会推荐共享内存的方法,其实个人还是比较推荐网络的方式,这样显得安全一点,而且一个嵌入式设备之间的网络通信也是非常快速的。另外即使数据量很大,也可以采用指针+网络传输的方式来解决,这也是可以的。关于网络,目前有一些类似的框架其实可以拿来参考,skynet就是不错的一种方法。它本身是用来做游戏的,但是其实用来做进程间通信也是不错的选择。
1、下载skynet代码
代码下载的地址在这个地方,
https://github.com/cloudwu/skynet/tree/master
2、下载jemalloc
编译skynet的时候,需要依赖jemalloc库,如果只是压缩包下载,是没有办法下载jemalloc的。所以这种情况下,我们需要手动下载一下代码包,
https://github.com/jemalloc/jemalloc/tree/54eaed1d8b56b1aa528be3bdd1877e59c56fa90c
3、解压代码,准备编译
将skynet先解压,紧接着把jemalloc解压到3rd/jemalloc下面的目录,下面就可以开始编译了。因为我们是需要在树莓派4b上面进行处理,所以这里直接输入对应的命令进行编译即可。
make linux
4、测试和验证
所有代码都编译好了之后,下面就可以开始测试。官方给了一个简单的测试案例,分别是
./skynet examples/config &
和
./3rd/lua/lua ./examples/client.lua
这样在输入相关的命令后,首先我们会看到这样的打印,
feixiaoxing@raspberrypi:~/Desktop/skynet/skynet-master $ ./skynet examples/config
[:01000002] LAUNCH snlua bootstrap
[:01000003] LAUNCH snlua launcher
[:01000004] LAUNCH snlua cmaster
[:01000004] master listen socket 0.0.0.0:2013
[:01000005] LAUNCH snlua cslave
[:01000005] slave connect to master 127.0.0.1:2013
[:01000004] connect from 127.0.0.1:39068 4
[:01000006] LAUNCH harbor 1 16777221
[:01000004] Harbor 1 (fd=4) report 127.0.0.1:2526
[:01000005] Waiting for 0 harbors
[:01000005] Shakehand ready
[:01000007] LAUNCH snlua datacenterd
[:01000008] LAUNCH snlua service_mgr
[:01000009] LAUNCH snlua main
[:01000009] Server start
[:0100000a] LAUNCH snlua protoloader
[:0100000b] LAUNCH snlua console
[:0100000c] LAUNCH snlua debug_console 8000
[:0100000c] Start debug console at 127.0.0.1:8000
[:0100000d] LAUNCH snlua simpledb
[:0100000e] LAUNCH snlua watchdog
[:0100000f] LAUNCH snlua gate
[:0100000f] Listen on 0.0.0.0:8888
[:01000009] Watchdog listen on 0.0.0.0:8888
[:01000009] KILL self
[:01000002] KILL self
紧接着,就可以看到这样的打印,
feixiaoxing@raspberrypi:~/Desktop/skynet/skynet-master $ ./3rd/lua/lua ./examples/client.lua
Request: 1
Request: 2
hello
Request: 3
5、开发方式
整个skynet是一个插件式的开发方式。系统一般会找到so文件,然后通过so文件名,调用对应的create、release、init函数。因为我们一版本不使用lua脚本开发,如果大家喜欢用c/c++开发,可以直接参考service-src下面的内容,当然喜欢lua的朋友也可以用lua。这里面又是以service_logger.c最为简单,
#include "skynet.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
struct logger {
FILE * handle;
char * filename;
uint32_t starttime;
int close;
};
struct logger *
logger_create(void) {
struct logger * inst = skynet_malloc(sizeof(*inst));
inst->handle = NULL;
inst->close = 0;
inst->filename = NULL;
return inst;
}
void
logger_release(struct logger * inst) {
if (inst->close) {
fclose(inst->handle);
}
skynet_free(inst->filename);
skynet_free(inst);
}
#define SIZETIMEFMT 250
static int
timestring(struct logger *inst, char tmp[SIZETIMEFMT]) {
uint64_t now = skynet_now();
time_t ti = now/100 + inst->starttime;
struct tm info;
(void)localtime_r(&ti,&info);
strftime(tmp, SIZETIMEFMT, "%d/%m/%y %H:%M:%S", &info);
return now % 100;
}
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:
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, sz , 1, inst->handle);
fprintf(inst->handle, "\n");
fflush(inst->handle);
break;
}
return 0;
}
int
logger_init(struct logger * inst, struct skynet_context *ctx, const char * parm) {
const char * r = skynet_command(ctx, "STARTTIME", NULL);
inst->starttime = strtoul(r, NULL, 10);
if (parm) {
inst->handle = fopen(parm,"a");
if (inst->handle == NULL) {
return 1;
}
inst->filename = skynet_malloc(strlen(parm)+1);
strcpy(inst->filename, parm);
inst->close = 1;
} else {
inst->handle = stdout;
}
if (inst->handle) {
skynet_callback(ctx, inst, logger_cb);
return 0;
}
return 1;
}
6、多线程通信
如果是用skynet实现多线程通信的话,那就是不管是中间信息发送程序,还是对应的节点收发程序,中间都是使用skynet框架来解决的。我们要做的就是实现对应的插件就好了。
leaf based on skynet ====》dispatcher based on skynet ===> leaf based on skynet