施磊老师rpc(二)

文章目录

protobuf使用(三)

复习

推荐用 bytes 替代 string:减少字符编码开销,提高性能。

message 支持复杂嵌套结构

  • 基本类型(string、int32、bool...);
  • 组合对象(message 嵌套 message);
  • 列表类型repeated
  • 映射表类型map<key_type, value_type>(注意不是 C++ STL 的 map);
  • 枚举类型(enum);
  • RPC 服务描述(service):这是本节新增重点。

注意 :Protobuf 的 map 类型与 C++ STL 中的 map 是不同的,仅为消息结构的语法糖。

"语法糖"是编程语言里的一个术语,意思是:

一种让代码更好写、更好读 的语法形式,本质上没有引入新功能,只是更简洁或直观。

!TIP

ProtobufmapC++ STLmap 的区别:

相同点:

  • 都是键值对的数据结构。
  • 都能用来表示:key -> value 的映射关系。
  • 都可以通过 key 查找对应的 value

不同点(关键):

项目 Protobuf 中的 map C++ STL 中的 map
类型 一种消息字段的语法糖 一个真正的容器类
底层实现 被编译成一个隐藏的 repeated message 基于平衡二叉树或哈希表
功能限制 不能做复杂操作(如排序、自定义比较函数) 支持全功能,如插入、排序、自定义比较等
使用目的 用来在消息中表达键值对结构 用来在内存中管理数据关系
支持语言广度 统一跨语言序列化格式 仅限于 C++ 标准库

Protobuf 与 RPC 的关系

  • Protobuf 本身不具备 RPC 能力 ,仅提供:
    • 参数的序列化
    • 响应的反序列化
  • RPC 框架使用 Protobuf 是为了结构化传输参数和结果

为什么要用 ProtoBuf

  • ProtoBuf 用于结构化数据的序列化与反序列化,提升网络传输效率;
  • 分布式通信(RPC)框架 中,ProtoBuf 可以用于描述调用的参数和返回值
  • 本质上:ProtoBuf 不负责通信,只负责消息格式的定义

service定义rpc

service 表示定义一个服务类;

rpc 方法名 (参数类型) returns (返回类型)

对应业务逻辑中的远程函数。

protobuf 复制代码
// 在protobuf 定义描述 rpc类型
service UserServiceRpc {
    rpc Login(LoginRequest) returns (LoginResponse);
    rpc GetFriendList(GetFriendListsRequest) returns (GetFriendListsResponse);
  }

必须启用 service 的生成:

只写 service 类, 进行protoc 编译, 是不会生成 service类的

c++ 复制代码
option cc_generic_services = true;
  • 默认不会生成 service 相关的类(包括 stub 类);
  • 必须加上该 option 才会生成带有 rpc 方法声明的类。

开启并编译后, 将会生成 以下

c++ 复制代码
class UserServiceRpc_Stub;

class UserServiceRpc 

protobuf代码结构--重点

Login 为例,会生成如下内容:

!TIP

新版messgae结构加了一个小东西---final

c++ 复制代码
class LoginRequest final :

  public ::PROTOBUF_NAMESPACE_ID::Message{}

在 C++ 中,final 是一个限定符(specifier),它的作用是:

禁止类被继承,或禁止虚函数被重写(override)

1. Message 类型:
c++ 复制代码
class LoginRequest : public google::protobuf::Message {
  // 提供 GetName(), SetName(), GetPwd(), SetPwd()
};


class LoginResponse : public google::protobuf::Message {
  // 同样提供成员变量的读写方法
};
2. Service 类型(抽象类):
c++ 复制代码
class UserServiceRpc_Stub;

class UserServiceRpc : public ::PROTOBUF_NAMESPACE_ID::Service
{
public:
	virtual void Login(::PROTOBUF_NAMESPACE_ID::RpcController *controller,
	const ::hzhpro::LoginRequest *request,
	::hzhpro::LoginResponse *response,
	::google::protobuf::Closure *done);
	
	
	virtual void GetFriendList(::PROTOBUF_NAMESPACE_ID::RpcController *controller,
	const ::hzhpro::GetFriendListsRequest *request,
	::hzhpro::GetFriendListsResponse *response,
	::google::protobuf::Closure *done);
	
	
	const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor *GetDescriptor();
    .....
}
3. Stub 类型(代理类):
c++ 复制代码
class UserServiceRpc_Stub : public UserServiceRpc {
  // 提供 Login 方法的远程代理实现
};
  • Stub 就是客户端代理类,封装了底层网络通信细节;
  • 实现原理类似于我们 RPC 框架图中讲的 stub -> channel -> codec -> network

理解service类

先简单了解

!IMPORTANT

这在 以后 写 rpc 项目 很有用, 是重点

尤其是 service类

1. getDescriptor() 方法的作用
  • 返回值 :一个指向 ServiceDescriptor 的指针。
  • 含义ServiceDescriptor 是对一个服务的描述对象。
  • 描述内容
    • 服务名(如 UserService
    • 服务中包含的 RPC 方法名(如 Login, GetFriendList
2. 为什么需要 ServiceDescriptor
  • 在进行 RPC 调用 时,需要知道:
    • 调用的是哪个服务(哪个类的对象)
    • 调用的是哪个方法(方法名)
  • ServiceDescriptor 就提供了这种"元信息",可以让框架动态识别服务与方法,进行 RPC 调度。
3. service 类是如何生成的
  • protoc 工具根据 .proto 文件生成。
  • 默认生成的类不是 UserServiceRpc,而是 UserService
  • 这个生成类用于服务端(provider / callee )(配合理论图 )实现接口,是 RPC 服务的提供者端
4. protobuf 中的两个核心抽象
  • message:表示消息结构(数据)
  • service:表示服务接口(方法)
5. RPC 方法结构统一
  • 每个 RPC 方法最终都以统一形式出现(一般4个参数)
  • 如:请求参数、响应参数都封装在对应的 xxxRequestxxxResponse 中。

protobuf使用(四)

Protobuf 生成的两个核心类

  1. UserServiceRpc 类

    • RPC 服务提供者 端使用的类。
    • 继承自 ::google::protobuf::Service
    • 包含 RPC 方法(如 LoginGetFriendList)的 虚函数声明 ,供服务端进行 具体业务实现
    • 构造函数无参,是服务端主动注册服务对象时使用的。
  2. UserServiceRpc_Stub 类(桩类)

    • RPC 服务消费者(调用者) 使用的代理类。
    • 继承自 UserServiceRpc
    • 实现了基类中的虚函数(比如 Login),但实现方式不是执行业务 ,而是调用底层 RpcChannel::CallMethod
    • 构造函数必须接收一个 RpcChannel\* 指针,这是关键点。
    c++ 复制代码
    // .h
    class UserServiceRpc_Stub : public UserServiceRpc
      {
      public:
        UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel *channel);
        UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel *channel,
                            ::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership);
        void Login(::PROTOBUF_NAMESPACE_ID::RpcController *controller,
                   const ::hzhpro::LoginRequest *request,
                   ::hzhpro::LoginResponse *response,
                   ::google::protobuf::Closure *done);
        void GetFriendList(::PROTOBUF_NAMESPACE_ID::RpcController *controller,
                           const ::hzhpro::GetFriendListsRequest *request,
                           ::hzhpro::GetFriendListsResponse *response,
                           ::google::protobuf::Closure *done);
    	....
        
      private:
        ::PROTOBUF_NAMESPACE_ID::RpcChannel *channel_;
        ....
    }
    c++ 复制代码
    // .cc  具体实现
    
    void UserServiceRpc_Stub::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
                                  const ::hzhpro::LoginRequest* request,
                                  ::hzhpro::LoginResponse* response,
                                  ::google::protobuf::Closure* done) {
      channel_->CallMethod(descriptor()->method(0),
                           controller, request, response, done);
    }
    void UserServiceRpc_Stub::GetFriendList(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
                                  const ::hzhpro::GetFriendListsRequest* request,
                                  ::hzhpro::GetFriendListsResponse* response,
                                  ::google::protobuf::Closure* done) {
      channel_->CallMethod(descriptor()->method(1),
                           controller, request, response, done);
    }

    !IMPORTANT

    具体实现 的 函数------> 没有实现业务, 而是调用 channel

    channel 又是 ::PROTOBUF_NAMESPACE_ID::RpcChannel * 类型

c++ 复制代码
class PROTOBUF_EXPORT RpcChannel {
 public:
 	....
  virtual void CallMethod(const MethodDescriptor* method,
                          RpcController* controller, const Message* request,
                          Message* response, Closure* done) = 0;
	....
 private:
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};

RpcChannel 的作用与机制

  • RpcChannel 是一个 抽象类 ,定义了纯虚函数 CallMethod

  • 所有 RPC 方法最终都通过 CallMethod 转发出去,实现 序列化、网络发送、响应接收与反序列化

  • 桩类方法中调用的是:

    复制代码
    _channel->CallMethod(method, controller, request, response, done);

自定义 RpcChannel 实现

为了真正执行 RPC 远程调用,需要开发者自己实现一个类

c++ 复制代码
class MyRpcChannel : public RpcChannel {
public:
    void CallMethod(...) override {
        // 1. 对 request 序列化
        // 2. 构造 RPC 请求包
        // 3. 发送网络请求
        // 4. 接收响应并反序列化到 response
    }
};

该类实现了 CallMethod 的逻辑,真正完成 RPC 通信。

调用流程总结

  1. 客户端构造一个桩类对象:

    复制代码
    UserServiceRpc_Stub stub(new MyRpcChannel());
  2. 调用 RPC 方法:

    复制代码
    stub.Login(controller, &request, &response, nullptr);
  3. 实际执行:

    • 并不会执行业务逻辑。
    • 而是调用 MyRpcChannel::CallMethod,由你自定义实现网络通信和序列化逻辑。

理解的重点建议

  • 理解 Stub 类 ≠ 业务逻辑 ,它是一个 代理类 ,所有调用都重定向到 RpcChannel
  • 理解 Protobuf 提供的 类结构层次:Service → Stub → Channel
  • 理解 继承 + 多态 + 虚函数重写 在其中的作用机制。
  • 最终所有复杂的远程通信都被隐藏在了 RpcChannel::CallMethod 背后。

!TIP

此时 再去 看 rpc那个 理论图, 会很清晰, 会明白 什么是代理类(stub)

!IMPORTANT

最好有项目实践, 不然很难理解

本地服务发布rpc(一)

项目目标

  • 不是做业务系统,而是做一个RPC 框架
  • 框架核心职责:让上层应用的本地方法变成 RPC 远程可调用方法
  • 框架不做具体业务,服务于业务
  • 但是 不能脱离 业务

开发方式

  • 业务驱动框架设计:先引出具体业务需求,再倒推需要框架提供哪些功能
  • 以 UserService(登录 + 获取好友列表)为示例贯穿开发过程

项目结构说明

复制代码
项目根目录/
├── bin/            # 存放最终生成的可执行文件(服务提供者/消费者)
├── build/          # CMake 构建过程中的中间文件
├── example/        # 业务代码:使用框架的服务端/客户端示例
│   ├── callee/     # 服务提供者:提供 RPC 方法(如 login)
│   └── caller/     # 服务消费者:远程调用 RPC 方法
├── lib/            # 框架本身编译后的动态库
├── src/            # 框架核心代码
├── test/           # 示例代码(如 protobuf、zookeeper 测试)
├── CMakeLists.txt  # 顶级构建脚本
├── README.md       # 项目说明
└── autogen.ac      # 自动化构建脚本

CMakeLists.txt

c++ 复制代码
# 设置cmake最低版本 和 项目名称
cmake_minimum_required(VERSION 3.0)
project(mprpc-hzh)

# 设置项目可执行文件输出的路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 设置项目库文件输出的路径
set(LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
# 设置编译头文件搜索路径 -I
include_directories(${PROJECT_SOURCE_DIR}/src)
# 设置项目库文件搜索路径 -L
link_directories(${PROJECT_SOURCE_DIR}/lib)

add_subdirectory(src) 
# 放 使用 rpc服务的 使用者 caller和 消费者callee
add_subdirectory(example)   

业务示例 - UserService

把一个原本本地类的方法 UserService::Login(name, pwd) 变成一个可以远程调用的 RPC 方法

c++ 复制代码
#include <iostream>
#include <string>


/*
UserService 原来是一个 本地服务, 提供了 两个进程内的本地方法, Login和GetFriendLists
*/
class UserService 
{
    public:
    bool  Login(const std::string username, const std::string password) 
    {
        std::cout << "doing local service : Login" << std::endl;
        std::cout<<"username: " << username << " password: " << password << std::endl;
    }
};

int main(int argc, char* argv[])
{

    
    return 0;
}

RPC 方法定义(Protobuf)

example/user.proto

c++ 复制代码
syntax = "proto3";
package example;

option cc_generic_services = true; // 生成服务类和rpc方法描述, 默认不生成

message ResultCode
{
    int32 errcode = 1;
    bytes errmsg = 2;
}


// 定义登录请求消息类型 name pwd
message LoginRequest
{
    bytes name = 1;
    bytes pwd = 2;
}

// 定义登录响应消息类型
message LoginResponse
{
    ResultCode result = 1;
    bool success = 3;
}

service UserServiceRpc {
    rpc Login(LoginRequest) returns (LoginResponse);
}

编译---->在cmake 加 头文件搜索路径

example/CMakeLists.txt

c++ 复制代码
add_subdirectory(callee)

example/callee/CMakeLists.txt

c++ 复制代码
set(SRC_LIST userservice.cc ../user.pb.cc)

add_executable(provider ${SRC_LIST})

修改业务代码-加rpc

c++ 复制代码
#include <iostream>
#include <string>
#include "user.pb.h"

/*
UserService 原来是一个 本地服务, 提供了 两个进程内的本地方法, Login和GetFriendLists
*/
class UserService : public UserServiceRpc
{
    public:
    bool  Login(const std::string username, const std::string password) 
    {
        std::cout << "doing local service : Login" << std::endl;
        std::cout<<"username: " << username << " password: " << password << std::endl;
    }
};

int main(int argc, char* argv[])
{


    return 0;
}

错误-1:

add_subdirectory(src) , src里面必须有 cmake配置文件, 哪怕是空的

add_subdirectory() 会递归调用指定目录下的 CMakeLists.txt,如果这个文件不存在,CMake 配置过程就会报错,提示找不到构建脚本。

错误-2:

!WARNING

proto 相关的 命名空间!!!

c++ 复制代码
#include <iostream>
#include <string>
#include "user.pb.h"
// using namespace hzhrpc;

/*
UserService 原来是一个 本地服务, 提供了 两个进程内的本地方法, Login和GetFriendLists
*/
class UserService : public hzhrpc::UserServiceRpc
{
    public:
    bool  Login(const std::string username, const std::string password) 
    {
        std::cout << "doing local service : Login" << std::endl;
        std::cout<<"username: " << username << " password: " << password << std::endl;
        return true;
    }
};

int main(int argc, char* argv[])
{


    return 0;
}

重写基类的虚函数(部分)

c++ 复制代码
// 重写  UserServiceRpc 里的 Login方法
    void Login(::google::protobuf::RpcController* controller,   // 这里命名空间不写hzhrpc::, 直接写google::protobuf   特别注意
        const ::hzhrpc::LoginRequest* request,
        ::hzhrpc::LoginResponse* response,
        ::google::protobuf::Closure* done);

各参数理解

  1. ::google::protobuf::RpcController* controller
  • 作用:用于控制整个 RPC 调用过程,包括处理错误、取消、设置/获取错误信息等。
  • 说明:客户端和服务器都可以使用它来反映 RPC 状态(如失败信息)。如果你需要反馈 RPC 错误,比如请求格式不对、认证失败,可以通过它设置错误。

  1. const ::hzhrpc::LoginRequest* request
  • 作用 :客户端发来的请求数据,封装成了 LoginRequest 对象。
  • 说明:这是 protobuf 自动生成的请求结构体,包含用户登录所需的信息,比如用户名、密码等。服务器通过读取它来处理登录逻辑。

  1. ::hzhrpc::LoginResponse* response
  • 作用:用于将服务端处理结果返回给客户端。
  • 说明:服务器会填充这个响应结构体,比如设置登录是否成功、用户ID、错误信息等,然后通过 RPC 框架发回客户端。

  1. ::google::protobuf::Closure* done
  • 作用:这是一个回调函数指针,表示 RPC 方法处理完成时需要执行的操作。
  • 说明
    • 服务端逻辑处理完成后,必须调用 done->Run() 来触发后续操作,如将 response 发回客户端。
    • 如果不调用 done->Run(),客户端将一直等待响应,造成阻塞或超时。

补充说明:

命名空间为什么用 ::google::protobuf:: 而不是 hzhrpc::

这是因为:

  • RpcControllerClosureGoogle Protobuf 官方定义的基类接口 ,属于 google::protobuf 命名空间;
  • LoginRequestLoginResponse 是你自定义的 protobuf 服务结构体,位于 hzhrpc 命名空间。

本地服务发布rpc(二)

背景说明

  • 我们希望把本地的 login 函数变成远程可以调用的 RPC 服务
  • 整个过程基于 Protobuf 的序列化能力 + 自定义 RPC 框架的流程控制实现。
  • 客户端通过网络发送了 LoginRequest 请求,服务器框架接收到后会自动触发业务方实现的 Login 方法。

两个rpc类->proto后

UserServiceRpc:服务端实现继承这个类,重写 RPC 方法。

UserServiceRpc_Stub:客户端通过它调用 RPC 方法。

再次回顾四个参数

参数 作用
controller 控制器对象,可用于传递额外的控制信息(如超时、取消、错误处理等,暂不关注)
request 已经被框架反序列化好的请求对象(从客户端发来的 LoginRequest)
response 框架提前创建的响应对象,业务方填写响应结果即可
done 回调对象,框架用于发送响应的关键机制:调用 done->Run() 触发序列化 + 网络发送

把本地服务变成远程服务流程-总结

1. 定义 proto 文件
  • 写明:
    • rpc方法名(如 Login
    • 请求参数类型(LoginRequest
    • 响应参数类型(LoginResponse
  • 作用:这是客户端与服务端的通信约定,客户端只需按这个约定调用即可。

举例:

c++ 复制代码
service UserServiceRpc {
    rpc Login(LoginRequest) returns (LoginResponse);
}
2. 从生成的服务类中继承
  • 编译 proto 后会生成一个 UserServiceRpc 类(或类似)
  • 在你的业务代码中继承这个类,并重写其中的方法 (如 Login 方法)
  • 也可以使用 lambda 表达式 来传递函数对象实现方法逻辑
3. 业务处理逻辑
  • Login 方法会由框架自动调用
    • 框架接收到客户端请求后,会通过方法名匹配调用相应函数
    • 请求参数会通过函数参数传入
    • 在函数内:
      • 解析请求数据
      • 进行本地业务处理(比如验证用户名密码)
      • 构造响应对象 LoginResponse
      • 填写响应内容
4. 发送响应数据-run实现
  • 响应对象必须通过 序列化 变成字节流再通过网络发送
  • 序列化方式:SerializeToArray()SerializeToString()(由 protobuf 提供)
  • 最后一步是调用 done->Run(),由框架统一处理序列化与网络发送

callee代码整体

注意一下, Login 函数有两个, 名字没起好, 要明白 各个Login的含义

c++ 复制代码
#include <iostream>
#include <string>
#include "user.pb.h"
// using namespace hzhrpc;

/*
UserService 原来是一个 本地服务, 提供了 两个进程内的本地方法, Login和GetFriendLists
*/
class UserService : public hzhrpc::UserServiceRpc  // 这个rpc类使用在 rpc服务发布端(rpc服务提供者) 
{
    public:
    bool  Login(const std::string username, const std::string password) 
    {
        std::cout << "doing local service : Login" << std::endl;
        std::cout<<"username: " << username << " password: " << password << std::endl;
        return true;
    }


    // 重写  UserServiceRpc 里的 Login方法
    // 1. caller ====> 发起请请求 Login(request)  ==> 通过 muduo  ==> callee
    // 2. callee ====> 收到请求 Login(request)  ==> 交到下面重写的 Login方法
    void Login(::google::protobuf::RpcController* controller,   // 这里命名空间不写hzhrpc::, 直接写google::protobuf   特别注意
        const ::hzhrpc::LoginRequest* request,
        ::hzhrpc::LoginResponse* response,
        ::google::protobuf::Closure* done)
        {

            // 1. 框架给业务上报了请求参数LoginRequest, 应用 获取相应数据做本地业务
            std::string username = request->name();
            std::string password = request->pwd();


            // 2. 做本地业务
            bool login_result = Login(username, password); 

            // 3. 业务处理完毕, 设置响应参数
            // 包括 设置返回码, 提示信息, 以及业务处理结果
            hzhrpc::ResultCode* code = response->mutable_result();  // 可修改的
            code->set_errcode(0);  // 0表示成功
            code->set_errmsg("success");  // 成功的提示信息
            response->set_success(login_result);  // 设置成功与否的标志

            // 4. 执行回调函数
            // 通过查看 Closure 类, run是个 纯虚函数, 即回调函数, 需要重写
            // 这里的done是个回调函数, 由框架提供, 业务上不需要关心
            // 任务: 执行响应对象数据的序列化和网络发送 (都是由 框架来完成的)
           done->Run();  

        }
};

int main(int argc, char* argv[])
{


    return 0;
}

阶段大总结-实现流程

目标

将业务层的本地方法(如用户登录)发布成 RPC 服务,供远程客户端调用。

实现步骤

1. 编写 .proto 文件
  • 定义 RPC 方法签名(例如 rpc Login(...) returns (...)
  • 定义请求消息类型(如 LoginRequest
  • 定义响应消息类型(如 LoginResponse
  • 注意:这是与远程调用方的通信协议,必须明确方法名、入参、出参
2. 使用 protoc 生成代码
  • 使用 protoc 编译 .proto 文件,生成 C++ 代码
  • 生成两个关键类:
    • UserServiceRpc:服务提供方继承并实现的类
    • UserServiceRpc_Stub:服务调用方使用的类(Stub)
  • 当前仅关注服务端:我们要实现 并 发布服务
3. UserServiceRpc 继承并重写方法
  • 在业务代码中继承该类
  • 重写 .proto 中定义的 RPC 方法(如 Login
    • 参数说明
      • 第一个参数, 不是很重要
      • const LoginRequest* request:请求参数对象
      • LoginResponse* response:用于填写响应内容
      • Closure* done:框架提供的回调,调用后会执行序列化并发送响应(通过 done->Run()
4. 框架负责调用逻辑
  • 框架在收到远程请求后:
    • 根据方法名匹配对应函数
    • 填充参数并调用你实现的 Login 方法
    • 最终执行 done->Run(),序列化响应并通过网络发出

后续将进行如何发布

mprpc框架基础类设计

目标

将本地 UserService::Login() 方法发布成一个远程可调用的 RPC 方法,形成一个"可独立部署、可多实例扩展"的微服务节点。

初始化框架

  • 创建类:MprpcApplication

  • 提供接口: ---- 本身是个服务器, 传参 不写死, ip/port

复制代码
  void Init(int argc, char **argv);
  • 功能:读取配置文件(如ip/port),初始化日志模块等

  • 推荐设计:单例模式,方便在全局共享框架信息

  • 传参 读 配置文件----[框架初始化函数-文件读取](# 框架初始化函数-文件读取)

创建 服务发布 对象

  • 类名:RpcProvider
  • 核心功能:
    • NotifyService(google::protobuf::Service* service)
      注册 RPC 服务对象(不能依赖具体业务类,只接受抽象基类)
    • Run()
      启动 RPC 网络服务,等待远程调用

业务代码中使用框架发布服务

从业务入手, 看框架

直接看框架 是学不明白的

c++ 复制代码
int main(int argc, char* argv[])
{
    // 1. 初始化框架
    MprpcApplication::Init(argc, argv);

    // 2. 创建 provider 并注册服务
    RpcProvider provider;
    provider.NotifyService(new UserService());  // 注册并发布服务, 这里的UserService是个本地服务, 不是rpc服务

    // 3. 启动rpc服务发布 节点
    provider.Run();  // 启动服务, 这里的Run是阻塞状态, 一直监听rpc调用请求

    return 0;
}

为什么这么设计?

  • 解耦服务:login 是一个独立服务模块,改动后只需重新部署 login,不影响其他功能。
  • 易于扩展:未来可以部署多个 login 节点,支持分布式部署、负载均衡。
  • 易用性强:框架使用简单,封装复杂逻辑,让用户只需关注服务发布即可。
  • 面向抽象设计 :框架接受 protobuf 的基类 Service*,支持任意 rpc 服务,而不是绑定某个业务类。

框架目录结构

c++ 复制代码
src/
├── include/
│   ├── mprpcapplication.h    // 初始化类头文件
│   └── rpcprovider.h         // 服务发布类头文件
├── mprpcapplication.cc
├── rpcprovider.cc

CMake 配置中记得加上:

复制代码
include_directories(${PROJECT_SOURCE_DIR}/src/include)

框架类为什么设计成单例?

1. 只需要初始化一次
  • 配置信息(如ip、端口、日志级别)只在程序启动时读取一次,之后整个程序生命周期内共享使用。
  • 不需要每次调用服务都重复读取配置,节省资源、避免重复初始化。
2. 全局访问方便
  • 一旦初始化完成,后续任意模块都可以通过 MprpcApplication::GetInstance() 获取到配置信息。
  • 避免传递多个参数或对象,提高代码整洁度与可维护性。
3. 确保状态一致
  • 使用单例可以避免多个实例产生不同的状态副本(如多个日志配置、多个端口号),保证全局行为一致。
    网络服务模块可以直接通过单例获取配置,如绑定地址、端口。

日志系统可以读取框架统一配置,决定是否输出调试信息。

RPC 调用封装中可以依赖框架统一的超时时间、协议设置等。

框架类.h-code

回顾单例 设计模式!!

c++ 复制代码
#pragma once

// mprpc框架的基础类, 负责框架的初始化工作
class MprpcApplication {
    public:
        static void Init(int argc, char **argv);
        static MprpcApplication& GetInstance();
       
    
    private:
        MprpcApplication() {}                         // 禁止外部构造
        MprpcApplication(const MprpcApplication&) = delete; // 禁止拷贝构造
        MprpcApplication& operator=(const MprpcApplication&) = delete; // 禁止赋值构造
    
    };
    

RpcProvider:RPC 服务发布者

!TIP

由于 这是一个 服务类, 就会被许多人 请求这个 rpc调用

因此, 必须是 高并发的 , 所以要使用 muduo 网络库

!IMPORTANT

不要写死 框架类!!
RpcProvider 中直接写 UserService* 这样的指针,是严重不合理的。

因为:

  • 框架的本质是通用性 :它应该支持任意业务服务的注册,而不仅仅是 UserService
  • 如果在框架代码里依赖了业务类,就会导致框架无法复用,一旦业务变了,框架也得改 ------ 完全违背"高内聚、低耦合"的设计原则。
框架应该是通用的服务容器
  • 例如 RpcProvider 提供一个接口叫 NotifyService(service)
  • 传进来的 service,可能是 UserServiceFriendServiceOrderService 等等。
  • 所以框架必须设计成能够接收任意 service 的注册,而不是依赖于具体类。
    通过 查看 pb.h

UserSeviceRpc 源于

c++ 复制代码
class UserServiceRpc : public ::PROTOBUF_NAMESPACE_ID::Service{}
c++ 复制代码
Service--->google::protobuf::Service

设计理念总结

  • 从"怎么用"出发设计框架类结构
  • 强调解耦易用性扩展性
  • 框架代码避免依赖业务代码,支持任意服务注册

服务发布者类.h-code

c++ 复制代码
#pragma one
#include "google/protobuf/service.h" // 这个头文件里有 RpcController, Closure, Service

// 框架提供的专门服务发布 rpc 服务的类对象
class RpcProvider {
    public:

    // 这里是框架提供给外部使用的, 可以发布rpc方法的函数接口
        void NotifyService(google::protobuf::Service *server); // 注册服务  
        // 传参那边是 new UserService() 这个对象, 这里是个指针, 传递给框架, 不能用引用

        // 启动rpc服务发布 节点, 开始提供rpc远程网络调用服务
        void Run(); 
    };
    
相关推荐
Shanxun Liao10 分钟前
DELL EMC Power Edge:如何使用 System Setup 菜单在 RAID 控制器中导入外部配置
运维·服务器·数据库
ephemerals__12 分钟前
【Linux】命令行参数与环境变量
linux·运维·服务器
指剑27 分钟前
通过AWS Console连接服务器,简化运维过程
运维·服务器·aws
CodeWithMe1 小时前
【中间件】brpc_基础_remote_task_queue
c语言·c++·中间件·rpc
lswzw1 小时前
rsync命令详解与实用案例
linux·服务器·网络
PWRJOY1 小时前
Ubuntu环境下使用uWSGI服务器【以flask应用部署为例】
服务器·ubuntu·flask
ARM2NCWU3 小时前
边缘计算服务器
服务器·人工智能·边缘计算
matlabgoodboy4 小时前
Haskell Drracket OCaml Prolog 逻辑式编程函数式编程代写代做
vscode·ci/cd·docker
Zhuai-行淮4 小时前
施磊老师rpc(四)
网络协议·rpc