目录
前文我们就将 Rpc 通用类都实现完啦,接下来我们继续~
回忆:Rpc 作用:
- 调用 服务端的算力 来解决 客户端请求的问题
项目服务端独用类的实现
1. RpcRouter类的实现
(1)主要的功能:
- 提供Rpc请求处理回调函数。(含 检查操作)
- 内部的服务管理。
-
- 方法名称。
- 参数信息。
- 对外提供参数校验接口。
相当于 是在 client 和 server 之间,除了 dipatcher 又套了一层 server
(2)具体实现:
框架 搭建:
#pragma once
#include "../common/dispatcher.hpp"
#include "../common/message.hpp"
#include <vector>
using namespace bitrpc;
namespace server
{
// 嵌套一层 server 命名空间,避免和client 部分接口 重合;
enum class VType
{
BOOL = 0,
INTEGRAL,
NUMERIC,
STRING,
ARRAY,
OBJECT,
};
class ServiceDescribe{
public:
using ptr=std::shared_ptr<ServiceDescribe>;
using ServiceCallback=std::function<void(const Json::Value&,Json::Value&)>;
using ParamsDescribe=std::pair<std::string,VType>;
ServiceDescribe();
private:
std::string _method_name; // 方法名称
ServiceCallback _callback; // 实际的业务回调函数
std::vector<ParamsDescribe> _params_desc; // 参数字段格式描述
VType _return_type; // 结果作为返回值类型的描述
};
///
//调用 建造者模式,便于服务生产
class SDescribeFactory{
public:
private:
std::string _method_name; // 方法名称
ServiceDescribe::ServiceCallback _callback; // 实际的业务回调函数
std::vector<ServiceDescribe::ParamsDescribe> _params_desc; // 参数字段格式描述
VType _return_type; // 结果作为返回值类型的描述
};
///
class ServiceManager{
public:
using ptr=std::shared_ptr<ServiceManager>;
//增
void insert(const ServiceDescribe::ptr &desc)
{
}
//查
//删
private:
std::mutex _mutex;
std::unordered_map<std::string,ServiceDescribe::ptr> _services;
};
///
class RpcRouter
{
public:
using ptr = std::shared_ptr<RpcRouter>;
RpcRouter() : _service_manager(std::make_shared<ServiceManager>()) {}
private:
ServiceManager::ptr _service_manager;
};
}
ServiceDescribe
class ServiceDescribe{
public:
using ptr=std::shared_ptr<ServiceDescribe>;
using ServiceCallback=std::function<void(const Json::Value&,Json::Value&)>;
using ParamsDescribe=std::pair<std::string,VType>;
//构造 函数
ServiceDescribe(std::string &&mname, std::vector<ParamsDescribe> &&desc,
VType vtype, ServiceCallback &&handler) :
_method_name(std::move(mname)), _callback(std::move(handler)),
_params_desc(std::move(desc)), _return_type(vtype)
{}
//通过 move && 减少了拷贝
const std::string &method()
{return _method_name;}
//针对 请求中的参数 进行校验
bool paramCheck(const Json::Value ¶ms)
{
// 对params进行参数校验---判断所描述的参数字段是否存在,类型是否一致
//! eg. <"num1",int>
for (auto &desc : _params_desc)
{
if (params.isMember(desc.first) == false)
{
ELOG("参数字段完整性校验失败!%s 字段缺失!", desc.first.c_str());
return false;
}
if (check(desc.second, params[desc.first]) == false)
{
ELOG("%s 参数类型校验失败!", desc.first.c_str());
return false;
}
}
return true;
}
bool call(const Json::Value ¶ms, Json::Value &result)
{
_callback(params, result);
if (rtypeCheck(result) == false)
{
ELOG("回调处理函数中的响应信息校验失败!");
return false;
}
return true;
}
private:
bool rtypeCheck(const Json::Value &val)
{
return check(_return_type, val);
}
bool check(VType vtype, const Json::Value &val)
{
switch (vtype)
{
case VType::BOOL:
return val.isBool();
case VType::INTEGRAL:
return val.isIntegral();
case VType::NUMERIC:
return val.isNumeric();
case VType::STRING:
return val.isString();
case VType::ARRAY:
return val.isArray();
case VType::OBJECT:
return val.isObject();
}
return false;
}
private:
std::string _method_name; // 方法名称
ServiceCallback _callback; // 实际的业务回调函数
std::vector<ParamsDescribe> _params_desc; // 参数字段格式描述
VType _return_type; // 结果作为返回值类型的描述
};
1.
SDescribeFactory
//调用 建造者模式,便于服务生产
//防止 数据 被修改,实现了 解耦合
class SDescribeFactory {
public:
void setMethodName(const std::string &name) {
_method_name = name;
}
void setReturnType(VType vtype) {
_return_type = vtype;
}
void setParamsDesc(const std::string &pname, VType vtype) {
_params_desc.push_back(ServiceDescribe::ParamsDescribe(pname, vtype));
}
void setCallback(const ServiceDescribe::ServiceCallback &cb) {
_callback = cb;
}
ServiceDescribe::ptr build() {
return std::make_shared<ServiceDescribe>(std::move(_method_name),
std::move(_params_desc), _return_type, std::move(_callback));
}
private:
std::string _method_name;
ServiceDescribe::ServiceCallback _callback; // 实际的业务回调函数
std::vector<ServiceDescribe::ParamsDescribe> _params_desc; // 参数字段格式描述
VType _return_type; //结果作为返回值类型的描述
};
⭕ Builder模式
详见:设计模式 专栏,之后可能会从语雀 迁移同步到 CSDN
1. 动机
在软件系统中,有时候面临着“一个复杂对象”的创建工作
其通常由各个部分的子对象用一定的算法构成;
由于需求的变化,这个复杂对象的 各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。如何应对这种变化?
如何提供一种“封装机制”来隔离出 “复杂对象的各个部分” 的变化,从而保持系统中的“稳定构建算法”不 随着需求改变而改变?
2. 模式定义
- 定义:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。
- 结构:未详细描述。
3. 要点总结
- Builder模式主要用于“分步骤构建一个复杂的对象”。这其中 “分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
- 变化点在哪里,封装哪里 —— Builder模式主要在于 应对“复杂对象各个部分”的频繁需求变动。
- 缺点:难以应对“分步骤构建算法”的需求变动。
- 在Builder模式中,要注意不同语言中构造器内调用虚函数的差别(C++ vs C#)。
- 与工厂模式相比:
-
- 注重点不同:Builder模式更注重于方法的调用过程;工厂模式注重于创建产品,不关心方法调用的顺序。
- 创建对象力度不同:建造者模式 可以创建复杂的产品,由各种复杂的部件组成;工厂模式创建出来的都是相同的实例对象。
4. 代码感受
// 抽象基类
class House {};
// 抽象基类
class HouseBuilder
{
public:
House* GetResult()
{
return pHouse;
}
virtual ~HouseBuilder(){}
protected:
House* pHouse;
virtual void BuildPart1() = 0;
virtual void BuildPart2() = 0;
virtual void BuildPart3() = 0;
virtual void BuildPart4() = 0;
virtual void BuildPart5() = 0;
};
class StoneHouse: public House {};
class StoneHouseBuilder: public HouseBuilder
{
protected:
virtual void BuildPart1()
{
//pHouse->Part1 = ...;
}
virtual void BuildPart2(){}
virtual void BuildPart3(){}
virtual void BuildPart4(){}
virtual void BuildPart5(){}
};
class HouseDirector
{
public:
HouseBuilder* pHouseBuilder;
HouseDirector(HouseBuilder* pHouseBuilder)
{
this->pHouseBuilder = pHouseBuilder;
}
House* Construct()
{
pHouseBuilder->BuildPart1();
for (int i = 0; i < 4; i++)
{
pHouseBuilder->BuildPart2();
}
bool flag=pHouseBuilder->BuildPart3();
if(flag)
{
pHouseBuilder->BuildPart4();
}
pHouseBuilder->BuildPart5();
return pHouseBuilder->GetResult();
}
};
ServiceManager
实现 对于<主题method,描述 desc> 的增 删 查 功能
class ServiceManager {
//!<主题method,描述>
public:
using ptr = std::shared_ptr<ServiceManager>;
void insert(const ServiceDescribe::ptr &desc) {
std::unique_lock<std::mutex> lock(_mutex);
_services.insert(std::make_pair(desc->method(), desc));
}
ServiceDescribe::ptr select(const std::string &method_name) {
std::unique_lock<std::mutex> lock(_mutex);
auto it = _services.find(method_name);
if (it == _services.end()) {
return ServiceDescribe::ptr();
}
return it->second;
}
void remove(const std::string &method_name) {
std::unique_lock<std::mutex> lock(_mutex);//加锁
//自动释放锁
_services.erase(method_name);
}
private:
std::mutex _mutex;
std::unordered_map<std::string, ServiceDescribe::ptr> _services;
};
RpcRouter
class RpcRouter {
public:
using ptr = std::shared_ptr<RpcRouter>;
RpcRouter(): _service_manager(std::make_shared<ServiceManager>()){}
//!!!!在Dispatcher前,进行Router检查
void onRpcRequest(const BaseConnection::ptr &conn, RpcRequest::ptr &request){
//1. 查询客户端请求的方法描述--判断当前服务端能否提供对应的服务
auto service = _service_manager->select(request->method());
if (service.get() == nullptr) {
ELOG("%s 服务未找到!", request->method().c_str());
return response(conn, request, Json::Value(), RCode::RCODE_NOT_FOUND_SERVICE);
}
//2. 进行参数校验,确定能否提供服务
if (service->paramCheck(request->params()) == false) {
ELOG("%s 服务参数校验失败!", request->method().c_str());
return response(conn, request, Json::Value(), RCode::RCODE_INVALID_PARAMS);
}
//3. 调用业务回调接口进行业务处理
Json::Value result;
//<int,33>
bool ret = service->call(request->params(), result);
if (ret == false) {
ELOG("%s 服务参数校验失败!", request->method().c_str());
return response(conn, request, Json::Value(), RCode::RCODE_INTERNAL_ERROR);
//内部错误
}
//4. 处理完毕得到结果,组织响应,向客户端发送
return response(conn, request, result, RCode::RCODE_OK);
}
//服务注册接口
void registerMethod(const ServiceDescribe::ptr &service) {
return _service_manager->insert(service);
}
private:
void response(const BaseConnection::ptr &conn,
const RpcRequest::ptr &req,
const Json::Value &res, RCode rcode) {
auto msg = MessageFactory::create<RpcResponse>();
msg->SetId(req->GetId());
msg->SetMType(bitrpc::MType::RSP_RPC);
msg->setRCode(rcode);
msg->setResult(res);
conn->send(msg);
}
private:
ServiceManager::ptr _service_manager;
};
RpcRouter模块:一个枚举四个类
1. 枚举类:
- 枚举出rpc请求参数的类型(布尔,整形,浮点型,字符串,数组,对象)
2. 服务描述类:
- 业务回调函数 --- Add处理回调函数
- 参数信息描述 --- pair<参数字段名称,参数字段类型> {<"num1", int>, <"num2", int>}
- 返回值类型描述 --- int
- 提供参数校验接口 --- 针对请求中的参数,判断是否包含有num1字段,其类型是否是整形
-
- 处理逻辑:收到一个rpc请求后,取出方法名称,参数信息,
-
-
- 通过方法名称Add,找到Add服务的描述对象,先进行参数校验,校验参数中是否具有num1字段,且类型是整形,
- 判断都没问题则调用回调函数进行处理
-
3. 服务管理类:
- 服务端会提供很多方法服务,需要进行良好的管理
-
- std::hash_map<方法名称method,服务描述desc>
- 通过这个hash_map 就可以很容易判断能否提供某个服务
- 判断完 之后 ,再通过dispatcher 判断Callback
4. sum: 对外RpcRouter类:
- 服务注册接口;
- 提供给dispatcher模块的rpc请求处理回调函数( RpcRouter 检查后,再 Dispetcher 调度)