Please refer to other parts of the documentation for the triple protocol specification and basic usage. This article elaborates on specific details in the Java implementation of the triple protocol.
When using the triple protocol, developers can define RPC services using either Java Interface
or Protobuf (IDL)
. Both definitions have equal protocol capabilities and only affect the programming experience and serialization methods, with the choice of development model depending on the user’s business background.
Suitable for Dubbo veteran users and development teams without cross-language requirements, with the advantage of low learning costs. Dubbo2 legacy users can switch to this protocol at no cost.
Service definition example:
public interface DemoService {
String sayHello(String name);
}
In this mode, serialization methods can include Hessian, JSON, Kryo, JDK, custom extensions, etc. The experience is similar to the older version of the Dubbo protocol, requiring only a change in one protocol configuration item, making migration from Dubbo protocol to triple smoother.
Please check [advanced studies - communication protocols] for specific examples using Java Interface + Triple protocol.
Defining services using Protobuf (IDL) is suitable for development teams that currently or in the future have cross-language requirements. The same IDL service can be used for Java/Go/Node.js, but it has a higher learning cost.
syntax = "proto3";
option java_multiple_files = true;
package org.apache.dubbo.springboot.demo.idl;
message GreeterRequest {
string name = 1;
}
message GreeterReply {
string message = 1;
}
service Greeter{
rpc greet(GreeterRequest) returns (GreeterReply);
}
Using the protoc compilation plugin provided by Dubbo, the above IDL service definition is precompiled into relevant stub code, which includes the interface definitions required by Dubbo; thus, the subsequent coding differs little, except that the plugin automatically generates the interface definitions compared to the earlier user-defined Java Interface mode.
// Generated by dubbo protoc plugin
public interface Greeter extends org.apache.dubbo.rpc.model.DubboStub {
String JAVA_SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter";
String SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter";
org.apache.dubbo.springboot.demo.idl.GreeterReply greet(org.apache.dubbo.springboot.demo.idl.GreeterRequest request);
// more generated codes here...
}
The Protobuf mode supports serialization modes of Protobuf Binary and Protobuf JSON. Lastly, please check [advanced studies - communication protocols] for specific examples of Protobuf (IDL) + Triple protocol.
Yes | No | |
---|---|---|
Does the company’s business involve other languages besides Java, and are cross-language scenarios common? | Protobuf | Java Interface |
Are developers in the company familiar with Protobuf and willing to accept the additional costs? | Protobuf | Java Interface |
Is there a need for standard gRPC interoperability? | Protobuf | Java Interface |
Are you a Dubbo2 veteran user wishing to smoothly migrate to the triple protocol? | Java Interface | Protobuf |
The Triple
protocol’s streaming mode
From the protocol layer’s perspective, Triple
is built on HTTP2
, so it inherits all the capabilities of HTTP2
, including streaming and full-duplex capabilities.
From the framework level, org.apache.dubbo.common.stream.StreamObserver
provides a streaming interface for users, allowing for stream processing of input and output parameters. The framework makes the corresponding interface calls when sending and receiving stream data, ensuring a complete lifecycle for the stream.
Streaming is a new type of invocation provided by Dubbo3, and it is recommended to use streaming in the following scenarios:
Streams are divided into three types.
Server-streaming RPC is similar to Unary RPC, except the server responds to the client’s request and returns a stream of messages. After sending all messages (often multiple), the server sends status information (status code and optional status message) and optional trailing metadata to the client, ending the server stream once all status information is sent. The stream is completed once the client has received all server messages through StreamObserver.
Client-streaming RPC is similar to Unary RPC, except the client sends a message stream (often containing multiple messages) rather than a single message. The server responds with a single message (including status details and optional trailing metadata) - typically after receiving all client messages.
In bidirectional streaming RPC, the client initiates a method call, and the server receives metadata, method name, and deadline from the client’s call, initiating a complete bidirectional stream channel. The server can choose to return its initial metadata or wait for the client to start streaming messages.
Both the client and server stream processing are application-specific. Since these two streams are independent, clients and servers can read and write messages in any order. For instance, a server may wait until it has received all client messages before sending a message back, or they can play “ping pong” — the server receives a request and then sends a response, and the client sends another request based on the response, etc.
For specific examples of Streaming, please refer to Streaming communication.
By adding annotations to the Java interface, REST-style triple services can be published. A specific code example can be found here.
Java Interface
service definition model, unlike Dubbo and the triple protocol. In REST scenarios, we need to add annotations to the Interface, supporting both Spring MVC and JAX_RS annotations.If you recall, the triple protocol natively supports cURL access, similar to the access mode of org.apache.dubbo.springboot.demo.idl.Greeter/greet
. By adding the above annotations, additional REST-style access support can be provided for the triple service, such as a GET request for demo/greet
.
Spring MVC service definition example:
@RestController
@RequestMapping("/demo")
public interface DemoService {
@GetMapping(value = "/hello")
String sayHello();
}
JAX-RS service definition example:
@Path("/demo")
public interface DemoService {
@GET
@Path("/hello")
String sayHello();
}
Business exceptions generated at the Provider end need to be returned as response values to the Consumer client. The consumer can use try catch
to capture possible exceptions:
try {
greeterProxy.echo(REQUEST_MSG);
} catch (YourCustomizedException e) {
e.printStackTrace();
} catch (RpcException e) {
e.printStackTrace();
}
The Dubbo framework will send exception type responses on the provider side according to the following process. Not all business exceptions can be returned as they are; exceptions that cannot be handled will be encapsulated and returned as RpcException
type:
For users planning to migrate completely from the Java interface to Protobuf, the information here may serve as a reference to understand the limitations that may arise with type migration and whether the Protobuf descriptor language can fully describe Java data types.
This article compares the differences between Protobuf and Java Interface as two IDLs, helping Dubbo protocol developers understand Protobuf and paving the way for transitioning to Triple protocol and Grpc protocol.
Protobuf Type | Java Type |
---|---|
double | double |
float | float |
int32 | int |
int64 | long |
uint32 | int[Ref] |
uint64 | long[Ref] |
sint32 | int |
sint64 | long |
fixed32 | int[Ref] |
fixed64 | long[Ref] |
sfixed32 | int |
sfixed64 | long |
bool | boolean |
string | String |
bytes | ByteString |
enum TrafficLightColor {
TRAFFIC_LIGHT_COLOR_INVALID = 0;
TRAFFIC_LIGHT_COLOR_UNSET = 1;
TRAFFIC_LIGHT_COLOR_GREEN = 2;
TRAFFIC_LIGHT_COLOR_YELLOW = 3;
TRAFFIC_LIGHT_COLOR_RED = 4;
}
Enum constants should be in uppercase.
message VipIDToRidReq {
repeated uint32 vipID = 1;
}
Underlyingly, it is actually an ArrayList.
PB does not support unordered and unique collections, only uses arrays as a workaround and requires manual deduplication.
message BatchOnlineRes {
map<uint32, uint32> onlineMap = 1; // Online status
}
message BatchAnchorInfoRes {
map<uint32, AnchorInfo> list = 1; // User info map list
}
/*
* The functionality of the corresponding interface: Batch or single retrieval of user info
*/
message AnchorInfo {
uint32 ownerUid = 1 [json_name="uid"]; // User id
string nickName = 2 [json_name="nn"]; // User nickname
string smallAvatar = 3 [json_name="savt"]; // User avatar full path - small
string middleAvatar = 4 [json_name="mavt"]; // User avatar full path - medium
string bigAvatar = 5 [json_name="bavt"]; // User avatar full path - large
string avatar = 6 [json_name="avt"]; // User avatar
}
Feature | Java Interface | Protobuf | Notes |
---|---|---|---|
Method Overloading | √ | × | |
Generics/Templating | √ | × | |
Method Inheritance | √ | × | |
Nested Definitions | √ | Partially supported | PB only supports nesting of message and enum |
Import Files | √ | √ | |
Nullable Fields | √ | × | |
Multiple Input Parameters | √ | × | PB only supports single input parameter |
Zero Input Parameters | √ | × | PB requires at least one input parameter |
Zero Output Parameters | √ | × | PB requires at least one output parameter |
Input/Output as Abstract Classes | √ | × | PB input/output must be concrete classes |
Input/Output as Interfaces | √ | × | PB input/output must be concrete classes |
Input/Output as Basic Types | √ | × | PB input/output must be structs |