The current chapter is the continuation of Golang related series started with the previous ones:
Golang. CRUD in REST API in a generic way.
Building REST API and implementing CRUD operations are among the most common tasks for Backend developers. Golang…
Golang. Start using Protobuf.
The current chapter is the continuation of Golang related series started with the previous one:
The input for the current chapter is here. The project has REST API endpoint implementation to support usual CRUD operations. It also utilizes Protobuf to persist storage into the file on the disk to keep it between server runs. This chapter goes one step further using Protobuf schema to describe gRPC service and use existing message to pass between server and remote client.
gRPC is open sources framework for interprocess and intercomputer communications. In its simplest and most straightforward usage, gRPC uses Protobuf serialized (i.e. binary format) messages and TCP as transport. In the previous chapter, I used Protobuf only as entity persistence format. As a starting point, the Protobuf schema defining message only already exists:
The schema needs to be extended with Protobuf service which results in RPC generated code:
#5..#6: importing useful here-and-there general purpose messages.
#18..#21: RPC operation can only have one request parameter according to specification. For Update part of CRUD, two parameters need to be passed (ID defined at #19 and Brand defined at #20) so the workaround is to create composite message type.
#23..#29: PRC service definition. This service consists of 5 self-explanatory operations(#24 up to #28). The interesting one is GetList(). As it should return a set of Brands, the return type is marked with stream operator, which is at runtime yielding items one after the other. (The alternative approach is to define response message type, which has repeated Brands field close to what is at #15).
With that schema it is possible to generate Go files for Protobuf messages and gRPC service with the following command:
— go_out: as before telling proto-compiler that the destination folder for Protobuf messages is protobuf.
— go-grpc_out: similar but for gRPC client and server.
— go-grpc_opt: instructs gRPC Go plugin to skip unimplemented feature.
brand.pb.go: generated Go file with Protobuf messages.
brand_grpc.pb.go: generate Go file with gRPC client and server.
Files can be found here. Particular interest draws file brand_grpc.pb.go as it has the signatures of all RPC operation needs to be implemented on server side:
Luckily, it is possible to reuse existing Brand Repository type in gRPC server implementation, to mostly pass data between layers. Moreover, same Repository is used in REST API server created before and it stores data in same file (BTW, serialized with help of Protobuf messages defined in brand.pb.go).
The implemented gRPC server is in protobuf/server/server.go file:
CRUDServiceServer composes Brand Repository. It function signatures repeat generated RPC interface and pass input to Repository and back. The implementation is really straightforward.
Next step is to write function to run gRPC server. In the current chapter, I will have to servers running: old REST API on port 6000 and new gRPC server. It is a somewhat too much and only OK for demo purposes. Luckily, the Brand Repository is the same for both servers, even one instance. Thus, the will be reflection of what changed via REST API with gRPC and vice versa.
Here is the new function in main.go:
#2: create TCP server on port 6002.
#7: create new instance of gRPC server from the standard gRPC package.
#8: link to standard server custom part which knows how to handle CRUD service, defined in Protobuf schema.
#11: put gRPC server online.
Function StartRPCServer(…) should be called as goroutine to allow both servers to operate. Final code of main.go is here.
As I can continue to use brand.rest and VC plugin to test REST API, for gRPC part the Postman can be utilized. Here is blog post on how to set it up. I created the new Postman collection (CRUD) and imported brand.proto within the project. After Postman knows operations and signatures, they become visible in combobox:
Knowing that GetList() has no parameters, it is easy to execute it:
This operation is particularly interesting. Remember how this operation is defined in Protobuf schema:
It returns stream of Brands. Thus, the log shows (bottom up) that the initial response was empty and only after Brands were reaching Postman one after another.
Same results for GetList(…) counterparty in REST API:
Let’s return to Postman to execute GetOne(id) operation:
Passing non-existing ID to this operation results with failure status code and even error message in red box:
The next test is to update the existing Brand:
And again select all Brands to see updated also:
Latest data is also in REST API endpoint:
The final version of the project is here.
gRPC is becoming the major protocol of inter-service communication within microservice architecture. And there multiple reasons ‘why’. My test project is somewhat redundant because it runs 2 servers (for REST API and gRPC) and manipulates with two Brands entity definitions (as plain Go struct for REST API and Protobuf message for gRPC). Hopefully, the next chapter will fix it.