You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不重启且不使用实验性命名空间的C++ gRPC服务器中更新证书对?

C++ gRPC 服务器证书热更新方案(非实验性API)

核心思路

gRPC C++ 提供的grpc::Server::RefreshCredentials稳定API,可实现不重启服务器的SSL凭证更新。通过定时检测证书文件变更,重新加载新证书并调用该方法,即可满足:

  • 已建立的客户端连接继续使用旧证书完成通信
  • 新发起的连接自动使用更新后的新证书

具体实现步骤

1. 初始创建SSL服务器凭证

基于初始证书构建SslServerCredentials并启动gRPC服务器:

#include <grpcpp/grpcpp.h>
#include <grpcpp/security/server_credentials.h>
#include <filesystem>
#include <thread>
#include <chrono>
#include <iostream>

namespace fs = std::filesystem;

// 读取证书/密钥文件内容
std::string LoadCertFile(const std::string& file_path) {
    std::ifstream file(file_path);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file: " + file_path);
    }
    return std::string(std::istreambuf_iterator<char>(file), {});
}

// 构建SSL凭证配置
grpc::SslServerCredentialsOptions BuildSslOptions() {
    grpc::SslServerCredentialsOptions options;
    options.pem_key_cert_pairs.emplace_back(
        LoadCertFile("server.key"),
        LoadCertFile("server.crt")
    );
    // 若需验证客户端证书,添加CA根证书
    // options.pem_root_certs = LoadCertFile("ca.crt");
    return options;
}

int main() {
    // 初始化服务器凭证
    auto initial_creds = grpc::SslServerCredentials(BuildSslOptions());
    
    grpc::ServerBuilder builder;
    builder.AddListeningPort("0.0.0.0:50051", initial_creds);
    // 注册你的gRPC服务实现...
    
    std::unique_ptr<grpc::Server> server = builder.BuildAndStart();
    if (!server) {
        std::cerr << "Failed to start gRPC server" << std::endl;
        return 1;
    }
    std::cout << "Server running on 0.0.0.0:50051" << std::endl;

    // 启动证书监控线程
    std::thread cert_monitor([&server]() {
        // 记录证书文件上次修改时间
        auto last_key_mtime = fs::last_write_time("server.key");
        auto last_crt_mtime = fs::last_write_time("server.crt");

        while (true) {
            std::this_thread::sleep_for(std::chrono::hours(1)); // 每小时检查一次

            // 检查证书文件是否更新
            auto curr_key_mtime = fs::last_write_time("server.key");
            auto curr_crt_mtime = fs::last_write_time("server.crt");
            if (curr_key_mtime != last_key_mtime || curr_crt_mtime != last_crt_mtime) {
                try {
                    // 加载新证书并创建新凭证
                    auto new_creds = grpc::SslServerCredentials(BuildSslOptions());
                    // 刷新服务器凭证
                    grpc::Status refresh_status = server->RefreshCredentials(new_creds);
                    if (refresh_status.ok()) {
                        std::cout << "Credentials refreshed successfully" << std::endl;
                        // 更新上次修改时间记录
                        last_key_mtime = curr_key_mtime;
                        last_crt_mtime = curr_crt_mtime;
                    } else {
                        std::cerr << "Refresh credentials failed: " << refresh_status.error_message() << std::endl;
                    }
                } catch (const std::exception& e) {
                    std::cerr << "Failed to load new certs: " << e.what() << std::endl;
                }
            }
        }
    });

    server->Wait();
    cert_monitor.join();
    return 0;
}

2. 关键细节说明

  • 旧连接兼容性:调用RefreshCredentials后,已建立的TLS连接不会被强制断开,会沿用旧的SSL上下文直到连接自然终止,完全保证业务连续性。
  • 证书有效性校验:加载新证书前需确保文件可读、密钥与证书匹配,避免更新失败导致新连接无法建立。
  • 检测频率调整:可根据证书有效期设置检测间隔,比如在证书过期前一周改为每10分钟检查一次。
  • 错误处理:刷新失败时需保留旧凭证继续服务,同时记录日志触发告警,便于人工排查。

方案可行性说明

grpc::Server::RefreshCredentials是gRPC C++的稳定生产级API,底层会替换服务器的SSL上下文,新连接自动使用新凭证,现有连接不受影响,完全符合需求且无需依赖实验性接口。

内容的提问来源于stack exchange,提问作者Huy Mai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 16:32:42