Guide to Migrating Dubbo Protocol to Triple Protocol
Triple Introduction
For the format and principle of the Triple
protocol, please refer to RPC Communication Protocol
According to the goals of Triple design, the Triple
protocol has the following advantages:
- Capable of cross-language interaction. Both the traditional multi-language and multi-SDK mode and the Mesh cross-language mode require a more general and scalable data transmission protocol.
- Provide a more complete request model. In addition to supporting the traditional Request/Response model (Unary one-way communication), it also supports Stream (streaming communication) and Bidirectional (two-way communication).
- Easy to expand, high penetration, including but not limited to Tracing / Monitoring and other support, should also be recognized by devices at all levels, gateway facilities, etc. can identify data packets, friendly to Service Mesh deployment, and reduce the difficulty of understanding for users.
- Fully compatible with grpc, the client/server can communicate with the native grpc client.
- Components in the existing grpc ecosystem can be reused to meet cross-language, cross-environment, and cross-platform intercommunication requirements in cloud-native scenarios.
For Dubbo users currently using other protocols, the framework provides migration capabilities compatible with existing serialization methods. Without affecting existing online businesses, the cost of migrating protocols is almost zero.
Dubbo users who need to add new connection to Grpc service can directly use the Triple protocol to achieve the connection, and do not need to introduce the grpc client separately to complete it. Not only can the existing Dubbo ease of use be retained, but also the complexity of the program and the development and maintenance can be reduced. Cost, it can be connected to the existing ecology without additional adaptation and development.
For Dubbo users who need gateway access, the Triple protocol provides a more native way to make gateway development or use open source grpc gateway components easier. The gateway can choose not to parse the payload, which greatly improves the performance. When using the Dubbo protocol, the language-related serialization method is a big pain point for the gateway, and the traditional HTTP-to-Dubbo method is almost powerless for cross-language serialization. At the same time, since Triple’s protocol metadata is stored in the request header, the gateway can easily implement customized requirements, such as routing and current limiting.
Dubbo2 protocol migration process
Dubbo2 users use dubbo protocol + custom serialization, such as hessian2 to complete remote calls.
By default, Grpc only supports Protobuf serialization, and it cannot support multi-parameter and method overloading in the Java language.
At the beginning of Dubbo3, one goal was to be perfectly compatible with Dubbo2. Therefore, in order to ensure the smooth upgrade of Dubbo2, the Dubbo framework has done a lot of work to ensure that the upgrade is seamless. Currently, the default serialization is consistent with Dubbo2 as hessian2
.
Therefore, if you decide to upgrade to the Triple
protocol of Dubbo3, you only need to modify the protocol name in the configuration to tri
(note: not triple).
Next we use a [project] (https://github.com/apache/dubbo-samples/tree/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/ org/apache/dubbo/sample/tri/migration) as an example, how to upgrade safely step by step.
- Only use the
dubbo
protocol to startprovider
andconsumer
, and complete the call. - Use
dubbo
andtri
protocols to startprovider
, usedubbo
protocol to startconsumer
, and complete the call. - Start
provider
andconsumer
using onlytri
protocol, and complete the call.
Define the service
- Define the interface
public interface IWrapperGreeter {
//...
/**
* This is a normal interface, not serialized using pb
*/
String sayHello(String request);
}
- The implementation class is as follows
public class IGreeter2Impl implements IWrapperGreeter {
@Override
public String sayHello(String request) {
return "hello," + request;
}
}
Only use dubbo protocol
To ensure compatibility, we first upgrade some providers to the dubbo3
version and use the dubbo
protocol.
Start a [Provider
] using the dubbo
protocol (https://github.com/apache/dubbo-samples/blob/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org /apache/dubbo/sample/tri/migration/ApiMigrationDubboProvider.java) and Consumer
, complete the call, the output is as follows:
Use dubbo and triple protocol at the same time
For the upgrade of online services, it is impossible to complete the provider and consumer upgrades at the same time. It needs to be operated step by step to ensure business stability. In the second step, the provider provides a dual-protocol way to support dubbo + tri clients at the same time.
The structure is shown in the figure:
According to the recommended upgrade steps, the provider already supports the tri protocol, so the consumer of dubbo3 can directly use the tri protocol
Start [Provider
] using dubbo
protocol and triple
protocol (https://github.com/apache/dubbo-samples/blob/master/3-extensions/protocol/dubbo-samples-triple/src/main /java/org/apache/dubbo/sample/tri/migration/ApiMigrationBothProvider.java) and [Consumer
](https://github.com/apache/dubbo-samples/blob/master/3-extensions/protocol/ dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration/ApiMigrationBothConsumer.java), complete the call, the output is as follows:
Only use triple protocol
When all consuemr are upgraded to a version that supports the Triple
protocol, the provider can be switched to only use the Triple
protocol to start
The structure is shown in the figure:
[Provider](https://github.com/apache/dubbo-samples/blob/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/ migration/ApiMigrationTriProvider.java) and [Consumer](https://github.com/apache/dubbo-samples/blob/master/3-extensions/protocol/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri /migration/ApiMigrationTriConsumer.java) to complete the call, the output is as follows:
Implementation principle
Through the upgrade process described above, we can easily complete the upgrade by modifying the protocol type. How does the framework help us do this?
Through the introduction of the Triple
protocol, we know that the data type of Triple
in Dubbo3 is a protobuf
object, so why non-protobuf
java objects can also be transmitted normally.
Here Dubbo3 uses an ingenious design, first judge whether the parameter type is a protobuf
object, if not. Use a protobuf
object to wrap request
and response
, which shields the complexity brought by other serialization. Declare the serialization type inside the wrapper
object to support serialization extensions.
The IDL of protobuf
of wrapper is as follows:
syntax = "proto3";
package org.apache.dubbo.triple;
message TripleRequestWrapper {
//hessian4
// json
string serializeType = 1;
repeated bytes args = 2;
repeated string argTypes = 3;
}
message TripleResponseWrapper {
string serializeType = 1;
bytes data = 2;
string type = 3;
}
For requests, use TripleRequestWrapper
for wrapping, and for responses, use TripleResponseWrapper
for wrapping.
For request parameters, you can see that args is modified by
repeated
, this is because multiple parameters of the java method need to be supported. Of course, serialization can only be one. The implementation of serialization follows the spi implemented by Dubbo2
Multilingual users (using Protobuf)
It is recommended that all new services use this method
For Dubbo3 and Triple, the main recommendation is to use protobuf
serialization, and use IDL
defined by proto
to generate related interface definitions. Using IDL
as a common interface convention in multiple languages, coupled with the natural interoperability of Triple
and Grpc
, can easily achieve cross-language interaction, such as Go language.
Use the dubbo-compiler
plug-in to compile the prepared .proto
file and write the implementation class to complete the method call:
From the upgrade example above, we can know that the Triple
protocol uses protbuf
objects to be serialized for transmission, so there is no other logic for methods that are themselves protobuf
objects.
The interface after compiling with the protobuf
plugin is as follows:
public interface PbGreeter {
static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter";
static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter";
static final boolean inited = PbGreeterDubbo.init();
org.apache.dubbo.sample.tri.GreeterReply greet(org.apache.dubbo.sample.tri.GreeterRequest request);
default CompletableFuture<org.apache.dubbo.sample.tri.GreeterReply> greetAsync(org.apache.dubbo.sample.tri.GreeterRequest request){
return CompletableFuture. supplyAsync(() -> greet(request));
}
void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver);
org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterRequest> greetStream(org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver);
}
Open the new feature of Triple - Stream (stream)
Stream is a new call type provided by Dubbo3. It is recommended to use stream in the following scenarios:
- The interface needs to send a large amount of data. These data cannot be placed in an RPC request or response, and need to be sent in batches. However, if the application layer cannot solve the order and performance problems in the traditional multiple RPC method, if the order needs to be guaranteed , it can only be sent serially
- In streaming scenarios, data needs to be processed in the order they are sent, and the data itself has no definite boundary
- In push scenarios, multiple messages are sent and processed in the context of the same call
Stream is divided into the following three types:
- SERVER_STREAM (server stream)
- CLIENT_STREAM (client stream)
- BIDIRECTIONAL_STREAM (bidirectional stream)
Due to the limitations of the
java
language, the implementation of BIDIRECTIONAL_STREAM and CLIENT_STREAM is the same.
In Dubbo3, the stream interface is declared and used as SteamObserver
, and users can use and implement this interface to send and handle stream data, exceptions, and end.
For Dubbo2 users, they may be unfamiliar with StreamObserver, which is a stream type defined by Dubbo3. There is no Stream type in Dubbo2, so it has no impact on migration scenarios.
Stream Semantic Guarantees
- Provide message boundaries, which can easily process messages separately
- Strictly ordered, the order of the sender is consistent with the order of the receiver
- Full duplex, no need to wait for sending
- Support cancellation and timeout
Non-PB serialized stream
- api
public interface IWrapperGreeter {
StreamObserver<String> sayHelloStream(StreamObserver<String> response);
void sayHelloServerStream(String request, StreamObserver<String> response);
}
The method input parameters and return values of the Stream method are strictly agreed. In order to prevent problems caused by writing errors, the Dubbo3 framework side checks the parameters, and throws an exception if there is an error. For
BIDIRECTIONAL_STREAM
, it should be noted thatStreamObserver
in the parameter is the response stream, andStreamObserver
in the return parameter is the request stream.
- Implementation class
public class WrapGreeterImpl implements WrapGreeter {
//...
@Override
public StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
return new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println(data);
response.onNext("hello,"+data);
}
@Override
public void onError(Throwable throwable) {
throwable. printStackTrace();
}
@Override
public void onCompleted() {
System.out.println("onCompleted");
response.onCompleted();
}
};
}
@Override
public void sayHelloServerStream(String request, StreamObserver<String> response) {
for (int i = 0; i < 10; i++) {
response.onNext("hello," + request);
}
response.onCompleted();
}
}
- Call method
delegate.sayHelloServerStream("server stream", new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println(data);
}
@Override
public void onError(Throwable throwable) {
throwable. printStackTrace();
}
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
});
StreamObserver<String> request = delegate.sayHelloStream(new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println(data);
}
@Override
public void onError(Throwable throwable) {
throwable. printStackTrace();
}
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
});
for (int i = 0; i < n; i++) {
request.onNext("stream request" + i);
}
request.onCompleted();
Serialized stream using Protobuf
For the Protobuf
serialization method, it is recommended to write IDL
and use the compiler
plugin to compile and generate. The generated code is roughly as follows:
public interface PbGreeter {
static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter";
static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter";
static final boolean inited = PbGreeterDubbo.init();
//...
void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver);
org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterRequest> greetStream(org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver);
}
Implementation principle of stream
How does the streaming mode of the Triple
protocol support it?
From the perspective of the protocol layer,
Triple
is built on the basis ofHTTP2
, so it directly has all the capabilities ofHTTP2
, so it has the ability to splitstream
and full-duplex.In terms of the framework layer,
StreamObserver
is provided to users as a stream interface to provide stream processing for input and output parameters. The framework makes corresponding interface calls when sending and receiving stream data, so as to ensure the integrity of the life cycle of the stream.
Triple and application-level registration discovery
The application-level service registration and discovery of the Triple protocol is consistent with other languages, and you can learn more about it through the following content.
Intercommunicate with GRPC
Through the introduction of the protocol, we know that the Triple
protocol is based on HTTP2
and compatible with GRPC
. In order to ensure and verify the interoperability with GRPC
, Dubbo3 has also written various tests in slave scenarios. For details, you can learn more through here.
Future: Everything on Stub
Students who have used Grpc
should be familiar with Stub
.
Grpc uses compiler
to compile the written proto
file into related protobuf objects and related rpc interfaces. By default several different stubs
will be generated at the same time
-blockingStub
- futureStub -reactorStub -…
stub
helps us shield the details of different calling methods in a unified way. However, currently Dubbo3
only supports the traditional way of defining and calling interfaces.
In the near future, Triple
will also implement various commonly used Stub
, allowing users to write a proto
file, which can be conveniently used in any scene through comipler
, please wait and see.