Skip to content

Commit cc9555b

Browse files
committed
grpc updated
1 parent 254fc47 commit cc9555b

File tree

9 files changed

+2111
-3
lines changed

9 files changed

+2111
-3
lines changed

docs/microservices/grpc.md

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
## gRPC
2+
Let's create a simple gRPC-based calculator application in C++ using CMake. We'll have a `CalculatorService` that performs basic operations like addition and subtraction. The server will expose this service, and the client will call the service to request operations.
3+
4+
### Installation
5+
Installation has been done with `vcpk`. Please check the corresponding files.
6+
run this
7+
8+
The content of `vcpkg.json`:
9+
10+
```
11+
{
12+
"name": "microservices",
13+
"version-string": "1.1.0",
14+
"dependencies": [
15+
{ "name": "grpc" }
16+
]
17+
}
18+
```
19+
20+
and `CMakeLists.txt`:
21+
22+
23+
```
24+
cmake_minimum_required(VERSION 3.14)
25+
project(microservices CXX)
26+
27+
if (NOT DEFINED CMAKE_TOOLCHAIN_FILE)
28+
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "toolchain file")
29+
endif()
30+
message("toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
31+
32+
# Find Protobuf and gRPC packages
33+
find_package(Protobuf CONFIG REQUIRED)
34+
find_package(gRPC CONFIG REQUIRED)
35+
36+
message("gRPC_FOUND: "${gRPC_FOUND})
37+
message("gRPC_VERSION: "${gRPC_VERSION})
38+
39+
40+
message("Protobuf_FOUND: "${Protobuf_FOUND})
41+
message("Protobuf_VERSION: "${Protobuf_VERSION})
42+
```
43+
44+
Now run:
45+
46+
```
47+
cmake -S . -B build -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake
48+
```
49+
50+
### Project Structure:
51+
```
52+
.
53+
├── CMakeLists.txt
54+
├── proto
55+
│   └── calculator.proto
56+
├── src
57+
│   ├── client.cpp
58+
│   └── server.cpp
59+
└── vcpkg.json
60+
61+
```
62+
63+
### Step 1: Define the `calculator.proto` file
64+
This `.proto` file defines the service and message types that gRPC will use to generate code for both the server and client.
65+
66+
**calculator.proto**:
67+
```proto
68+
syntax = "proto3";
69+
70+
package calculator;
71+
72+
// The request message containing two numbers.
73+
message CalcRequest {
74+
double number1 = 1;
75+
double number2 = 2;
76+
}
77+
78+
// The response message containing the result.
79+
message CalcResponse {
80+
double result = 1;
81+
}
82+
83+
// The Calculator service definition.
84+
service CalculatorService {
85+
// Performs addition of two numbers.
86+
rpc Add(CalcRequest) returns (CalcResponse);
87+
88+
// Performs subtraction of two numbers.
89+
rpc Subtract(CalcRequest) returns (CalcResponse);
90+
}
91+
```
92+
93+
- **message CalcRequest**: This message defines two numbers (`number1` and `number2`) for the input.
94+
- **message CalcResponse**: This message will hold the result of the calculation.
95+
- **CalculatorService**: Defines two RPC methods, `Add` and `Subtract`, which accept a `CalcRequest` and return a `CalcResponse`.
96+
97+
### Step 2: Generate gRPC Code
98+
Using the `.proto` file, you will generate C++ classes for gRPC. Assuming you have the `protoc` and gRPC plugins available, run the following commands in your terminal:
99+
100+
101+
The `vcpkg` build the `protoc` in `build/vcpkg_installed/x64-linux/tools/protobuf`, and `grpc_cpp_plugin` in `build/vcpkg_installed/x64-linux/tools/grpc/` so from the root of the project, add them to the path:
102+
103+
```
104+
export PATH=$PWD/build/vcpkg_installed/x64-linux/tools/protobuf:$PWD/build/vcpkg_installed/x64-linux/tools/grpc/:$PATH
105+
```
106+
107+
Then go to `proto` directory
108+
109+
```bash
110+
cd proto
111+
protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` calculator.proto
112+
protoc -I=. --cpp_out=. calculator.proto
113+
```
114+
115+
This will generate `calculator.grpc.pb.cc`, `calculator.grpc.pb.h`, `calculator.pb.cc`, and `calculator.pb.h` files, which we will use in our client and server code.
116+
117+
copy these file into your `generated` directory:
118+
119+
```
120+
.
121+
├── CMakeLists.txt
122+
├── generated
123+
│   ├── calculator.grpc.pb.cc
124+
│   ├── calculator.grpc.pb.h
125+
│   ├── calculator.pb.cc
126+
│   └── calculator.pb.h
127+
├── proto
128+
│   └── calculator.proto
129+
├── src
130+
│   ├── client.cpp
131+
│   └── server.cpp
132+
└── vcpkg.json
133+
134+
```
135+
136+
137+
### Step 3: Write the Server
138+
139+
**server.cpp**:
140+
```cpp
141+
// Implementation of the Calculator Service
142+
class CalculatorServiceImpl final : public CalculatorService::Service {
143+
Status Add(ServerContext* context, const CalcRequest* request, CalcResponse* response) override {
144+
double sum = request->number1() + request->number2();
145+
response->set_result(sum);
146+
return Status::OK;
147+
}
148+
149+
Status Subtract(ServerContext* context, const CalcRequest* request, CalcResponse* response) override {
150+
double diff = request->number1() - request->number2();
151+
response->set_result(diff);
152+
return Status::OK;
153+
}
154+
};
155+
156+
void RunServer() {
157+
std::string server_address("0.0.0.0:50051");
158+
CalculatorServiceImpl service;
159+
160+
ServerBuilder builder;
161+
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
162+
builder.RegisterService(&service);
163+
164+
std::unique_ptr<Server> server(builder.BuildAndStart());
165+
std::cout << "Server listening on " << server_address << std::endl;
166+
server->Wait();
167+
}
168+
169+
int main() {
170+
RunServer();
171+
return 0;
172+
}
173+
```
174+
175+
- **CalculatorServiceImpl**: Implements the service defined in the `proto` file. It overrides the `Add` and `Subtract` methods to provide logic for the operations.
176+
- **RunServer**: Sets up and starts the gRPC server on port `50051`.
177+
178+
### Step 4: Write the Client
179+
180+
**client.cpp**:
181+
```cpp
182+
class CalculatorClient {
183+
public:
184+
CalculatorClient(std::shared_ptr<Channel> channel)
185+
: stub_(CalculatorService::NewStub(channel)) {}
186+
187+
double Add(double num1, double num2) {
188+
CalcRequest request;
189+
request.set_number1(num1);
190+
request.set_number2(num2);
191+
192+
CalcResponse response;
193+
ClientContext context;
194+
195+
Status status = stub_->Add(&context, request, &response);
196+
197+
if (status.ok()) {
198+
return response.result();
199+
} else {
200+
std::cout << "RPC failed" << std::endl;
201+
return 0.0;
202+
}
203+
}
204+
205+
double Subtract(double num1, double num2) {
206+
CalcRequest request;
207+
request.set_number1(num1);
208+
request.set_number2(num2);
209+
210+
CalcResponse response;
211+
ClientContext context;
212+
213+
Status status = stub_->Subtract(&context, request, &response);
214+
215+
if (status.ok()) {
216+
return response.result();
217+
} else {
218+
std::cout << "RPC failed" << std::endl;
219+
return 0.0;
220+
}
221+
}
222+
223+
private:
224+
std::unique_ptr<CalculatorService::Stub> stub_;
225+
};
226+
227+
int main() {
228+
CalculatorClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
229+
230+
double num1 = 10.0;
231+
double num2 = 5.0;
232+
233+
std::cout << "Add: " << client.Add(num1, num2) << std::endl;
234+
std::cout << "Subtract: " << client.Subtract(num1, num2) << std::endl;
235+
236+
return 0;
237+
}
238+
```
239+
240+
- **CalculatorClient**: Defines a client that connects to the server and calls the `Add` and `Subtract` methods. It sets up an RPC request, sends it to the server, and retrieves the result.
241+
242+
### Step 5: CMakeLists.txt
243+
244+
Here's how you can set up the CMake build file.
245+
246+
```cmake
247+
set(GENERATED_DIR "${CMAKE_SOURCE_DIR}/generated" )
248+
set(PROTO_SRCS "${GENERATED_DIR}/calculator.pb.cc" )
249+
set(GRPC_SRCS "${GENERATED_DIR}/calculator.grpc.pb.cc")
250+
251+
include_directories(${GENERATED_DIR})
252+
253+
254+
add_executable(server src/server.cpp ${PROTO_SRCS} ${GRPC_SRCS})
255+
add_executable(client src/client.cpp ${PROTO_SRCS} ${GRPC_SRCS})
256+
257+
target_link_libraries(server PRIVATE gRPC::grpc++ protobuf::libprotobuf)
258+
target_link_libraries(client PRIVATE gRPC::grpc++ protobuf::libprotobuf)
259+
```
260+
261+
262+
### Step 6: Build and Run
263+
In the root of the project, run:
264+
265+
```
266+
cmake -S . -B build -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake
267+
268+
```
269+
270+
Start the server in one terminal:
271+
```bash
272+
./server
273+
```
274+
275+
Run the client in another terminal:
276+
```bash
277+
./client
278+
```

src/microservices/CMakeLists.txt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
1-
cmake_minimum_required(VERSION 3.16.3)
1+
cmake_minimum_required(VERSION 3.14)
2+
project(microservices CXX)
23

34
if (NOT DEFINED CMAKE_TOOLCHAIN_FILE)
45
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "toolchain file")
56
endif()
6-
77
message("toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
8-
project(microservices)
98

9+
# Find Protobuf and gRPC packages
10+
find_package(Protobuf CONFIG REQUIRED)
1011
find_package(gRPC CONFIG REQUIRED)
1112

1213
message("gRPC_FOUND: "${gRPC_FOUND})
1314
message("gRPC_VERSION: "${gRPC_VERSION})
15+
16+
17+
message("Protobuf_FOUND: "${Protobuf_FOUND})
18+
message("Protobuf_VERSION: "${Protobuf_VERSION})
19+
20+
21+
set(GENERATED_DIR "${CMAKE_SOURCE_DIR}/generated" )
22+
set(PROTO_SRCS "${GENERATED_DIR}/calculator.pb.cc" )
23+
set(GRPC_SRCS "${GENERATED_DIR}/calculator.grpc.pb.cc")
24+
25+
include_directories(${GENERATED_DIR})
26+
27+
28+
add_executable(server src/server.cpp ${PROTO_SRCS} ${GRPC_SRCS})
29+
add_executable(client src/client.cpp ${PROTO_SRCS} ${GRPC_SRCS})
30+
31+
target_link_libraries(server PRIVATE gRPC::grpc++ protobuf::libprotobuf)
32+
target_link_libraries(client PRIVATE gRPC::grpc++ protobuf::libprotobuf)

0 commit comments

Comments
 (0)