From Hunt Framework 3.4, a new component named GRPC is added, which provides a convenient way for programming with RPC.
You should have already installed the tools needed to generate client and server interface code – if you haven’t, see the here.
Our first step (as you’ll know from the Introduction to gRPC) is to define the gRPC service and the method request
and response
types using protocol buffers. For the complete .proto
file, see helloworld.proto.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayGoodBye (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
Next we need to generate the gRPC client and server interfaces from our .proto
service definition. We do this using the protocol buffer compiler protoc
with a special gRPC D plugin. This is similar to what we did in the grpc-dlang.
From the protos
directory, run the following command:
protoc --plugin=/usr/local/bin/protoc-gen-d --d_out=../source -I ./ helloworld.proto
protoc --plugin=protoc-gen-grpc=/usr/local/bin/grpc_dlang_plugin -I ./ --grpc_out=../source/helloworld helloworld.proto
Running this command generates the following files in the helloworld
directory:
helloworld.d
, which contains all the protocol buffer code to populate, serialize, and retrieve request and response message types.helloworldrpc.d
, which contains the following: An interface type (or stub) for clients to call with the methods defined in theGreeter
service. An interface type for servers to implement, also with the methods defined in theGreeter
service.
In the source
directory, create a file named GreeterImpl.d
:
module GreeterImpl;
import hunt.logging;
import helloworld.helloworld;
import helloworld.helloworldrpc;
import grpc;
class GreeterImpl : GreeterBase {
override Status SayHello(HelloRequest request, ref HelloReply reply) {
reply.message = "Hello " ~ request.name;
tracef("request: %s, reply: %s", request.name, reply.message);
return Status.OK;
}
override Status SayGoodBye(HelloRequest request, ref HelloReply reply) {
reply.message = "Bye " ~ request.name;
tracef("request: %s, reply: %s", request.name, reply.message);
return Status.OK;
}
}
Open the configuration file application.conf
for your Hunt Framework application, and add the settings.
# gRPC
grpc.server.enabled = true
grpc.server.host = 127.0.0.1
grpc.server.port = 50051
grpc.server.workerThreads = 4
Open the main
file of the Hunt application:
import hunt.framework;
import GreeterImpl;
void main(string[] args) {
app.booting(() {
app.grpc.server.register( new GreeterImpl());
});
}
In this section, we’ll look at creating a D client for our Greeter
service.
In the configuration file application.conf
, you can add some settings for one or more gRPC clients:
grpc.clientChannels[0].name = ch1
grpc.clientChannels[0].host = 127.0.0.1
grpc.clientChannels[0].port = 50051
grpc.clientChannels[0].timeout = 15000
grpc.clientChannels[1].name = ch2
grpc.clientChannels[1].host = 127.0.0.1
grpc.clientChannels[1].port = 50052
grpc.clientChannels[1].timeout = 15000
Then you can use the channel name to get one gRPC client.
To call service methods, we first need to create a gRPC channel to communicate with the server. Once the gRPC channel is setup, we need a client stub to perform RPCs. We get it by creating a GreeterClient
instance which is provided by the helloworld
package generated from the example .proto
file. Here is the complete example client code.
import hunt.logging;
import grpc;
import helloworld.helloworld;
import helloworld.helloworldrpc;
class IndexController : Controller {
mixin MakeController;
@Action
string testGrpc() {
string[string] queryParameters = request.queries();
string channelName = request.queries()["channel"];
try {
Channel channel = app.grpc.getChannel(channelName);
scope(exit) {
channel.destroy();
}
GreeterClient client = new GreeterClient(channel);
HelloRequest request = new HelloRequest();
request.name = "Hunt";
HelloReply replyHello = client.SayHello(request);
tracef("++++++++++++++++%s", replyHello.message);
HelloReply replyBye = client.SayGoodBye(request);
tracef("++++++++++++++++%s", replyBye.message);
return channelName ~ " passed";
} catch(Exception ex) {
return ex.msg;
}
}
}
-
Build the Hunt Framework application, and run the server.
2021-Feb-07 14:57:26.399074 | 15835 | info | register.__lambda1 | gRPC server started at 0.0.0.0:50051. | ../../hunt-framework/source/hunt/framework/provider/GrpcServiceProvider.d:41
-
Visit the server in the browser or
curl
.$ curl 'http://127.0.0.1:8080/grpc?channel=ch1'
You’ll see output in the server's terminal like this:
2021-Feb-07 14:57:41.59891 | 15854 | trace | GreeterImpl.SayHello | request: Hunt, reply: Hello Hunt | source/GreeterImpl.d:15
2021-02-07 14:57:41 (15892) [debug] testGrpc - ++++++++++++++++Hello Hunt - source/app/controller/IndexController.d:788
2021-Feb-07 14:57:41.6429258 | 15854 | trace | GreeterImpl.SayGoodBye | request: Hunt, reply: Bye Hunt | source/GreeterImpl.d:21
2021-02-07 14:57:41 (15892) [debug] testGrpc - ++++++++++++++++Bye Hunt - source/app/controller/IndexController.d:791