--- title: Asynchronous-API tutorial linkTitle: Async-API tutorial weight: 60 spelling: cSpell:ignore classgrpc Impl's --- This tutorial shows you how to write a simple server and client in C++ using gRPC's asynchronous/non-blocking APIs. It assumes you are already familiar with writing simple synchronous gRPC code, as described in [Basics tutorial]({{< relref "basics" >}}). The example used in this tutorial follows from the basic [Greeter example](https://github.com/grpc/grpc/tree/{{< param grpc_vers.core >}}/examples/cpp/helloworld) used in the [quick start]({{< relref "quickstart" >}}). You'll find it along with installation instructions in [grpc/examples/cpp/helloworld](https://github.com/grpc/grpc/tree/{{< param grpc_vers.core >}}/examples/cpp/helloworld). ### Overview gRPC uses the [CompletionQueue](/grpc/cpp/classgrpc_1_1_completion_queue.html) API for asynchronous operations. The basic work flow is as follows: - bind a `CompletionQueue` to an RPC call - do something like a read or write, present with a unique `void*` tag - call `CompletionQueue::Next` to wait for operations to complete. If a tag appears, it indicates that the corresponding operation is complete. ### Async client To use an asynchronous client to call a remote method, you first create a channel and stub, just as you do in a [synchronous client](https://github.com/grpc/grpc/blob/{{< param grpc_vers.core >}}/examples/cpp/helloworld/greeter_client.cc). Once you have your stub, you do the following to make an asynchronous call: - Initiate the RPC and create a handle for it. Bind the RPC to a `CompletionQueue`. ```c CompletionQueue cq; std::unique_ptr > rpc( stub_->AsyncSayHello(&context, request, &cq)); ``` - Ask for the reply and final status, with a unique tag ```c Status status; rpc->Finish(&reply, &status, (void*)1); ``` - Wait for the completion queue to return the next tag. The reply and status are ready once the tag passed into the corresponding `Finish()` call is returned. ```c void* got_tag; bool ok = false; cq.Next(&got_tag, &ok); if (ok && got_tag == (void*)1) { // check reply and status } ``` You can see the complete client example in [greeter_async_client.cc](https://github.com/grpc/grpc/blob/{{< param grpc_vers.core >}}/examples/cpp/helloworld/greeter_async_client.cc). ### Async server The server implementation requests an RPC call with a tag and then waits for the completion queue to return the tag. The basic flow for handling an RPC asynchronously is: - Build a server exporting the async service ```c helloworld::Greeter::AsyncService service; ServerBuilder builder; builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); builder.RegisterService(&service); auto cq = builder.AddCompletionQueue(); auto server = builder.BuildAndStart(); ``` - Request one RPC, providing a unique tag ```c ServerContext context; HelloRequest request; ServerAsyncResponseWriter responder; service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); ``` - Wait for the completion queue to return the tag. The context, request and responder are ready once the tag is retrieved. ```c HelloReply reply; Status status; void* got_tag; bool ok = false; cq.Next(&got_tag, &ok); if (ok && got_tag == (void*)1) { // set reply and status responder.Finish(reply, status, (void*)2); } ``` - Wait for the completion queue to return the tag. The RPC is finished when the tag is back. ```c void* got_tag; bool ok = false; cq.Next(&got_tag, &ok); if (ok && got_tag == (void*)2) { // clean up } ``` This basic flow, however, doesn't take into account the server handling multiple requests concurrently. To deal with this, our complete async server example uses a `CallData` object to maintain the state of each RPC, and uses the address of this object as the unique tag for the call. ```c class CallData { public: // Take in the "service" instance (in this case representing an asynchronous // server) and the completion queue "cq" used for asynchronous communication // with the gRPC runtime. CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq) : service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) { // Invoke the serving logic right away. Proceed(); } void Proceed() { if (status_ == CREATE) { // As part of the initial CREATE state, we *request* that the system // start processing SayHello requests. In this request, "this" acts are // the tag uniquely identifying the request (so that different CallData // instances can serve different requests concurrently), in this case // the memory address of this CallData instance. service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, this); // Make this instance progress to the PROCESS state. status_ = PROCESS; } else if (status_ == PROCESS) { // Spawn a new CallData instance to serve new clients while we process // the one for this CallData. The instance will deallocate itself as // part of its FINISH state. new CallData(service_, cq_); // The actual processing. std::string prefix("Hello "); reply_.set_message(prefix + request_.name()); // And we are done! Let the gRPC runtime know we've finished, using the // memory address of this instance as the uniquely identifying tag for // the event. responder_.Finish(reply_, Status::OK, this); status_ = FINISH; } else { GPR_ASSERT(status_ == FINISH); // Once in the FINISH state, deallocate ourselves (CallData). delete this; } } } ``` For simplicity the server only uses one completion queue for all events, and runs a main loop in `HandleRpcs` to query the queue: ```c void HandleRpcs() { // Spawn a new CallData instance to serve new clients. new CallData(&service_, cq_.get()); void* tag; // uniquely identifies a request. bool ok; while (true) { // Block waiting to read the next event from the completion queue. The // event is uniquely identified by its tag, which in this case is the // memory address of a CallData instance. cq_->Next(&tag, &ok); GPR_ASSERT(ok); static_cast(tag)->Proceed(); } } ``` #### Shutting Down the Server We've been using a completion queue to get the async notifications. Care must be taken to shut it down *after* the server has also been shut down. Remember we got our completion queue instance `cq_` in `ServerImpl::Run()` by running `cq_ = builder.AddCompletionQueue()`. Looking at `ServerBuilder::AddCompletionQueue`'s documentation we see that > ... Caller is required to shutdown the server prior to shutting down the > returned completion queue. Refer to `ServerBuilder::AddCompletionQueue`'s full docstring for more details. What this means in our example is that `ServerImpl's` destructor looks like: ```c ~ServerImpl() { server_->Shutdown(); // Always shutdown the completion queue after the server. cq_->Shutdown(); } ``` You can see our complete server example in [greeter_async_server.cc](https://github.com/grpc/grpc/blob/{{< param grpc_vers.core >}}/examples/cpp/helloworld/greeter_async_server.cc).