OLD | NEW |
(Empty) | |
| 1 # gRPC C++ Hello World Tutorial |
| 2 |
| 3 ### Install gRPC |
| 4 Make sure you have installed gRPC on your system. Follow the instructions here: |
| 5 [https://github.com/grpc/grpc/blob/master/INSTALL](../../../INSTALL.md). |
| 6 |
| 7 ### Get the tutorial source code |
| 8 |
| 9 The example code for this and our other examples lives in the `examples` |
| 10 directory. Clone this repository to your local machine by running the |
| 11 following command: |
| 12 |
| 13 |
| 14 ```sh |
| 15 $ git clone https://github.com/grpc/grpc.git |
| 16 ``` |
| 17 |
| 18 Change your current directory to examples/cpp/helloworld |
| 19 |
| 20 ```sh |
| 21 $ cd examples/cpp/helloworld/ |
| 22 ``` |
| 23 |
| 24 ### Defining a service |
| 25 |
| 26 The first step in creating our example is to define a *service*: an RPC |
| 27 service specifies the methods that can be called remotely with their parameters |
| 28 and return types. As you saw in the |
| 29 [overview](#protocolbuffers) above, gRPC does this using [protocol |
| 30 buffers](https://developers.google.com/protocol-buffers/docs/overview). We |
| 31 use the protocol buffers interface definition language (IDL) to define our |
| 32 service methods, and define the parameters and return |
| 33 types as protocol buffer message types. Both the client and the |
| 34 server use interface code generated from the service definition. |
| 35 |
| 36 Here's our example service definition, defined using protocol buffers IDL in |
| 37 [helloworld.proto](../../protos/helloworld.proto). The `Greeting` |
| 38 service has one method, `hello`, that lets the server receive a single |
| 39 `HelloRequest` |
| 40 message from the remote client containing the user's name, then send back |
| 41 a greeting in a single `HelloReply`. This is the simplest type of RPC you |
| 42 can specify in gRPC - we'll look at some other types later in this document. |
| 43 |
| 44 ```protobuf |
| 45 syntax = "proto3"; |
| 46 |
| 47 option java_package = "ex.grpc"; |
| 48 |
| 49 package helloworld; |
| 50 |
| 51 // The greeting service definition. |
| 52 service Greeter { |
| 53 // Sends a greeting |
| 54 rpc SayHello (HelloRequest) returns (HelloReply) {} |
| 55 } |
| 56 |
| 57 // The request message containing the user's name. |
| 58 message HelloRequest { |
| 59 string name = 1; |
| 60 } |
| 61 |
| 62 // The response message containing the greetings |
| 63 message HelloReply { |
| 64 string message = 1; |
| 65 } |
| 66 |
| 67 ``` |
| 68 |
| 69 <a name="generating"></a> |
| 70 ### Generating gRPC code |
| 71 |
| 72 Once we've defined our service, we use the protocol buffer compiler |
| 73 `protoc` to generate the special client and server code we need to create |
| 74 our application. The generated code contains both stub code for clients to |
| 75 use and an abstract interface for servers to implement, both with the method |
| 76 defined in our `Greeting` service. |
| 77 |
| 78 To generate the client and server side interfaces: |
| 79 |
| 80 ```sh |
| 81 $ make helloworld.grpc.pb.cc helloworld.pb.cc |
| 82 ``` |
| 83 Which internally invokes the proto-compiler as: |
| 84 |
| 85 ```sh |
| 86 $ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin
../../protos/helloworld.proto |
| 87 $ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto |
| 88 ``` |
| 89 |
| 90 ### Writing a client |
| 91 |
| 92 - Create a channel. A channel is a logical connection to an endpoint. A gRPC |
| 93 channel can be created with the target address, credentials to use and |
| 94 arguments as follows |
| 95 |
| 96 ```cpp |
| 97 auto channel = CreateChannel("localhost:50051", InsecureChannelCredentials()
); |
| 98 ``` |
| 99 |
| 100 - Create a stub. A stub implements the rpc methods of a service and in the |
| 101 generated code, a method is provided to created a stub with a channel: |
| 102 |
| 103 ```cpp |
| 104 auto stub = helloworld::Greeter::NewStub(channel); |
| 105 ``` |
| 106 |
| 107 - Make a unary rpc, with `ClientContext` and request/response proto messages. |
| 108 |
| 109 ```cpp |
| 110 ClientContext context; |
| 111 HelloRequest request; |
| 112 request.set_name("hello"); |
| 113 HelloReply reply; |
| 114 Status status = stub->SayHello(&context, request, &reply); |
| 115 ``` |
| 116 |
| 117 - Check returned status and response. |
| 118 |
| 119 ```cpp |
| 120 if (status.ok()) { |
| 121 // check reply.message() |
| 122 } else { |
| 123 // rpc failed. |
| 124 } |
| 125 ``` |
| 126 |
| 127 For a working example, refer to [greeter_client.cc](greeter_client.cc). |
| 128 |
| 129 ### Writing a server |
| 130 |
| 131 - Implement the service interface |
| 132 |
| 133 ```cpp |
| 134 class GreeterServiceImpl final : public Greeter::Service { |
| 135 Status SayHello(ServerContext* context, const HelloRequest* request, |
| 136 HelloReply* reply) override { |
| 137 std::string prefix("Hello "); |
| 138 reply->set_message(prefix + request->name()); |
| 139 return Status::OK; |
| 140 } |
| 141 }; |
| 142 |
| 143 ``` |
| 144 |
| 145 - Build a server exporting the service |
| 146 |
| 147 ```cpp |
| 148 GreeterServiceImpl service; |
| 149 ServerBuilder builder; |
| 150 builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials())
; |
| 151 builder.RegisterService(&service); |
| 152 std::unique_ptr<Server> server(builder.BuildAndStart()); |
| 153 ``` |
| 154 |
| 155 For a working example, refer to [greeter_server.cc](greeter_server.cc). |
| 156 |
| 157 ### Writing asynchronous client and server |
| 158 |
| 159 gRPC uses `CompletionQueue` API for asynchronous operations. The basic work flow |
| 160 is |
| 161 - bind a `CompletionQueue` to a rpc call |
| 162 - do something like a read or write, present with a unique `void*` tag |
| 163 - call `CompletionQueue::Next` to wait for operations to complete. If a tag |
| 164 appears, it indicates that the corresponding operation is complete. |
| 165 |
| 166 #### Async client |
| 167 |
| 168 The channel and stub creation code is the same as the sync client. |
| 169 |
| 170 - Initiate the rpc and create a handle for the rpc. Bind the rpc to a |
| 171 `CompletionQueue`. |
| 172 |
| 173 ```cpp |
| 174 CompletionQueue cq; |
| 175 auto rpc = stub->AsyncSayHello(&context, request, &cq); |
| 176 ``` |
| 177 |
| 178 - Ask for reply and final status, with a unique tag |
| 179 |
| 180 ```cpp |
| 181 Status status; |
| 182 rpc->Finish(&reply, &status, (void*)1); |
| 183 ``` |
| 184 |
| 185 - Wait for the completion queue to return the next tag. The reply and status are |
| 186 ready once the tag passed into the corresponding `Finish()` call is returned. |
| 187 |
| 188 ```cpp |
| 189 void* got_tag; |
| 190 bool ok = false; |
| 191 cq.Next(&got_tag, &ok); |
| 192 if (ok && got_tag == (void*)1) { |
| 193 // check reply and status |
| 194 } |
| 195 ``` |
| 196 |
| 197 For a working example, refer to [greeter_async_client.cc](greeter_async_client.c
c). |
| 198 |
| 199 #### Async server |
| 200 |
| 201 The server implementation requests a rpc call with a tag and then wait for the |
| 202 completion queue to return the tag. The basic flow is |
| 203 |
| 204 - Build a server exporting the async service |
| 205 |
| 206 ```cpp |
| 207 helloworld::Greeter::AsyncService service; |
| 208 ServerBuilder builder; |
| 209 builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); |
| 210 builder.RegisterAsyncService(&service); |
| 211 auto cq = builder.AddCompletionQueue(); |
| 212 auto server = builder.BuildAndStart(); |
| 213 ``` |
| 214 |
| 215 - Request one rpc |
| 216 |
| 217 ```cpp |
| 218 ServerContext context; |
| 219 HelloRequest request; |
| 220 ServerAsyncResponseWriter<HelloReply> responder; |
| 221 service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); |
| 222 ``` |
| 223 |
| 224 - Wait for the completion queue to return the tag. The context, request and |
| 225 responder are ready once the tag is retrieved. |
| 226 |
| 227 ```cpp |
| 228 HelloReply reply; |
| 229 Status status; |
| 230 void* got_tag; |
| 231 bool ok = false; |
| 232 cq.Next(&got_tag, &ok); |
| 233 if (ok && got_tag == (void*)1) { |
| 234 // set reply and status |
| 235 responder.Finish(reply, status, (void*)2); |
| 236 } |
| 237 ``` |
| 238 |
| 239 - Wait for the completion queue to return the tag. The rpc is finished when the |
| 240 tag is back. |
| 241 |
| 242 ```cpp |
| 243 void* got_tag; |
| 244 bool ok = false; |
| 245 cq.Next(&got_tag, &ok); |
| 246 if (ok && got_tag == (void*)2) { |
| 247 // clean up |
| 248 } |
| 249 ``` |
| 250 |
| 251 To handle multiple rpcs, the async server creates an object `CallData` to |
| 252 maintain the state of each rpc and use the address of it as the unique tag. For |
| 253 simplicity the server only uses one completion queue for all events, and runs a |
| 254 main loop in `HandleRpcs` to query the queue. |
| 255 |
| 256 For a working example, refer to [greeter_async_server.cc](greeter_async_server.c
c). |
| 257 |
| 258 |
| 259 |
| 260 |
OLD | NEW |