Various example/benchmarking code (#16)

* initial echo client/server examples

* registered and unregistered versions of the example client

* ignore pyc files

* cpp echo code, flag to build examples

* threaded server example
This commit is contained in:
Connor Clark 2016-06-03 10:34:09 -07:00
parent 9ffdec4c56
commit ce56953b24
21 changed files with 1135 additions and 6 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
dist dist
.stack-work/ .stack-work/
*.pyc
*.pyo
*.o

View file

@ -0,0 +1,37 @@
{-# LANGUAGE OverloadedStrings #-}
import Control.Concurrent (threadDelay)
import Control.Monad (forever)
import Data.ByteString ()
import qualified Data.Map as M
import Network.GRPC.LowLevel
echoMethod :: MethodName
echoMethod = MethodName "/echo.Echo/DoEcho"
ntimes :: Int -> IO () -> IO ()
ntimes 1 f = f
ntimes n f = f >> (ntimes (n-1) f)
unregClient :: IO ()
unregClient = do
withGRPC $ \grpc ->
withClient grpc (ClientConfig "localhost" 50051) $ \client ->
ntimes 100000 $ do
reqResult <- clientRequest client echoMethod "localhost:50051" 1 "hi" M.empty
case reqResult of
Left x -> error $ "Got client error: " ++ show x
Right resp -> return ()
regClient :: IO ()
regClient = do
withGRPC $ \grpc ->
withClient grpc (ClientConfig "localhost" 50051) $ \client -> ntimes 100000 $ do
regMethod <- clientRegisterMethod client echoMethod "localhost:50051" Normal
reqResult <- clientRegisteredRequest client regMethod 1 "hi" M.empty
case reqResult of
Left x -> error $ "Got client error: " ++ show x
Right resp -> return ()
main :: IO ()
main = regClient

View file

@ -0,0 +1,10 @@
CXX = clang++
CXXFLAGS += -std=c++11
CPPFLAGS += -I/usr/local/include -pthread
LDFLAGS += -L/usr/local/lib -lgrpc++_unsecure -lgrpc -lprotobuf -lpthread -ldl
echo-server: echo.pb.o echo.grpc.pb.o echo-server.o
clang++ $^ $(LDFLAGS) -o $@
echo-client: echo.pb.o echo.grpc.pb.o echo-client.o
clang++ $^ $(LDFLAGS) -o $@

Binary file not shown.

View file

@ -0,0 +1,48 @@
#include <string>
#include <iostream>
#include <grpc++/grpc++.h>
#include "echo.grpc.pb.h"
using namespace std;
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using echo::EchoRequest;
using echo::Echo;
class EchoClient {
public:
EchoClient(shared_ptr<Channel> chan) : stub_(Echo::NewStub(chan)) {}
Status DoEcho(const string& msg){
EchoRequest req;
req.set_message(msg);
EchoRequest resp;
ClientContext ctx;
return stub_->DoEcho(&ctx, req, &resp);
}
private:
unique_ptr<Echo::Stub> stub_;
};
int main(){
EchoClient client(grpc::CreateChannel("localhost:50051",
grpc::InsecureChannelCredentials()));
string msg("hi");
for(int i = 0; i < 100000; i++){
Status status = client.DoEcho(msg);
if(!status.ok()){
cout<<"Error: "<<status.error_code()<<endl;
return 1;
}
}
return 0;
}

Binary file not shown.

View file

@ -0,0 +1,34 @@
#include <string>
#include <grpc++/grpc++.h>
#include "echo.grpc.pb.h"
using namespace std;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using echo::EchoRequest;
using echo::Echo;
class EchoServiceImpl final : public Echo::Service {
Status DoEcho(ServerContext* ctx, const EchoRequest* req,
EchoRequest* resp) override {
resp->set_message(req->message());
return Status::OK;
}
};
int main(){
string server_address("localhost:50051");
EchoServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
unique_ptr<Server> server(builder.BuildAndStart());
server->Wait();
return 0;
}

View file

@ -0,0 +1,59 @@
// Generated by the gRPC protobuf plugin.
// If you make any local change, they will be lost.
// source: echo.proto
#include "echo.pb.h"
#include "echo.grpc.pb.h"
#include <grpc++/impl/codegen/async_stream.h>
#include <grpc++/impl/codegen/async_unary_call.h>
#include <grpc++/impl/codegen/channel_interface.h>
#include <grpc++/impl/codegen/client_unary_call.h>
#include <grpc++/impl/codegen/method_handler_impl.h>
#include <grpc++/impl/codegen/rpc_service_method.h>
#include <grpc++/impl/codegen/service_type.h>
#include <grpc++/impl/codegen/sync_stream.h>
namespace echo {
static const char* Echo_method_names[] = {
"/echo.Echo/DoEcho",
};
std::unique_ptr< Echo::Stub> Echo::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) {
std::unique_ptr< Echo::Stub> stub(new Echo::Stub(channel));
return stub;
}
Echo::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel)
: channel_(channel), rpcmethod_DoEcho_(Echo_method_names[0], ::grpc::RpcMethod::NORMAL_RPC, channel)
{}
::grpc::Status Echo::Stub::DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) {
return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_DoEcho_, context, request, response);
}
::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>* Echo::Stub::AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) {
return new ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>(channel_.get(), cq, rpcmethod_DoEcho_, context, request);
}
Echo::Service::Service() {
AddMethod(new ::grpc::RpcServiceMethod(
Echo_method_names[0],
::grpc::RpcMethod::NORMAL_RPC,
new ::grpc::RpcMethodHandler< Echo::Service, ::echo::EchoRequest, ::echo::EchoRequest>(
std::mem_fn(&Echo::Service::DoEcho), this)));
}
Echo::Service::~Service() {
}
::grpc::Status Echo::Service::DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) {
(void) context;
(void) request;
(void) response;
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
} // namespace echo

View file

@ -0,0 +1,103 @@
// Generated by the gRPC protobuf plugin.
// If you make any local change, they will be lost.
// source: echo.proto
#ifndef GRPC_echo_2eproto__INCLUDED
#define GRPC_echo_2eproto__INCLUDED
#include "echo.pb.h"
#include <grpc++/impl/codegen/async_stream.h>
#include <grpc++/impl/codegen/async_unary_call.h>
#include <grpc++/impl/codegen/proto_utils.h>
#include <grpc++/impl/codegen/rpc_method.h>
#include <grpc++/impl/codegen/service_type.h>
#include <grpc++/impl/codegen/status.h>
#include <grpc++/impl/codegen/stub_options.h>
#include <grpc++/impl/codegen/sync_stream.h>
namespace grpc {
class CompletionQueue;
class RpcService;
class ServerCompletionQueue;
class ServerContext;
} // namespace grpc
namespace echo {
class Echo GRPC_FINAL {
public:
class StubInterface {
public:
virtual ~StubInterface() {}
virtual ::grpc::Status DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) = 0;
std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>> AsyncDoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) {
return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>>(AsyncDoEchoRaw(context, request, cq));
}
private:
virtual ::grpc::ClientAsyncResponseReaderInterface< ::echo::EchoRequest>* AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) = 0;
};
class Stub GRPC_FINAL : public StubInterface {
public:
Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);
::grpc::Status DoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::echo::EchoRequest* response) GRPC_OVERRIDE;
std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>> AsyncDoEcho(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) {
return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>>(AsyncDoEchoRaw(context, request, cq));
}
private:
std::shared_ptr< ::grpc::ChannelInterface> channel_;
::grpc::ClientAsyncResponseReader< ::echo::EchoRequest>* AsyncDoEchoRaw(::grpc::ClientContext* context, const ::echo::EchoRequest& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE;
const ::grpc::RpcMethod rpcmethod_DoEcho_;
};
static std::unique_ptr<Stub> NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions());
class Service : public ::grpc::Service {
public:
Service();
virtual ~Service();
virtual ::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response);
};
template <class BaseClass>
class WithAsyncMethod_DoEcho : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(Service *service) {}
public:
WithAsyncMethod_DoEcho() {
::grpc::Service::MarkMethodAsync(0);
}
~WithAsyncMethod_DoEcho() GRPC_OVERRIDE {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) GRPC_FINAL GRPC_OVERRIDE {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
void RequestDoEcho(::grpc::ServerContext* context, ::echo::EchoRequest* request, ::grpc::ServerAsyncResponseWriter< ::echo::EchoRequest>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) {
::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag);
}
};
typedef WithAsyncMethod_DoEcho<Service > AsyncService;
template <class BaseClass>
class WithGenericMethod_DoEcho : public BaseClass {
private:
void BaseClassMustBeDerivedFromService(Service *service) {}
public:
WithGenericMethod_DoEcho() {
::grpc::Service::MarkMethodGeneric(0);
}
~WithGenericMethod_DoEcho() GRPC_OVERRIDE {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
::grpc::Status DoEcho(::grpc::ServerContext* context, const ::echo::EchoRequest* request, ::echo::EchoRequest* response) GRPC_FINAL GRPC_OVERRIDE {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
};
};
} // namespace echo
#endif // GRPC_echo_2eproto__INCLUDED

View file

@ -0,0 +1,384 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: echo.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
#include "echo.pb.h"
#include <algorithm>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/wire_format.h>
// @@protoc_insertion_point(includes)
namespace echo {
namespace {
const ::google::protobuf::Descriptor* EchoRequest_descriptor_ = NULL;
const ::google::protobuf::internal::GeneratedMessageReflection*
EchoRequest_reflection_ = NULL;
} // namespace
void protobuf_AssignDesc_echo_2eproto() {
protobuf_AddDesc_echo_2eproto();
const ::google::protobuf::FileDescriptor* file =
::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
"echo.proto");
GOOGLE_CHECK(file != NULL);
EchoRequest_descriptor_ = file->message_type(0);
static const int EchoRequest_offsets_[1] = {
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, message_),
};
EchoRequest_reflection_ =
::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
EchoRequest_descriptor_,
EchoRequest::default_instance_,
EchoRequest_offsets_,
-1,
-1,
-1,
sizeof(EchoRequest),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, _internal_metadata_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoRequest, _is_default_instance_));
}
namespace {
GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
inline void protobuf_AssignDescriptorsOnce() {
::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
&protobuf_AssignDesc_echo_2eproto);
}
void protobuf_RegisterTypes(const ::std::string&) {
protobuf_AssignDescriptorsOnce();
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
EchoRequest_descriptor_, &EchoRequest::default_instance());
}
} // namespace
void protobuf_ShutdownFile_echo_2eproto() {
delete EchoRequest::default_instance_;
delete EchoRequest_reflection_;
}
void protobuf_AddDesc_echo_2eproto() {
static bool already_here = false;
if (already_here) return;
already_here = true;
GOOGLE_PROTOBUF_VERIFY_VERSION;
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\necho.proto\022\004echo\"\036\n\013EchoRequest\022\017\n\007mes"
"sage\030\001 \001(\t28\n\004Echo\0220\n\006DoEcho\022\021.echo.Echo"
"Request\032\021.echo.EchoRequest\"\000b\006proto3", 116);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"echo.proto", &protobuf_RegisterTypes);
EchoRequest::default_instance_ = new EchoRequest();
EchoRequest::default_instance_->InitAsDefaultInstance();
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_echo_2eproto);
}
// Force AddDescriptors() to be called at static initialization time.
struct StaticDescriptorInitializer_echo_2eproto {
StaticDescriptorInitializer_echo_2eproto() {
protobuf_AddDesc_echo_2eproto();
}
} static_descriptor_initializer_echo_2eproto_;
namespace {
static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD;
static void MergeFromFail(int line) {
GOOGLE_CHECK(false) << __FILE__ << ":" << line;
}
} // namespace
// ===================================================================
#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int EchoRequest::kMessageFieldNumber;
#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
EchoRequest::EchoRequest()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
SharedCtor();
// @@protoc_insertion_point(constructor:echo.EchoRequest)
}
void EchoRequest::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
EchoRequest::EchoRequest(const EchoRequest& from)
: ::google::protobuf::Message(),
_internal_metadata_(NULL) {
SharedCtor();
MergeFrom(from);
// @@protoc_insertion_point(copy_constructor:echo.EchoRequest)
}
void EchoRequest::SharedCtor() {
_is_default_instance_ = false;
::google::protobuf::internal::GetEmptyString();
_cached_size_ = 0;
message_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
EchoRequest::~EchoRequest() {
// @@protoc_insertion_point(destructor:echo.EchoRequest)
SharedDtor();
}
void EchoRequest::SharedDtor() {
message_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
if (this != default_instance_) {
}
}
void EchoRequest::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
GOOGLE_SAFE_CONCURRENT_WRITES_END();
}
const ::google::protobuf::Descriptor* EchoRequest::descriptor() {
protobuf_AssignDescriptorsOnce();
return EchoRequest_descriptor_;
}
const EchoRequest& EchoRequest::default_instance() {
if (default_instance_ == NULL) protobuf_AddDesc_echo_2eproto();
return *default_instance_;
}
EchoRequest* EchoRequest::default_instance_ = NULL;
EchoRequest* EchoRequest::New(::google::protobuf::Arena* arena) const {
EchoRequest* n = new EchoRequest;
if (arena != NULL) {
arena->Own(n);
}
return n;
}
void EchoRequest::Clear() {
message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
bool EchoRequest::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
::google::protobuf::uint32 tag;
// @@protoc_insertion_point(parse_start:echo.EchoRequest)
for (;;) {
::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
tag = p.first;
if (!p.second) goto handle_unusual;
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// optional string message = 1;
case 1: {
if (tag == 10) {
DO_(::google::protobuf::internal::WireFormatLite::ReadString(
input, this->mutable_message()));
DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
this->message().data(), this->message().length(),
::google::protobuf::internal::WireFormatLite::PARSE,
"echo.EchoRequest.message"));
} else {
goto handle_unusual;
}
if (input->ExpectAtEnd()) goto success;
break;
}
default: {
handle_unusual:
if (tag == 0 ||
::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
goto success;
}
DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
break;
}
}
}
success:
// @@protoc_insertion_point(parse_success:echo.EchoRequest)
return true;
failure:
// @@protoc_insertion_point(parse_failure:echo.EchoRequest)
return false;
#undef DO_
}
void EchoRequest::SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const {
// @@protoc_insertion_point(serialize_start:echo.EchoRequest)
// optional string message = 1;
if (this->message().size() > 0) {
::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
this->message().data(), this->message().length(),
::google::protobuf::internal::WireFormatLite::SERIALIZE,
"echo.EchoRequest.message");
::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
1, this->message(), output);
}
// @@protoc_insertion_point(serialize_end:echo.EchoRequest)
}
::google::protobuf::uint8* EchoRequest::SerializeWithCachedSizesToArray(
::google::protobuf::uint8* target) const {
// @@protoc_insertion_point(serialize_to_array_start:echo.EchoRequest)
// optional string message = 1;
if (this->message().size() > 0) {
::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
this->message().data(), this->message().length(),
::google::protobuf::internal::WireFormatLite::SERIALIZE,
"echo.EchoRequest.message");
target =
::google::protobuf::internal::WireFormatLite::WriteStringToArray(
1, this->message(), target);
}
// @@protoc_insertion_point(serialize_to_array_end:echo.EchoRequest)
return target;
}
int EchoRequest::ByteSize() const {
int total_size = 0;
// optional string message = 1;
if (this->message().size() > 0) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::StringSize(
this->message());
}
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = total_size;
GOOGLE_SAFE_CONCURRENT_WRITES_END();
return total_size;
}
void EchoRequest::MergeFrom(const ::google::protobuf::Message& from) {
if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
const EchoRequest* source =
::google::protobuf::internal::DynamicCastToGenerated<const EchoRequest>(
&from);
if (source == NULL) {
::google::protobuf::internal::ReflectionOps::Merge(from, this);
} else {
MergeFrom(*source);
}
}
void EchoRequest::MergeFrom(const EchoRequest& from) {
if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
if (from.message().size() > 0) {
message_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.message_);
}
}
void EchoRequest::CopyFrom(const ::google::protobuf::Message& from) {
if (&from == this) return;
Clear();
MergeFrom(from);
}
void EchoRequest::CopyFrom(const EchoRequest& from) {
if (&from == this) return;
Clear();
MergeFrom(from);
}
bool EchoRequest::IsInitialized() const {
return true;
}
void EchoRequest::Swap(EchoRequest* other) {
if (other == this) return;
InternalSwap(other);
}
void EchoRequest::InternalSwap(EchoRequest* other) {
message_.Swap(&other->message_);
_internal_metadata_.Swap(&other->_internal_metadata_);
std::swap(_cached_size_, other->_cached_size_);
}
::google::protobuf::Metadata EchoRequest::GetMetadata() const {
protobuf_AssignDescriptorsOnce();
::google::protobuf::Metadata metadata;
metadata.descriptor = EchoRequest_descriptor_;
metadata.reflection = EchoRequest_reflection_;
return metadata;
}
#if PROTOBUF_INLINE_NOT_IN_HEADERS
// EchoRequest
// optional string message = 1;
void EchoRequest::clear_message() {
message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
const ::std::string& EchoRequest::message() const {
// @@protoc_insertion_point(field_get:echo.EchoRequest.message)
return message_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
void EchoRequest::set_message(const ::std::string& value) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
// @@protoc_insertion_point(field_set:echo.EchoRequest.message)
}
void EchoRequest::set_message(const char* value) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
// @@protoc_insertion_point(field_set_char:echo.EchoRequest.message)
}
void EchoRequest::set_message(const char* value, size_t size) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
::std::string(reinterpret_cast<const char*>(value), size));
// @@protoc_insertion_point(field_set_pointer:echo.EchoRequest.message)
}
::std::string* EchoRequest::mutable_message() {
// @@protoc_insertion_point(field_mutable:echo.EchoRequest.message)
return message_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
::std::string* EchoRequest::release_message() {
return message_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
void EchoRequest::set_allocated_message(::std::string* message) {
if (message != NULL) {
} else {
}
message_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), message);
// @@protoc_insertion_point(field_set_allocated:echo.EchoRequest.message)
}
#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
// @@protoc_insertion_point(namespace_scope)
} // namespace echo
// @@protoc_insertion_point(global_scope)

View file

@ -0,0 +1,183 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: echo.proto
#ifndef PROTOBUF_echo_2eproto__INCLUDED
#define PROTOBUF_echo_2eproto__INCLUDED
#include <string>
#include <google/protobuf/stubs/common.h>
#if GOOGLE_PROTOBUF_VERSION < 3000000
#error This file was generated by a newer version of protoc which is
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
#endif
#include <google/protobuf/arena.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/metadata.h>
#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/unknown_field_set.h>
// @@protoc_insertion_point(includes)
namespace echo {
// Internal implementation detail -- do not call these.
void protobuf_AddDesc_echo_2eproto();
void protobuf_AssignDesc_echo_2eproto();
void protobuf_ShutdownFile_echo_2eproto();
class EchoRequest;
// ===================================================================
class EchoRequest : public ::google::protobuf::Message {
public:
EchoRequest();
virtual ~EchoRequest();
EchoRequest(const EchoRequest& from);
inline EchoRequest& operator=(const EchoRequest& from) {
CopyFrom(from);
return *this;
}
static const ::google::protobuf::Descriptor* descriptor();
static const EchoRequest& default_instance();
void Swap(EchoRequest* other);
// implements Message ----------------------------------------------
inline EchoRequest* New() const { return New(NULL); }
EchoRequest* New(::google::protobuf::Arena* arena) const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
void CopyFrom(const EchoRequest& from);
void MergeFrom(const EchoRequest& from);
void Clear();
bool IsInitialized() const;
int ByteSize() const;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(EchoRequest* other);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
}
inline void* MaybeArenaPtr() const {
return _internal_metadata_.raw_arena_ptr();
}
public:
::google::protobuf::Metadata GetMetadata() const;
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
// optional string message = 1;
void clear_message();
static const int kMessageFieldNumber = 1;
const ::std::string& message() const;
void set_message(const ::std::string& value);
void set_message(const char* value);
void set_message(const char* value, size_t size);
::std::string* mutable_message();
::std::string* release_message();
void set_allocated_message(::std::string* message);
// @@protoc_insertion_point(class_scope:echo.EchoRequest)
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
bool _is_default_instance_;
::google::protobuf::internal::ArenaStringPtr message_;
mutable int _cached_size_;
friend void protobuf_AddDesc_echo_2eproto();
friend void protobuf_AssignDesc_echo_2eproto();
friend void protobuf_ShutdownFile_echo_2eproto();
void InitAsDefaultInstance();
static EchoRequest* default_instance_;
};
// ===================================================================
// ===================================================================
#if !PROTOBUF_INLINE_NOT_IN_HEADERS
// EchoRequest
// optional string message = 1;
inline void EchoRequest::clear_message() {
message_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline const ::std::string& EchoRequest::message() const {
// @@protoc_insertion_point(field_get:echo.EchoRequest.message)
return message_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline void EchoRequest::set_message(const ::std::string& value) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
// @@protoc_insertion_point(field_set:echo.EchoRequest.message)
}
inline void EchoRequest::set_message(const char* value) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
// @@protoc_insertion_point(field_set_char:echo.EchoRequest.message)
}
inline void EchoRequest::set_message(const char* value, size_t size) {
message_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
::std::string(reinterpret_cast<const char*>(value), size));
// @@protoc_insertion_point(field_set_pointer:echo.EchoRequest.message)
}
inline ::std::string* EchoRequest::mutable_message() {
// @@protoc_insertion_point(field_mutable:echo.EchoRequest.message)
return message_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline ::std::string* EchoRequest::release_message() {
return message_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline void EchoRequest::set_allocated_message(::std::string* message) {
if (message != NULL) {
} else {
}
message_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), message);
// @@protoc_insertion_point(field_set_allocated:echo.EchoRequest.message)
}
#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS
// @@protoc_insertion_point(namespace_scope)
} // namespace echo
// @@protoc_insertion_point(global_scope)
#endif // PROTOBUF_echo_2eproto__INCLUDED

View file

@ -0,0 +1,13 @@
from grpc.beta import implementations
import echo_pb2
def main():
for _ in xrange(1000):
channel = implementations.insecure_channel('localhost', 50051)
stub = echo_pb2.beta_create_Echo_stub(channel)
message = echo_pb2.EchoRequest(message='foo')
response = stub.DoEcho(message, 15)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,118 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: echo.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='echo.proto',
package='echo',
syntax='proto3',
serialized_pb=_b('\n\necho.proto\x12\x04\x65\x63ho\"\x1e\n\x0b\x45\x63hoRequest\x12\x0f\n\x07message\x18\x01 \x01(\t28\n\x04\x45\x63ho\x12\x30\n\x06\x44oEcho\x12\x11.echo.EchoRequest\x1a\x11.echo.EchoRequest\"\x00\x62\x06proto3')
)
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_ECHOREQUEST = _descriptor.Descriptor(
name='EchoRequest',
full_name='echo.EchoRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='message', full_name='echo.EchoRequest.message', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=20,
serialized_end=50,
)
DESCRIPTOR.message_types_by_name['EchoRequest'] = _ECHOREQUEST
EchoRequest = _reflection.GeneratedProtocolMessageType('EchoRequest', (_message.Message,), dict(
DESCRIPTOR = _ECHOREQUEST,
__module__ = 'echo_pb2'
# @@protoc_insertion_point(class_scope:echo.EchoRequest)
))
_sym_db.RegisterMessage(EchoRequest)
import abc
from grpc.beta import implementations as beta_implementations
from grpc.framework.common import cardinality
from grpc.framework.interfaces.face import utilities as face_utilities
class BetaEchoServicer(object):
"""<fill me in later!>"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def DoEcho(self, request, context):
raise NotImplementedError()
class BetaEchoStub(object):
"""The interface to which stubs will conform."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def DoEcho(self, request, timeout):
raise NotImplementedError()
DoEcho.future = None
def beta_create_Echo_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
import echo_pb2
import echo_pb2
request_deserializers = {
('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.FromString,
}
response_serializers = {
('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.SerializeToString,
}
method_implementations = {
('echo.Echo', 'DoEcho'): face_utilities.unary_unary_inline(servicer.DoEcho),
}
server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
return beta_implementations.server(method_implementations, options=server_options)
def beta_create_Echo_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
import echo_pb2
import echo_pb2
request_serializers = {
('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.SerializeToString,
}
response_deserializers = {
('echo.Echo', 'DoEcho'): echo_pb2.EchoRequest.FromString,
}
cardinalities = {
'DoEcho': cardinality.Cardinality.UNARY_UNARY,
}
stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
return beta_implementations.dynamic_stub(channel, 'echo.Echo', cardinalities, options=stub_options)
# @@protoc_insertion_point(module_scope)

View file

@ -0,0 +1,20 @@
from grpc.beta import implementations
import echo_pb2
import time
class Echo(echo_pb2.BetaEchoServicer):
def DoEcho(self, request, context):
return echo_pb2.EchoRequest(message=request.message)
def main():
server = echo_pb2.beta_create_Echo_server(Echo())
server.add_insecure_port('[::]:50051')
server.start()
try:
time.sleep(600)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,64 @@
{-# LANGUAGE OverloadedStrings #-}
import Control.Concurrent.Async (async, wait)
import Control.Monad (forever)
import Data.ByteString (ByteString)
import qualified Data.Map as M
import Network.GRPC.LowLevel
serverMeta :: MetadataMap
serverMeta = M.fromList [("test_meta", "test_meta_value")]
handler :: ByteString -> MetadataMap -> MethodName
-> IO (ByteString, MetadataMap, StatusDetails)
handler reqBody reqMeta method = do
--putStrLn $ "Got request for method: " ++ show method
--putStrLn $ "Got metadata: " ++ show reqMeta
return (reqBody, serverMeta, StatusDetails "")
unregMain :: IO ()
unregMain = withGRPC $ \grpc -> do
withServer grpc (ServerConfig "localhost" 50051 []) $ \server -> forever $ do
result <- serverHandleNormalCall server 15 serverMeta handler
case result of
Left x -> putStrLn $ "handle call result error: " ++ show x
Right _ -> return ()
regMain :: IO ()
regMain = withGRPC $ \grpc -> do
let methods = [(MethodName "/echo.Echo/DoEcho", Normal)]
withServer grpc (ServerConfig "localhost" 50051 methods) $ \server ->
forever $ do
let method = head (registeredMethods server)
result <- serverHandleNormalRegisteredCall server method 15 serverMeta $
\reqBody _reqMeta -> return (reqBody, serverMeta, serverMeta,
StatusDetails "")
case result of
Left x -> putStrLn $ "registered call result error: " ++ show x
Right _ -> return ()
-- | loop to fork n times
regLoop :: Server -> RegisteredMethod -> IO ()
regLoop server method = forever $ do
result <- serverHandleNormalRegisteredCall server method 15 serverMeta $
\reqBody _reqMeta -> return (reqBody, serverMeta, serverMeta,
StatusDetails "")
case result of
Left x -> putStrLn $ "registered call result error: " ++ show x
Right _ -> return ()
regMainThreaded :: IO ()
regMainThreaded = do
withGRPC $ \grpc -> do
let methods = [(MethodName "/echo.Echo/DoEcho", Normal)]
withServer grpc (ServerConfig "localhost" 50051 methods) $ \server -> do
let method = head (registeredMethods server)
tid1 <- async $ regLoop server method
tid2 <- async $ regLoop server method
wait tid1
wait tid2
return ()
main :: IO ()
main = regMainThreaded

11
examples/echo/echo.proto Normal file
View file

@ -0,0 +1,11 @@
syntax = "proto3";
package echo;
service Echo {
rpc DoEcho (EchoRequest) returns (EchoRequest) {}
}
message EchoRequest {
string message = 1;
}

View file

@ -18,6 +18,11 @@ Flag Debug
Manual: True Manual: True
Default: False Default: False
flag with-examples
description: Also build example executables.
manual: True
default: False
library library
build-depends: build-depends:
base ==4.8.* base ==4.8.*
@ -65,6 +70,35 @@ library
CPP-Options: -DDEBUG CPP-Options: -DDEBUG
CC-Options: -DGRPC_HASKELL_DEBUG CC-Options: -DGRPC_HASKELL_DEBUG
executable echo-server
if flag(with-examples)
build-depends:
base ==4.8.*
, bytestring == 0.10.*
, grpc-haskell
, containers ==0.5.*
, async
else
buildable: False
default-language: Haskell2010
ghc-options: -Wall -g -threaded
hs-source-dirs: examples/echo/echo-server
main-is: Main.hs
executable echo-client
if flag(with-examples)
build-depends:
base ==4.8.*
, bytestring == 0.10.*
, grpc-haskell
, containers ==0.5.*
else
buildable: False
default-language: Haskell2010
ghc-options: -Wall -g -threaded
hs-source-dirs: examples/echo/echo-client
main-is: Main.hs
test-suite test test-suite test
build-depends: build-depends:
base ==4.8.* base ==4.8.*

View file

@ -22,6 +22,9 @@ GRPC
, GRPCMethodType(..) , GRPCMethodType(..)
, RegisteredMethod , RegisteredMethod
, NormalRequestResult(..) , NormalRequestResult(..)
, MetadataMap
, MethodName(..)
, StatusDetails(..)
-- * Server -- * Server
, ServerConfig(..) , ServerConfig(..)
@ -53,7 +56,6 @@ GRPC
, runServerUnregOps , runServerUnregOps
, Op(..) , Op(..)
, OpRecvResult(..) , OpRecvResult(..)
, StatusDetails(..)
) where ) where

View file

@ -90,6 +90,10 @@ serverUnregCallGetMethodName :: ServerUnregCall -> IO MethodName
serverUnregCallGetMethodName ServerUnregCall{..} = serverUnregCallGetMethodName ServerUnregCall{..} =
MethodName <$> C.callDetailsGetMethod callDetails MethodName <$> C.callDetailsGetMethod callDetails
serverUnregCallGetHost :: ServerUnregCall -> IO Host
serverUnregCallGetHost ServerUnregCall{..} =
Host <$> C.callDetailsGetHost callDetails
debugClientCall :: ClientCall -> IO () debugClientCall :: ClientCall -> IO ()
{-# INLINE debugClientCall #-} {-# INLINE debugClientCall #-}
#ifdef DEBUG #ifdef DEBUG

View file

@ -35,7 +35,7 @@ data ServerConfig =
-- how this is used. Setting to "localhost" works fine in tests. -- how this is used. Setting to "localhost" works fine in tests.
port :: Int, port :: Int,
-- ^ Port to listen for requests on. -- ^ Port to listen for requests on.
methodsToRegister :: [(MethodName, Host, GRPCMethodType)] methodsToRegister :: [(MethodName, GRPCMethodType)]
-- ^ List of (method name, method host, method type) tuples -- ^ List of (method name, method host, method type) tuples
-- specifying all methods to register. You can also handle -- specifying all methods to register. You can also handle
-- other unregistered methods with `serverHandleNormalCall`. -- other unregistered methods with `serverHandleNormalCall`.
@ -51,8 +51,8 @@ startServer grpc ServerConfig{..} = do
cq <- createCompletionQueue grpc cq <- createCompletionQueue grpc
serverRegisterCompletionQueue server cq serverRegisterCompletionQueue server cq
methods <- forM methodsToRegister $ methods <- forM methodsToRegister $
\(name, host, mtype) -> \(name, mtype) ->
serverRegisterMethod server name host mtype serverRegisterMethod server name (Host hostPort) mtype
C.grpcServerStart server C.grpcServerStart server
return $ Server server cq methods return $ Server server cq methods
@ -249,6 +249,8 @@ serverHandleNormalCall s@Server{..} timeLimit srvMetadata f = do
requestMeta <- serverUnregCallGetMetadata call requestMeta <- serverUnregCallGetMetadata call
grpcDebug $ "got client metadata: " ++ show requestMeta grpcDebug $ "got client metadata: " ++ show requestMeta
methodName <- serverUnregCallGetMethodName call methodName <- serverUnregCallGetMethodName call
hostName <- serverUnregCallGetHost call
grpcDebug $ "call_details host is: " ++ show hostName
(respBody, respMetadata, details) <- f body requestMeta methodName (respBody, respMetadata, details) <- f body requestMeta methodName
let status = C.GrpcStatusOk let status = C.GrpcStatusOk
let respOps = serverOpsSendNormalResponse let respOps = serverOpsSendNormalResponse

View file

@ -44,7 +44,7 @@ testClientCreateDestroy =
payloadLowLevelServer :: TestServer payloadLowLevelServer :: TestServer
payloadLowLevelServer = TestServer $ \grpc -> do payloadLowLevelServer = TestServer $ \grpc -> do
let conf = (ServerConfig "localhost" 50051 [("/foo", "localhost", Normal)]) let conf = (ServerConfig "localhost" 50051 [("/foo", Normal)])
withServer grpc conf $ \server -> do withServer grpc conf $ \server -> do
let method = head (registeredMethods server) let method = head (registeredMethods server)
result <- serverHandleNormalRegisteredCall server method 11 M.empty $ result <- serverHandleNormalRegisteredCall server method 11 M.empty $
@ -109,7 +109,7 @@ testClientRequestNoServer =
testServerAwaitNoClient :: TestTree testServerAwaitNoClient :: TestTree
testServerAwaitNoClient = testCase "server wait times out when no client " $ do testServerAwaitNoClient = testCase "server wait times out when no client " $ do
withGRPC $ \grpc -> do withGRPC $ \grpc -> do
let conf = (ServerConfig "localhost" 50051 [("/foo", "localhost", Normal)]) let conf = (ServerConfig "localhost" 50051 [("/foo", Normal)])
withServer grpc conf $ \server -> do withServer grpc conf $ \server -> do
let method = head (registeredMethods server) let method = head (registeredMethods server)
result <- serverHandleNormalRegisteredCall server method 1 M.empty $ result <- serverHandleNormalRegisteredCall server method 1 M.empty $