kedao中间件-C++服务篇
@
一、概述
本文主要介绍在kedao中间件中如何注册服务、编写服务代码、调试服务。
使用 VSCode 作为开发工具,通过远程连接到开发服务器的方式,开展远程开发。
kedao中间件官网:https://www.yckj-kedao.com
系列文章:
《kedao中间件-安装篇》
《kedao中间件-Java服务篇》
《kedao中间件-Vue开发篇》
《kedao中间件-集群及发布篇》
《kedao中间件-并发测试篇》
《kedao中间件-Linux系统安装篇》
《kedao中间件-数据库安装篇》
二、前提条件
已经部署 kedao中间件 开发服务器、数据库,并且安装了 kedao creator 客户端,注册了用户。
本文的涉及到开发用户、开发服务器和数据库服务器相关信息:
1)开发服务器的IP:192.168.43.30,操作系统:Ubuntu24.04,系统用户:kedao
2)kedao中间件的安装路径:/opt/kedao
3)kedao中间件监听的端口:80,协议:http
4)kedao中间件的注册用户:lym_test
5)数据库服务器IP:192.168.43.135,端口:5432,用户:postgres,密码:postgres,数据库实例:asv_db
如果还没有了解如何部署 kedao中间件 开发服务器,请参考《kedao中间件-安装篇》。
三、创建C++系统
1、创建系统
登录 kedao creator,在【基础功能】-【创建系统】功能中,点击右上角的创建系统按钮,打开创建系统对话框,填入系统名称为 prj_c_test,数据为 PostgreSQL,开发语言为C++,签名算法为 SHA256,如图:

创建成功后,左侧菜单将自动出现刚才创建的系统,如图:

2、源代码目录结构
C++项目源码的目录在: kedao中间件的安装路径/src_c,即 /opt/kedao/src_c
进入 /opt/kedao/src_c 目录:
kedao@kedao:/$ cd /opt/kedao/src_c
kedao@kedao:/opt/kedao/src_c$ ls
include lym_test
目录说明:
1)include:公共包含文件目录,所有创建的C++工程,均使用到该目录下的头文件
2)lym_test:kedao中间件的注册用户名,所有的C++系统,都在该目录下
进入 lym_test 目录:
kedao@kedao:/opt/kedao/src_c$ cd lym_test
kedao@kedao:/opt/kedao/src_c/lym_test$ ls
prj_c_test
这时,看到刚才生成的C++工程prj_c_test,如果创建了多套C++工程,这里将会以工程名称生成相应的工程目录。
继续进入prj_c_test工程目录:
kedao@kedao:/opt/kedao/src_c/lym_test$ cd prj_c_test
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test$ ls
debugger make_all.sh model modules public utils
目录及文件说明:
1)debugger:调试工程的文件夹,每一个用户一个调试工程,自动生成,不需要人为操作。
2)make_all.sh:全编译脚本,自动生成,不需要人为操作;只有在需要全编译时使用,平常用不到。
3)model:数据模型类文件夹,自动生成。
4)modules:服务模块文件夹,服务按模块分组,每个模块下可以有多个服务,所有的服务代码都在这个目录里。
5)public:公共函数(方法)文件夹,根据自己项目的需要自定义实现。
6)utils:工具类文件夹,包含一些默认的工具类,可以根据自己项目的需要自定义实现。
这里,需要重点了解 modules 的结构,进入modules目录:
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test$ cd modules
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules$ ls
atom_service basic_func_employee basic_func_funcRounter basic_func_organization basic_func_post basic_func_role_permission sys_commons sys_login
目录说明:
1)atom_service:原子服务文件夹,自动生成。需要注意的是:这里只生成数据表的原子服务,非数据表则不生成;同时,表结构必须要设有主键字段,否则原子服务将不会执行。
2)其他为默认的基础功能模块,主要实现了组织机构、员工/用户、岗位、权限、菜单/路由配置、登录等基础功能。
进入一个模块目录,以sys_login为例来了解一个模块的组成,进入sys_login目录:
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules$ cd sys_login
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules/sys_login$ ls
makefile public service sys_login.cpp
目录及文件说明:
1)makefile:编译文件,创建模块时生成,可以根据自己的需求进行修改。
2)public:模块内部的公共文件夹。
3)service:注册服务的源码目录,该模块下的所有服务代码都在此目录下,编写代码的核心目录。
4)sys_login.cpp:模块的 main 文件,自动生成,每次注册服务,或者服务调整时,会自动更新,不可以修改。注:即使修改了,一旦模块下的服务有变动,都会自动生成。每个模块的目录下只有一个 *.cpp 文件
进入 service 目录:
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules/sys_login/service$ ls
svc_get_userInfo.hpp svc_login.hpp
这里每一个服务为一个独立的 *.hpp 文件。服务编程时,将围绕着对应服务的 *.hpp 文件进行编码。
到这里,对C++工程源代码的结构有了初步了解。
3、编译工程
点击操作下的编译按钮,进行一次全编译,如图:

编译完成后,在 /opt/kedao/bin/lib/lym_test/prj_c_test 目录下看到编译好的动态库文件。
也可以进入到工程的根目录 /opt/kedao/src_c/lym_test/prj_c_test 执行 make_all.sh 脚本进行全编译:
kedao@kedao:/$ cd /opt/kedao/src_c/lym_test/prj_c_test
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test$ ./make_all.sh
4、动态库文件目录结构
编译后生成的动态库存放路径为:kedao中间件的安装路径/bin/lib/kedao中间件的注册用户/工程名称/,即:/opt/kedao/bin/lib/lym_test/prj_c_test/
进入/opt/kedao/bin/lib/lym_test/prj_c_test/目录:
kedao@kedao:~$ cd /opt/kedao/bin/lib/lym_test/prj_c_test/
kedao@kedao:/opt/kedao/bin/lib/lym_test/prj_c_test$ ls
backup db_connect.ini db_sql libatom_service.so libbasic_func_employee.so libbasic_func_funcRounter.so libbasic_func_organization.so libbasic_func_post.so libbasic_func_role_permission.so libsys_commons.so libsys_login.so sqlite_db
目录说明:
1)backup:备份目录,目前没有使用。
2)db_sql:数据库脚本目录,包含了数据初始化 和 查询表结构的SQL语句。注意:在【数据模型】功能中表结构导入(数据库)用到了这里的SQL语句。
3)sqlite_db:如果数据库时 SQLite,数据库文件所在的目录。
4)db_connect.ini:数据库连接配置文件,根据 odbc.ini 对应的配置来设置。
5).so:动态库文件,每一个服务模块对应一个动态库。
5、数据库配置
1)配置数据 /ect/odbc.ini:
kedao@kedao:/opt/kedao/bin/lib/lym_test/prj_c_test$ sudo vi /etc/odbc.ini
根据预先建好的数据库配置,数据库服务器IP:192.168.43.135,端口:5432,用户:postgres,密码:postgres,数据库实例:asv_db。配置如下:
[myPgDB]
Description = link to pg
Driver = PostgreSQL ANSI
Database = asv_db
Servername = 192.168.43.135
UserName = postgres
Password = postgres
Port = 5432
ReadOnly = 0
ConnSettings = set client_encoding to UTF8
验证配置,执行命令 isql -v myPgDB:
kedao@kedao:/opt/kedao/bin/lib/lym_test/prj_c_test$ isql -v myPgDB
+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| echo [string] |
| quit |
| |
+---------------------------------------+
SQL>
连接成功,quit 退出。
2)配置 db_connect.ini :
kedao@kedao:/opt/kedao/bin/lib/lym_test/prj_c_test$ sudo vi db_connect.ini
文件内容设置如下:
[lym_test]
prj_c_test=postgres/postgres@myPgDB
说明:
1) lym_test 为 kedao中间件的注册用户;
2)prj_c_test 为系统名称;
3)postgres分别为数据库的用户和密码;
4)myPgDB 为 /ect/odbc.ini 中数据库连接的节点名称
6、数据库初始化
1)使用数据库管理工具连接到数据库,并打开预先创建好的数据库实例 asv_db:
2)在 db_sql 目录找到 PostgreSQL 数据库的初始化脚本:initialize_table_struct_for_PostgreSQL.sql
3)在 asv_db 实例中执行初始化的脚本:initialize_table_struct_for_PostgreSQL.sql
初始化完成后,数据库初始创建了:sys_employee、sys_function、sys_organization、sys_post、sys_role、sys_role_function_relation、sys_user、sys_user_role 等8张数据表,满足了一套系统最基本的框架结构需求,包括:组织管理、员工、角色、岗位、权限、登录、路由(菜单)配置等基础功能。
这已经满足绝大多数的系统需求,可以直接使用,只需设计自己的业务数据表,添加业务即可;
当然,如果不能满足自己系统的需求,也可以自行修改。
四、注册服务
在kedao creator的菜单中找到【我的系统】-【prj_c_test】功能,如图:

这里提供了系统基础功能的服务,只需根据业务需求注册新的服务。
1、增加模块
点击增加模块按钮,在弹出的对话框中增加一个测试模块 mdl_test,如:

2、增加服务
在模块列表中选中mdl_test,点击增加服务按钮,在弹出的对话框中增加一个测试服务 svc_test,如:

3、选择服务参数
参数说明:
1)默认的服务入参和出参分别是:SVC_REQUEST_OBJ<string> 和 SVC_RESPONSE_OBJ<string>
2)SVC_REQUEST_OBJ 是默认的服务入参数据结构,Json 结构如下:
{
"sys_head": {
"usr_id": "",
"org_id": "",
"sys_id": "",
"mdl_func_id": "",
"login_key": ""
},
"data": ""
}
其中data是一个泛型对象,也就是真正的请求业务数据;而 sys_head 主要保存服务请求者的身份信息,用于身份认证。
C++中对应的数据结构:
/* 请求头包对象*/
class SYS_HEAD
{
public:
string usr_id = ""; // 用户登录名称
string org_id = ""; // 组织代码
string sys_id = ""; // 系统代码
string mdl_func_id = ""; // 功能代码
string login_key = ""; // 登录验证信息,每次登录成功后更新该值。服务调用时,必须传该值
AIGC_JSON_HELPER(usr_id, org_id, sys_id, mdl_func_id, login_key);
};
/* 请求参数对象*/
template <typename T>
class SVC_REQUEST_OBJ
{
public:
SYS_HEAD sys_head; // 头包对象
T data; // 请求数据(泛型数据对象)
AIGC_JSON_HELPER(sys_head, data);
};
3)SVC_RESPONSE_OBJ 是默认的服务出参数据结构,Json 结构如下:
{
"code": 0,
"err_msg": "",
"otl_exc": {
"code": 0,
"msg": "",
"stm_text": "",
"sqlstate": "",
"var_info": ""
},
"data": ""
}
其中data是一个泛型对象,也就是真正的返回业务数据;code 是返回码;err_msg 是错误信息;otl_exc 是数据库错误信息。
C++中对应的数据结构:
/* OTL异常信息*/
class OTL_EXC
{
public:
int code = 0; // 错误码
string msg = ""; // 错误消息
string stm_text = ""; // 导致错误的SQL语句
string sqlstate = ""; // SQLSTATE消息
string var_info = ""; // 导致错误的变量
AIGC_JSON_HELPER(code, msg, stm_text, sqlstate, var_info);
};
/* 响应结果对象*/
template <typename T>
class SVC_RESPONSE_OBJ
{
public:
int code = 0; // 返回码
string err_msg = ""; // 错误信息
OTL_EXC otl_exc; // 错误信息
T data; // 响应数据
AIGC_JSON_HELPER(code, err_msg, otl_exc, data);
};
根据业务需求选择自己的参数,这里,我们注册一个查询系统功能的服务,涉及到数据表是 sys_function,服务的入参是一个对象,返回是一个列表,入参选择界面,如:

出参选择界面,如:

注:选择参数的数据结构,在【数据模型】功能中维护。【数据模型】支持直接从数据库导入表结构功能,只需在数据库中设计好数据表,通过表结构导入功能一键导入,能节省很大部分的时间
4、保存服务
选择完参数的最终服务注册界面,如:

点击保存按钮,完成注册,返回服务列表界面:

现在到系统源代码目录下,直接进入到服务所在目录:
kedao@kedao:/$ cd /opt/kedao/src_c/lym_test/prj_c_test/modules/mdl_test/service
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules/mdl_test/service$ ls
svc_test.hpp
服务源代码文件 svc_test.hpp 已经生成。
五、编写代码
1、使用 VSCode 开发
使用 VSCode 远程连接到开发服务器 192.168.43.30,连接用户为:kedao,打开服务源码文件 svc_test.hpp,如图:

下面是服务函数代码,代码结构比较简单,自己花点时间阅读理解。现在直接在服务函数中增加业务逻辑代码,,为了便演示调用服务,先把调用服务者的身份认证注释掉,整个服务代码如下:
#pragma once
#include <iostream>
#include <vector>
#include "../../../model/tb_struct.hpp"
#include "../../../utils/OtlV4Helper.hpp"
#include "../../../utils/f_check_user_login.hpp"
#include "../../../../../include/kedao_sync_logs.h"
#include "../../../../../include/kedao_public.h"
#include "../../../../../include/openssl_crypto.h"
using namespace kedao_utils;
using namespace prj_c_test;
extern "C"
int svc_test(const string &in_data, string &out_data, const string &request_msg)
{
int iRst = 0;
string sql_txt = "";
vector<string> v_params;
SVC_REQUEST_OBJ<SYS_FUNCTION> req;
SVC_RESPONSE_OBJ<vector<SYS_FUNCTION>> resp;
OtlV4Helper &dbconn = *((OtlV4Helper *)g_SHARED_PTR->get());
SVC_REQUEST_MSG svc_request_msg;
JsonHelper::JsonToObject(svc_request_msg, request_msg);
try {
// 设置保持连接,不 logoff(),避免高并发下的数据库不停建立新连接带来的消耗(不同数据库建立连接的耗时在20~200ms不等)
dbconn.set_keep_connection(true);
dbconn.conn();
const string &req_jsonStr = in_data;
// 解析入参到对象
if (!aigc::JsonHelper::JsonToObject(req, req_jsonStr))
{
// 解包失败
iRst = -1;
resp.code = iRst;
resp.otl_exc.code = iRst;
resp.err_msg = "服务 svc_test :解包失败。";
JsonHelper::ObjectToJson(resp, out_data);
// 打印错误日志
sync_cerr << out_data << endl;
// 断开数据库连接
dbconn.logoff();
return iRst;
}
// 检查用户登录有效性(为了便于演示调用服务,先把登录身份验证注释掉)
/*
if (check_user_login(dbconn, req.sys_head.usr_id, req.sys_head.login_key))
{
iRst = -1;
resp.code = iRst;
resp.otl_exc.code = iRst;
resp.err_msg = "服务 svc_test :用户登录有效性校验失败。";
JsonHelper::ObjectToJson(resp, out_data);
// 打印错误日志
sync_cerr << out_data << endl;
// 断开数据库连接
dbconn.logoff();
return iRst;
}
*/
// 开始事务
dbconn.begin();
// 此处添加业务处理逻辑
v_params.clear();
// 组织查询 SQL语句 和 参数
sql_txt = "SELECT * FROM sys_function ";
sql_txt += "WHERE 1 = 1 ";
if (req.data.func_code != "")
{
sql_txt += " AND func_code = :P01<char[33]> ";
// 参数
v_params.push_back(req.data.func_code);
}
if (req.data.menu_title != "")
{
sql_txt += " AND menu_title = :P02<char[65]> ";
// 参数
v_params.push_back(req.data.menu_title);
}
// 执行 SQL 查询,将数据返回到 resp.data
iRst = dbconn.f_otl_query_to_objs(sql_txt, v_params, resp.data);
if (iRst != 0)
{
resp.code = iRst;
resp.otl_exc.code = iRst;
resp.err_msg = "服务 svc_test :查询 sys_function 失败。";
JsonHelper::ObjectToJson(resp, out_data);
// 打印错误日志
sync_cerr << out_data << endl;
// 断开数据库连接
dbconn.logoff();
return iRst;
}
// 打包返回结果
out_data = "";
resp.code = iRst;
resp.otl_exc.code = iRst;
if (!aigc::JsonHelper::ObjectToJson(resp, out_data))
{
// 打包失败
iRst = -1;
resp.code = iRst;
resp.otl_exc.code = iRst;
resp.err_msg = "服务 svc_test :打包失败。";
JsonHelper::ObjectToJson(resp, out_data);
// 打印错误日志
sync_cerr << out_data << endl;
// 事务回滚
dbconn.rollback();
// 断开数据库连接
dbconn.logoff();
return iRst;
}
// 提交事务
dbconn.commit();
}
catch(otl_exception& p){
// 如果错误码是 8 或 35,通常是数据库驱动版本与数据库不兼容,升级数据库驱动或者对数据库版本降级
if (p.code == 35 || p.code == 8) { *g_SHARED_PTR = make_shared<OtlV4Helper>(); }
// 错误码
iRst = p.code;
resp.code = iRst;
// 获取异常数据
resp.otl_exc.code = p.code;
resp.err_msg = (char *)p.msg;
resp.otl_exc.stm_text = p.stm_text;
resp.otl_exc.sqlstate = (char *)p.sqlstate;
resp.otl_exc.var_info = p.var_info;
JsonHelper::ObjectToJson(resp, out_data);
// 打印错误日志
sync_cerr << out_data << endl;
// 事务回滚(如果是查询,出现异常时,这里没有必要进行回滚,否则,会再次触发异常,导致程序终止,前端得不到异常返回的信息)
dbconn.rollback();
}
// 断开数据库连接
dbconn.logoff();
return iRst;
}
服务开发说明:
C++服务开发数据库使用的是 otlv4.h ,并对其进一步封装成OtlV4Helper类,通过OtlV4Helper来实现对数据库的操作。重点掌握 OtlV4Helper 的实现逻辑,如果不满足需求,可以自行扩展。
otlv4.h 组织SQL语句时参数结构 :参数名<参数类型[字段的长度 + 1]>
其中:
1)每个参数以冒号 ":" 标识;
2)冒号后面跟着参数名称(参数名任意,在同一条语句中不重复即可);
3)参数名后面用一对尖括号 <> ,尖括号中为传入参数的数据类型,即字段的类型;
4)常用的参数类型有: char、double、int、long、long long 等等;
5)如果字符串类型,要指定长度,且长度比字段长度多1(结束符);
6)如果拼接的 SQL 语句中包含 ":",冒号要进行转义,比如 PostgreSQL 中通过获取 uuid_generate_v4()::text 获取 uuid 时,要写成 uuid_generate_v4()\\:\\:text
OtlV4Helper 中常用的函数有:
1)原子服务,实现数据表的增删改查;使用原子服务时,数据表必须有唯一健字段
atom_exec_delete()
atom_exec_insert()
atom_exec_select()
atom_exec_update()
2)执行SQL语句,无结果返回
f_otl_exec()
3)执行SQL语句,返回单个值(字符串)
f_otl_query_to_singleValue()
4)执行SQL语句,返回多条记录容器 vector<>
f_otl_query_to_objs()
5)执行SQL语句,返回单条记录的数据对象
f_otl_query_to_obj()
6)执行SQL语句,返回多条记录 vector<map<string, string>>
f_otl_query_to_maps()
7)执行SQL语句,返回单条记录的 map<string, string>
f_otl_query_to_map()
关于日志输出
日志输出有3种方式,都支持多线程高并发:
1)日志输出到 stdout,通常用在开发过程中打印日志,用来辅助测试,发布生产环境时,应该删除此类日志的输出
sync_cout << "日志输出到 bin/stdout 文件" << endl;
2)错误日志输出到 stderr 文件,通常用在服务发生错误时的日志打印
sync_cerr << "错误日志输出到 bin/stderr 文件" << endl;
3)日志输出到 log 日志文件,每天产生一个log日志文件,在 bin/log 目录下;kedao中间件默认输出每一个服务的执行情况
sync_clog << "日志输出到 bin/log/8位日期_log 文件" << endl;
注:整个服务编码过程是比较简单的,只需要关心业务逻辑的实现,对于数据库操作来说,就是组织编写业务逻辑的 SQL 语句 和 组织返回数据的过程。
数据库开发帮助类 OtlV4Helper,建议重点掌握。
服务以模块分组,一个模块为一个独立动态库工程。
六、编译服务
在服务模块下,执行 make 命令进行编译,如:
kedao@kedao:/opt/kedao$ cd src_c/lym_test/prj_c_test/modules/mdl_test/
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules/mdl_test$ ls
makefile mdl_test.cpp public service
kedao@kedao:/opt/kedao/src_c/lym_test/prj_c_test/modules/mdl_test$ make
g++ --std=c++23 -O3 -mavx2 -DNDEBUG -fPIC -o mdl_test.o -c mdl_test.cpp -I/usr/include/ -I../../../../include/ -L/usr/lib64/ -L/usr/lib/ -L../../../../../bin/lib -ldl -lkedao_utils -lodbc -lstdc++fs -fPIC -rdynamic -lpthread
g++ --std=c++23 -O3 -mavx2 -DNDEBUG -shared -fPIC -o ../../../../../bin/lib/lym_test/prj_c_test/libmdl_test.so mdl_test.o -I/usr/include/ -I../../../../include/ -L/usr/lib64/ -L/usr/lib/ -L../../../../../bin/lib -ldl -lkedao_utils -lodbc -lstdc++fs -fPIC -rdynamic -lpthread -Wl,-rpath=./:./lib:'$ORIGIN':'$ORIGIN/lib'
rm -rf mdl_test.o
在 VSCode 中操作,如图:

编译完成后,动态库自动生成到 /opt/kedao/bin/lib/lym_test/prj_c_test/ 目录。
七、调试服务
1、创建调试账号
在kedao creator中打开【我的系统】->【系统名称】->【项目成员】功能,在 "成员列表" 中,选择要创建测试账号的项目组成员,点击操作列的【创建调试账号】,创建成功后,将会生成调试工程代码,如:

调试工程的代码路径 /opt/kedao/src_c/lym_test/prj_c_test/debugger/debug_lym_test
在配置 VSCode的 tasks.json 时,需参照调试工程的 makefile 文件
2、VSCode 中配置调试任务
在 /opt/kedao/.vscode 配置目录中设置 launch.json 和 tasks.json 这两个文件。
1)设置 launch.json
在 /opt/kedao/.vscode 文件夹中找到 launch.json 文件,在 "configurations" 节点中增加预启动任务,如:
{
"version": "0.2.0",
"configurations": [
{
"preLaunchTask": "debug_lym_test",
"name": "debug_lym_test",
"type": "cppdbg",
"request": "launch",
"targetArchitecture": "x64",
"program": "${workspaceFolder}/bin/debug_lym_test",
"args": [
">>",
"stdout"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/bin/",
"environment": [],
"internalConsoleOptions": "openOnSessionStart",
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
其中:
${workspaceFolder}:VSCode远程连接的目录,即 kedao 的根目录
preLaunchTask:为了便于记忆,设置为调试程序的名称 debug_lym_test
program:调试程序的运行路径,默认 /opt/kedao/bin 目录下
name:调试程序名称,与 preLaunchTask 相同,即 tasks.json 中任务的 lable ,建立预启动任务与调试任务的关系
2)设置 tasks.json
在 launch.json 同级的目录中找到 tasks.json 文件,在 "tasks" 节点中增加调试任务,如:
{
"version": "2.0.0",
"tasks": [
{
"label": "debug_lym_test",
"type": "shell",
"command": "g++",
"args": [
"--std=c++23",
"${workspaceFolder}/src_c/lym_test/prj_c_test/debugger/debug_lym_test/debug_lym_test.cpp",
"-fPIC",
"-o",
"${workspaceFolder}/bin/debug_lym_test",
"-g",
"-Wall",
"-I/usr/include/",
"-I${workspaceFolder}/src_c/include", // 公共 include
"-I${workspaceFolder}/src_c/lym_test/prj_c_test/debugger/debug_include", // 系统下的 debug_include
"-L/usr/lib64/",
"-L${workspaceFolder}/bin/lib",
"-ldl",
"-lodbc",
"-lkedao_utils",
"-lstdc++fs",
"-rdynamic",
"-Wl,-rpath=./:./lib:'$$ORIGIN':'$$ORIGIN/lib'"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared"
},
"problemMatcher": "$msCompile"
}
]
}
其中:
label:launch.json 文件预启动任务的 name 相同,两者通过此来建立关联关系;
args:g++的编译参数,参照调试工程的 makefile 文件编写
注意:路径别写错了。
3)开始调试
步骤1:打开调试界面
在 kedao creator【注册服务】功能界面的操作列中,点击【调试】按钮,打开【服务debug调试(C/C++)】调试界面;
在调试界面中,点击【增加测试用例】按钮,创建一个测试用例,如图:

步骤2:在 VSCode 中启动调试程序
在 VSCode 中切换到 Run and Debug 窗口,选择调试任务 debug_lym_test,点击 运行 按钮,启动调试程序,如图:

步骤3:编辑服务请求参数
根据调试的需求,编辑相应的请求参数。这里使用默认参数来调试。
步骤4:保存并调试
在 kedao creator【服务debug调试(C/C++)】中点击【保存并调试】按钮,将服务参数发送到开发服务器,调试程序接收到请求参数后,进入调试界面,如:

调试完成返回 kedao creator【服务debug调试(C/C++)】查看结果
如果调试的时间比较长(超过65秒),调试结果返回服务超时的错误信息,不影响调试和服务逻辑,如:
{
"code": -1,
"err_msg": "timeout of 65000ms exceeded"
}
如果响应结果返回 "调试程序[debug_lym_test]未启动。" 的错误提示信息,请先到 VSCode 中启动调试任务(步骤2),如:
{
"code": -1,
"err_msg": "调试程序[debug_lym_test]未启动。",
"data": []
}
八、调用服务
示例一
在 kedao creator【注册服务】功能界面的操作列中,点击【测试】按钮,弹出测试服务界面,增加测试用例,然后点保存并测试,如:

示例二
在 Postman 中调用,由于服务默认需求进行签名,才能调用,手动签名比较麻烦,先将服务设置为无签名模式。
在 kedao creator【注册服务】功能界面的操作列中,点击【修改】按钮,将签名算法改为:无

由于svc_test 服务已经加载为需要签名验证模式,需要重启服务 sudo systemctl restart kedao 才会加载成不需要签名模式。
kedao@kedao:/opt$ sudo systemctl restart kedao
从示例一的测试服务界面中赋值 url 和 请求参数 到Postman 中。这里需要注意2点,一是 url 后面的 /api,不要忘记了;二是请求参数中的服务名称 svc_name 要把测试标识 @test去掉
http方法:POST
Url:http://192.168.43.30/api
请求参数:
{
"appid": "5bfb7eb48c9b4190b81e947e111ea6b1",
"sys_name": "prj_c_test",
"mdl_name": "mdl_test",
"svc_name": "svc_test",
"body": {
"sys_head": {
"usr_id": "",
"org_id": "",
"sys_id": "",
"mdl_func_id": "",
"login_key": ""
},
"data": {
"func_id": "",
"func_parent_id": "",
"func_code": "",
"menu_title": "",
"router_path": "",
"component_path": "",
"icon": "",
"func_type": 0,
"sort_num": 0
}
}
}
Postman调用如图:

九、并发测试
测试服务器配置:
Windows操作系统:Windows10;CPU:酷睿9;硬盘:SSD PCIe 4.0 x4
Linux操作系统:虚拟机 Ubuntu24.04-live-server
CPU:2核心
内存:4G
kedao并发数:server_concurrency_process=16
在 kedao creator【注册服务】功能界面的操作列中,点击【测试】按钮,打开测试界面,调用服务仍然是 svc_test ,但服务代码中去掉了业务逻辑,同时去掉数据库连接(避免数据库对测试的影响),只保留基本的入参和出参,并且加上签名算法,这次测试服务次数 10万次,并发 1000(模拟客户端数量),测试结果如下图:

同时,在 /opt/kedao/bin/log 目录下查看服务调用日志,服务在服务器端的执行时长在毫秒级别,QPS > 1200。
注:这里通过客户端模拟并发访问服务器上的服务,受到客户端与服务器之间的连接数和请求参数、响应参数网络传输时间的影响,实际服务器端处理服务能力的QPS比现在看到的值要高很多。
关于server_concurrency_process的一点说明:
1)server_concurrency_process的值要根据CPU核心数、服务的业务类型来设置;
2)假如服务的逻辑都在本服务器上运算,那么server_concurrency_process的值要与CPU核心(超线程)数量相当;
3)假如服务的部分逻辑在其他服务器上,比如访问数据库,执行SQL的运算在数据库服务器上,在执行SQL语句期间,服务处于等待状态(CPU处于闲置,可以让给其他服务运算),那么server_concurrency_process的值要与大于CPU核心(超线程)数量;至于大多少,就要一点一点的测试调整。可以按CPU核心(超线程)数量的倍数调整。
4)如果服务器运算量很少,主要用于服务分发(比如集群主服务器),那就要设置的比较大了。
注:这里并非服务器并发性能测试,而是测试某个服务中并发能力,同时检测服务的平均执行时间,以验证单个服务的性能,为服务代码性能调优提供参考依据。
具体的服务器性能测试,参见《kedao中间件-并发测试篇》
至此,C++服务篇完成。
如何在代码中调用服务,将在Vue工程篇讲解。
十、总结
kedao中间件提供了一套完整体系的C++系统结构,包括服务源代码、数据库结构和前端vue工程源码,只需添加业务数据表、设计服务、开发功能即可。
初学者可以通过本系统深入学习,快速入门,缩短编程学习周期,快速晋升高手行列。
久经沙场的老将也如虎添翼,利刃在手,所向披靡。

浙公网安备 33010602011771号