By Deng Ming
First, let's consider a brief overview of gRPC. It's a Remote Procedure Call (RPC) framework launched by Google. It is implemented by using Interface Definition Language (IDL) to compile a client in different languages. gRPC is a very standard implementation of RPC theory. Therefore, gRPC inherently supports a variety of languages. Over the past few years, it has become a standard way to implement cross-language RPC frameworks. Many outstanding RPC frameworks, such as Spring Cloud and Alibaba's Dubbo, support gRPC.
The following snippet shows the server usage in Go.
This mainly consists of two steps. s := grpc.NewServer()
and pb.RegisterGreeterServer(s, &server{})
.
The first step is easy, but the second step, RegisterGreeterServer
, is more difficult because pb.RegisterGreeterServer(s, &server{})
is compiled by the user-defined protobuf
.
Fortunately, the compiled method is essentially as follows:
Therefore, obtain _ Greeter_serviceDesc
in Dubbo-go, to register the server. Hence, in Dubbo-go, the most important issue is how to obtain serviceDesc.
The following snippet displays the client usage.
The procedure is relatively complicated:
1) Create a Connection: conn, err := grpc.Dial(address)
.
2) Create a Client: c := pb.NewGreeterClient(conn)
.
3) Call Mthod: r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
.
It is easy to solve the first problem by reading the address from the user's configuration. The difficulty lies in the second problem. Just like RegisterGreeterServer
, NewGreeterClient
is compiled. It seems that the third problem might be solved by reflection.
However, while opening SayHello, the following screen appears.
Given the definition of greetClient, it is easy to find that the most important part is err := c.cc.Invoke ( ctx, "/helloworld.Greeter/SayHello", in, out, opts... )
. In other words, just create a connection and obtain a method and parameters to impersonate c.SayHello through similar calls. Based on this simple analysis of gRPC, it's easy to figure out a solution. However, there is still a need to link the solution with Dubbo-go.
First, review the overall design of Dubbo-go and identify the level on which to adapt gRPC.
From the preceding features of gRPC, note that gRPC has solved the problems at the codec and transport levels. However, gRPC does not involve the cluster and upper levels. Therefore, this figure shows that the protocol level is most suitable for implementing the adaptation. This implies that it's possible to develop a gRPC protocol like a Dubbo protocol.
The gRPC protocol is basically an adapter that links the underlying gRPC implementation with a specific Dubbo-go.
In Dubbo-go, gRPC is mainly related to the following:
Let's see how the key points mentioned in the gRPC section are implemented.
The preceding figure presents the elements clearly. Like other Dubbo-go protocols, obtain the service and then the serviceDesc to register the service.
Note the string indicated by a red arrow in the preceding figure, ds, ok := service.(" DubboGrpcService)
.
This string seems strange. In theory, the service registered here is the one that is compiled by protobuf on the gRPC server. Obviously, compiling a protobuf interface alone does not implement the DubboGrpcService API.
So, thie critical issue at this point is how to ensure that the execution of ds, ok := service.( DubboGrpcService)
is successful? This is explained in the article later..
Dubbo-go designs its own client to impersonate and encapsulate the client in gRPC.
The definition of this client is similar to that of the preceding greetClient. Check the following NewClient method, which creates a connection and then a client instance through this connection.
Here, the maintained invoker is actually a stub.
The following snippet shows what happens when a call is actually initiated.
The red box indicates the key steps. Use reflection to obtain the called method from the invoker, which is stub. Then, call the method through reflection.
As mentioned earlier, the problem for ds, ok := service.( DubboGrpcService)
is how to have the code compiled by protobuf implement the DubboGrpcService interface.
Some of the preceding code shows how to obtain method instances through reflection based on names, such as method := reflect.ValueOf(impl).MethodByName("GetDubboStub")
in the NewClient method. Here, impl indicates the implementation of the service, which is compiled in protobuf. But how can the code compiled in protobuf contain this GetDubboStub method?
The answer is to modify the code generation logic compiled by protobuf.
Fortunately, protobuf allows us to develop our own code generation logic in the form of plug-ins.
Therefore, we simply need to register our own plug-in as shown below.
Then, this plug-in embeds the required code. For example, it may embed the GetDubboStub
method as hown below.
It may also embed the DubboGrpcService
API as shown below.
This may seem tricky, but it is not! It is only difficult if you don't know how to modify the generated code in the form of a plug-in
. However, once you know this, the process becomes much easier.
Design Ideas for Improving the Transaction System of Ele.me, Alibaba's Food Delivery Service
206 posts | 12 followers
FollowAlibaba Cloud Native Community - January 26, 2024
Alibaba Developer - May 20, 2021
Alibaba Cloud Native Community - December 30, 2021
Alibaba Cloud Native Community - September 12, 2023
Alibaba Cloud Native Community - March 6, 2023
Alibaba Cloud Native Community - April 23, 2023
206 posts | 12 followers
FollowAn end-to-end solution to efficiently build a secure data lake
Learn MoreMulti-source metrics are aggregated to monitor the status of your business and services in real time.
Learn MoreMSE provides a fully managed registration and configuration center, and gateway and microservices governance capabilities.
Learn MoreMore Posts by Alibaba Cloud Native