Chap04-GrpcAndConfigManager

Chap04-GrpcAndConfigManager

grpc

选择原因

后端服务之间通信中,为了效率(也可能是惯例),更多的使用grpc进行通信,能够实现了类似本地的无感服务调用.当然也可以选用json,但是效率会低一些.

关于配置grpc,cmake如下.

pkg_check_modules(GRPC REQUIRED REQUIRED IMPORTED_TARGET grpc)
pkg_check_modules(GRPCPP REQUIRED REQUIRED IMPORTED_TARGET grpc++)
pkg_check_modules(PROTOBUF REQUIRED IMPORTED_TARGET protobuf)
pkg_check_modules(ABSL REQUIRED IMPORTED_TARGET absl_strings)

target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GRPC)
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GRPCPP)
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::PROTOBUF)
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::ABSL)

proto文件

proto文件定义了grpc通信需要的结构和服务.

syntax = "proto3"; // 语法使用proto3
package message;	// 类似作用域

// 定义服务
service VarifyService{
    rpc GetSecurityCode(GetSecurityCodeRequest) returns (GetSecurityCodeResponse){}
}

// 定义作用于下的GetSecurityCodeRequest结构
message GetSecurityCodeRequest{
    string email = 1;
}

// 定义作用于下的GetSecurityCodeResponse结构
message GetSecurityCodeResponse{
    int32 error = 1;
    string email = 2;
    string code = 3;
}

protoc操作

  • protoc --cpp_out=. "message.proto"

    • 生成数据序列化代码
    • .pb.cc .pb.h
    • 数据存储、序列化、RPC 数据格式
  • protoc --cpp_out=. --grpc_out=. -I"." --plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) ./message.proto

    • 使用了grpc插件
    • 生成数据序列化 + gRPC 服务代码
    • .grpc.pb.cc .grpc.pb.h
    • 完整的 gRPC 服务实现

对应的cpp服务结构

VerifyClient是使用了grpc进行真正通信的类.

定义如下:

#ifndef VERIFYCLIENT_H
#define VERIFYCLIENT_H

#include "Singleton.h"
#include "const.h"
#include "message.grpc.pb.h"
#include "message.pb.h"
#include <grpc/grpc.h>
#include <grpcpp/create_channel.h>

using message::GetSecurityCodeRequest;
using message::GetSecurityCodeResponse;
using message::VarifyService;

class VerifyClient : public Singleton<VerifyClient> {
    friend class Singleton<VerifyClient>;

public:
    GetSecurityCodeResponse GetSecurityCode(const std::string& email)
    {
        grpc::ClientContext context;
        GetSecurityCodeRequest request;
        GetSecurityCodeResponse response;
        request.set_email(email);

        grpc::Status status = _stub->GetSecurityCode(&context, request, &response);

        if (!status.ok()) {
            response.set_error(static_cast<int>(ErrorCodes::RPCFAILED));
        }

        return response;
    }

private:
    VerifyClient()
    {
        std::cout << "sdasd" << std::endl;
        std::shared_ptr<grpc::Channel> _channel = grpc::CreateChannel("0.0.0.0:50051", grpc::InsecureChannelCredentials());
        _stub = message::VarifyService::NewStub(_channel);
    }

private:
    std::unique_ptr<VarifyService::Stub> _stub;
};

#endif

其中GetSecurityCode函数是真正用于使用服务的操作.

用法就是在LogicSystem类中解析出客户端发来的json中的email后,调用grpc服务执行发送验证码的操作.

GetSecurityCodeResponse response = VerifyClient::GetInstance()->GetSecurityCode(email);

ConfigManager的作用

类似Qt中使用config.ini文件读取配置,我们的GateWayServer也采用如此操作.不过cpp没有原生的类似读取ini的库,因此我们将进行简单的封装.

我们使用了boost/filesystem,boost/property_tree::ptree,boost/property_tree/ini_parsr.简而言之就是boost提供了读取ini文件的函数,我们将ini文件读取到树形结构pt,然后遍历组合.

#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H

#include <boost/filesystem.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <filesystem>
#include <iostream>
#include <string>
#include <unordered_map>

namespace fs = std::filesystem;

struct Items {
    Items() { };
    ~Items()
    {
        _items.clear();
    };

    Items(const Items& src)
    {
        _items = src._items;
    }

    Items& operator=(const Items& src)
    {
        if (this == &src) {
            return *this;
        }
        _items = src._items;
        return *this;
    }

    std::unordered_map<std::string, std::string> _items;

    std::string operator[](const std::string& key)
    {
        auto it = _items.find(key);
        if (it == _items.end()) {
            return "";
        }
        return it->second;
    }
};

class ConfigManager {
public:
    ~ConfigManager()
    {
        _config_map.clear();
    }

    ConfigManager(const std::string& path)
    {
        fs::path current_path = fs::current_path();
        fs::path config_path = current_path / path;

        boost::property_tree::ptree pt;
        boost::property_tree::read_ini(config_path.string(), pt);

        for (const auto& item : pt) {
            const std::string& item_name = item.first;
            boost::property_tree::ptree ptc = item.second;
            std::unordered_map<std::string, std::string> items;
            for (const auto& [key, value] : ptc) {
                items[key] = value.get_value<std::string>();
            }
            Items item2;
            item2._items = std::move(items);
            _config_map[item_name] = item2;
        }
    }
    ConfigManager()
        : ConfigManager("config.ini")
    {
    }

    ConfigManager(const ConfigManager& src)
    {
        _config_map = src._config_map;
    }
    ConfigManager& operator=(const ConfigManager& src)
    {
        if (this == &src) {
            return *this;
        }
        _config_map = src._config_map;
        return *this;
    }

    Items operator[](const std::string& key)
    {
        auto it = _config_map.find(key);
        if (it == _config_map.end()) {
            return Items();
        }
        return it->second;
    }

private:
    std::unordered_map<std::string, Items> _config_map;
};

#endif

简单分析,ini文件是如下结构

[GateWayServer]
port=9999
host=***
time=***
[VarifyServer]
port=50051

可以看到分为两层结构,外层是[GateWayServer]->{} ,[VarifyServer]->{}

内层又各自为key->value.因此我们的ConfigManager就将其分为内外两层std::unordered_map.

内层存放std::string,std::string对应key:value.我们用一个结构体Items封装起来.

外层存放std::string,Items,即我们configManager.

posted @ 2025-12-24 23:16  大胖熊哈  阅读(1)  评论(0)    收藏  举报