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.

浙公网安备 33010602011771号