一、编译grpc源码

(1)源码下载,这里准备好了源码连接,直接按下面链接源码和依赖库。

链接: 百度网盘 请输入提取码

(2)安装必要依赖工具

sudo apt-get install autoconf automake libtool

(3)安装cmake和gcc/g++

本文主要用到cmake来进行编译,所以需要安装cmake和gcc/g++,注意需要保证cmake的版本大于3.15,gcc/g++的版本大于7.0吗。

如果已经安装的cmake,版本号低于3.15,先卸载再安装新的cmake。

卸载命令

sudo apt-get autoremove cmake

安装新的cmake

1.压缩包下载

wget https://cmake.org/files/v3.23/cmake-3.23.0-linux-x86_64.tar.gz

2.解压

tar zxf cmake-3.23.0-linux-x86_64.tar.gz

3.创建软连接

sudo ln -sf /opt/cmake-3.23.0/bin/*  /usr/bin/

 创建软连接到/usr/bin/下的目的是可以在系统的任何目录下直接运行cmake命令。

(4)解压grpc源码

tar -jxf grpc-v1.45.2.tar.bz2

(5)编译和安转

        命令如下,首先进入压缩后的grpc文件夹下,执行如下命令。

mkdir build
cd build
cmake ../
make
sudo make install

(6)protobuf安装

        这里的protobuf主要用来数据传输的序列化,使用grpc/third_party/protobuf 里面编译安装对应的 protobuf,不要手动下载其他的protbuf,不然版本可能和grcp不匹配。命令如下。

cd third_party/protobuf/
./autogen.sh 
./configure --prefix=/usr/local
make

sudo make install
sudo ldconfig  # 使得新安装的动态库能被加载

protoc --version
显示3.19.4

(7)测试

1.编译helloword例子

cd grpc/examples/cpp/helloworld/
mkdir build
cd build/
cmake ..
make

2.启动客户端和服务器

# 启动服务端,监听在50051端口
./greeter_server
Server listening on 0.0.0.0:50051
# 启动客户端,服务端返回Hello world
./greeter_client 
Greeter received: Hello world

出现上述结果说明编译成功。

二、如何在c++中使用grpc进行通信

(1)编写proto文件,这文件主要是用来定义服务接口函数和一些消息体结构,然后通过protobuf工具用该文件生成对应的grpc.pb.cc文件和消息体结构类。代码如下:

syntax = "proto3";//声明使用protoc3版本

package myserver;//定义命令空间,主要是为了区分不同的proto文件有相同的函数名和消息体名

service Greeter//定义接口服务
{
	rpc SayHello(HelloRequest) returns (HelloRepose){}//接口函数,通过该函数来与服务器进行通信
}

message HelloRequest//请求消息体结构
{
	bytes message = 1;//成员变量
}

message HelloRepose//响应消息体结构
{
	bytes message = 1;
}

上面代码中,定义个叫Greeter的服务类,在这个类中定义一个接口函数,该函数的功能是接受一个单一的消息,服务器返回单一的消息,这里我只定义了一个,大家可以随意定义。下面接受以下grpc的四种接口模式。

1.简单接口模型

即客户端发起一次请求,服务端响应处理后返回一个结果给客户端。在proto文件对应如下。

rpc SayHello(HelloRequest) returns (HelloResponse);

 2.服务端数据流模式

即客户端发起一次请求,服务端可以连续返回数据流。比如:客户端向服务端发送了一个查询数据库的请求,服务端持续返回多次结果。(即客户端发送一次请求,服务端查询到数据库有一万条数据,服务端分批返回10次,每次返回1000条数据给客户端)。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

用到了stream关键字。

3.客户端数据流模式

与服务端数据流模式相反,客户端持续向服务端发送数据流,在发送结束后,由服务端返回一个响应。比如:客户端有一万条数据 ,分批多次请求服务端,服务端接收后把这些数据都存到数据库,然后返回一次结果给客户端。

在 proto 文件中可如下定义:

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

4.双向数据流模式

也称双向流式 RPC,即客户端和服务端都可以向对方多次收发数据。比如:客户端有一万条数据 ,分批多次请求服务端,服务端每次接收后存到数据库后都发送一次结果给客户端。在 proto 文件中可如下定义:

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

(2)使用protobuf工具将上面的proto文件生成对应的c++语法结构的grpc.pb.cc和grpc.pb.h文件

命名如下:

上面--grpc_out是grpc.pb.cc文件的输出目录,--plugin是 grpc_cpp_plugin这个插件的路径。执行上面命令后会在当前项目路径下生成下面两个文件。

(3)使用protobuf工具将上面的proto文件生成对应的c++语法结构消息体结构类 

命令如下:

protoc -I=./ server.proto --cpp_out=./

-I表示proto文件所在目录,--cpp_out表示输出文件的输出目录。生成文件如下。

(4) 服务器代码

/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <iostream>                     // 包含输入输出流操作所需的头文件
#include <memory>                      // 包含智能指针(如 std::unique_ptr)的支持
#include <string>                      // 包含 std::string 类

#include <grpcpp/ext/proto_server_reflection_plugin.h> // 包含 gRPC 服务器反射插件的支持
#include <grpcpp/grpcpp.h>            // 包含 gRPC 核心功能的头文件
#include <grpcpp/health_check_service_interface.h> // 包含健康检查服务的接口

#include "server.grpc.pb.h"           // 包含由 .proto 文件生成的头文件

using grpc::Server;                   // 使用 gRPC Server 类
using grpc::ServerBuilder;            // 使用 gRPC ServerBuilder 类
using grpc::ServerContext;            // 使用 gRPC ServerContext 类
using grpc::Status;                  // 使用 gRPC Status 类
using myserver::Greeter;             // 使用从 .proto 文件生成的 Greeter 服务类
using myserver::HelloRepose;         // 使用从 .proto 文件生成的 HelloRepose 消息类
using myserver::HelloRequest;        // 使用从 .proto 文件生成的 HelloRequest 消息类

// Greeter 服务的实现类
class GreeterServiceImpl final : public Greeter::Service {
    // 实现 SayHello RPC 方法
    Status SayHello(ServerContext* context, const HelloRequest* request,
        HelloRepose* reply) override {
        // 创建问候前缀
        std::string prefix("Hello ");
        // 将请求中的内容与前缀拼接,并设置为响应内容
        reply->set_contents(prefix + request->contents());
        // 返回表示成功的状态
        return Status::OK;
    }
};

// 设置并运行服务器的函数
void RunServer() {
    std::string server_address("0.0.0.0:50051"); // 服务器监听的地址和端口
    GreeterServiceImpl service;                  // 创建 Greeter 服务的实现实例

    // 启用默认健康检查服务
    grpc::EnableDefaultHealthCheckService(true);
    // 初始化 gRPC 服务器反射插件以支持反射功能
    grpc::reflection::InitProtoReflectionServerBuilderPlugin();
    
    ServerBuilder builder;                      // 创建 ServerBuilder 对象
    // 将监听端口和不安全凭证添加到服务器构建器
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    // 在服务器构建器中注册服务实例
    builder.RegisterService(&service);
    // 构建并启动服务器
    std::unique_ptr<Server> server(builder.BuildAndStart());
    // 输出服务器的监听地址
    std::cout << "Server listening on " << server_address << std::endl;

    // 阻塞当前线程,直到服务器关闭
    // 服务器会一直运行,直到由其他线程显式关闭
    server->Wait();
}

// 程序的入口点
int main(int argc, char** argv) {
    // 启动服务器
    RunServer();

    // 返回成功的状态码
    return 0;
}

(5) 客户端代码

#include <string>                  // 包含 std::string 类,用于字符串操作
#include <iostream>                // 包含输入输出流操作所需的头文件
#include <memory>                 // 包含智能指针(如 std::unique_ptr)的支持
#include <grpcpp/grpcpp.h>        // 包含 gRPC 核心功能的头文件
#include "server.grpc.pb.h"       // 包含由 .proto 文件生成的头文件

using grpc::ClientContext;        // 使用 gRPC ClientContext 类
using grpc::Channel;              // 使用 gRPC Channel 类
using grpc::Status;               // 使用 gRPC Status 类
using myserver::HelloRepose;     // 使用从 .proto 文件生成的 HelloRepose 消息类
using myserver::HelloRequest;    // 使用从 .proto 文件生成的 HelloRequest 消息类
using myserver::Greeter;         // 使用从 .proto 文件生成的 Greeter 服务类

// FCClient 类,封装了与 gRPC 服务器的通信
class FCClient {
public:
    // 构造函数,接受一个共享的 Channel 对象,创建 Greeter 的 Stub
    FCClient(std::shared_ptr<Channel> channel)
        : stub_(Greeter::NewStub(channel)) {
    }

    // 发送 SayHello 请求到 gRPC 服务器
    std::string SayHello(std::string name) {
        ClientContext context;         // 创建客户端上下文对象
        HelloRepose reply;             // 用于接收服务器响应的对象
        HelloRequest request;          // 创建请求对象并设置内容
        request.set_contents(name);    // 将传入的名字设置到请求中

        // 调用 gRPC 服务方法 SayHello 并获取响应
        Status status = stub_->SayHello(&context, request, &reply);
        if (status.ok()) {
            // 如果请求成功,返回响应中的内容
            return reply.contents();
        } else {
            // 如果请求失败,返回错误信息
            return "failure " + status.error_message();
        }
    }

private:
    std::unique_ptr<Greeter::Stub> stub_;  // 用于调用 gRPC 服务的 Stub
};

// 程序的入口点
int main(int argc, char* argv[]) {
    // 创建与 gRPC 服务器的通道,并指定服务器地址和使用的不安全凭证
    auto channel = grpc::CreateChannel("127.0.0.1:50051", grpc::InsecureChannelCredentials());
    FCClient client(channel);             // 创建 FCClient 实例

    // 调用 SayHello 方法并阻塞,直到从 RPC 服务器获取结果
    std::string result = client.SayHello("hello , llfc.club !");
    // 输出结果
    printf("get result [%s]\n", result.c_str());

    return 0; // 返回成功状态码
}

(6)CmakeLists文件编写

        在linux端采用cmake来编译服务器端和客户端的代码。cmakelists文件如下。

cmake_minimum_required(VERSION 3.1)
project(GrpcClient LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#假设已经安装好grpc了
find_package(Threads REQUIRED)
set(protobuf_MODULE_COMPATIBLE TRUE)
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using protobuf ${Protobuf_VERSION}")
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)
# Find gRPC installation
# Looks for gRPCConfig.cmake file installed by gRPC's cmake installation.
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP gRPC::grpc++)
# 添加可执行文件和源文件
file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB PBSOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
add_executable(GrpcClient ${SOURCES}
        ${PBSOURCES})
target_link_libraries(GrpcClient
    ${_REFLECTION}
    ${_GRPC_GRPCPP}
    ${_PROTOBUF_LIBPROTOBUF})

将该文件分别放到,服务器端和客户端的项目文件夹下,比如:

在该目录下打开终端。执行如下命令生成可执行文件

mkdir build
cd ./build
cmake ../
make

 可执行就会生成在build文件夹下。

 服务器启动

客户端启动并收到服务器的回应

 总结,以上就是grpc在linux系统下的编译与简单应用,grpc主要用在服务器上各个服务之间的通信,基于http2,支持异步和同步,效率比较高。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐