This article is more than one year old. Older articles may contain outdated content. Check that the information in the page has not become incorrect since its publication.

Analysis of Features in Dubbo Java 2.7.5

Release of 2.7.5 and its feature analysis

Recently, the highly anticipated Dubbo 2.7.5 version was officially released. This version introduces many new features, enhances many existing functionalities, and achieves significant performance improvements. It will be a milestone version for both the Dubbo community and developers alike.

  • Application-level service registration【beta】
  • HTTP/2 (gRPC) protocol support
  • Protobuf support
  • Performance optimization, with a 30% improvement in call chain performance
  • Support for TLS secure transport
  • Optimized consumer-side thread model
  • New load balancing strategies better suited for multi-cluster deployment scenarios
  • A brand new application development API (compatible with older versions)【beta】
  • Additional feature enhancements and bug fixes

First, from the perspective of service discovery, the new version breaks through the previous interface-level model and introduces an application-level service discovery mechanism - service introspection. Although this mechanism is still in beta, it lays a solid foundation for Dubbo’s alignment with the entire microservices cloud-native system. Thanks to compact protocol design and optimization in code implementation, Dubbo has always performed well in terms of performance, with a further improvement in version 2.7.5. According to stress tests from the official maintenance team, performance in the call chain has improved by 30%. In the cloud-native microservices era, multilingual demand is becoming increasingly common. The universality and penetration of protocols are crucial for building a complete microservice system that connects front-end and back-end services. Dubbo supports HTTP/2 protocol by implementing gRPC protocol while also adding compatibility with Protobuf.

1. Application-level Service Registration【beta】

From the perspective of the Java implementation version, Dubbo is a service development framework focused on interface proxies. Service definitions, service publication, and service references are all based on interfaces, while service governance involving service discovery and various rule definitions is also interface-driven. The interface-based approach is a significant advantage of Dubbo, such as shielding developers from remote call details and offering finer governance granularity. However, the service definitions based on interfaces also present issues, such as service compatibility with industry-standard microservice systems.

servicediscovery-old.png

To address these issues, version 2.7.5 introduces a new service definition/governance mechanism: service introspection, which is essentially a service governance solution based on application granularity. An instance registers a single record to the registry, thoroughly resolving performance bottlenecks related to service propagation, and since this model is inherently equivalent to mainstream microservice systems like SpringCloud and K8S, it removes barriers for Dubbo to interconnect with such heterogeneous systems. For more information on how Dubbo’s service introspection mechanism resolves interoperability issues in heterogeneous microservice systems, please refer to our previous article titled “How Dubbo Becomes the Best Service Development Framework for Connecting Heterogeneous Microservice Systems.”

Here is a basic working principle diagram of the service introspection mechanism.

servicediscovery-new.png

For more details about the workings of service introspection, please refer to the official documentation and subsequent articles.

Service introspection is complementary to existing mechanisms. The Dubbo framework will continue to maintain interface-level service governance advantages, realizing a scenario where both interface and application granularities complement each other, balancing performance, flexibility, and universality, striving to make Dubbo the best framework for microservice development.

2. HTTP/2 (gRPC) Protocol Support

Dubbo RPC protocol is built on TCP, which has many advantages but also some drawbacks, such as limited universality and protocol penetration, and is not very friendly for multi-language implementations. Given its standard HTTP protocol attributes, HTTP/2 undoubtedly offers better universality. It is expected to receive good support across various network devices now or in the future. The reason gRPC chose HTTP/2 as its transport-layer carrier is largely due to this factor. Currently, gRPC’s recognition and adoption in cloud-native and Mesh frameworks are gradually increasing, indicating a trend toward becoming the RPC protocol transport standard. While Dubbo and gRPC are competitors at the protocol level, they emphasize different aspects in framework implementation. Dubbo undeniably offers a richer service development and governance experience.

The intuitive benefits of Dubbo supporting gRPC protocol include:

  • Official support for remote communication based on HTTP/2, further enhancing protocol universality and penetration.
  • Support for inter-process streaming communication, enabling reactive-style RPC programming.
  • It resolves the issue where the gRPC framework is difficult to use directly for microservice development, integrating it into Dubbo’s service governance framework.
  • It provides support for connecting existing gRPC or multi-language systems within an organization.

Starting from version 2.7.5, gRPC (HTTP/2) becomes a first-class citizen in the Dubbo protocol system. Developers needing to do so can activate gRPC protocol in the microservice systems developed with Dubbo without being confined to Dubbo’s own protocol. We similarly expressed this viewpoint in the article “How Dubbo Becomes the Best Service Development Framework for Connecting Heterogeneous Microservice Systems.”

For details on how to develop gRPC (HTTP/2) services in Dubbo, please refer to the article “Exploring Dubbo in Cross-language and Protocol Penetration.” For information on how to enable TLS and use Reactive RPC programming, please refer to the examples. Additionally, the Go version of Dubbo also provides equivalent support for gRPC protocol; please follow the dubbogo community’s release plans for specifics.

3. Protobuf Support

Protobuf support is primarily aimed at addressing Dubbo’s cross-language usability.

Cross-language service development involves multiple aspects, requiring language neutrality in service definitions, RPC protocols, and serialization protocols, while also implementing corresponding SDKs for each language. Although due to community contributions, Dubbo has seen gradual improvements in multi-language SDK implementations, already providing client or full implementation versions for Java, Go, PHP, C#, Python, NodeJs, and C, the previously mentioned cross-language friendliness still has many areas for improvement.

In terms of protocol, version 2.7.5 supports gRPC, while Protobuf provides a good solution for service definition and serialization.

  • Service Definition. Currently, Dubbo’s service definitions are tightly coupled to specific programming languages and do not offer a language-neutral service description format. For example, in Java, an interface is defined, while other languages require redefining it in a different format. Thus, Dubbo achieves language-neutral service definitions by supporting Protobuf.
  • Serialization. The serialization formats currently supported by Dubbo include Json, Hessian2, Kryo, FST, Java, etc., but only Json and Hessian2 support cross-language capabilities. Standard Json has inherent performance issues, while Hessian2 is lacking in both efficiency and multi-language SDKs. Therefore, Dubbo offers a more efficient and user-friendly cross-language serialization scheme by supporting Protobuf serialization.

In the future, regardless of which language version is used to develop Dubbo services, we can directly use IDL to define services as shown below; please refer to the example.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "org.apache.dubbo.demo";
option java_outer_classname = "DemoServiceProto";
option objc_class_prefix = "DEMOSRV";

package demoservice;

// The demo service definition.
service DemoService {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

4. Performance Optimization

4.1 Call Chain Optimization

The 2.7.5 version performs comprehensive optimization across the entire call chain. According to stress test results, overall QPS performance has improved by nearly 30%, while also reducing memory allocation overhead during the calling process. One notable design point is that version 2.7.5 introduced the concept of ServiceRepository, generating ServiceDescriptor and MethodDescriptor in advance during the service registration phase to reduce resource consumption caused by computing service metadata during RPC calls.

4.2 Consumer-side Thread Pool Model Optimization

For Dubbo applications prior to 2.7.5, especially some consumer-side applications, when facing a large volume of service consumption with high concurrency (such as gateway scenarios), excessive thread allocation on the consumer side often occurs. Specific problem discussions can be found in the following issue:

https://github.com/apache/dubbo/issues/2013

The improved consumer-side thread pool model effectively solves this problem by reusing blocked threads on the business side.

Old Thread Pool Model

消费端线程池.png

Let’s focus on the Consumer section:

  1. The business thread sends a request and obtains a Future instance.
  2. The business thread then immediately calls future.get to block and wait for the business result to return.
  3. After the business data returns, it is handed over to an independent consumer-side thread pool for deserialization and other processing, and calls future.set to put back the deserialized business result.
  4. The business thread directly returns the result.

Thread Pool Model Introduced in Version 2.7.5

消费端线程池新.png

  1. The business thread sends a request and obtains a Future instance.
  2. Before calling future.get(), it first calls ThreadlessExecutor.wait(), which makes the business thread wait in a blocking queue until an element is added to the queue.
  3. When the business data returns, a Runnable Task is generated and placed in the ThreadlessExecutor queue.
  4. The business thread retrieves the Task and executes it in the same thread: deserializing business data and setting it to Future.
  5. The business thread directly returns the result.

This way, compared to the old thread pool model where the business thread was responsible for monitoring and parsing the return result, it eliminates the overhead of an extra consumer-side thread pool.

Regarding performance optimization, continuous efforts will be made in subsequent versions, primarily focusing on the following two areas:

  1. RPC call chain. Visible points include: further reducing memory allocations in the execution chain, improving protocol transmission efficiency while ensuring protocol compatibility, and increasing the efficiency of calculations in Filters, Routers, etc.
  2. Service governance chain. Further reducing resource consumption caused by address propagation, service governance rule propagation, etc.

Version 2.7.5 has made significant progress in the security of the transmission link. Both the built-in Dubbo Netty Server and the newly introduced gRPC protocol provide a TLS-based secure link transmission mechanism.

The configuration for TLS has a unified entry point, as shown below:

Provider Side

SslConfig sslConfig = new SslConfig();
sslConfig.setServerKeyCertChainPath("path to cert");
sslConfig.setServerPrivateKeyPath(args[1]);
// If mutual cert authentication is enabled
if (mutualTls) {
  sslConfig.setServerTrustCertCollectionPath(args[2]);
}

ProtocolConfig protocolConfig = new ProtocolConfig("dubbo/grpc");
protocolConfig.setSslEnabled(true);

Consumer Side

if (!mutualTls) {
    sslConfig.setClientTrustCertCollectionPath(args[0]);
} else {
    sslConfig.setClientTrustCertCollectionPath(args[0]);
    sslConfig.setClientKeyCertChainPath(args[1]);
    sslConfig.setClientPrivateKeyPath(args[2]);
}

To ensure flexibility in application startups, the specification of TLS Cert can also be dynamically specified during startup based on deployment environments via -D parameters or environment variables, please see Dubbo’s configuration reading rules and TLS examples.

Dubbo Configuration Reading Rules: /zh-cn/docs/user/configuration/configuration-load-process.html

TLS Examples: https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-ssl

If you are using the gRPC protocol, the protocol negotiation mechanism will be utilized when enabling TLS. Therefore, you must use a Provider that supports the ALPN mechanism, with netty-tcnative being the recommended choice. For specifics, please refer to the gRPC Java community’s summary: https://github.com/grpc/grpc-java/blob/master/SECURITY.md

Regarding the security of service calls, Dubbo will continue to invest in future versions, with the authentication mechanism for service discovery/calls expected to be introduced in upcoming releases.

6. Bootstrap API【beta】

In the section discussing “Service Introspection,” we mentioned Dubbo’s interface-oriented design, involving programming, service discovery, and service governance through interfaces. With the introduction of application-level service discovery, version 2.7.5 has also optimized the programming entry point, while being compatible with older version APIs, it has added a new application-oriented programming interface - DubboBootstrap.

For example, programming using the Dubbo API previously required this:

ServiceConfig<GreetingsService> service1 = new ServiceConfig<>();
service1.setApplication(new ApplicationConfig("first-dubbo-provider"));
service1.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
service1.export();

ServiceConfig<GreetingsService> service2 = new ServiceConfig<>();
service2.setApplication(new ApplicationConfig("first-dubbo-provider"));
service2.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
service2.export();

Global configuration such as ApplicationConfig, RegistryConfig, ProtocolConfig would need to be configured for each service; moreover, from the perspective of the Dubbo framework, due to the lack of a unified server entry point, some instance-level configurations like ShutdownHook, ApplicationListener, and application-level service governance components lack a loading driver point.

With the introduction of DubboBootstrap, the new programming model becomes simpler and also addresses the lack of an instance-level startup entry.

ProtocolConfig protocolConfig = new ProtocolConfig("grpc");
protocolConfig.setSslEnabled(true);

SslConfig sslConfig = new SslConfig();
sslConfig.setXxxCert(...);

DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap.application(new ApplicationConfig("ssl-provider"))
  .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
  .protocol(protocolConfig)
  .ssl(sslConfig);

ServiceConfig<GreetingsService> service1 = new ServiceConfig<>();
ServiceConfig<GreetingsService> service2 = new ServiceConfig<>();

bootstrap.service(service1).service(service2);
bootstrap.start();

7. Multi-Registry Cluster Load Balancing

For scenarios subscribing to multiple registries, a new layer of load balancing between registry clusters has been introduced when selecting addresses:

cluster-lb.png

At the Cluster Invoker level, the supported selection strategies include (2.7.5+ version; see documentation for specifics):

  • Specify Priority

    <!-- Addresses from the preferred="true" registry will be prioritized, falling back to others only when no available addresses are found -->
    <dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />
    
  • Same Zone Priority

    <!-- The selection will match with the zone key in the traffic, preferring to dispatch traffic to addresses in the same zone -->
    <dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />
    
  • Weighted Round Robin

    <!-- Addresses from Beijing and Shanghai clusters will distribute traffic at a ratio of 10:1 -->
    <dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight=”100“ />
    <dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight=”10“ />
    
  • Default, stick to any available

Regarding the multiple registry subscription model, Dubbo also offers a Multi-Registry merging solution. We welcome discussions on the following PR: https://github.com/apache/dubbo/issues/5399

8. Other Feature Enhancements

  • New address change event notification interface to help the business side perceive address changes
  • New external configuration loading entry to facilitate developers in customizing service startup parameters during startup
  • Refactoring of the config module
  • Enhanced parameters extension configuration
  • Other bug fixes

From the perspective of the Dubbo framework itself, version 2.7.5 also involves many refactoring and optimizations (such as the refactoring of the config module), which may not be perceptible to users but are great preparations for future feature development and the introduction of new mechanisms through optimizing the internal structure of the entire Dubbo code.

9. Summary and Outlook

In future versions, Dubbo will continue to optimize and iterate rapidly, focusing primarily on the following aspects:

  • Further exploration of service introspection as the main promoted service governance model by Dubbo.
  • Continuing to promote the evolution of the framework for scenarios that enterprise users are concerned about in microservices solutions, including ongoing development of configuration, service authentication mechanisms, circuit breaking, etc. There will also be attempts to collaboratively drive the construction of peripheral supporting facilities such as gateways and governance platforms. We are very much looking forward to enthusiastic participation from the community in this aspect.
  • In terms of performance optimization, efforts will be made primarily in two areas: continuous optimization of the call chain and continued exploration of new, more universal RPC protocols; and optimizations in the service governance propagation mechanism to further enhance Dubbo’s performance in large-scale service address propagation scenarios.
  • In the cloud-native direction, upcoming versions will explore 1. how to better support Dubbo’s deployment and service governance on Kubernetes; 2. for hybrid deployment scenarios, such as traditional VM and K8S hybrid deployments, how to provide better support for long-term coexistence or migration in mixed deployment scenarios.