Protocol Overview

This article details specific aspects of the triple in the Dubbo Java implementation, including configuration methods, performance metrics, etc.

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.

Programming Model

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.

Java Interface

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:

  1. public interface DemoService {
  2. String sayHello(String name);
  3. }

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.

Protobuf

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.

  1. syntax = "proto3";
  2. option java_multiple_files = true;
  3. package org.apache.dubbo.springboot.demo.idl;
  4. message GreeterRequest {
  5. string name = 1;
  6. }
  7. message GreeterReply {
  8. string message = 1;
  9. }
  10. service Greeter{
  11. rpc greet(GreeterRequest) returns (GreeterReply);
  12. }

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.

  1. // Generated by dubbo protoc plugin
  2. public interface Greeter extends org.apache.dubbo.rpc.model.DubboStub {
  3. String JAVA_SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter";
  4. String SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter";
  5. org.apache.dubbo.springboot.demo.idl.GreeterReply greet(org.apache.dubbo.springboot.demo.idl.GreeterRequest request);
  6. // more generated codes here...
  7. }

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.

3. Which programming model should I use, and how to choose?

YesNo
Does the company’s business involve other languages besides Java, and are cross-language scenarios common?ProtobufJava Interface
Are developers in the company familiar with Protobuf and willing to accept the additional costs?ProtobufJava Interface
Is there a need for standard gRPC interoperability?ProtobufJava Interface
Are you a Dubbo2 veteran user wishing to smoothly migrate to the triple protocol?Java InterfaceProtobuf

Streaming Communication

Streaming Implementation Principles

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.

Applicable Scenarios

Streaming is a new type of invocation provided by Dubbo3, and it is recommended to use streaming in the following scenarios:

  • When interfaces need to send large amounts of data that cannot fit in a single RPC request or response and need to be sent in batches.
  • In streaming scenarios, data must be processed in the order sent, with no definite boundaries for the data itself.
  • In push scenarios, multiple messages are sent and processed within the same call context.

Streams are divided into three types.

SERVER_STREAM

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.

Server Stream

CLIENT_STREAM

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.

Client Stream

BIDIRECTIONAL_STREAM

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.

Bidirectional Stream

Stream Semantics Guarantee

  • Provides message boundaries, allowing for separate message handling
  • Strictly ordered, the sending order matches the receiving order
  • Full-duplex, sending does not require waiting
  • Supports cancellation and timeouts

For specific examples of Streaming, please refer to Streaming communication.

REST Support

By adding annotations to the Java interface, REST-style triple services can be published. A specific code example can be found here.

Stream Semantics Guarantee

Currently, the REST protocol only supports the 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 Web Annotations

Spring MVC service definition example:

  1. @RestController
  2. @RequestMapping("/demo")
  3. public interface DemoService {
  4. @GetMapping(value = "/hello")
  5. String sayHello();
  6. }

JAX-RS Annotations

JAX-RS service definition example:

  1. @Path("/demo")
  2. public interface DemoService {
  3. @GET
  4. @Path("/hello")
  5. String sayHello();
  6. }

Exception Type Transmission

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:

  1. try {
  2. greeterProxy.echo(REQUEST_MSG);
  3. } catch (YourCustomizedException e) {
  4. e.printStackTrace();
  5. } catch (RpcException e) {
  6. e.printStackTrace();
  7. }

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:

triple-exception

Appendix

Comparison of Protobuf and Java Native Data Types

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.

1. Data Types

1.1. Basic Types
Protobuf TypeJava Type
doubledouble
floatfloat
int32int
int64long
uint32int[Ref]
uint64long[Ref]
sint32int
sint64long
fixed32int[Ref]
fixed64long[Ref]
sfixed32int
sfixed64long
boolboolean
stringString
bytesByteString

Note

In Java, unsigned 32-bit and 64-bit integers are represented using their signed counterparts, where the top bit is stored only in the sign bit.

2. Composite Types

2.1. Enum
  • Original pb code
  1. enum TrafficLightColor {
  2. TRAFFIC_LIGHT_COLOR_INVALID = 0;
  3. TRAFFIC_LIGHT_COLOR_UNSET = 1;
  4. TRAFFIC_LIGHT_COLOR_GREEN = 2;
  5. TRAFFIC_LIGHT_COLOR_YELLOW = 3;
  6. TRAFFIC_LIGHT_COLOR_RED = 4;
  7. }
  • Generated Java code

image

Enum constants should be in uppercase.

2.2. Arrays
  • Original pb code
  1. message VipIDToRidReq {
  2. repeated uint32 vipID = 1;
  3. }
  • Generated Java code

image

Underlyingly, it is actually an ArrayList.

2.3. Collections

PB does not support unordered and unique collections, only uses arrays as a workaround and requires manual deduplication.

2.4. Dictionaries
  • Original pb code
  1. message BatchOnlineRes {
  2. map<uint32, uint32> onlineMap = 1; // Online status
  3. }
  • Generated Java code

image

2.5. Nesting
  • Original pb code
  1. message BatchAnchorInfoRes {
  2. map<uint32, AnchorInfo> list = 1; // User info map list
  3. }
  4. /*
  5. * The functionality of the corresponding interface: Batch or single retrieval of user info
  6. */
  7. message AnchorInfo {
  8. uint32 ownerUid = 1 [json_name="uid"]; // User id
  9. string nickName = 2 [json_name="nn"]; // User nickname
  10. string smallAvatar = 3 [json_name="savt"]; // User avatar full path - small
  11. string middleAvatar = 4 [json_name="mavt"]; // User avatar full path - medium
  12. string bigAvatar = 5 [json_name="bavt"]; // User avatar full path - large
  13. string avatar = 6 [json_name="avt"]; // User avatar
  14. }
  • Generated Java code

image

3. Default Field Values

  • For strings, the default value is an empty string.
  • For bytes, the default value is empty bytes.
  • For booleans, the default value is false.
  • For numeric types, the default value is zero.
  • For enums, the default value is the first defined enum value, which must be 0.
  • For message fields, the unset field’s exact value is language-specific. For details, refer to the generated code guide.

4. Overall Structure

FeatureJava InterfaceProtobufNotes
Method Overloading×
Generics/Templating×
Method Inheritance×
Nested DefinitionsPartially supportedPB 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

5. Community Resources

Feedback

Was this page helpful?

Yes No

Last modified September 30, 2024: Update & Translate Overview Docs (#3040) (d37ebceaea7)