最近要用到grpc
,因此写一篇关于 grpc 静态编译和使用的文章
下载
在 Linux/ubuntu 上安装需要的软件:
apt install make gcc g++ cmake build-essential autoconf libtool pkg-config git unzip
下载库:
#记得改为最新版本的 grpc
git clone -b v1.27.3 https://github.com/grpc/grpc
cd grpc
git submodule update --init --recursive
安装 protobuf-protoc
google的grpc使用的protobuf
作为序列化数据的格式。
推荐编译安装:
cd third_party/protobuf
./autogen.sh
./configure
make -j 2
警告:如果你想静态编译程序,不要安装!!
sudo make install
sudo ldconfig # refresh shared library cache.
或者直接从网上下载最新版本的protobuf
:
#! /bin/bash
# Make sure you grab the latest version
curl -OL https://github.com/google/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
# Unzip
unzip protoc-3.11.4-linux-x86_64.zip -d protoc3
# Move protoc to /usr/local/bin/
sudo cp -r protoc3/bin/* /usr/local/bin/
# Move protoc3/include to /usr/local/include/
sudo cp -r protoc3/include/* /usr/local/include/
# Optional: change owner
sudo chown $USER /usr/local/bin/protoc
sudo chown -R $USER /usr/local/include/google
sudo ldconfig # refresh shared library cache.
-> # protoc --version
libprotoc 3.11.4
不推荐直接安装protoc
,库中的protoc
版本比较旧:
apt install libprotobuf-dev protobuf-compiler
-> # protoc --version
libprotoc 3.0.0
静态编译 grpc/ static link build grpc
export CFLAGS='-Wno-implicit-fallthrough'
export CXXFLAGS='-Wno-expansion-to-defined -Wno-implicit-fallthrough'
cmake .
make -j 2
#make REQUIRE_CUSTOM_LIBRARIES_opt=1 static
注意:如果安装grpc
到系统目录后,无法轻易卸载
警告:如果你想静态编译程序,不要安装!!
sudo make install
动态编译测试
警告:如果测试通不过,后续就不用做了,重装系统吧
如果没安装protoc
或者grpc
,自然无法编译,具体的静态编译过程见下面的示例
编译官方样例:
cd examples/cpp/helloworld/
make
# ./greeter_server
Server listening on 0.0.0.0:50051
~/grpc/examples/cpp/helloworld$ ./greeter_client
Greeter received: Hello world
自己编写 Protobuf 文件与服务器/客户端代码
examples.proto
:
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
服务器端代码 examples_server.cc
:
#include <iostream>
#include <memory>
#include <string>
#include <grpc++/grpc++.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "examples.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service
{
Status SayHello(ServerContext *context, const HelloRequest *request,
HelloReply *reply) override
{
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer()
{
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl service;
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
int main(int argc, char **argv)
{
RunServer();
return 0;
}
客户端代码 examples_client.cc
:
#include <iostream>
#include <memory>
#include <string>
#include <grpc++/grpc++.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "examples.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterClient
{
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back
// from the server.
std::string SayHello(const std::string &user)
{
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
Status status = stub_->SayHello(&context, request, &reply);
// Act upon its status.
if (status.ok())
{
return reply.message();
}
else
{
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char **argv)
{
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint (in this case,
// localhost at port 50051). We indicate that the channel isn't authenticated
// (use of InsecureChannelCredentials()).
GreeterClient greeter(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
静态编译链接程序
静态链接的成果是不依赖外部库,可以放到其他机器上直接运行。
编写Makefile
,记得把GRPC_PATH
和PROTOBUF_PATH
换成你的目录。
subdir = ./
SOURCES = $(wildcard $(subdir)*.cc)
SRCOBJS = $(patsubst %.cc,%.o,$(SOURCES))
GXX = g++
GXXFLAGS = -O0 -g2 \
-Wall -std=c++11 -I./ \
-I$(PROTOBUF_PATH) -I$(GRPC_PATH)/include
GRPC_PATH= /home/zhang/grpc
PROTOBUF_PATH= /home/zhang/grpc/third_party/protobuf/src
BUILDFLAGS = -static-libgcc -static-libstdc++ -std=c++11
DLIBS = -pthread -ldl
SLIBS = $(PROTOBUF_PATH)/.libs/libprotobuf.a \
$(GRPC_PATH)/libs/opt/libares.a \
$(GRPC_PATH)/libs/opt/libboringssl.a \
$(GRPC_PATH)/libs/opt/libgpr.a \
$(GRPC_PATH)/libs/opt/libgrpc.a \
$(GRPC_PATH)/libs/opt/libgrpc++.a \
$(GRPC_PATH)/libs/opt/libgrpc++_core_stats.a \
$(GRPC_PATH)/libs/opt/libgrpc++_error_details.a \
$(GRPC_PATH)/libs/opt/libgrpc_plugin_support.a \
$(GRPC_PATH)/libs/opt/libgrpc_unsecure.a \
$(GRPC_PATH)/libs/opt/libgrpc++_unsecure.a \
$(GRPC_PATH)/libs/opt/libz.a \
-Wl,--no-as-needed $(GRPC_PATH)/libs/opt/libgrpc++_reflection.a -Wl,--as-needed
%.grpc.pb.cc: %.proto
$(PROTOBUF_PATH)/protoc --grpc_out=./ --plugin=protoc-gen-grpc=$(GRPC_PATH)/bins/opt/grpc_cpp_plugin $^
%.pb.cc: %.proto
$(PROTOBUF_PATH)/protoc --cpp_out=./ $^
%.pb.o: %.pb.cc
$(GXX) $(GXXFLAGS) -c $^ -o $@
%.o:%.cc
$(GXX) $(GXXFLAGS) -c $< -o $@
%.o: %.cpp
$(GXX) $(GXXFLAGS) -c $^ -o $@
all: client server
client: examples.pb.o examples.grpc.pb.o examples_client.o
$(GXX) $(BUILDFLAGS) $^ -o $@ $(SLIBS) $(DLIBS)
server: examples.pb.o examples.grpc.pb.o examples_server.o
$(GXX) $(BUILDFLAGS) $^ -o $@ $(SLIBS) $(DLIBS)
clean:
rm *.o *.pb.cc *.pb.h server client
编译完成后程序很大,但不依赖grpc.so等库
-> % ldd server
linux-vdso.so.1 (0x00007ffdb2154000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff6f766f000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff6f7450000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff6f705f000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff6f8104000)
后记
关于grpc
的其他文章如下:
文档信息
- 本文作者:last2win
- 本文链接:https://last2win.com/grpc-and-protobuf/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)