[实现Rpc] 服务端 | RpcRouter实现 | Builder模式

news/2025/2/23 6:23:27

目录

项目服务端独用类的实现

1. RpcRouter类的实现

ServiceDescribe

SDescribeFactory

⭕ Builder模式

1. 动机

2. 模式定义

3. 要点总结

4. 代码感受

ServiceManager

RpcRouter

4. 代码感受

ServiceManager

RpcRouter


前文我们就将 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 &params)
            {
                // 对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 &params, 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 调度)

http://www.niftyadmin.cn/n/5863107.html

相关文章

MySQL 架构

目录 1. MySQL 架构概览 (1) 客户端/服务器架构 (2) 存储引擎架构 2. 主要组件 (1) 客户端工具 (2) MySQL 服务器 (3) 存储引擎 3. MySQL 架构图 4. MySQL 架构的特点 5. MySQL 的高级架构 (1) 主从复制&#xff08;Master-Slave Replication&#xff09; (2) 主主…

透彻理解:方差、协方差、相关系数、协方差矩阵及其应用

最近看了几篇跨领域特征对齐方面的经典文献&#xff0c;学者们搞了很多花样&#xff0c;如有的提出一阶统计特征对齐&#xff0c;有的提出二阶统计特征对齐&#xff0c;有的学者提出高阶统计特征对齐。 通俗而言&#xff0c;就是在统计特征层面对跨域特征进行对齐&#xff0c;…

.NET MVC实现电影票管理

.NET MVC&#xff08;Model-View-Controller&#xff09;是微软推出的基于 Model-View-Controller 设计模式的 Web 应用框架&#xff0c;属于 ASP.NET Core 的重要组成部分。其核心目标是通过清晰的分层架构实现 高内聚、低耦合 的开发模式&#xff0c;适用于构建可扩展的企业级…

【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)

必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 对于本节我们要提前掌握前一节课堆的相关实现才能学好本次的知识&#xff0c;一定要多画图多敲代码看看实现的效果是啥&#xff08;Crazy&#xff01;&#xff09;开始吧&#xff01; …

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…

C++面试笔记(持续更新...)

1.C的特性 封装&#xff1a;将数据和具体实现在类中隐藏&#xff0c;对外只留出接口方便调用。 继承&#xff1a;子类继承父类的方法和全部数据&#xff0c;提高软件按复用率 多态&#xff1a;自继承的条件下&#xff0c;继承自同一父类的类的同一的方法对同一个事物具有不同的…

【LeetCode】239. 滑动窗口的最大值

239. 滑动窗口的最大值 单调队列的模板题&#xff0c;但是我一直记不住&#x1f622;。 题目描述如下&#xff1a; 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右…

使用ArcGIS Pro自动矢量化水系

在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;自动矢量化是一项至关重要的技术&#xff0c;它能够将栅格图像中的要素转换为矢量数据&#xff0c;从而方便后续的分析和处理。本文将详细介绍如何使用ArcGIS Pro自动矢量化水系&#xff0c;适用于那些颜色相对统一、…