本篇文章是针对:https://blog.csdn.net/T_Solotov/article/details/124170856中的项目的运行实例:
2、./userservice -i config.ini
//执行文件,需要一个配置文件,配置文件需要将zookeeper服务器运行起来
3、如何将zookeeper服务器运行起来:https://blog.csdn.net/lamfang/article/details/108866448或者https://blog.csdn.net/qq_52595134/article/details/123467180
4、zookeeper服务器需要java环境,安装java环境:https://blog.csdn.net/weixin_44904239/article/details/137240064
5、运行./userservice -i config.ini
运行成功,此时再12345端口上正在运行RPC服务;可以使用客户端连接到该服务;
6、运行./calluserservice -i 1config.ini 也就是rpc客户端连接rpc服务端
我的配置文件config.ini
rpc客户端和服务端都使用的这个配置文件
# RPC 服务端配置
rpcserverip = 128.0.0.1
rpcserverport = 12345
# ZooKeeper 服务端配置
zookeeperip = 127.0.0.1
zookeeperport = 2181
验证:
完整执行流程过程(完整过程看这里嗷)
1、通过CMakefile文件生成库
修改一下每个源文件的包含头文件地址;
建立一个build文件夹,在这个文件夹下,进行cmake …/src/;然后再make;发现报错:/home/kk/workSpace/yyg_rpc_server/src/rpcheader.pb.cc:175:59: error: ‘ReadVarint32’ is not a member of ‘google::protobuf::internal’; did you mean ‘ReadVarint’?
175 | args_size_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
| ^~~~~~~~~~~~
按提示修改报错即可;此时再次进行make继续报错:
/home/kk/workSpace/yyg_rpc_server/src/zookeeperutil.cc:68:16: error: ‘zoo_exists’ was not declared in this scope; did you mean ‘zoo_aexists’?
68 | flag = zoo_exists(m_zhandle, path, 0, nullptr);
| ^~~~~~~~~~
| zoo_aexists
/home/kk/workSpace/yyg_rpc_server/src/zookeeperutil.cc:72:24: error: ‘zoo_create’ was not declared in this scope; did you mean ‘zoo_acreate’?
72 | flag = zoo_create(m_zhandle, path, data, datalen,
| ^~~~~~~~~~
| zoo_acreate
/home/kk/workSpace/yyg_rpc_server/src/zookeeperutil.cc: In member function ‘std::string ZkClient::GetData(const char*)’:
/home/kk/workSpace/yyg_rpc_server/src/zookeeperutil.cc:92:20: error: ‘zoo_get’ was not declared in this scope; did you mean ‘zoo_aget’?
92 | int flag = zoo_get(m_zhandle, path, 0, buffer, &bufferlen, nullptr);
| ^~~~~~~
| zoo_aget
这个错误是因为异步和同步函数的问题,我们使用的函数是同步函数,但是在zookeeper.h的头文件中默认打开的是异步的,所以我们要跳转过去打开同步的函数;解决办法:跳转到zookeeper.h看到是ifdef THREADED控制的同步代码,所以我们要在包含此头文件之前写明ifdef THREADED(也就是在include/zookeeperutil.h中写) 之后再次make;成功,可以看到build文件夹下出现了一个libmprpc.a库
2、编写protoc文件
syntax = "proto3";
package fixbug;
option cc_generic_services = true;
message LoginRequest {
bytes name = 1;
bytes pwd = 2;
}
message LoginResponse {
ResultCode result = 1;
bool success = 2;
}
message RegisterRequest {
uint32 id = 1;
string name = 2;
string pwd = 3;
}
message RegisterResponse {
ResultCode result = 1;
bool success = 2;
}
message ResultCode {
int32 errcode = 1;
string errmsg = 2;
}
service UserServiceRPC {
rpc Login(LoginRequest) returns (LoginResponse);
rpc Register(RegisterRequest) returns (RegisterResponse);
}
编译命令;
protoc --cpp_out=./ user.proto
生成user.pb.cc和user.pb.h文件;
3、编写服务端实现代码和客户端实现代码;
userserver.cc//服务端
/***
文件注释:
文件名: userservice.cc
callee端代码:callee提供caller想要调用的Login函数。
***/
#include <iostream>
#include <string>
#include "user.pb.h"
#include "mprpcapplication.h"
#include "rpcprovider.h"
class UserService : public fixbug::UserServiceRPC // 使用在rpc服务发布端(rpc服务提供者)
{
public:
bool Login(std::string name, std::string pwd)
{
/**** 业务层代码 ****/
std::cout << "doing local service: Login" << std::endl;
std::cout << "name:" << name << " pwd:" << pwd << std::endl;
return false;
}
void Register(::google::protobuf::RpcController* controller,
const ::fixbug::RegisterRequest* request,
::fixbug::RegisterResponse* response,
::google::protobuf::Closure* done)
{
std::cout << "hhh" << std::endl;
}
void Login(::google::protobuf::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done)
{
/**** callee要继承UserServiceRpc并重写它的Login函数 ****/
std::string name = request->name();
std::string pwd = request->pwd();
//request存着caller发来的Login函数需要的参数
bool login_result = Login(name, pwd);
//处理Login函数的逻辑,这部分逻辑单独写了一个函数。处于简化目的,就只是打印一下name和pwd。
fixbug::ResultCode *code = response->mutable_result();
code->set_errcode(0);
code->set_errmsg("");
response->set_success(login_result);
//将逻辑处理结果写入到response中。
done->Run();
//将结果发送回去
}
};
int main(int argc, char **argv)
{
MprpcApplication::Init(argc, argv);
//想要用rpc框架就要先初始化
RpcProvider provider;
// provider是一个rpc对象。它的作用是将UserService对象发布到rpc节点上,暂时不理解没关系!!
provider.NotifyService(new UserService());
// 将UserService服务及其中的方法Login发布出去,供远端调用。
// 注意我们的UserService是继承自UserServiceRpc的。远端想要请求UserServiceRpc服务其实请求的就是UserService服务。而UserServiceRpc只是一个虚类而已。
provider.Run();
// 启动一个rpc服务发布节点 Run以后,进程进入阻塞状态,等待远程的rpc调用请求
return 0;
}
calluserserver.cc,客户端代码
/****
文件注释:
文件名: calluserservice.cc
caller端代码:caller向callee发起远端调用,即caller想要调用处于callee中的Login函数。
****/
#include <iostream>
#include "mprpcapplication.h"
#include "user.pb.h"
#include "mprpcchannel.h"
int main(int argc, char **argv)
{
MprpcApplication::Init(argc, argv);
//MprpcApplication类提供了解析argc和argv参数的方法,我们在终端执行这个程序的时候,需要通过-i参数给程序提供一个配置文件,这个配置文件里面包含了一些通信地址信息(后面提到)
fixbug::UserServiceRPC_Stub stub(new MprpcChannel());
//这一步操作后面会讲,这里就当是实例化UserServiceRpc_Stub对象吧。UserServiceRpc_Stub是由user.proto生成的类,我们之前在user.proto中注册了Login方法,
fixbug::LoginRequest request;
request.set_name("zhang san");
request.set_pwd("123456");
//回想起我们的user.proto中注册的服务方法:
// rpc Login(LoginRequest) returns(LoginResponse);
// callee的Login函数需要参数LoginRequest数据结构数据
fixbug::LoginResponse response;
// callee的Login函数返回LoginResponse数据结构数据
stub.Login(nullptr, &request, &response, nullptr);
//caller发起远端调用,将Login的参数request发过去,callee返回的结果放在response中。
if (0 == response.result().errcode())
std::cout << "rpc login response success:" << response.success() << std::endl;
else
std::cout << "rpc login response error : " << response.result().errmsg() << std::endl;
//打印response中的内容,别忘了这个result和success之前在user.proto注册过
return 0;
}
4、生成可执行文件
接下来根据库和写的proto消息生成的.cc一块编译,生成可执行文件;首先是服务端生成可执行文件:
g++ userservice.cc user.pb.cc -o ./../bin/userservice -L./../build -lmprpc -lprotobuf -lzookeeper_mt -lpthread -lmuduo_net -lmuduo_base
生成客户端执行代码:
g++ calluserserver.cc user.pb.cc -o ./../bin/calluserserver -L./../build -lmprpc -lprotobuf -lzookeeper_mt -lpthread -lmuduo_net -lmuduo_base
5、配置文件的创建
服务端和客户端都使用的此配置文件
# RPC 服务端配置
rpcserverip = 127.0.0.1
rpcserverport = 12345
# ZooKeeper 服务端配置
zookeeperip = 127.0.0.1
zookeeperport = 2181
6、启动zookeeper服务
1、查看当前zookeeper服务器是否在运行:
netstat -plnt | grep 2181
若无输出,说明没有运行,我们要去启动zookeeper服务器;(你需要去下载zookeeper,https://blog.csdn.net/lamfang/article/details/108866448或者https://blog.csdn.net/qq_52595134/article/details/123467180,参考gpt;
)
7、运行
服务端运行
./userservice -i config.ini
这样的话,服务端就被运行起来了;见下图:
客户端运行;
./userservice -i ./../config.ini
此时就完成了客户端和服务端的通信;
报错;
解决启动zookeeper时Could not find or Load main class org.apache.zookeeper.server.quorum.QuorumPeerMain的报错
两种办法,一是看README.txt文件,得到编译命令;sudo apt install maven mvn javadoc:aggregate
或者是直接下载带bin的压缩文件夹:https://blog.csdn.net/Melo_FengZhi/article/details/109121715