From 2df7d409144af95c99cdabefcc7a3ad841c464d0 Mon Sep 17 00:00:00 2001 From: Michael Yeh Date: Wed, 4 May 2016 10:59:03 -0700 Subject: Modified sample async client to be non-blocking. A reader thread is spawned to handle all the RPC results. --- examples/cpp/helloworld/greeter_async_client.cc | 93 +++++++++++++++---------- 1 file changed, 57 insertions(+), 36 deletions(-) (limited to 'examples/cpp/helloworld/greeter_async_client.cc') diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc index 35014267fe..ca5a7defb0 100644 --- a/examples/cpp/helloworld/greeter_async_client.cc +++ b/examples/cpp/helloworld/greeter_async_client.cc @@ -36,6 +36,7 @@ #include #include +#include #include "helloworld.grpc.pb.h" @@ -55,71 +56,91 @@ class GreeterClient { // Assambles the client's payload, sends it and presents the response back // from the server. - std::string SayHello(const std::string& user) { + void 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 producer-consumer queue we use to communicate asynchronously with the - // gRPC runtime. - CompletionQueue cq; - - // Storage for the status of the RPC upon completion. - Status status; + AsyncClientCall* call = new AsyncClientCall; // stub_->AsyncSayHello() perform the RPC call, returning an instance we // store in "rpc". Because we are using the asynchronous API, we need the - // hold on to the "rpc" instance in order to get updates on the ongoig RPC. - std::unique_ptr > rpc( - stub_->AsyncSayHello(&context, request, &cq)); + // hold on to the "rpc" instance in order to get updates on the ongoing RPC. + call->response_reader = stub_->AsyncSayHello(&call->context, request, &cq_); + // Request that, upon completion of the RPC, "reply" be updated with the // server's response; "status" with the indication of whether the operation - // was successful. Tag the request with the integer 1. - rpc->Finish(&reply, &status, (void*)1); + // was successful. Tag the request with the memory address of call object. + call->response_reader->Finish(&call->reply, &call->status, (void*)call); + + } + void AsyncCompleteRpc() + { void* got_tag; bool ok = false; + // Block until the next result is available in the completion queue "cq". - cq.Next(&got_tag, &ok); - - // Verify that the result from "cq" corresponds, by its tag, our previous - // request. - GPR_ASSERT(got_tag == (void*)1); - // ... and that the request was completed successfully. Note that "ok" - // corresponds solely to the request for updates introduced by Finish(). - GPR_ASSERT(ok); - - // Act upon the status of the actual RPC. - if (status.ok()) { - return reply.message(); - } else { - return "RPC failed"; + while (cq_.Next(&got_tag, &ok)) + { + AsyncClientCall* call = static_cast(got_tag); + if (ok) + std::cout << "Greeter received: " << call->reply.message() << std::endl; + else + std::cout << "RPC failed" << std::endl; + delete call; } } private: + + // Structure for keeping state and data information + struct AsyncClientCall + { + // 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; + + // Storage for the status of the RPC upon completion. + Status status; + + + std::unique_ptr> response_reader; + }; + // Out of the passed in Channel comes the stub, stored here, our view of the // server's exposed services. std::unique_ptr stub_; + + // The producer-consumer queue we use to communicate asynchronously with the + // gRPC runtime. + CompletionQueue cq_; }; 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); // The actual RPC call! - std::cout << "Greeter received: " << reply << std::endl; + + // Spawn reader thread that loops indefinitely + std::thread thread_ = std::thread(&GreeterClient::AsyncCompleteRpc, &greeter); + + for (int i = 0; i < 100; i++) + { + std::string user("world " + std::to_string(i)); + greeter.SayHello(user); // The actual RPC call! + } + + std::cout << "Press control-c to quit" << std::endl << std::endl; + thread_.join(); //blocks forever return 0; } -- cgit v1.2.3 From 470cbd1c6e0f1e4d49265a1ed403ffb1219fffa6 Mon Sep 17 00:00:00 2001 From: Michael Yeh Date: Wed, 4 May 2016 12:39:20 -0700 Subject: Created a new sample async client that is non-blocking. A reader thread is spawned to handle all the RPC results. --- examples/cpp/helloworld/Makefile | 7 +- examples/cpp/helloworld/greeter_async_client.cc | 93 ++++++-------- examples/cpp/helloworld/greeter_async_client2.cc | 148 +++++++++++++++++++++++ 3 files changed, 189 insertions(+), 59 deletions(-) create mode 100644 examples/cpp/helloworld/greeter_async_client2.cc (limited to 'examples/cpp/helloworld/greeter_async_client.cc') diff --git a/examples/cpp/helloworld/Makefile b/examples/cpp/helloworld/Makefile index 470b83573e..58a82dbb83 100644 --- a/examples/cpp/helloworld/Makefile +++ b/examples/cpp/helloworld/Makefile @@ -41,7 +41,7 @@ PROTOS_PATH = ../../protos vpath %.proto $(PROTOS_PATH) -all: system-check greeter_client greeter_server greeter_async_client greeter_async_server +all: system-check greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server greeter_client: helloworld.pb.o helloworld.grpc.pb.o greeter_client.o $(CXX) $^ $(LDFLAGS) -o $@ @@ -52,6 +52,9 @@ greeter_server: helloworld.pb.o helloworld.grpc.pb.o greeter_server.o greeter_async_client: helloworld.pb.o helloworld.grpc.pb.o greeter_async_client.o $(CXX) $^ $(LDFLAGS) -o $@ +greeter_async_client2: helloworld.pb.o helloworld.grpc.pb.o greeter_async_client2.o + $(CXX) $^ $(LDFLAGS) -o $@ + greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.o $(CXX) $^ $(LDFLAGS) -o $@ @@ -64,7 +67,7 @@ greeter_async_server: helloworld.pb.o helloworld.grpc.pb.o greeter_async_server. $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< clean: - rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_server + rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server # The following is to test your system and ensure a smoother experience. diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc index ca5a7defb0..35014267fe 100644 --- a/examples/cpp/helloworld/greeter_async_client.cc +++ b/examples/cpp/helloworld/greeter_async_client.cc @@ -36,7 +36,6 @@ #include #include -#include #include "helloworld.grpc.pb.h" @@ -56,91 +55,71 @@ class GreeterClient { // Assambles the client's payload, sends it and presents the response back // from the server. - void SayHello(const std::string& user) { + std::string SayHello(const std::string& user) { // Data we are sending to the server. HelloRequest request; request.set_name(user); - AsyncClientCall* call = new AsyncClientCall; + // 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 producer-consumer queue we use to communicate asynchronously with the + // gRPC runtime. + CompletionQueue cq; + + // Storage for the status of the RPC upon completion. + Status status; // stub_->AsyncSayHello() perform the RPC call, returning an instance we // store in "rpc". Because we are using the asynchronous API, we need the - // hold on to the "rpc" instance in order to get updates on the ongoing RPC. - call->response_reader = stub_->AsyncSayHello(&call->context, request, &cq_); - + // hold on to the "rpc" instance in order to get updates on the ongoig RPC. + std::unique_ptr > rpc( + stub_->AsyncSayHello(&context, request, &cq)); // Request that, upon completion of the RPC, "reply" be updated with the // server's response; "status" with the indication of whether the operation - // was successful. Tag the request with the memory address of call object. - call->response_reader->Finish(&call->reply, &call->status, (void*)call); - - } - void AsyncCompleteRpc() - { + // was successful. Tag the request with the integer 1. + rpc->Finish(&reply, &status, (void*)1); void* got_tag; bool ok = false; - // Block until the next result is available in the completion queue "cq". - while (cq_.Next(&got_tag, &ok)) - { - AsyncClientCall* call = static_cast(got_tag); - if (ok) - std::cout << "Greeter received: " << call->reply.message() << std::endl; - else - std::cout << "RPC failed" << std::endl; - delete call; + cq.Next(&got_tag, &ok); + + // Verify that the result from "cq" corresponds, by its tag, our previous + // request. + GPR_ASSERT(got_tag == (void*)1); + // ... and that the request was completed successfully. Note that "ok" + // corresponds solely to the request for updates introduced by Finish(). + GPR_ASSERT(ok); + + // Act upon the status of the actual RPC. + if (status.ok()) { + return reply.message(); + } else { + return "RPC failed"; } } private: - - // Structure for keeping state and data information - struct AsyncClientCall - { - // 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; - - // Storage for the status of the RPC upon completion. - Status status; - - - std::unique_ptr> response_reader; - }; - // Out of the passed in Channel comes the stub, stored here, our view of the // server's exposed services. std::unique_ptr stub_; - - // The producer-consumer queue we use to communicate asynchronously with the - // gRPC runtime. - CompletionQueue cq_; }; 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())); - - // Spawn reader thread that loops indefinitely - std::thread thread_ = std::thread(&GreeterClient::AsyncCompleteRpc, &greeter); - - for (int i = 0; i < 100; i++) - { - std::string user("world " + std::to_string(i)); - greeter.SayHello(user); // The actual RPC call! - } - - std::cout << "Press control-c to quit" << std::endl << std::endl; - thread_.join(); //blocks forever + std::string user("world"); + std::string reply = greeter.SayHello(user); // The actual RPC call! + std::cout << "Greeter received: " << reply << std::endl; return 0; } diff --git a/examples/cpp/helloworld/greeter_async_client2.cc b/examples/cpp/helloworld/greeter_async_client2.cc new file mode 100644 index 0000000000..cdbdd1f306 --- /dev/null +++ b/examples/cpp/helloworld/greeter_async_client2.cc @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include +#include + +#include "helloworld.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; +using helloworld::HelloRequest; +using helloworld::HelloReply; +using helloworld::Greeter; + +class GreeterClient { + public: + explicit GreeterClient(std::shared_ptr channel) + : stub_(Greeter::NewStub(channel)) {} + + // Assambles the client's payload, sends it and presents the response back + // from the server. + void SayHello(const std::string& user) { + // Data we are sending to the server. + HelloRequest request; + request.set_name(user); + + AsyncClientCall* call = new AsyncClientCall; + + // stub_->AsyncSayHello() perform the RPC call, returning an instance we + // store in "rpc". Because we are using the asynchronous API, we need the + // hold on to the "rpc" instance in order to get updates on the ongoing RPC. + call->response_reader = stub_->AsyncSayHello(&call->context, request, &cq_); + + + // Request that, upon completion of the RPC, "reply" be updated with the + // server's response; "status" with the indication of whether the operation + // was successful. Tag the request with the memory address of call object. + call->response_reader->Finish(&call->reply, &call->status, (void*)call); + + } + + // Loop while listening for completed responses + void AsyncCompleteRpc() + { + void* got_tag; + bool ok = false; + + // Block until the next result is available in the completion queue "cq". + while (cq_.Next(&got_tag, &ok)) + { + AsyncClientCall* call = static_cast(got_tag); + if (ok) + std::cout << "Greeter received: " << call->reply.message() << std::endl; + else + std::cout << "RPC failed" << std::endl; + delete call; + } + } + + private: + + // struct for keeping state and data information + struct AsyncClientCall + { + // 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; + + // Storage for the status of the RPC upon completion. + Status status; + + + std::unique_ptr> response_reader; + }; + + // Out of the passed in Channel comes the stub, stored here, our view of the + // server's exposed services. + std::unique_ptr stub_; + + // The producer-consumer queue we use to communicate asynchronously with the + // gRPC runtime. + CompletionQueue cq_; +}; + +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())); + + // Spawn reader thread that loops indefinitely + std::thread thread_ = std::thread(&GreeterClient::AsyncCompleteRpc, &greeter); + + for (int i = 0; i < 100; i++) + { + std::string user("world " + std::to_string(i)); + greeter.SayHello(user); // The actual RPC call! + } + + std::cout << "Press control-c to quit" << std::endl << std::endl; + thread_.join(); //blocks forever + + return 0; +} -- cgit v1.2.3 From f9b89b181d243f8b3eaddeb000f751d71c257ad5 Mon Sep 17 00:00:00 2001 From: Michael Yeh Date: Wed, 4 May 2016 16:36:20 -0700 Subject: Updated to more closely mimic greeter_async_client.cc. Fixed typos in comments. --- examples/cpp/helloworld/greeter_async_client.cc | 8 ++--- examples/cpp/helloworld/greeter_async_client2.cc | 37 ++++++++++++++---------- 2 files changed, 25 insertions(+), 20 deletions(-) (limited to 'examples/cpp/helloworld/greeter_async_client.cc') diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc index 35014267fe..c1f5eb55f0 100644 --- a/examples/cpp/helloworld/greeter_async_client.cc +++ b/examples/cpp/helloworld/greeter_async_client.cc @@ -53,7 +53,7 @@ class GreeterClient { explicit GreeterClient(std::shared_ptr channel) : stub_(Greeter::NewStub(channel)) {} - // Assambles the client's payload, sends it and presents the response back + // 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. @@ -74,9 +74,9 @@ class GreeterClient { // Storage for the status of the RPC upon completion. Status status; - // stub_->AsyncSayHello() perform the RPC call, returning an instance we - // store in "rpc". Because we are using the asynchronous API, we need the - // hold on to the "rpc" instance in order to get updates on the ongoig RPC. + // stub_->AsyncSayHello() performs the RPC call, returning an instance we + // store in "rpc". Because we are using the asynchronous API, we need to + // hold on to the "rpc" instance in order to get updates on the ongoing RPC. std::unique_ptr > rpc( stub_->AsyncSayHello(&context, request, &cq)); diff --git a/examples/cpp/helloworld/greeter_async_client2.cc b/examples/cpp/helloworld/greeter_async_client2.cc index cdbdd1f306..0902376074 100644 --- a/examples/cpp/helloworld/greeter_async_client2.cc +++ b/examples/cpp/helloworld/greeter_async_client2.cc @@ -54,42 +54,49 @@ class GreeterClient { explicit GreeterClient(std::shared_ptr channel) : stub_(Greeter::NewStub(channel)) {} - // Assambles the client's payload, sends it and presents the response back - // from the server. + // Assembles the client's payload and sends it to the server. void SayHello(const std::string& user) { // Data we are sending to the server. HelloRequest request; request.set_name(user); + // Call object to store rpc data AsyncClientCall* call = new AsyncClientCall; - // stub_->AsyncSayHello() perform the RPC call, returning an instance we - // store in "rpc". Because we are using the asynchronous API, we need the - // hold on to the "rpc" instance in order to get updates on the ongoing RPC. + // stub_->AsyncSayHello() performs the RPC call, returning an instance to + // store in "call". Because we are using the asynchronous API, we need to + // hold on to the "call" instance in order to get updates on the ongoing RPC. call->response_reader = stub_->AsyncSayHello(&call->context, request, &cq_); // Request that, upon completion of the RPC, "reply" be updated with the // server's response; "status" with the indication of whether the operation - // was successful. Tag the request with the memory address of call object. + // was successful. Tag the request with the memory address of the call object. call->response_reader->Finish(&call->reply, &call->status, (void*)call); } - // Loop while listening for completed responses - void AsyncCompleteRpc() - { + // Loop while listening for completed responses. + // Prints out the response from the server. + void AsyncCompleteRpc() { void* got_tag; bool ok = false; // Block until the next result is available in the completion queue "cq". - while (cq_.Next(&got_tag, &ok)) - { + while (cq_.Next(&got_tag, &ok)) { + // The tag in this example is the memory location of the call object AsyncClientCall* call = static_cast(got_tag); - if (ok) + + // Verify that the request was completed successfully. Note that "ok" + // corresponds solely to the request for updates introduced by Finish(). + GPR_ASSERT(ok); + + if (call->status.ok()) std::cout << "Greeter received: " << call->reply.message() << std::endl; else std::cout << "RPC failed" << std::endl; + + // Once we're complete, deallocate the call object. delete call; } } @@ -97,8 +104,7 @@ class GreeterClient { private: // struct for keeping state and data information - struct AsyncClientCall - { + struct AsyncClientCall { // Container for the data we expect from the server. HelloReply reply; @@ -135,8 +141,7 @@ int main(int argc, char** argv) { // Spawn reader thread that loops indefinitely std::thread thread_ = std::thread(&GreeterClient::AsyncCompleteRpc, &greeter); - for (int i = 0; i < 100; i++) - { + for (int i = 0; i < 100; i++) { std::string user("world " + std::to_string(i)); greeter.SayHello(user); // The actual RPC call! } -- cgit v1.2.3