19 Star 92 Fork 45

极简美 / ERPC-doc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
3.UserGuide.md 18.73 KB
一键复制 编辑 原始数据 按行查看 历史
极简美 提交于 2019-08-05 17:18 . update User guide markdown

用户手册

整个ERPC框架由一个环境变量(ERPC_PROFILE_PATH)、两个配置文件(部署配置文件、日志配置文件)和11个函数接口构成,其中11个函数接口分别分拆到3个不同的头文件中。

本文核心讲解环境变量和11个函数接口,有关配置文件,详见第4章:配置文件

目录

一、介绍

整个ERPC的文件构成结构如下所示(这里只展示核心文件):

framework/
├── bin
│   └── zlog-chk-conf
├── include
│   ├── efsm_conf.h
│   ├── efsm.h
│   ├── efsmt.h
│   ├── erpc.h
│   ├── mt_timer.h
│   ├── rpc_core.h
│   ├── rpc_logger.h
│   ├── rpc_observer.h
│   ├── rpc_service.h
│   ├── rpc_util.h
└── lib
    └── librpc.so

如上文件为ERPC相关的核心文件。在framework目录中,还有很多其他的头文件和库,为ERPC实现的基石,或者扩展功能,可在外围功能的文档中进行学习。

  • bin/zlog-chk-conf:是配置文件检测工具,可提供json配置文件有效性检测;
  • lib/librpc.so:框架核心库,整个框架动态库,所有头文件提供的功能实现均集成在本库中;
  • include/rpc_core.h:框架核心接口3个:框架初始化、框架主循环、框架退出;
  • include/rpc_service.h:框架远程调用接口3个:注册服务、注销服务、远程调用;
  • include/rpc_observer.h:框架观察者模式接口5个:创建观察对象、销毁观察对象、注册观察者、注销观察者、消息广播;
  • include/rpc_util.h:框架工具集接口,包括获取版本信息、获取框架运行状态、当前进程名、cJSON复制、设置数据加密算法等;
  • include/mt_timer.h:多远定时器,详见多远定时器
  • include/efsm.h and include/efsmt.h:事件驱动型有限状态机,详见EFSM

其中多远定时器(mult_timer)和事件驱动型(EFSM)为通用功能,可独立于ERPC框架运行。

为了便于使用,用户可只包含erpc.h一个头文件即可使用核心框架的所有功能,该头文件将ERPC的核心头文件全部包含在一起:

#ifndef __ERPC_H__
#define __ERPC_H__
#ifdef __cplusplus
extern "C" {
#endif

#include "rpc_core.h"
#include "rpc_service.h"
#include "rpc_observer.h"

#include "rpc_util.h"
#include "rpc_logger.h"

#ifdef __cplusplus
}
#endif

#endif  // __ERPC_H__

框架的正确使用流程是:

  1. 框架初始化
  2. 向注册服务
  3. 若有观察者,则创建观察者、注册观察者
  4. 启动自定义业务线程
  5. 进入框架循环

二、环境变量

要想正确的使用ERPC,必须在系统中通过export设置ERPC_PROFILE_PATH环境变量,该环境变量指向ERPC的配置文件,比如:

export ERPC_PROFILE_PATH=/etc/erpc.json

若系统没有配置该环境变量,则ERPC框架会寻找默认路径:"/etc/erpc.json",当寻找配置文件失败时,会提示如下错误:

[INFO]   [./business/rpc_core.c][erpc_framework_init():146]:RPC : Environment ERPC_PROFILE_PATH has not set in system!
[INFO]   [./business/rpc_core.c][erpc_framework_init():147]:RPC : ERPC will load default profile: /etc/erpc.json
[ERROR]  [./configuration/rpc_profile_json.c][rpc_profile_parse():167]:RPCConfig : open /etc/erpc.json config-file failed!

有关配置文件的详细介绍,详见:配置文件

三、框架接口

与框架控制相关的只有三个接口:框架初始化、框架主循环和框架退出,它们定义在rpc_core.h头文件中。

3.1 框架初始化

框架初始化必须在其它所有框架类接口之前调用,函数原型如下:

extern int erpc_framework_init(char *process);

参数process为运行ERPC框架的进程名称:若传入进程名,则框架将以传入的名称为准;若process为NULL,则ERPC将会从系统自动识别当前运行进程的名称(从PID获取名称)。

框架初始化成功返回0,失败返回-1,并输出对应的错误信息。可能出错的情况有:

  • 配置文件加载失败,或配置异常;
  • 获取当前进程配置失败;
  • 线程池创建失败;
  • 通信层创建失败;
  • 内存配置表初始化失败;

框架初始化函数,只需要在整个程序开始处调用一次,多次调用无效并将获得如下信息:

RPC : xxxx have init RPC framework before!

其中xxxx为当前进程名。

3.2 框架主循环

当注册服务、创建观察者、注册观察者业务都完成之后,且业务线程也已经启动完成,最后主线程调用框架主循环进入监控状态:

typedef enum {
    ERPC_LOOP_EXIT,
    ERPC_LOOP_DEFAULT,
    ERPC_LOOP_ONCE,
    ERPC_LOOP_NOWAIT
}erpc_loop_t;

extern int erpc_framework_loop(erpc_loop_t way);

参数way为进入监控状态的方式,可选的值为:

  • ERPC_LOOP_EXIT:立即退出,这在注册服务失败、创建观察者失败、或者用户业务启动失败的时候使用;
  • ERPC_LOOP_DEFAULT:默认监控方式,监控配置文件状态,监控进程运行状态,若有栈溢出、非法访问、堆溢出等异常,则打印详细的出错信息;
  • ERPC_LOOP_ONCE:当前不支持;
  • ERPC_LOOP_NOWAIT:当前不支持。

该函数执行成功不返回,出错返回-1,并打印对应的错误信息。可能出错的情况有:

  • 异常监控注册失败;
  • 配置文件监控注册失败;
  • 框架被动结束(主动调用erpc_framework_break())。

3.3 框架退出

当业务运行出错,或者应业务需求,需要退出框架结束运行时,可以调用下面函数:

extern void erpc_framework_break(void);

该函数将结束框架主循环,并释放所有与框架相关的资源。

四、远程调用接口

与远程调用相关的有3个接口:注册服务、注销服务和远程调用,它们定义在rpc_service.h头文件中。

4.1 注册服务

当系统启动初始化框架完成后,可调用该接口向ERPC系统注册服务:

typedef cJSON *(*erpc_service_callback_t)(cJSON *params);

extern int erpc_service_register(const char *module, const char *service, erpc_service_callback_t pointer);

该函数执行成功返回0,出错返回-1。共有三个参数:

  • module:服务所述模块名称;与service一起在ERPC内部建表,用于查找服务;
  • service:服务的具体名称;与module一起在ERPC内部建表,用于查找服务;
  • pointer:具体服务的函数指针,函数类型为:
typedef cJSON *(*erpc_service_callback_t)(cJSON *params);

服务函数接收一个cJSON对象参数,返回值也是一个cJSON的对象。也即:服务的具体参数由服务编写者决定,当服务执行完毕后,返回的数据类型也由服务编写者决定;且整个服务的编写过程与在本地实现一个功能函数类似:处理参数,返回结果!

4.2 注销服务

当不再提供某项服务的时候,可以调用下面的接口从ERPC系统中注销该服务:

extern int erpc_service_unregister(const char *module, const char *service);

注销后的服务,再执行远程调用将会失败。

对于嵌入式设备来讲,在系统启动的时候,启动各个后台进程,并注册服务,直到设备关机,一般不会存在注销某个服务的情况。

4.3 远程调用

当应用者需要使用某个服务时,可以使用远程调用接口调用远端的服务:

extern int erpc_service_proxy_call(const char *module, const char *service, cJSON *send, cJSON **recv, struct timeval *tv);

该接口调用成功返回0,失败返回-1,可能出错的情况有:

  • 当前系统不存在module;
  • 当前系统的module没有service服务;
  • 调用远程module:service超时;
  • 创建远程代理失败;
  • 远程调用数据收发异常;

其参数详细说明如下:

  • module:要调用服务所属的模块;
  • service:要调用的服务具体名称;
  • send:调用服务发送的参数cJSON对象,若无参数则传NULL;
  • recv:当服务执行完的数据返回,是cJSON的二级指针,若服务无数据返回,则recv为NULL;
  • tv:此次远程调用允许的超时时长:
    • 当传入NULL,则使用进程的配置,若进程无该配置,则使用全局响应超时配置;
    • 若传入为0的参数(tv->tv_sec = tv->tv_usec = 0)则为无限等待;
    • 若传入非NULL,且非0(tv->tv_sec = tv->tv_usec != 0),则以用户传入时间为准。

特殊说明:若服务无数据返回,则返回是NULL,对应recv参数也为NULL,但该接口正确收到服务的返回则照样返回0

4.4 使用建议

对于服务接口的使用,建议编写服务的人员不仅实现服务的开发,并且还要完成接口的开发。这与面向对象开发语言不谋而合:开发过程分为实体实现和接口实现。因为:

  • 作为服务的实现者,对于服务的每个接口的使用、返回值、需要的响应时间都是非常清楚的;
  • 服务开发者对服务的执行结果也是非常了解的,也有必要在接口处进行部分数据的解析,这样最终提供给APP开发者的接口参数将会更加简单;
  • 在接口开发时,可以按照功能拆分为多个功能接口,这样可以利用函数命名规则,让APP开发者更加明了(所见即所得),减少文档的维护;

五、观察者模式接口

与观察者相关的有5个接口:创建观察者对象、销毁观察者对象、注册观察者、注销观察者和消息广播,它们定义在rpc_observer.h头文件中。

5.1 创建观察对象

在服务侧,若有状态供其他模块观察,则需要在服务侧创建观察者对象,调用下面接口:

extern int erpc_observed_create(const char *module, const char *observed);

该函数创建成功返回0;失败返回-1,并打印错误信息。

参数module是观察者所属模块名,observed是观察者对象的名称。

5.2 销毁观察对象

当服务侧不再需要观察者对象时,可以调用下面接口销毁一个观察者对象,销毁的同时会清空以注册的观察者:

extern int erpc_observed_destroy(const char *module, const char *observed);

参数module是观察者所属模块名,observed是观察者对象的名称。

5.3 注册观察者

对于应用者,若关注某个模块的状态,且该模块已经创建了观察者对象,可以通过下面接口注册观察者:

extern int erpc_observer_register(const char *module, const char *observed, erpc_observer_callback_t action, struct timeval *tv);

该函数执行成功返回0,出错返回-1,并打印出错信息。可能出错的情况有:

  • 当前系统不存在module;
  • module的观察者对象observed不存在;
  • 注册观察者超时;
  • 创建远程代理失败;
  • 数据收发异常;

其参数详细说明如下:

  • module:要注册观察者所属的模块名;
  • observed:要注册观察者的观察者对象名;
  • action:观察者的回调函数,其类型如下:
typedef void (*erpc_observer_callback_t)(cJSON *params);

与远程调用的服务相比,观察者回调是不需要返回值的。

  • tv:此次观察者注册允许的超时时长:
    • 当传入NULL,则使用进程的超时配置,若进程无该配置,则使用全局响应超时配置;
    • 若传入为0的参数(tv->tv_sec = tv->tv_usec = 0)则为无限等待;
    • 若传入非NULL,且非0(tv->tv_sec = tv->tv_usec != 0),则以用户传入时间为准。

5.4 注销观察者

当我们不再关注模块的状态时,可以使用下面函数注销对该观察者对象的观察者:

extern int erpc_observer_unregister(const char *module, const char *observed, struct timeval *tv);

该函数执行成功返回0,出错返回-1,并打印出错信息。可能出错的情况有:

  • 当前系统不存在module;
  • module的观察者对象observed不存在;
  • 注销观察者超时;
  • 创建远程代理失败;
  • 数据收发异常;

其参数详细说明如下:

  • module:要注销观察者所属的模块名;
  • observed:要注销观察者的观察者对象名;
  • tv:此次观察者注销允许的超时时长:
    • 当传入NULL,则使用进程的超时配置,若进程无该配置,则使用全局响应超时配置;
    • 若传入为0的参数(tv->tv_sec = tv->tv_usec = 0)则为无限等待;
    • 若传入非NULL,且非0(tv->tv_sec = tv->tv_usec != 0),则以用户传入时间为准。

5.5 消息广播

在服务侧,当观察者对象的状态发生改变,则可通过下面接口广播状态:

extern int erpc_observer_invoke(const char *module, const char *observed, cJSON *params);

该函数执行成功返回0,失败返回-1,并打印出错信息。

参数module是观察者对象所述模块,observed是观察者对象的名称,params是状态数据,也可为NULL,对应的观察者回调也会接收到NULL参数。

注意:观察者对象的状态广播时,所有消息的ID均为同一个。

5.6 使用建议

除了模块或硬件的状态变更时可以使用观察者接口来实现外,对于数据准备需要比较长的时间时也可以使用观察者接口和服务来配合实现:

  • 第一种实现方式是使用远程调用发起数据请求服务,当服务端准备好数据后,通过观察者广播接口把数据发送给服务;
  • 第二种实现方式是使用远程调用发起数据请求服务,当服务端准备好数据后,通过观察者广播接口通知服务数据已经准备好,服务端再发起远程调用获取数据。

从交互过程就知道,第一种数据是采用广播方式,不能保证正确性,第二种方法虽然繁琐一点,但是数据是有保障的。具体根据应用场景而定。

六、工具集

除了ERPC核心框架的11个接口外,我们还提供了与框架相关的其他功能函数,比如获取版本信息、框架运行状态,设置周期任务,设置数据加密算法等等。

6.1 版本信息

通过下面接口可以获取当前ERPC框架的版本信息:

extern char *erpc_version(void);

该函数返回版本字符串,比如:"ERPC-V1.6"

6.2 运行状态

当我们的应用运行状态依赖于ERPC框架的运行状态时,可以使用下面接口获取:

extern int erpc_is_running(void);

函数返回0代表框架正在运行,返回-1代表框架还未开始运行,或已经退出。

6.3 cJSON复制

有时我们的cJSON参数或对象需要放在多个地方处理,为了防止多次cJSON_Delete()而导致内存异常,可以使用下面函数复制新的一份相同的cJSON对象:

extern int erpc_duplicate_params(cJSON *params, cJSON **object);

复制成功返回0,复制失败返回-1。若复制成功,后续params和*object对象都需要分别进行cJSON_Delete(),否则会导致内存泄漏。

6.4 周期任务

ERPC框架提供了一个内部周期任务,周期最小单位为S,通过下面两个接口,你可以自定义周期时间和周期任务:

extern void erpc_timer_period_set(struct timeval tv);
extern void erpc_timer_handler_set(void (*handler)(void));

这两个函数始终都会成功。erpc_timer_period_set()设置周期时间,erpc_timer_handler_set()设置周期回调函数,回调函数是不带参数、无返回值的函数。

6.5 设置数据加密算法

当我们对数据安全性有要求时,可以为通信的数据增加加密/解密算法:

typedef void (*rpc_message_handler_t)(int sockfd, char *data, size_t len);
typedef int (*rpc_protocol_encrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
typedef int (*rpc_protocol_decrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
extern void erpc_information_security(rpc_protocol_encrypt_t encrypt, rpc_protocol_decrypt_t decrypt);

该函数始终会执行成功,用于设置加密函数回调encrypt和解密函数decrypt,两个函数必须成对注册,否则会导致收发双方信息不对称。

加密/解密回调函数类型是一样的:

typedef int (*rpc_protocol_encrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
typedef int (*rpc_protocol_decrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);

它们接收二进制数据和长度,在内部进行数据处理(加密/解密),并将处理后的送到数据handler指针指向的函数中,其中sockfd为handler的第一个参数:

typedef void (*rpc_message_handler_t)(int sockfd, char *data, size_t len);

也即加密/解密函数的执行流程为(下面为伪代码):

int encrypt_handler(char *data, size_t len, int sockfd, rpc_message_handler_t handler)
{
	size_t en_len = 0;
	char *en_data = NULL;
	
	/* encrypt data */
	en_len = encrypt_function(data, len, en_data);
	
	/* transmit data */
	handler(sockfd, en_data, en_len);
	
	/* free data */
	free(en_data);
}
int decrypt_handler(char *data, size_t len, int sockfd, rpc_message_handler_t handler)
{
	size_t de_len = 0;
	char *de_data = NULL;
	
	/* decrypt data */
	de_len = decrypt_function(data, len, de_data);
	
	/* transmit data */
	handler(sockfd, de_data, de_len);
	
	/* free data */
	free(de_data);
}
C
1
https://gitee.com/simpost/ERPC-doc.git
git@gitee.com:simpost/ERPC-doc.git
simpost
ERPC-doc
ERPC-doc
master

搜索帮助