《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
🎬 艾莉丝的简介:
文章目录
- 1 ~> ChatServer main.cpp 工程实现
- 1.1 整体需求规约
- 1.1.1 业务需求清单
- 1.1.2 文件约束
- 1.2 ServerConfig 结构体定义
- 1.3 gflags 参数定义 main.cpp 头部
- 1.4 工具函数实现
- 1.4.1 环境变量读取工具
- 1.4.2 信号处理回调函数
- 1.4.3 参数校验函数 validateConfig
- 1.4.4 帮助打印函数 showAPIInfo
- 1.5 main 主函数完整实现
- 1.6 ChatServer::start 内部线程实现 (ChatServer.cpp)
- 2 ~> ChatServer.conf 配置文件模板
- 3 ~> 环境变量配置(云端 API 密钥)
- 4 ~> 关键易错点总结(面试高频)
- 4.1 gflags flagfile 加载顺序
- 4.2 线程模型关键点
- 4.3 信号处理注意事项
- 4.4 密钥安全规范
- 4.5 头文件路径坑
- 4.6 参数校验边界
- 5 ~> CMakeLists.txt 关键片段
- 6 ~> 程序运行命令示例
- 结尾
1 ~> ChatServer main.cpp 工程实现
背景:基于 C++ + gflags + spdlog 二次封装日志库,实现 AI 聊天服务入口 main 程序;核心职责:参数解析、配置校验、日志初始化、信号处理、服务启动、主线程保活。
1.1 整体需求规约
1.1.1 业务需求清单
- 使用 gflags 库定义全部
ServerConfig配置项;参数来源优先级:gflags 默认值 < ChatServer.conf 配置文件 < 命令行参数 - 网络服务默认绑定地址
0.0.0.0,端口8080;默认日志级别INFO - 云端大模型(deepseek /chatgpt/gemini)API 密钥从操作系统环境变量读取,禁止通过命令行传入;Ollama 全套配置通过 gflags 命令行 / 配置文件提供
- 模型默认参数:
temperature=0.7,max_tokens=2048 - 配置文件
ChatServer.conf放置在 ChatServer 项目目录,由 gflags 原生 flagfile 机制加载,禁止手写自定义配置解析逻辑,禁止代码生成配置文件 - 命令行功能:
-h / --help:打印帮助文档、使用示例、全部 HTTP API 接口说明-v / --version:打印程序版本号,默认版本1.0.0
- 参数合法性校验规则
- temperature 取值范围必须
0.0 ~ 2.0 - max_tokens 必须大于 0,不能为负数
- 云端 API Key 至少存在一个不为空;若填写 Ollama 任意一项,则 Ollama 全部三项配置(模型名称、描述、endpoint)必须全部非空
- temperature 取值范围必须
- 服务器业务线程:ChatServer 内部将 HTTP 服务运行在独立 detach 分离线程;main 主线程负责保活、信号响应,不能阻塞在服务监听逻辑。
- 日志模块:项目 sdk 封装 spdlog 日志
bite::Logger,头文件安装到系统 include,直接引入<ai_chat_sdk/util/myLog.h>,禁止使用相对路径引用头文件。
1.1.2 文件约束
- main.cpp 存放路径:ChatServer 目录下
- 需求文档:
ChatServer/required/main需求.txt
1.2 ServerConfig 结构体定义
文件位置 ChatServer/ChatServer.h
#pragmaonce#include<string>#include<httplib.h>namespaceai_chat_server{structServerConfig{// 网络服务配置std::string host="0.0.0.0";intport=8080;std::string logLevel="INFO";// LLM模型公共参数doubletemperature=0.7;intmaxTokens=2048;// 云端模型密钥,从环境变量读取std::string deepseekAPIKey;std::string chatGPTAPIKey;std::string geminiAPIKey;// Ollama本地大模型配置,由gflags参数传入std::string ollamaModelName;std::string ollamaModelDesc;std::string ollamaEndpoint;};classChatServer{public:explicitChatServer(constServerConfig&config);boolstart();voidstop();boolisRunning()const;private:// http路由、业务处理成员省略};}1.3 gflags 参数定义 main.cpp 头部
#include"ChatServer.h"#include<ai_chat_sdk/util/myLog.h>#include<gflags/gflags.h>#include<fstream>#include<thread>#include<csignal>#include<cstdlib>// 网络服务参数DEFINE_string(host,"0.0.0.0","服务器绑定监听地址");DEFINE_int32(port,8080,"HTTP服务监听端口");DEFINE_string(log_level,"INFO","日志级别: TRACE|DEBUG|INFO|WARN|ERROR|CRITICAL");DEFINE_string(config_file,"./ChatServer.conf","gflags配置文件路径(flagfile格式)");// LLM模型公共参数DEFINE_double(temperature,0.7,"模型温度,取值范围0.0~2.0");DEFINE_int32(max_tokens,2048,"模型最大输出token数量");// Ollama相关参数,命令行/配置文件传入DEFINE_string(ollama_model_name,"","Ollama模型名称");DEFINE_string(ollama_model_desc,"","Ollama模型描述信息");DEFINE_string(ollama_endpoint,"","Ollama服务API地址,例如http://127.0.0.1:11434");constexprconstchar*VERSION="1.0.0";// 全局服务器智能指针,用于信号回调访问服务实例namespaceai_chat_server{std::unique_ptr<ChatServer>g_server;std::atomic<bool>g_running{true};}1.4 工具函数实现
1.4.1 环境变量读取工具
// 读取系统环境变量,不存在返回空字符串staticstd::stringgetEnvVar(conststd::string&key){constchar*val=std::getenv(key.c_str());returnval?std::string(val):"";}1.4.2 信号处理回调函数
捕获
SIGINT(Ctrl+C)、SIGTERM信号,优雅停止服务,安全退出进程
staticvoidsignalHandler(intsig){usingnamespaceai_chat_server;(void)sig;g_running.store(false);if(g_server){g_server->stop();}}1.4.3 参数校验函数 validateConfig
staticboolvalidateConfig(constai_chat_server::ServerConfig&config){// 校验temperature范围if(config.temperature<0.0||config.temperature>2.0){ERR("配置校验失败: temperature必须处于0.0~2.0,当前值={}",config.temperature);returnfalse;}// maxTokens必须大于0if(config.maxTokens<=0){ERR("配置校验失败: max_tokens必须大于0,当前值={}",config.maxTokens);returnfalse;}// 云端密钥:至少有一个API Key不为空boolhasCloudKey=!config.deepseekAPIKey.empty()||!config.chatGPTAPIKey.empty()||!config.geminiAPIKey.empty();// Ollama:只要任意一个字段非空,则全部三个字段必须全部配置boolhasAnyOllama=!config.ollamaModelName.empty()||!config.ollamaModelDesc.empty()||!config.ollamaEndpoint.empty();boolollamaComplete=!config.ollamaModelName.empty()&&!config.ollamaModelDesc.empty()&&!config.ollamaEndpoint.empty();if(hasAnyOllama&&!ollamaComplete){ERR("配置校验失败:Ollama配置项不能部分填写,model_name、model_desc、endpoint必须同时提供");returnfalse;}// 必须至少拥有云端key,或者完整Ollama配置if(!hasCloudKey&&!ollamaComplete){ERR("配置校验失败:至少配置一个云端API Key,或者一套完整Ollama模型配置");returnfalse;}returntrue;}1.4.4 帮助打印函数 showAPIInfo
-h / --help触发,打印参数说明、启动示例、全部 REST API 接口清单
staticvoidshowAPIInfo(){std::cout<<"\n==== AIChatServer 使用帮助 ====\n";std::cout<<"版本: "<<VERSION<<"\n\n";std::cout<<"启动示例:\n";std::cout<<" ./AIChatServer 使用默认参数启动\n";std::cout<<" ./AIChatServer --port=9000 指定端口启动\n";std::cout<<" ./AIChatServer --config_file=./ChatServer.conf 加载配置文件\n";std::cout<<"\n环境变量设置示例(云端密钥):\n";std::cout<<" export DEEPSEEK_API_KEY=xxx\n";std::cout<<"\n可用HTTP API接口:\n";std::cout<<" POST /api/session 创建会话\n";std::cout<<" GET /api/sessions 获取全部会话列表\n";std::cout<<" GET /api/models 获取可用模型列表\n";std::cout<<" DELETE/api/session/{id} 删除指定会话\n";std::cout<<" GET /api/session/{id}/history 获取会话历史\n";std::cout<<" POST /api/message 发送消息,全量返回\n";std::cout<<" POST /api/message/async 发送消息,流式返回\n";std::cout<<"\n";}1.5 main 主函数完整实现
执行流程:
- 设置 gflags 提示文本、版本字符串
- 原生 gflags 解析命令行
- 判断配置文件是否存在,使用
gflags::SetCommandLineOption("flagfile",xxx)加载配置文件- 打印帮助文档判断
- 注册信号捕获
- 组装 ServerConfig 结构体:云端 key 读环境变量,Ollama 参数读取 gflags
- 参数合法性校验
- 初始化 spdlog 日志器,设置日志级别
- 实例化 ChatServer,调用 start (),内部 HTTP 服务运行在 detach 分离子线程
- 主线程循环保活,等待停止信号
- 服务退出,程序返回
intmain(intargc,char**argv){usingnamespaceai_chat_server;try{// gflags初始化gflags::SetUsageMessage("AI聊天服务,AI ChatServer");gflags::SetVersionString(VERSION);// 解析命令行参数,remove_flags=true将解析过的flag从argv移除gflags::ParseCommandLineFlags(&argc,&argv,true);// 使用gflags原生flagfile加载配置文件,禁止手写配置解析逻辑std::ifstreamconfFile(FLAGS_config_file);if(confFile.is_open()){gflags::SetCommandLineOption("flagfile",FLAGS_config_file.c_str());confFile.close();INFO("成功加载配置文件:{}",FLAGS_config_file);// 注意:flagfile会重新触发参数解析,优先级:命令行 > flagfile > 默认值}else{WARN("配置文件{}不存在,将仅使用命令行与默认参数",FLAGS_config_file);}// -h / --help 打印帮助信息if(argc>=1){std::stringopt(argv[1]);if(opt=="-h"||opt=="--help"){showAPIInfo();return0;}}// 注册信号处理器,捕获Ctrl+C、进程终止信号signal(SIGINT,signalHandler);signal(SIGTERM,signalHandler);// =========组装ServerConfig配置=========ServerConfig config;config.host=FLAGS_host;config.port=FLAGS_port;config.logLevel=FLAGS_log_level;config.temperature=FLAGS_temperature;config.maxTokens=FLAGS_max_tokens;// 云端大模型密钥从系统环境变量读取,不在命令行暴露密钥config.deepseekAPIKey=getEnvVar("DEEPSEEK_API_KEY");config.chatGPTAPIKey=getEnvVar("CHATGPT_API_KEY");config.geminiAPIKey=getEnvVar("GEMINI_API_KEY");// Ollama参数全部取自gflags命令行/配置文件config.ollamaModelName=FLAGS_ollama_model_name;config.ollamaModelDesc=FLAGS_ollama_model_desc;config.ollamaEndpoint=FLAGS_ollama_endpoint;// 参数安全校验if(!validateConfig(config)){ERR("服务配置校验失败,程序退出");returnEXIT_FAILURE;}// =========初始化spdlog封装日志器bite::Logger=========spdlog::level::level_enum logLevel=spdlog::level::info;if(config.logLevel=="TRACE")logLevel=spdlog::level::trace;elseif(config.logLevel=="DEBUG")logLevel=spdlog::level::debug;elseif(config.logLevel=="INFO")logLevel=spdlog::level::info;elseif(config.logLevel=="WARN")logLevel=spdlog::level::warn;elseif(config.logLevel=="ERROR")logLevel=spdlog::level::err;elseif(config.logLevel=="CRITICAL")logLevel=spdlog::level::critical;// 初始化日志实例;输出至控制台+日志文件chat_server.logbite::Logger::initLogger("ChatServer","chat_server.log",logLevel);// 打印启动配置信息INFO("==== AIChatServer启动配置 ====");INFO("版本号:{}",VERSION);INFO("监听地址:{}:{}",config.host,config.port);INFO("日志级别:{}",config.logLevel);INFO("temperature:{}",config.temperature);INFO("max_tokens:{}",config.maxTokens);INFO("DeepSeek Key:{}",config.deepseekAPIKey.empty()?"未设置":"已设置");INFO("ChatGPT Key:{}",config.chatGPTAPIKey.empty()?"未设置":"已设置");INFO("Gemini Key:{}",config.geminiAPIKey.empty()?"未设置":"已设置");INFO("Ollama模型:{}",config.ollamaModelName.empty()?"未启用":config.ollamaModelName);// 创建服务实例,ChatServer::start内部启动独立detach线程运行http服务g_server=std::make_unique<ChatServer>(config);boolstartOk=g_server->start();if(!startOk){ERR("ChatServer服务启动失败");returnEXIT_FAILURE;}INFO("ChatServer启动成功,地址 http://{}:{}",config.host,config.port);INFO("按下 Ctrl+C 停止服务");// =========主线程保活循环=========// http服务运行在子线程;主线程不能立刻return,否则进程直接退出// g_running原子布尔,收到SIGINT/SIGTERM会被置false,退出循环while(g_running.load()&&g_server->isRunning()){std::this_thread::sleep_for(std::chrono::seconds(1));}INFO("收到停止信号,服务正常退出");g_server.reset();returnEXIT_SUCCESS;}catch(conststd::exception&e){ERR("程序发生异常: {}",e.what());returnEXIT_FAILURE;}catch(...){ERR("程序发生未知异常");returnEXIT_FAILURE;}}1.6 ChatServer::start 内部线程实现 (ChatServer.cpp)
HTTP 服务运行在 detach 分离线程,不阻塞 main 主线程
boolChatServer::start(){// ...省略路由初始化代码std::threadserverThread([this](){_chatServer->listen(_config.host,_config.port);});// detach分离线程,线程独立运行,不需要主线程joinserverThread.detach();_isRunning.store(true);returntrue;}2 ~> ChatServer.conf 配置文件模板
放置于 ChatServer 目录;gflags flagfile 格式,每行写
--flag_name=value,#代表注释;程序代码不生成该文件,由使用者手动编写维护
# ChatServer.conf gflags flagfile配置文件 # 网络服务配置 --host=0.0.0.0 --port=8080 --log_level=INFO # LLM公共参数 --temperature=0.7 --max_tokens=2048 # Ollama本地模型配置,使用ollama才需要打开注释 #--ollama_endpoint=http://127.0.0.1:11434 #--ollama_model_name=deepseek‑r1:1.5b #--ollama_model_desc="本地部署开源推理模型"3 ~> 环境变量配置(云端 API 密钥)
API 密钥属于敏感信息,不在配置文件 / 命令行明文存储,设置 Linux shell 环境变量
# ~/.bashrc 添加exportDEEPSEEK_API_KEY="sk‑xxxx"exportCHATGPT_API_KEY="sk‑xxxx"exportGEMINI_API_KEY="xxxx"# 生效配置source~/.bashrc# 验证环境变量是否生效echo$DEEPSEEK_API_KEY4 ~> 关键易错点总结(面试高频)
4.1 gflags flagfile 加载顺序
- gflags 内置默认值
- flagfile 配置文件参数
- 命令行传入参数(优先级最高,覆盖配置文件)
- 错误做法:手写文件解析,字符串切割模拟 flagfile;正确:调用
gflags::SetCommandLineOption("flagfile", path)交给库原生解析。
4.2 线程模型关键点
- ChatServer HTTP 监听运行在
detach()分离子线程; - main 主线程必须保留循环保活,如果 main 直接 return,整个进程会销毁,子线程同步终止,服务立刻停止;
- 使用
std::atomic<bool>原子变量做运行标记,在信号处理器修改原子标记,保证多线程内存可见性。
4.3 信号处理注意事项
- 信号回调函数上下文限制:不能执行复杂 IO、不能调用 spdlog 打印日志;只修改原子标记、调用 stop、重置智能指针。
- 捕获
SIGINT(Ctrl+C),SIGTERM(kill 命令终止进程),实现优雅关闭。
4.4 密钥安全规范
- 云端 API 密钥禁止 gflags 命令行参数传入;命令行参数可以被 ps 命令全局查看,造成密钥泄露;
- 密钥从操作系统环境变量读取。
4.5 头文件路径坑
日志库头文件已经安装进系统 include 目录,#include <ai_chat_sdk/util/myLog.h>;禁止使用#include "../sdk/include/util/myLog.h"相对路径,跨编译环境会编译失败。
4.6 参数校验边界
- temperature 闭区间
[0.0, 2.0]; - max_tokens 严格大于 0;
- Ollama 配置:部分字段填写属于非法状态,要么全部为空,要么三项全部非空;
- 整体至少有一套可用模型:云端 key OR 完整 Ollama 配置。
5 ~> CMakeLists.txt 关键片段
project(AIChatServer) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 依赖库 gflags spdlog find_package(gflags REQUIRED) find_package(spdlog REQUIRED) add_executable(AIChatServer ChatServer/main.cpp ChatServer/ChatServer.cpp ) target_link_libraries(AIChatServer gflags::gflags spdlog::spdlog ai_chat_sdk )6 ~> 程序运行命令示例
# 1. 默认参数启动,读取ChatServer.conf./AIChatServer# 2. 指定端口,命令行参数优先级高于配置文件./AIChatServer--port=9000#3. 指定自定义配置文件路径./AIChatServer--config_file=./my.conf#4. 查看帮助文档./AIChatServer-h#5. 查看版本./AIChatServer-v结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
【AI大模型接入SDK】Trae生成代码介绍
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა