ToB企服应用市场:ToB评测及商务社交产业平台

标题: [实现Rpc] 客户端划分 | 框架设计 | common类的实现 [打印本页]

作者: 梦应逍遥    时间: 2025-2-17 11:36
标题: [实现Rpc] 客户端划分 | 框架设计 | common类的实现
目录
3. 客户端模块划分
3.1 Network模块
3.2 Protocol模块
3.3 Dispatcher模块
3.4 Requestor模块
3.5 RpcCaller模块
3.6 Publish-Subscribe模块
3.7 Registry-Discovery模块
3.8 Client模块
4. 框架设计
4.1 抽象层
4.2 具象层
4.3 业务层
⭕4.4 团体设计框架
5. common 类的实现
5.1 常用的零碎功能接口类实现
5.1.1 简单日记宏实现
5.1.2 Json序列化/反序列化的封装
5.1.3 UUID的天生
5.2 fields 消息类型定义
5.2.1 请求字段宏的定义:
5.2.2 消息类型定义:
5.2.3 相应码类型定义:
5.2.4 RPC请求类型定义:
5.2.5 主题操作类型定义:
5.2.6 服务操作类型定义:


前文:[实现Rpc] 项目设计 | 服务端模块划分 | rpc | topic | server
3. 客户端模块划分

在客户端的模块划分中,基于以上明白的功能,可以划分出这么级个模块:
3.1 Network模块

网络通讯基于muduo库实现网络通讯客户端。
3.2 Protocol模块

应用层通讯协议处理,与服务端保持⼀致。
3.3 Dispatcher模块

IO数据分发处理,逻辑与服务端⼀致。

3.4 Requestor模块

Requestor模块存在的意义:针对客⼾端的每⼀条请求进⾏管理,以便于对请求对应的相应做出合适的操作。

针对以上问题,我们则创建出当前的请求管理模块来解决:

针对这个思想,我们再进⼀步,可以将每个请求进⼀步封装描述,添加⼊异步的future控制,或者设置回调函数的方式,在不仅可以壅闭获取相应,也可以实现 异步获取相应以及回调处理相应。



3.5 RpcCaller模块

(1)RpcCaller模块存在的意义:向用户提供举行rpc调⽤的模块。
Rpc服务调用模块,这个模块相对简单,只需要向外提供几个rpc调用的接口,内部实现向服务端发送请求,期待获取结果即可,稍微麻烦⼀些的是Rpc调用我们需要提供多种差异方式的调用:



3.6 Publish-Subscribe模块

(1)Publish-Subscribe模块存在意义:向用户提供发布订阅所需的接口,针对推送过来的消息举行处理。



3.7 Registry-Discovery模块

(1)服务注册和发现模块需要实现的功能会复杂⼀些,因为分为两个角色来完成其功能:



3.8 Client模块

将以上模块举行整合就可以实现各个功能的客户端了。











4. 框架设计

在当前项目标实现中,我们将整个项目标实现划分为三层来举行实现:
4.1 抽象层

在本项目标实现当中,网络通讯部分采用了第三方库Muduo库,以及通讯协议使用了LV格式的通讯协议解决粘包问题,数据正文中采用了Json格式举行序列化和反序列化,而这几方面我们都可能会存在继续优化的可能,甚至在序列化⽅方面不⼀定非要采用Json
因此在设计项目框架的时候,我们对于底层通讯部分干系功能先辈行抽象,形成⼀层抽象层
而上层业务部分根据抽象层来完成功能,这样的好处是在具体的底层功能实现部分,我们可以实现插拔式的模块化更换,以此来进步项目标机动性和扩展性。







4.2 具象层

具象层就是针对抽象的具体实现。
而具体的实现也比力简单,从抽象类派生出具体功能的派生类,然后在内部实现各个接口功能即可。

不过这⼀层中比力特殊的是,我们需要 针对差异的请求,从BaseMessage中派生出差异的请求和相应类型,以便于在针对指定消息处理时,能够更加轻松的获取或设置请求及相应中的各项数据元素。


4.3 业务层

业务层就是基于底层的通讯框架,针对项目中具体的业务功能的实现了,比如Rpc请求的处理,发布订阅请求的处理以及服务注册与发现的处理等等。
(1)Rpc:


(2)发布订阅:


(3)服务注册&发现:



⭕4.4 团体设计框架



项目当中会有server和client共同使用的类,也有单独使用的类,所以我们分别实现对应的功能
5. common 类的实现

5.1 常用的零碎功能接口类实现

5.1.1 简单日记宏实现

日记宏意义:快速定位程序运行逻辑出错的位置。
参考前文:简单日记宏实现(C++)
项目在运行中可能会出现各种问题,出问题不可怕,关键的是要能找到问题,并解决问题。解决问题的方式:

  1. #pragma once
  2. #include <cstdio>
  3. #include <ctime>
  4. #define LDBG 0
  5. #define LINF 1
  6. #define LERR 2
  7. #define LDEFAULT LDBG
  8. #define LOG(level, format, ...)                                                                       \
  9.     {                                                                                                 \
  10.         if (level >= LDEFAULT)                                                                        \
  11.         {                                                                                             \
  12.             time_t t = time(NULL);                                                                    \
  13.             struct tm *lt = localtime(&t);                                                            \
  14.             char time_tmp[32] = {0};                                                                  \
  15.             strftime(time_tmp, 31, "%m-%d %T", lt);                                                   \
  16.             fprintf(stdout, "[%s][%s:%d] " format "\n", time_tmp, __FILE__, __LINE__, ##__VA_ARGS__); \
  17.         }                                                                                             \
  18.     }
  19. #define DLOG(format, ...) LOG(LDBG, format, ##__VA_ARGS__);
  20. #define ILOG(format, ...) LOG(LINF, format, ##__VA_ARGS__);
  21. #define ELOG(format, ...) LOG(LERR, format, ##__VA_ARGS__);
复制代码
5.1.2 Json序列化/反序列化的封装

  1. #include <iostream>
  2. #include <sstream>
  3. #include <string>
  4. #include <memory>
  5. #include <jsoncpp/json/json.h>
  6. class JSON
  7. {
  8. public:
  9.     static bool serialize(const Json::Value &val, std::string &body)
  10.     {
  11.         std::stringstream ss;
  12.         // 先实例化一个工厂类对象
  13.         Json::StreamWriterBuilder swb;
  14.         // 通过工厂类对象来生产派生类对象
  15.         std::unique_ptr<Json::StreamWriter> w(swb.newStreamWriter());
  16.         bool ret = w->write(val, &ss);
  17.         if (ret != 0)
  18.         {
  19.             ELOG("json serialize failed!");
  20.             return false;
  21.         }
  22.         body = ss.str();
  23.         return true;
  24.     }
  25.     static bool deserialize(const std::string &body, Json::Value &val)
  26.     {
  27.         Json::CharReaderBuilder crb;
  28.         std::unique_ptr<Json::CharReader> r(crb.newCharReader());
  29.         std::string errs;
  30.         bool ret = r->parse(body.c_str(), body.c_str() + body.size(), &val, &errs);
  31.         if (ret == false)
  32.         {
  33.             ELOG("json deserialize failed : %s", errs.c_str());
  34.             return false;
  35.         }
  36.         return true;
  37.     }
  38. };
复制代码

5.1.3 UUID的天生

UUID(Universally Unique Identifier),也叫通用唯⼀辨认码,通常由32位16进制数字字符组成。

  1. #include <iostream>
  2. #include <chrono>
  3. #include <random>
  4. #include <string>
  5. #include <sstream>
  6. #include <atomic>
  7. #include <iomanip>
  8. class UUID
  9. {
  10. public:
  11.     static std::string uuid()
  12.     {
  13.         std::stringstream ss;
  14.         //1. 构造一个机器随机数对象
  15.         std::random_device rd;
  16.         //2. 以机器随机数为种子构造伪随机数对象
  17.         std::mt19937 generator (rd());
  18.         //3. 构造限定数据范围的对象
  19.         std::uniform_int_distribution<int> distribution(0, 255);
  20.         // 4. 生成8个随机数,按照特定格式组织成为16进制数字字符的字符串
  21.         for (int i = 0; i < 8; i++)
  22.         {
  23.             if (i == 4 || i == 6)
  24.             {
  25.                 ss << "-";
  26.             }
  27.             ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
  28.         }
  29.         ss << "-";
  30.         //5. 定义一个8字节序号,逐字节组织成为16进制数字字符的字符串
  31.         static std::atomic<size_t> seq(1);
  32.         size_t cur = seq.fetch_add(1);
  33.         for (int i = 7; i >= 0; i--)
  34.         {
  35.             if (i == 5)
  36.             {
  37.                 ss << "-";
  38.             }
  39.             ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i * 8)) & 0xFF);
  40.         }
  41.         return ss.str();
  42.     }
  43. };
复制代码
定义一个天生 UUID(通用唯一标识符) 的工具类 UUID,其核心功能是通过结合随机数和计数器天生一个类似于尺度 UUID 格式的字符串。
这种方式天生的 UUID 包含了随机性和递增序列,确保了其在大多数环境下的唯一性。

5.2 fields 消息类型定义

5.2.1 请求字段宏的定义:

定义如下:
  1. #define KEY_METHOD "method"
  2. #define KEY_PARAMS "parameters"
  3. #define KEY_TOPIC_KEY "topic_key"
  4. #define KEY_TOPIC_MSG "topic_msg"
  5. #define KEY_OPTYPE "optype"
  6. #define KEY_HOST "host"
  7. #define KEY_HOST_IP "ip"
  8. #define KEY_HOST_PORT "port"
  9. #define KEY_RCODE "rcode"
  10. #define KEY_RESULT "result"
复制代码
  进步代码的可读性:通过给常量字符串定义宏名称,可以使代码更加易读和易于明白。例如,KEY_METHOD 比直接使用 "method" 更直观地表达了该字符串在代码中的作用。
  5.2.2 消息类型定义:

(1)主要功能:

(2)具体定义如下:
  1. enum class MType
  2. {
  3. /*
  4. Request
  5. Response
  6. */
  7. REQ_RPC = 0,
  8. RSP_RPC,
  9. REQ_TOPIC,
  10. RSP_TOPIC,
  11. REQ_SERVICE,
  12. RSP_SERVICE
  13. };
复制代码
5.2.3 相应码类型定义:

(1)主要功能:

(2)具体定义如下:
  1. enum class RCode
  2. {
  3.     RCODE_OK = 0,
  4.     RCODE_PARSE_FAILED,
  5.     RCODE_ERROR_MSGTYPE,
  6.     RCODE_INVALID_MSG,
  7.     RCODE_DISCONNECTED,
  8.     RCODE_INVALID_PARAMS,
  9.     RCODE_NOT_FOUND_SERVICE,
  10.     RCODE_INVALID_OPTYPE,
  11.     RCODE_NOT_FOUND_TOPIC,
  12.     RCODE_INTERNAL_ERROR
  13. };
  14. static std::string errReason(RCode code)
  15. {
  16.     std::unordered_map<RCode, std::string> err_map = {
  17.         {RCode::RCODE_OK, "成功处理!"},
  18.         {RCode::RCODE_PARSE_FAILED, "消息解析失败!"},
  19.         {RCode::RCODE_ERROR_MSGTYPE, "消息类型错误!"},
  20.         {RCode::RCODE_INVALID_MSG, "无效消息"},
  21.         {RCode::RCODE_DISCONNECTED, "连接已断开!"},
  22.         {RCode::RCODE_INVALID_PARAMS, "无效的Rpc参数!"},
  23.         {RCode::RCODE_NOT_FOUND_SERVICE, "没有找到对应的服务!"},
  24.         {RCode::RCODE_INVALID_OPTYPE, "无效的操作类型"},
  25.         {RCode::RCODE_NOT_FOUND_TOPIC, "没有找到对应的主题!"},
  26.         {RCode::RCODE_INTERNAL_ERROR, "内部错误!"}};
  27.     auto iter = err_map.find(code);
  28.     if (iter == err_map.end())
  29.     {
  30.         return "未知错误";
  31.     }
  32.     return iter->second;
  33. }
复制代码
5.2.4 RPC请求类型定义:

(1)主要功能:

(2)具体定义如下:
  1. enum class RType
  2. {
  3.     REQ_ASYNC = 0,
  4.     REQ_CALLBACK
  5. };
复制代码
5.2.5 主题操作类型定义:

(1)主要功能:

(2)具体定义如下:
  1. enum class TopicOptype
  2. {
  3.     TOPIC_CREATE = 0,
  4.     TOPIC_REMOVE,
  5.     TOPIC_SUBSCRIBE,
  6.     TOPIC_CANCEL,
  7.     TOPIC_PUBLISH
  8. };
复制代码
5.2.6 服务操作类型定义:

(1)主要功能:

(2)具体定义如下:
  1. enum class ServiceOptype
  2. {
  3.     SERVICE_REGISTRY = 0,
  4.     SERVICE_DISCOVERY,
  5.     SERVICE_ONLINE,
  6.     SERVICE_OFFLINE,
  7.     SERVICE_UNKNOW
  8. };
复制代码





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4