服务端开发
服务端开发
本节内容介绍 VSOA 服务端使用 RPC 的方法。
开发须知
微服务对外提供 RPC 服务时,需要指定该 RPC 的 URL 资源标识并将该 URL 加入监听,同时需要指定 RPC 的回调函数用于处理 RPC 被调用时的逻辑。
常用接口
说明:
RPC 服务端的详细接口说明可参考以下手册:
- C 版本可参考 VSOA C 语言编程手册。
- JavaScript 版本可参考 VSOA JavaScript 编程手册。
- Java 版本可参考 VSOA Java 编程手册。
- Golang 版本可参考 VSOA Golang 编程手册。
开发示例
例如:将“灯光状态”定义为一个 /light
的 URL 资源标识,供调用 RPC 的客户端获取灯光状态。
向所创建的 VSOA 服务中增加一个 RPC 实现,需要指定当客户端调用 /light
时,将被执行的 RPC 回调函数。
说明:
- 在 Node.js 的环境中,不需要事件循环,即删除
require('iosched').forever();
。- 在 Node.js 的环境中,
socket.AF_INET
应为vsoa.AF_INET
,详情可见 https://www.npmjs.com/package/vsoa 。
VSOA vsoa_payload_t 结构体
在使用 C 语言开发过程中,需要特别注意 vsoa_payload_t
结构体的使用方法。该结构体中有 param 和 data 两个字段提供给用户以携带数据,长度分别为 param_len 和 data_len。其中: param 参数推荐用于字符串类型参数,data 参数用于二进制数据或者 base64 类型数据。如果用户没有用到该字段,需要将对应的字段置为 NULL 并将其长度置为 0。
例如,如果只需要 param 参数不需要 data 参数,需要按照如下形式生成 payload:
vsoa_payload_t send;
send.data = NULL;
send.data_len = 0;
send.param = "{\"light\": 1}";
send.param_len = strlen(send.param);
关于 vsoa_payload_t
的详细说明可见 VSOA Base Data Struct 中的 Payload 说明。
RPC GET/SET
如同定义函数时习惯区分函数的功能为“设置/获取”或“输入/输出”的概念,在 VSOA 的 RPC 中也可以通过读取 VSOA 报文头标志对 RPC 请求区分 GET 或 SET 操作。针对如下示例约定,当需要开启灯光时,向灯光控制微服务发送数据 "1";相反,当需要关闭灯光时,向灯光控制微服务发送数据 "0"。
并行 RPC
以上 RPC 使用方式中的所有回调函数都是串行执行的,VSOA 为 C 语言开发版本提供了并行处理 RPC 请求的功能,开发者可以通过并发 RPC 相关接口创建线程池并行处理 RPC 请求。
如下是将以上开发示例修改为并行 RPC 的示例:
/*
* Application: light_server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "vsoa_server.h"
#include "vsoa_platform.h"
#include "vsoa_plistener.h"
#define MY_SERVER_PASSWD "123456"
static vsoa_server_t *server;
/*
* /light Callback
*/
static void command_light (void *arg, vsoa_server_t *server, vsoa_cli_id_t cid,
vsoa_header_t *vsoa_hdr, vsoa_url_t *url,
vsoa_payload_t *payload)
{
vsoa_payload_t send;
uint32_t seqno = vsoa_parser_get_seqno(vsoa_hdr);
send.data = NULL;
send.data_len = 0;
send.param = "{\"light\": 1}";
send.param_len = strlen(send.param);
vsoa_server_cli_reply(server, cid, 0, seqno, 0, &send);
}
int main (int argc, char **argv)
{
int cnt, max_fd;
fd_set fds;
struct sockaddr_in addr;
vsoa_url_t url;
vsoa_plistener_t *pl;
vsoa_plistener_handler_t handler;
struct timespec timeout = { 1, 0 };
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(3001);
addr.sin_addr.s_addr = INADDR_ANY;
#ifdef VSOA_HAS_SIN_LEN
addr.sin_len = sizeof(struct sockaddr_in);
#endif
/*
* Initialize server
*/
server = vsoa_server_create("{\"name\":\"light_server\"}");
if (!server) {
fprintf(stderr, "Can not create VSOA server!\n");
return (-1);
}
/*
* If need password
*/
vsoa_server_passwd(server, MY_SERVER_PASSWD);
/*
* Create plistener
*/
pl = vsoa_server_plistener_create(2);
if (!pl) {
vsoa_server_close(server);
fprintf(stderr, "Can not create VSOA parallel server!\n");
return (-1);
}
/*
* Add /light listener
*/
url.url = "/light";
url.url_len = strlen(url.url);
vsoa_server_add_listener(server, &url, command_light, NULL);
/*
* Add parallel listener
*/
handler = vsoa_server_plistener_handler(pl, true, 0, command_light, NULL);
vsoa_server_add_listener(server, &url, handler.callback, handler.arg);
/*
* Start server
*/
if (!vsoa_server_start(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) {
vsoa_server_close(server);
fprintf(stderr, "Can not start VSOA server!\n");
return (-1);
}
while (1) {
FD_ZERO(&fds);
max_fd = vsoa_server_fds(server, &fds);
cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
if (cnt > 0) {
vsoa_server_input_fds(server, &fds);
}
}
return (0);
}
说明:
VSOA 并行 RPC 接口说明可参考 C 扩展编程手册。
URL 匹配规则
URL 标识 | RPC 匹配规则 |
---|---|
"/" | 默认监听所有事件 |
"/a/b/c" | 只处理 "/a/b/c" 标识的调用 |
"/a/b/c/" | 处理 "/a/b/c" 和 "/a/b/c/..." 标识的所有调用 |
注意:
如果 "/a/b/c" 和 "/a/b/c/" 的 RPC 监听处理同时存在,则客户端发起一个 "/a/b/c" 标识的 RPC 调用时, 系统将优先匹配 "/a/b/c",而非 "/a/b/c/"。
注意事项
C/C++ 服务端编译时需链接如下表所示的 VSOA 动态库,在 RealEvo-IDE 中配置时请参考 C/C++ 开发示例,Linux 下开发请参考 搭建 Linux 运行环境 提供的 C 语言范例进行配置。
库名称 | 功能 |
---|---|
libvsoa-json.so | 提供 JSON 功能 |
libvsoa-server.so | 提供服务端功能 |
libvsoa-parser.so | 提供参数解析功能 |