LinuxC++——etcd分布式键值存储系统API(libetcd-cpp-api3)下载与二次封装

作者:深思慎考日期:10/2/2025

文章目录

  • etcdAPI的介绍与安装
      • 安装 etcd-cpp-apiv3
        • 使用示例与编译
        • 主要特性与应用场景
  • etcd二次封装
    • etcdTool.hpp
    • Log.hpp
    • etcd-cpp-api精简版源代码参考

etcdAPI的介绍与安装

etcd-cpp-apiv3 是一个 C++ 语言编写的 etcd 客户端库,用于与 etcd 分布式键值存储系统进行交互。下面这个表格汇总了它的核心信息:

特性说明
项目简介基于 C++ 的 etcd v3 API 客户端库
核心功能分布式键值存储、配置管理、服务发现、分布式锁
主要依赖Boost、gRPC、Protobuf、cpprestsdk
官方仓库github下载地址
通信协议通过 gRPC 与 etcd 服务器通信 (HTTP2 + protobuf)

安装 etcd-cpp-apiv3

安装 etcd-cpp-apiv3 前需要解决其依赖。以下是在 Ubuntu 系统上的主要步骤:

  1. 安装系统依赖
    打开终端,执行以下命令安装编译所需的工具和库:
1sudo apt install -y ca-certificates ccache cmake libboost-all-dev libcurl4-openssl-dev libgrpc-dev libgrpc++-dev libprotobuf-dev libssl-dev libz-dev lsb-release protobuf-compiler-grpc screenfetch wget  

这个命令安装了 Boost、Protobuf、gRPC 等必要的开发库。 2. 编译安装 cpprestsdk
etcd-cpp-apiv3 依赖 cpprestsdk。如果系统仓库的版本不满足要求,可以手动编译:

1git clone https://github.com/microsoft/cpprestsdk.git  
2cd cpprestsdk  
3mkdir build && cd build  
4cmake .. -DBUILD_TESTS=ON -DBUILD_SAMPLES=ON -DCPPREST_EXCLUDE_WEBSOCKETS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache  
5make -j  
6sudo make install  

这里克隆了 cpprestsdk 的源码,并通过 CMake 进行配置和编译安装。 3. 编译安装 etcd-cpp-apiv3
最后,克隆 etcd-cpp-apiv3 的官方仓库并编译安装:

1git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git  
2cd etcd-cpp-apiv3  
3mkdir build && cd build  
4cmake .. -DBUILD_ETCD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache  
5make -j  
6sudo make install  

这个过程会将库文件和头文件安装到系统路径。

使用示例与编译

安装完成后,你就可以在 C++ 项目中使用 etcd-cpp-apiv3 了。

  1. 示例代码
    下面是一个简单的示例,演示如何设置和获取键值对:
1#include <etcd/Client.hpp>  
2#include <etcd/Response.hpp>  
3#include <iostream>  
4int main() {  
5    // 初始化客户端,连接到本地 etcd 服务器  
6    etcd::Client etcd("http://127.0.0.1:2379");  
7      
8    // 设置键值对  
9    etcd::Response response = etcd.set("foo", "bar").get();  
10    if (response.is_ok()) {  
11        std::cout << "Key set successfully" << std::endl;  
12    } else {  
13        std::cerr << "Error: " << response.error_message() << std::endl;  
14    }  
15      
16    // 获取键值对  
17    response = etcd.get("foo").get();  
18    if (response.is_ok()) {  
19        std::cout << "Value: " << response.value().as_string() << std::endl;  
20    } else {  
21        std::cerr << "Error: " << response.error_message() << std::endl;  
22    }  
23    return 0;  
24}  
  1. 编译命令
    假设你的源代码文件名为 main.cpp,可以使用类似下面的命令来编译:
g++ -std=c++11 main.cpp -o main -letcd-cpp-api -lprotobuf -lgrpc++ -lgrpc -lz -lcpprest -lssl -lcrypto -lboost_system -lpthread  

然后运行程序:

./main  

主要特性与应用场景

  • 主要特性etcd-cpp-apiv3 提供了简单易用的 C++ 接口,支持包括 KV 存储、租约(Lease)、观察者(Watcher)和分布式锁(Lock)在内的多种操作。
  • 应用场景:这个库常用于微服务架构中的服务发现配置管理,可以实现服务实例的自动注册与发现,以及配置信息的集中管理和动态更新

etcd二次封装

etcdTool.hpp

1#pragma once
2#include <algorithm>
3#include <cstdint>
4#include <etcd/Client.hpp>
5#include <etcd/Response.hpp>
6#include <etcd/SyncClient.hpp>
7#include <etcd/Value.hpp>
8#include <etcd/Watcher.hpp>
9#include <etcd/KeepAlive.hpp>
10#include <mutex>
11#include <pplx/pplxtasks.h>
12#include <string>
13#include <functional>
14#include <memory>
15#include <unordered_map>
16#include <utility>
17#include <vector>
18#include "Log.hpp"
19
20namespace Etcd_Tool
21{
22    typedef struct ResgisterInfo
23    {
24        //注册信息 服务名——服务地址——租约时间
25        std::string service_name_;
26        std::string service_addr_;
27        int ttl_ = 30;
28    }resinfo_t;
29
30    //根目录
31    //inline static std::string rootservice = "/services/";
32
33    //注册类
34    class RegisterEtcd
35    {
36        public:
37            RegisterEtcd(const std::string& serverinfo)
38            {
39                //初始化日志
40                LogModule::Log::Init();
41                //创建客户端连接服务器
42                try
43                {
44                    etcd_client_ = std::make_shared<etcd::Client>(serverinfo);
45                    LOG_TRACE("etcd Client link sucess!: {}", serverinfo);
46                }
47                catch (const std::exception& e) 
48                {
49                    LOG_DEBUG("create Client error: {}", e.what());
50                }
51                catch (...)
52                {
53                    LOG_ERROR("Unknown Error!");
54                }
55
56                //LOG_INFO("create client sucess!");
57            }
58
59            //注册服务函数
60            bool Register(const resinfo_t& info)
61            {
62                if(keepalives_.find(info.service_name_) != keepalives_.end())
63                {
64                    //检查是否已经注册过服务
65                    LOG_DEBUG("Service already registered: {}", info.service_name_);
66                    return false;
67                }
68
69                //加锁保护数据
70                std::lock_guard<std::mutex> lock(mutex_);
71
72                try
73                {
74                    //创建租约保活
75                    std::shared_ptr<etcd::KeepAlive> keepalive = etcd_client_->leasekeepalive(info.ttl_).get();
76                    if(!keepalive)
77                    {
78                        LOG_DEBUG("keepalive is null! leasekeepalive fail!");
79                        return false;
80                    }
81                    //获取租约id
82                    int64_t leaseid = keepalive->Lease();
83
84                    //注册服务信息,绑定租约
85                    etcd::Response r = etcd_client_->set(info.service_name_, info.service_addr_, leaseid).get();
86                    if(!r.is_ok())
87                    {
88                        LOG_DEBUG("set fail: {}" , r.error_message());
89                        //注册失败,处理租约
90                        keepalive.reset();
91                        return false;
92                    }
93
94                    //注册服务保存进入类信息
95                    keepalives_[info.service_name_] = keepalive;
96                    services_.push_back(info);
97
98                    LOG_INFO("Register sucess, service name is : {}", info.service_name_);
99                }
100                catch (const std::exception& e)
101                {
102                    LOG_ERROR("Register fail: {}", e.what());
103                    return false;
104                }
105                catch (...)
106                {
107                    LOG_ERROR("Unknown Error!");
108                    return false;
109                }
110
111                return true;
112            }
113
114            //注销服务函数——服务名
115            bool UnRegister(const std::string& service_name)
116            {
117                std::lock_guard<std::mutex> lock(mutex_);
118
119                //检查服务是否存在
120                auto it = keepalives_.find(service_name);
121                if(it == keepalives_.end())
122                {
123                    LOG_INFO("service name invalid!");
124                    return false;
125                }
126
127                try
128                {
129                    //服务存在,取消租约
130                    it->second.reset();
131                    //删除服务器中的服务
132                    auto res = etcd_client_->rm(it->first).get();
133                    if(!res.is_ok())
134                    {
135                        LOG_DEBUG("Client rm error: {}", res.error_message());
136                        return false;
137                    }
138
139                    //删除类中信息
140                    keepalives_.erase(it);
141
142                    services_.erase(std::remove_if(services_.begin(), services_.end()
143                    , [&](const resinfo_t& it){
144                        return it.service_name_ == service_name;
145                    }), services_.end());
146
147                    LOG_INFO("UnRegister sucess, service name: {}", service_name);
148
149                    return true;
150                }
151                catch (const std::exception& e)
152                {
153                    LOG_ERROR("UnRegister fail: {}", e.what());
154                    return false;
155                }
156                catch (...)
157                {
158                    LOG_ERROR("Unknown Error!");
159                    return false;
160                }
161
162                return true;
163            }
164
165            bool UpdateRegister(const resinfo_t& info)
166            {
167                //检查服务是否存在
168                auto it = keepalives_.find(info.service_name_);
169                if(it == keepalives_.end())
170                {
171                    LOG_DEBUG("Service not find: {}", info.service_name_);
172                    return false;
173                }
174
175                std::lock_guard<std::mutex> lock(mutex_);
176                try
177                {
178                    int64_t leaseid = it->second->Lease();
179                    //不存在则创建,存在则更新键值对
180                    auto res = etcd_client_->set(info.service_name_, info.service_addr_,  leaseid).get();
181                    if(!res.is_ok())
182                    {
183                        LOG_ERROR("Update Register Client set error: {}", res.error_message());
184                        return false;
185                    }
186
187                    //更新本地缓存
188                    for(auto& e : services_)
189                    {
190                        if(e.service_name_ == info.service_name_)
191                        {
192                            e = info;
193                            break;
194                        }
195                    }
196                }
197                catch (const std::exception& e)
198                {
199                    LOG_ERROR("UpdateRegister fail: {}", e.what());
200                    return false;
201                }
202                catch (...)
203                {
204                    LOG_ERROR("Unknown Error!");
205                    return false;
206                }
207
208                return true;
209            }
210
211            std::vector<resinfo_t> Have_Services()
212            {
213                std::lock_guard<std::mutex> lock(mutex_);
214                return services_;
215            }
216
217            bool Clear()
218            {
219                //防止死锁
220                bool allsucess = true;
221                std::vector<std::string> service_names;
222                {
223                    std::lock_guard<std::mutex> lock(mutex_);
224                    for(auto& e : keepalives_)
225                    {
226                        service_names.push_back(e.first);
227                    }
228                }
229
230                for(auto& e : service_names)
231                {
232                    bool b = UnRegister(e);
233                    if(!b)
234                    {
235                        LOG_ERROR("Clear fail!");
236                        allsucess = false;
237                    }
238                }
239
240                return allsucess;
241            }
242
243            ~RegisterEtcd() = default;
244        private:
245            std::shared_ptr<etcd::Client> etcd_client_;
246            std::unordered_map<std::string, std::shared_ptr<etcd::KeepAlive>> keepalives_;
247            std::vector<resinfo_t> services_;
248            mutable std::mutex mutex_;
249    };
250
251    enum filetype
252    {
253        DIR,
254        FILE
255    };
256
257    //监视回调函数
258    using callbackfunc = std::function<void (const etcd::Response& res)>;
259
260    typedef struct Monitorinfo
261    {
262        filetype type_;
263        std::string monitor_path_;
264        callbackfunc cbfunc_;
265    } monitor_t;
266
267    class MonitorEtcd
268    {
269        public:
270            MonitorEtcd(const std::string& serverinfo)
271            {
272                try
273                {
274                    LogModule::Log::Init();
275                    etcd_Client_ = std::make_shared<etcd::Client>(serverinfo);
276                    LOG_TRACE("etcd Client link sucess!: {}", serverinfo);
277                }
278                catch (const std::exception& e)
279                {
280                    LOG_ERROR("std::make_shared<etcd::Client> fail, server: {}, exception: {}", serverinfo, e.what());
281                    return;
282                }
283                catch (...)
284                {
285                    LOG_ERROR("Unknown Error!, server: {}", serverinfo);
286                    return;
287                }
288            }
289
290            bool PushMonitor(const monitor_t& info)
291            {
292                if (!etcd_Client_) 
293                {
294                    LOG_ERROR("etcd客户端未初始化");
295                    return false;
296                }
297
298                try
299                {
300                    std::lock_guard<std::mutex> lock(mtx_);
301                    auto watchopt = info.type_ == DIR ? true : false;
302                    std::unique_ptr<etcd::Watcher> w = std::make_unique<etcd::Watcher>(
303                        *etcd_Client_.get(), 
304                        info.monitor_path_,
305                        info.cbfunc_,
306                        watchopt);
307                    
308                    monitoring_set_[info.monitor_path_] = 
309                        std::make_pair(std::move(w), info);
310                    LOG_INFO("PushMonitor sucess: {}", info.monitor_path_);
311                }
312                catch (const std::exception& e)
313                {
314                    LOG_ERROR("PushMonitor fail, monitor path: {}, exception: {}", info.monitor_path_, e.what());
315                    return false;
316                }
317                catch (...)
318                {
319                    LOG_ERROR("Unknown Error!, monitor path: {}", info.monitor_path_);
320                    return false;
321                }
322                
323                return true;
324            }
325
326            bool CancelMonitor(const std::string& monitor_path)
327            {
328                if (!etcd_Client_) 
329                {
330                    LOG_ERROR("etcd客户端未初始化");
331                    return false;
332                }
333
334                std::lock_guard<std::mutex> lock(mtx_);
335
336                auto it = monitoring_set_.find(monitor_path);
337                if (it == monitoring_set_.end())
338                {
339                    LOG_DEBUG("monitor_path cannot find: {}", monitor_path);
340                    return false;
341                }
342
343                try
344                {
345                    bool b = it->second.first->Cancel();
346                    if (!b)
347                    {
348                        LOG_DEBUG("Cancel monitor error: {}", monitor_path);
349                    }
350
351                    monitoring_set_.erase(it);
352                    LOG_INFO("CancelMonitor sucess: {}", monitor_path);
353                }
354                catch (const std::exception& e)
355                {
356                    LOG_ERROR("CancelMonitor fail, monitor path: {}, exception: {}", monitor_path, e.what());
357                    return false;
358                }
359                catch (...)
360                {
361                    LOG_ERROR("Unknown Error!, monitor path: {}", monitor_path);
362                    return false;
363                }
364
365                return true;
366            }
367
368            ~MonitorEtcd()
369            {
370                std::lock_guard<std::mutex> lock(mtx_);
371                for (auto& [path, pair] : monitoring_set_) 
372                {
373                    pair.first->Cancel();  // 取消所有监听器
374                }
375                monitoring_set_.clear();
376            }
377        private:
378            mutable std::mutex mtx_;
379            std::shared_ptr<etcd::Client> etcd_Client_;
380            std::unordered_map<std::string, std::pair<std::unique_ptr<etcd::Watcher>, monitor_t>> monitoring_set_;
381    };
382}; // namespace etcd_service
383

Log.hpp

1#pragma once
2#include <atomic>
3#include <cstddef>
4#include <memory>
5#include <string>
6#include <vector>
7
8#include <spdlog/spdlog.h>
9#include <spdlog/async.h>
10#include <spdlog/async_logger.h>
11#include <spdlog/sinks/stdout_color_sinks.h>
12#include <spdlog/sinks/basic_file_sink.h>
13
14namespace LogModule 
15{
16    enum OutputMode
17    {
18        CONSOLE_ONLY,
19        FILE_ONLY,
20        BOTH
21    };
22
23    typedef struct LogInfo
24    {   
25        OutputMode outmode = CONSOLE_ONLY;
26        bool is_debug_ = true;
27        std::string logfile_ = "logfile.txt";
28        bool is_async_ = false;
29        size_t queue_size_ = 1 << 12;
30        size_t thread_num_ = 1;
31    } LogInfo_t;
32
33    class Log
34    {
35    public:
36        static void Init(const LogInfo_t& loginfo = LogInfo_t())
37        {
38            if(is_init_)
39                return;
40            logconf_ = loginfo;
41            create_logger();
42            is_init_ = true;
43        }
44
45        static Log& GetInstance()
46        {
47            static Log log;
48            return log;
49        }
50
51        template<typename... Args>
52        void trace(const char* fmt, const Args&... args)
53        {
54            if(logger_)
55                logger_->trace(fmt, args...);
56        }
57
58        template<typename... Args>
59        void info(const char* fmt, const Args&... args)
60        {
61            if(logger_)
62                logger_->info(fmt, args...);
63        }
64
65        template<typename... Args>
66        void debug(const char* fmt, const Args&... args)
67        {
68            if(logger_)
69                logger_->debug(fmt, args...);
70        }
71
72        template<typename... Args>
73        void warn(const char* fmt, const Args&... args)
74        {
75            if(logger_)
76                logger_->warn(fmt, args...);
77        }
78
79        template<typename... Args>
80        void error(const char* fmt, const Args&... args)
81        {
82            if(logger_)
83                logger_->error(fmt, args...);
84        }
85
86        template<typename... Args>
87        void critical(const char* fmt, const Args&... args)
88        {
89            if(logger_)
90                logger_->critical(fmt, args...);
91        }
92
93        static void shutdown()
94        {
95            spdlog::shutdown();
96        }
97
98    private:
99        Log() = default;
100        ~Log() = default;
101
102        Log& operator=(const Log&) = delete;
103        Log(const Log&) = delete;
104
105        static void create_logger()
106        {
107            std::vector<spdlog::sink_ptr> sinks;
108
109            if(logconf_.outmode == CONSOLE_ONLY || logconf_.outmode == BOTH)
110            {
111                auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();  
112                sinks.push_back(console_sink);
113            }
114            if(logconf_.outmode == FILE_ONLY || logconf_.outmode == BOTH)
115            {
116                auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logconf_.logfile_);
117                sinks.push_back(file_sink);
118            }
119
120            spdlog::level::level_enum lenum = 
121                logconf_.is_debug_ ? spdlog::level::trace : spdlog::level::info;
122            
123            if(logconf_.is_async_)
124            {
125                spdlog::init_thread_pool(logconf_.queue_size_, logconf_.thread_num_);
126                logger_ = std::make_shared<spdlog::async_logger>(
127                    "mainlog", sinks.begin(), sinks.end(), 
128                    spdlog::thread_pool(), 
129                    spdlog::async_overflow_policy::block);
130            }
131            else
132            {
133                logger_ = std::make_shared<spdlog::logger>(
134                    "mainlog", sinks.begin(), sinks.end());
135            }
136
137            logger_->set_level(lenum);
138            spdlog::set_default_logger(logger_);  // 重要:设置默认日志器
139            logger_->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%-8l]%v");
140        }
141
142    private:
143        static inline std::shared_ptr<spdlog::logger> logger_;
144        static inline LogInfo_t logconf_;
145        static inline std::atomic<bool> is_init_ = false;
146    };
147};
148
149// // 使用简化版本的宏定义
150// #define LOG_TRACE(...) LogModule::Log::GetInstance().trace(__VA_ARGS__)
151// #define LOG_INFO(...) LogModule::Log::GetInstance().info(__VA_ARGS__)
152// #define LOG_DEBUG(...) LogModule::Log::GetInstance().debug(__VA_ARGS__)
153// #define LOG_WARN(...) LogModule::Log::GetInstance().warn(__VA_ARGS__)
154// #define LOG_ERROR(...) LogModule::Log::GetInstance().error(__VA_ARGS__)
155// #define LOG_CRITICAL(...) LogModule::Log::GetInstance().critical(__VA_ARGS__)
156
157// 修改后宏定义:
158#define LOG_TRACE(fmt, ...) LogModule::Log::GetInstance().trace("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
159#define LOG_INFO(fmt, ...) LogModule::Log::GetInstance().info("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
160#define LOG_DEBUG(fmt, ...) LogModule::Log::GetInstance().debug("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
161#define LOG_WARN(fmt, ...) LogModule::Log::GetInstance().warn("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
162#define LOG_ERROR(fmt, ...) LogModule::Log::GetInstance().error("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
163#define LOG_CRITICAL(fmt, ...) LogModule::Log::GetInstance().critical("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
164
165

etcd-cpp-api精简版源代码参考

参考源代码函数参数


LinuxC++——etcd分布式键值存储系统API(libetcd-cpp-api3)下载与二次封装》 是转载文章,点击查看原文


相关推荐


从 .NET 到 Java 的转型指南:详细学习路线与实践建议
百锦再@新空间9/30/2025

本文探讨了从.NET转向Java开发的关键要点,主要包括: 转型背景分析 Java在跨平台、生态系统、成本控制和人才储备方面具有优势 .NET与Java在运行时环境、内存管理机制上存在架构差异 语言基础对比 Java的基本数据类型与包装类体系 字符串处理机制(String/StringBuilder/StringBuffer) 面向对象特性的实现差异(类继承、接口等) 学习策略建议 利用两种语言在OOP概念上的相似性 重点关注平台特性、工具链和部署方式的差异 文章通过大量代码示例(如类型系统、资源管理、字符


【关于虚拟机执行ip addr 命令不显示ip地址问题】
Mr.Ja9/30/2025

本文主要记录了作者在使用虚拟机时,遇到执行ip addr命令不显示 IP 地址的问题及解决过程。起初,作者发现虚拟机仅显示回环地址与网卡物理信息,无 IPv4 地址,先后通过检查 Linux 系统内网卡配置文件、重启网络服务、切换虚拟机网络模式,以及搜索技术博客、咨询 AI 等常规方式排查,均未解决问题。最终,作者偶然意识到虚拟机 NAT 模式依赖物理机的 VMware 相关服务,经检查发现 Windows 物理机中 “VMware NAT Service” 服务未启动,启动该服务后,在虚拟机中执行ifup


BFF层设计:GraphQL网关在微前端联调中的实践
qq_314166012025/10/2

在现代前端架构中,微前端(Micro-Frontend)和 GraphQL 已成为热门技术趋势。随着业务模块的复杂化,前后端协作变得越来越关键。在本文中,我们将深入探讨 BFF(Backend For Frontend)层在微前端联调中的实践经验,并分享如何通过 GraphQL 网关提升前后端协作效率 ??。 一、什么是 BFF 层 BFF(Backend For Frontend)是一种为前端量身定制的后端服务层。它的核心目标是将不同前端应用(Web、移动端等)对数据的需求进行统一


--group-start/--group-end 能不能解决 OpenSSL 1.0 vs 1.1 的优先级问题?
dlz08362025/10/2

--group-start / --group-end 其实是 ld 链接器的一个功能,专门用来解决 循环依赖的静态库 问题。 例如: -Wl,--start-group -lfoo -lbar -lbaz -Wl,--end-group 会让链接器在这几个库之间反复扫描,直到符号解析完为止。 🔎 那么能不能用它来“优先选 1.0 而不是 1.1”? 答案是:不行,原因如下: --start-group/--end-group 只是解决静态库依赖次序的问题,不会影响动态


ElementUI-Table 表格实现行拖拽
宣晨光2025/10/3

1、引入依赖 npm install sortablejs --save 2、table表格设置 1、添加属性 ref="multipleTable"  row-key="id" @row-drag-end="handleDragEnd"  2、添加列 <el-table-column width="50" align="center">             <template >               <i class="el-icon-rank drag-han


《Shell脚本门诊部:我治好了日志清理、自动备份和监控报警的“慢性病”》
鋯莂從媊2025/10/4

核心思路: 把Shell脚本比作“药方”,专门治疗运维工作中的那些重复、繁琐的“慢性病”。每个案例都是一个完整的、可复用的脚本。 内容要点与实操步骤: 案例一:日志清理脚本(“磁盘空间肥胖症”) 1.病症: /app/logs 目录动不动就100%,需要自动清理7天前的日志。 2.药方: #!/bin/bash # 日志清理专家 - 专治磁盘空间肥胖症 LOG_DIR="/app/logs" FIND_RESULT=$(find $LOG_DIR -name "*.log" -typ


Qiankun 子应用生命周期及使用场景解析
excel2025/10/5

在前端微前端架构中,Qiankun 是常用的微前端框架。它允许主应用动态加载多个子应用,而子应用必须遵循特定生命周期,保证能够被正确加载、挂载和卸载。本文将详细解析 子应用的三个核心生命周期函数:bootstrap、mount、unmount,并说明它们的使用场景,同时给出具体示例。 1. 子应用生命周期概览 子应用生命周期分为三个阶段: Bootstrap(初始化阶段) Mount(挂载阶段) Unmount(卸载阶段) 每个阶段有明确的调用时机和职责。 2. bootstrap:初始


Node.js(十二)插件开发
燎原人生2025/10/6

一、Node.js 原生插件(Native Addon)开发入门教程 1. 准备环境 安装 Node.js(建议最新版) 安装 node-gyp 工具(用于编译 C++ 代码) npm install -g node-gyp 安装编译环境 Windows: 需要安装 Windows Build ToolsmacOS: 需要安装 XcodeLinux: 需要安装 g++、make 等 2. 创建项目目录 mkdir my-native-addon cd my-native-


Spec-Kit:AI驱动的软件开发全流程管理工具套件
是魔丸啊2025/10/8

Spec-Kit 是一套专为AI辅助开发场景设计的命令行工具套件,提供了从需求规格到代码实现的标准化工作流程。通过8个核心命令,它确保软件开发的每个环节都保持高质量和一致性。 核心理念 端到端管理:覆盖需求规格化、架构设计、任务分解、代码实现全流程 AI优化设计:专为与Claude等AI助手协作而优化 质量保证:内置多层次验证机制 知识沉淀:项目宪法、设计决策完整记录 这篇文章主要是想记录下spec-kit的核心command具体在干嘛。所以翻译了下所有的command(按照实际使用顺序)


前端梳理体系从常问问题去完善-框架篇(react生态)
大前端helloworld2025/10/9

前言 国庆去趟了杭州,但是人太多了,走路都觉得空气很闷,天气也很热,玩了两天就回宿舍躺了,感觉人太多,看不到风景,而且消费也很高,性价比不是很值得,就呆在公寓,看了两本书,有一本是名著,《呼啸山庄》虽然是写的是爱情,但爱情背后是人性。爱情啊,这个课题本来就是让人很难读懂得,关于爱,也看了一篇文章。关于爱上人渣得,爱上人渣,或是那些求而不得甚至是受制于禁忌的爱,本质上也是在追求这种刺激,或者说正是因为这样的对象能给自己麻木的感官更大的刺激,从而误以为这就是「爱」的本质,就像是人们虽然知道「吊桥效应

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0