Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java Rust Dubbo Spring Boot

English | Turkish

A reflection-free Dubbo consumer and provider for Spring Boot 3. Java keeps the business logic. A small Rust native data plane handles TCP connections, Dubbo framing, timeouts, heartbeats, backpressure, and provider I/O.

The public package contains the Java API and verified Windows, Linux, and Apple Silicon macOS native artifacts. Rust source is maintained in a separate private repository.

Contents

At A Glance

You keep This library replaces Intentionally not included
Spring Boot, Java services, validation, repositories, DTOs Official Dubbo runtime, Netty, Java Hessian, runtime proxies and reflection ZooKeeper, Curator, metadata center, routers, generic invocation and callbacks
Spring bean
  -> generated typed client and Hessian codec
  -> JNI
  -> bounded Rust TCP data plane
  -> Dubbo provider
  -> generated Java dispatcher
  -> your Java service

Use this library when provider addresses are static or available through Kubernetes Service DNS. Use official Dubbo when registry governance or the omitted Dubbo features are required.

Requirements

  • Java 21
  • Spring Boot 3; version 3.2.4 is verified
  • Maven 3.9 or newer
  • Windows x64, Linux x64 with GLIBC 2.17 or newer, or Apple Silicon macOS 11 or newer
  • A shared Java contract artifact used by both consumer and provider

Current release: 0.4.1.

What Changed In 0.4.1

Provider exceptions now keep the provider message and reported exception type instead of becoming a generic framework error. This works for Rust-to-Rust calls and for supported Apache Dubbo interoperability in both directions. Synchronous failures and failed CompletableFuture results use the same contract.

import com.reactor.rust.dubbo.runtime.DubboRemoteBusinessException;

try {
    catalogService.findCustomer(customerId);
} catch (DubboRemoteBusinessException failure) {
    log.warn("Provider call failed: type={}, message={}",
            failure.remoteType(), failure.remoteMessage());
}

Use typed result records and stable error codes for expected outcomes. Use exceptions for failed calls. Remote stack traces are not transferred; inspect provider logs and traces for the full stack. The Java annotation API, generated method signatures, success path, configuration keys, and native ABI 3 are unchanged.

Quick Start

1. Allow Maven To Read GitHub Packages

GitHub Packages requires authentication even when the repository is public. Create a classic GitHub token with read:packages. Keep it outside the project.

Add the server to ~/.m2/settings.xml:

<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <servers>
    <server>
      <id>github</id>
      <username>YOUR_GITHUB_USERNAME</username>
      <password>${env.GITHUB_PACKAGES_TOKEN}</password>
    </server>
  </servers>
</settings>

Set the token only in the current shell:

$env:GITHUB_PACKAGES_TOKEN='YOUR_CLASSIC_PAT'
export GITHUB_PACKAGES_TOKEN='YOUR_CLASSIC_PAT'

2. Add The Maven Configuration

Add the repository, starter, code generator, one native platform artifact, and build enhancer to your application POM:

<properties>
  <java-rust-dubbo.version>0.4.1</java-rust-dubbo.version>
</properties>

<repositories>
  <repository>
    <id>github</id>
    <url>https://maven.pkg.github.com/esasmer-dou/java-rust-dubbo-spring-boot</url>
  </repository>
</repositories>

<pluginRepositories>
  <pluginRepository>
    <id>github</id>
    <url>https://maven.pkg.github.com/esasmer-dou/java-rust-dubbo-spring-boot</url>
  </pluginRepository>
</pluginRepositories>

<dependencies>
  <dependency>
    <groupId>com.reactor</groupId>
    <artifactId>java-rust-dubbo-spring-boot-starter</artifactId>
    <version>${java-rust-dubbo.version}</version>
  </dependency>

  <dependency>
    <groupId>com.reactor</groupId>
    <artifactId>java-rust-dubbo-native-linux-x64</artifactId>
    <version>${java-rust-dubbo.version}</version>
    <scope>runtime</scope>
  </dependency>

  <dependency>
    <groupId>com.reactor</groupId>
    <artifactId>java-rust-dubbo-codegen</artifactId>
    <version>${java-rust-dubbo.version}</version>
    <scope>provided</scope>
    <optional>true</optional>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.13.0</version>
      <configuration>
        <release>21</release>
        <parameters>true</parameters>
        <annotationProcessorPaths>
          <path>
            <groupId>com.reactor</groupId>
            <artifactId>java-rust-dubbo-codegen</artifactId>
            <version>${java-rust-dubbo.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>

    <plugin>
      <groupId>com.reactor</groupId>
      <artifactId>java-rust-dubbo-enhancer-maven-plugin</artifactId>
      <version>${java-rust-dubbo.version}</version>
      <executions>
        <execution>
          <goals>
            <goal>enhance</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Choose exactly one native artifact for the machine that runs the JVM:

Runtime Native artifact
Linux x64, GLIBC 2.17+ java-rust-dubbo-native-linux-x64
Windows x64 java-rust-dubbo-native-windows-x64
Apple Silicon macOS 11+ java-rust-dubbo-native-macos-aarch64

Do not package multiple native artifacts in one deployment.

Apple Silicon macOS

Use an ARM64 Java 21 JDK on M1, M2, M3, M4, or newer Apple Silicon hardware. Add this runtime dependency instead of the Linux or Windows artifact:

<dependency>
  <groupId>com.reactor</groupId>
  <artifactId>java-rust-dubbo-native-macos-aarch64</artifactId>
  <version>${java-rust-dubbo.version}</version>
  <scope>runtime</scope>
</dependency>

Confirm that both the machine and JVM are ARM64:

uname -m
java -XshowSettings:properties -version 2>&1 | grep os.arch

Expected values are arm64 from uname and aarch64 or arm64 from Java. An x86_64 JVM is running through Rosetta and cannot load this artifact. Intel Macs are not supported by this module.

The dylib is built on a GitHub-hosted Apple Silicon runner with MACOSX_DEPLOYMENT_TARGET=11.0. CI verifies its ARM64 Mach-O architecture, macOS 11 minimum version, system-only dynamic dependencies, code signature, and JNI ABI before publication. This is a macOS desktop JVM library, not an iOS library. The runtime extracts it to ~/.java-rust-dubbo/native/<hash>/ and reuses it.

3. Define A Shared Contract

Place the interface and DTOs in a small shared JAR. Consumer and provider must use the same contract version and package names.

package com.example.store.api;

public record StoreView(long id, String code, String name) {}
package com.example.store.api;

public interface StoreQueryService {
    StoreView find(long id);
}

4. Create The Consumer

Use the Reactor-owned annotations. They keep Apache and Rust classes unambiguous during a gradual migration:

package com.example.store.consumer;

import com.reactor.rust.dubbo.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableDubbo
@SpringBootApplication
public class StoreConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(StoreConsumerApplication.class, args);
    }
}
package com.example.store.consumer;

import com.example.store.api.StoreQueryService;
import com.example.store.api.StoreView;
import com.reactor.rust.dubbo.annotation.DubboReference;
import org.springframework.stereotype.Service;

@Service
public final class StoreFacade {
    @DubboReference(check = true, group = "store", version = "1.0")
    private StoreQueryService storeQueryService;

    public StoreView find(long id) {
        return storeQueryService.find(id);
    }
}

@DubboReference fields must be instance fields, assignable, and typed as an interface. Do not mark them static or final. The Maven enhancer injects the generated client without runtime reflection.

Consumer configuration:

reactor.dubbo.profile=micro
reactor.dubbo.consumer.providers=127.0.0.1:20880
reactor.dubbo.consumer.timeout-ms=3000

5. Create The Provider

Business code remains a normal Java implementation:

package com.example.store.provider;

import com.example.store.api.StoreQueryService;
import com.example.store.api.StoreView;
import com.reactor.rust.dubbo.annotation.DubboService;

@DubboService(
    interfaceClass = StoreQueryService.class,
    group = "store",
    version = "1.0",
    executor = "store-query")
public final class StoreQueryServiceImpl implements StoreQueryService {
    private final StoreRepository repository;

    public StoreQueryServiceImpl(StoreRepository repository) {
        this.repository = repository;
    }

    @Override
    public StoreView find(long id) {
        return repository.find(id);
    }
}

Provider configuration:

reactor.dubbo.profile=micro
reactor.dubbo.provider.enabled=true
reactor.dubbo.provider.port=20880
reactor.dubbo.provider.executors.store-query.max-concurrent=16

The generator creates the Spring bean registration, typed dispatcher, method IDs, and Hessian codecs during the build. No runtime classpath scan or Java proxy is created.

6. Build And Run

mvn -U clean package
java -jar target/your-application.jar

NetBeans 17 And Java 21

Use framework 0.3.1 or newer when the project is opened with NetBeans 17. Earlier code generators linked directly to a Java 21 compiler enum field that is absent from NetBeans 17's embedded parser model. Maven builds succeeded, but the IDE could mark valid @DubboReference classes as Error parsing file.

After upgrading the Maven property, reload the Maven project and run Clean and Build. No source-code or annotation change is required.

Annotation Package And 0.3 Upgrade

Version 0.3.0 uses one canonical package:

import com.reactor.rust.dubbo.annotation.DubboReference;
import com.reactor.rust.dubbo.annotation.DubboService;
import com.reactor.rust.dubbo.annotation.EnableDubbo;

When upgrading from 0.2.x, change these imports and the Maven version. Controllers, business services, contracts, DTOs, route properties, and native settings do not change.

The starter does not bring classes under org.apache.dubbo.*. This is intentional. An existing official Apache provider can remain unchanged while a new Rust consumer uses the canonical Reactor annotation. Official Apache annotations are ignored by Reactor codegen in the default build.

If a large application cannot change old imports immediately, add the optional java-rust-dubbo-apache-compat-annotations dependency. Then set -Areactor.dubbo.apacheCompatibility=true in maven-compiler-plugin and <apacheCompatibility>true</apacheCompatibility> in the enhancer plugin. Treat this as a temporary migration mode.

Do not enable compatibility mode in an application that still contains real Apache Dubbo annotations. That mode deliberately processes the old package and cannot distinguish an old Reactor shim from a real Apache service. Use canonical Reactor imports on only the classes you migrate.

If Spring Boot Actuator is present, the library contributes rustDubboHealthIndicator. Check it through /actuator/health. The details include the selected profile, client readiness, provider readiness, and native metrics.

Choose A Profile

Start with micro. Change profile only after measuring RSS, p99 latency, rejection count, CPU throttling, and downstream pool wait together.

Profile Use it for Trade-off
micro Low-traffic or memory-first pods Smallest worker, queue, connection, and buffer budgets
balanced Steady mixed traffic More concurrency and smoother bursts with moderate RSS
throughput Load-tested high-volume services Highest concurrency; larger queues and retained memory

An explicit property always overrides the selected profile.

Profile defaults
Setting micro balanced throughput
Runtime I/O workers 1 2 4
Callback workers 1 2 4
Callback queue 256 512 2048
Native thread stack 256 KiB 256 KiB 512 KiB
Connections per endpoint 2 2 4
Command queue per connection 32 128 256
Consumer max in-flight 64 256 1024
Retained request buffers 16 32 64
Largest retained buffer 64 KiB 128 KiB 256 KiB
Provider I/O workers 1 2 4
Provider business workers 4 8 16
Provider queue 64 256 1024
Default provider concurrency 16 128 512

Critical Runtime Limits

The following values are the effective micro defaults unless an explicit property overrides them. Profile-dependent values grow in balanced and throughput.

Limit Default What it protects
Consumer RPC deadline 3000 ms Total time spent waiting for an in-flight permit, command queue, and response
Consumer startup wait 3000 ms Time allowed for required providers to become reachable
TCP connect attempt 3000 ms Internal limit for one native connection attempt; reconnect continues after failure
Provider request deadline 30000 ms Provider queue, Java dispatch, and response completion time
Provider drain wait 10000 ms Graceful shutdown wait for active work
Consumer payload 8388608 bytes (8 MiB) One encoded request or response body
Provider payload 8388608 bytes (8 MiB) One incoming request or generated response body
Decoded collection 100000 items Object growth caused by a large List, Set, array, or Map
Connections per endpoint 2 Persistent native connections and Kubernetes pod distribution
Consumer max in-flight 64 Outstanding calls per generated client
Command queue 32 per connection Calls waiting for a native connection
Callback queue 256 Async completions waiting for a callback worker
Provider business workers 4 Java business dispatch threads
Provider default concurrency 16 Active calls sharing the default executor lane
Provider queue 64 Work waiting for Java business workers
Retained request buffers 16, at most 65536 bytes each Reusable direct-buffer retention after calls complete

How Payload Limits Work

max-payload-bytes is a per-call hard ceiling. It is not reserved memory and it is not a total process memory limit.

  • Consumer request buffers start at 1024 bytes and grow only when encoding needs more space.
  • Provider response buffers also start at 1024 bytes and grow only when needed.
  • Outbound requests are checked after Hessian2 encoding and before they enter the native command queue.
  • Incoming frame length is checked before the complete body is allocated.
  • Response growth stops at the configured provider limit.
  • The limit applies to the encoded Dubbo body, including protocol and Hessian2 data. It may be slightly larger than the business DTO content.
  • Consumer and provider limits should normally match. A provider limit may be lower when the service deliberately accepts smaller requests.

An 8 MiB limit does not allocate 8 MiB for every call. However, it permits a call to grow that large. Memory risk is therefore related to both payload and concurrency:

possible active payload memory ~= max-payload-bytes x active large calls

Do not keep an 8 MiB ceiling when valid payloads are below 256 KiB. Lowering the limit reduces the maximum burst allocation and rejects invalid data earlier.

reactor.dubbo.consumer.max-collection-items is a separate object-allocation guard. The generated provider dispatcher uses the same codec limit, even though the property is under consumer. A small encoded payload can still create many Java objects, so tune both byte and collection limits.

Example for small JSON-like DTO contracts:

reactor.dubbo.consumer.max-payload-bytes=1048576
reactor.dubbo.provider.max-payload-bytes=1048576
reactor.dubbo.consumer.max-collection-items=5000
reactor.dubbo.consumer.max-in-flight=32
reactor.dubbo.consumer.retained-buffers=8
reactor.dubbo.consumer.max-retained-buffer-bytes=65536

There is currently no per-method payload override. Move unusually large contracts to a separate deployment or process when they require a very different memory budget.

How Timeouts Work

The consumer timeout is one end-to-end RPC budget. Queue wait reduces the time left for the response. When the deadline expires, pending native state is removed and the invocation fails without automatic replay.

The provider timeout includes waiting in the provider business queue and waiting for the Java result. On expiry, Rust cancels the native response handle and returns a server-timeout response. Rust cannot safely interrupt a synchronous Java method that is blocked inside JDBC or an HTTP client. Those dependencies must have their own shorter timeouts.

A practical timeout chain leaves a small margin at every layer:

# Example when the inbound HTTP deadline is 3000 ms.
reactor.dubbo.provider.request-timeout-ms=2000
reactor.dubbo.consumer.timeout-ms=2500

In this example, database and outbound HTTP timeouts should be below 2000 ms. Per-method timeout annotation values are not supported.

Compression

The native wire path does not currently compress payloads. There is no Gzip, LZ4, Zstd, or compression negotiation. Frames use the supported Hessian2 encoding directly.

This avoids compression CPU, temporary buffers, and p99 latency on normal API payloads. For large data, prefer a smaller DTO, pagination, or a separate bulk-transfer design. Application-managed compressed byte[] is possible, but the application must enforce a maximum decompressed size and accept the extra CPU and allocation cost.

Common Production Recipes

Memory-First Consumer

Use this as a starting point for a small service with modest traffic and payloads below 1 MiB:

reactor.dubbo.profile=micro
reactor.dubbo.consumer.providers=store-provider:20880
reactor.dubbo.consumer.connections-per-endpoint=1
reactor.dubbo.consumer.max-in-flight=32
reactor.dubbo.consumer.command-queue-capacity=16
reactor.dubbo.consumer.retained-buffers=8
reactor.dubbo.consumer.max-payload-bytes=1048576

Query And Command Provider

Keep database writes inside the real DB pool capacity. A larger Dubbo queue does not create database capacity.

@DubboService(executor = "query")
final class QueryServiceImpl implements QueryService { /* business code */ }

@DubboService(executor = "command")
final class CommandServiceImpl implements CommandService { /* business code */ }
reactor.dubbo.profile=balanced
reactor.dubbo.provider.enabled=true
reactor.dubbo.provider.business-workers=8
reactor.dubbo.provider.queue-capacity=64
reactor.dubbo.provider.executors.query.max-concurrent=8
reactor.dubbo.provider.executors.command.max-concurrent=2

One Large Response

Raise only the required limit. Keep collection count and retained buffers bounded. Prefer pagination for large lists.

reactor.dubbo.consumer.max-payload-bytes=16777216
reactor.dubbo.consumer.max-collection-items=20000
reactor.dubbo.consumer.max-retained-buffer-bytes=65536

Kubernetes Without ZooKeeper

Expose the provider through a normal Kubernetes Service:

apiVersion: v1
kind: Service
metadata:
  name: store-provider
spec:
  selector:
    app: store-provider
  ports:
    - name: dubbo
      port: 20880
      targetPort: 20880

Point the consumer to that Service DNS name:

env:
  - name: REACTOR_DUBBO_PROFILE
    value: "micro"
  - name: REACTOR_DUBBO_CONSUMER_PROVIDERS
    value: "store-provider.platform.svc.cluster.local:20880"

Kubernetes balances new TCP connections. Existing persistent connections remain on the pod selected when they were opened. Increase connections-per-endpoint only when load tests show that more provider-pod distribution is needed. Use readiness probes, graceful shutdown, and a termination grace period longer than reactor.dubbo.provider.drain-timeout-ms.

Multiple Provider Applications

Keep the Java fields unchanged. The exact interface + group + version identity decides which route is used:

@Service
public final class CheckoutService {
    @DubboReference
    private CustomerService customerService;

    @DubboReference(group = "sales", version = "v2")
    private OrderService orderService;
}

Route each service to its own provider application:

reactor.dubbo.profile=micro
reactor.dubbo.consumer.require-explicit-routes=true

reactor.dubbo.consumer.routes.customer.interface-name=com.example.customer.CustomerService
reactor.dubbo.consumer.routes.customer.providers=customer-provider:20880
reactor.dubbo.consumer.routes.customer.connections-per-endpoint=2
reactor.dubbo.consumer.routes.customer.max-in-flight=32
reactor.dubbo.consumer.routes.customer.timeout-ms=750

reactor.dubbo.consumer.routes.order.interface-name=com.example.order.OrderService
reactor.dubbo.consumer.routes.order.group=sales
reactor.dubbo.consumer.routes.order.version=v2
reactor.dubbo.consumer.routes.order.providers=order-provider:20880
reactor.dubbo.consumer.routes.order.connections-per-endpoint=2
reactor.dubbo.consumer.routes.order.max-in-flight=16
reactor.dubbo.consumer.routes.order.timeout-ms=1500

customer and order are readable labels only. Matching uses the full interface name, group, and version. Omitted per-route limits inherit the selected profile or global consumer value.

Use require-explicit-routes=true in production when references target different provider applications. A missing or duplicate exact route then stops startup. With the default false, an unmatched reference uses the backward-compatible global reactor.dubbo.consumer.providers value.

Route selection happens once while generated clients are created. It adds no reflection, registry query, proxy dispatch, or Java map lookup to each call. Routes have separate bounded connections and queues but share one small Rust Tokio runtime; no thread pool is created per route.

For Kubernetes, put the route map in application.yml or a mounted ConfigMap. Dynamic map names should not be split into individual environment variables. If one environment variable is required, use Spring Boot's standard SPRING_APPLICATION_JSON:

env:
  - name: SPRING_APPLICATION_JSON
    value: >-
      {"reactor":{"dubbo":{"consumer":{"require-explicit-routes":true,
      "routes":{"customer":{"interface-name":"com.example.customer.CustomerService",
      "providers":"customer-provider.platform.svc.cluster.local:20880"},
      "order":{"interface-name":"com.example.order.OrderService","group":"sales",
      "version":"v2","providers":"order-provider.platform.svc.cluster.local:20880"}}}}}}

Conditional References

You can keep an optional integration behind a normal Spring condition:

@Component
@ConditionalOnProperty(name = "vehicle.express.enabled", havingValue = "true")
public final class ExpressVehicleClient {
    @DubboReference(group = "vehicle", version = "1.0")
    private ExpressVehicleService service;
}

With vehicle.express.enabled=false, Spring does not create the bean. The library therefore opens no native client, creates no socket, and does not require an explicit route for that reference.

With vehicle.express.enabled=true, the native client is created once during bean injection. Normal strict validation still applies. Add the exact route when require-explicit-routes=true:

reactor.dubbo.consumer.routes.express.interface-name=com.example.ExpressVehicleService
reactor.dubbo.consumer.routes.express.group=vehicle
reactor.dubbo.consumer.routes.express.version=1.0
reactor.dubbo.consumer.routes.express.providers=express-provider:20880

This behavior also applies to beans controlled by Spring profiles. It does not add runtime reflection or per-call route lookup.

Supported Contract Surface

  • Supported scalar types: Java primitives, boxed primitives, String, BigDecimal, Date, LocalDate, LocalTime, and LocalDateTime.
  • Supported structures: enums, arrays, byte[], List, Set, Collection, Map, nested records, and compatible Java beans.
  • Records are the simplest DTO choice. A bean must have readable properties and either a compatible builder or a no-argument constructor with writable properties.
  • Synchronous return values, void, and CompletableFuture<T> are supported.
  • Collection and payload limits are checked while decoding. Keep both limits close to valid business sizes.

@DubboService Parameters

Parameter Default Meaning and recommended use
interfaceClass void.class Type-safe service contract. Prefer interfaceClass = StoreQueryService.class in new code, especially when the implementation has multiple interfaces.
interfaceName Empty Fully qualified contract name as text. Use it only when source compatibility requires a string. Prefer interfaceClass; do not set both.
group Empty Logical namespace for services that share the same interface. The consumer value must match exactly.
version Empty Contract version used in service identity. The consumer value must match exactly. Deploy a new version when a contract change is not backward compatible.
export true Registers the generated dispatcher with the native provider. With false, Spring can still create the implementation bean, but the service is not reachable over Dubbo.
executor Empty Names a bounded concurrency lane. All methods and services using the same name share that lane's active-call limit. An empty name uses the global default lane.
executes -1 Positive value hard-codes the lane limit and overrides configuration. Prefer executor plus a property so each environment can be tuned without rebuilding.
async false Accepted for source compatibility. Real non-blocking dispatch is determined by a CompletableFuture<T> method return type; async = true does not make a blocking method asynchronous.

The provider service identity is the combination of interface name, group, and version. Consumer and provider must use the same three values.

If neither interfaceClass nor interfaceName is set, the implementation must implement exactly one interface. Explicit interfaceClass is clearer and safer for production code.

Named concurrency example:

@DubboService(
    interfaceClass = StoreQueryService.class,
    group = "store",
    version = "1.0",
    executor = "store-query")
public final class StoreQueryServiceImpl implements StoreQueryService {
    // Business code stays in Java.
}
reactor.dubbo.provider.executors.store-query.max-concurrent=16

The value above limits the total active calls across every method sharing store-query; it does not create 16 threads per method. The global reactor.dubbo.provider.business-workers limit still applies. Reusing one executor name with conflicting executes values is rejected during provider startup.

Concurrency precedence is explicit:

  1. @DubboService(executes = N) when N > 0.
  2. reactor.dubbo.provider.executors.<executor>.max-concurrent for a named executor.
  3. reactor.dubbo.provider.default-max-concurrent when no named value exists.

@DubboReference Parameters

Parameter Default Meaning and recommended use
interfaceClass, interfaceName Inferred from field Optional contract validation. The annotated field must already be typed as the service interface.
group, version Empty Must match the provider identity exactly.
check true Includes this reference in startup readiness. Set false only when that provider is intentionally optional during startup. It does not enable retries.

Unsupported Annotation Parameters

Unsupported values are not silently ignored. Explicit use fails the build.

Parameter Use instead
timeout reactor.dubbo.consumer.timeout-ms or reactor.dubbo.provider.request-timeout-ms
connections reactor.dubbo.consumer.connections-per-endpoint
payload Consumer/provider max-payload-bytes properties
retries Explicit application retry for idempotent operations only; native calls are not replayed automatically
registry Global reactor.dubbo.consumer.providers or exact reactor.dubbo.consumer.routes.<name> entries with static addresses/Kubernetes Service DNS
serialization The native protocol uses the supported Hessian2 subset
protocol The native data plane uses classic dubbo://
path, actives, cluster, loadbalance No annotation equivalent in the bounded native runtime

Failure And Capacity Model

  • The consumer keeps persistent bounded connections and reconnects after connection loss.
  • Disconnected sockets are not selected. If all replicas are down, waiting remains bounded by the original RPC timeout.
  • startup-check=true waits for required providers. @DubboReference(check = false) excludes only that reference from startup readiness.
  • Health details include unreadyClients. Native metrics include clientConnectionWaits and clientConnectionWaitTimeouts.
  • Every RPC has a deadline. Set the RPC timeout below the inbound HTTP timeout.
  • Queues and in-flight calls are bounded. Under overload, rejection is safer than unbounded RSS and tail latency growth.
  • Automatic business retries are not provided. Retry only idempotent operations and keep retries within the caller deadline.
  • Provider shutdown stops accepting work and waits up to the configured drain timeout.

Configuration Reference

Spring Boot relaxed binding applies. These three forms set the same value:

reactor.dubbo.consumer.max-in-flight=64
-Dreactor.dubbo.consumer.max-in-flight=64
- name: REACTOR_DUBBO_CONSUMER_MAX_IN_FLIGHT
  value: "64"

The three forms above apply to scalar properties. Use YAML/properties, a mounted ConfigMap, or SPRING_APPLICATION_JSON for the named route map.

All runtime and consumer properties
Property micro default Purpose
reactor.dubbo.enabled true Starts or disables the generated Dubbo runtime
reactor.dubbo.profile micro Selects the starting resource preset
reactor.dubbo.runtime.io-workers 1 Shared native consumer I/O workers
reactor.dubbo.runtime.callback-workers 1 Async completion workers
reactor.dubbo.runtime.callback-queue-capacity 256 Bounded async completion queue
reactor.dubbo.runtime.thread-stack-bytes 262144 Stack per native runtime thread
reactor.dubbo.consumer.providers 127.0.0.1:20880 Comma-separated host:port endpoints
reactor.dubbo.consumer.connections-per-endpoint 2 Persistent connections per endpoint
reactor.dubbo.consumer.command-queue-capacity 32 Waiting calls per connection
reactor.dubbo.consumer.max-in-flight 64 Outstanding calls per generated client
reactor.dubbo.consumer.heartbeat-interval-ms 30000 Idle connection heartbeat; 0 disables it
reactor.dubbo.consumer.timeout-ms 3000 RPC deadline
reactor.dubbo.consumer.max-payload-bytes 8388608 Request/response hard limit
reactor.dubbo.consumer.max-collection-items 100000 Decoded collection item limit
reactor.dubbo.consumer.initial-buffer-bytes 1024 Initial request buffer size
reactor.dubbo.consumer.retained-buffers 16 Reusable request buffers; 0 minimizes retention
reactor.dubbo.consumer.max-retained-buffer-bytes 65536 Largest buffer kept for reuse
reactor.dubbo.consumer.startup-check true Waits for required providers at startup
reactor.dubbo.consumer.startup-timeout-ms 3000 Maximum startup readiness wait
reactor.dubbo.consumer.require-explicit-routes false Requires an exact named route for every generated reference
reactor.dubbo.consumer.routes.<name>.interface-name None Fully qualified interface in the exact route key
reactor.dubbo.consumer.routes.<name>.group Empty Group matching @DubboReference
reactor.dubbo.consumer.routes.<name>.version Empty Version matching @DubboReference
reactor.dubbo.consumer.routes.<name>.providers None Service-specific endpoint list
reactor.dubbo.consumer.routes.<name>.<limit> Inherited Per-route connection, queue, in-flight, timeout, payload, collection, and buffer limits
All provider properties
Property micro default Purpose
reactor.dubbo.provider.enabled false Starts the native provider listener
reactor.dubbo.provider.port 20880 Dubbo TCP port
reactor.dubbo.provider.io-workers 1 Dedicated provider I/O workers
reactor.dubbo.provider.business-workers 4 Maximum Java dispatch workers
reactor.dubbo.provider.queue-capacity 64 Bounded waiting provider work
reactor.dubbo.provider.max-payload-bytes 8388608 Provider payload hard limit
reactor.dubbo.provider.request-timeout-ms 30000 Provider execution deadline
reactor.dubbo.provider.drain-timeout-ms 10000 Graceful shutdown wait
reactor.dubbo.provider.default-max-concurrent 16 Fallback method concurrency
reactor.dubbo.provider.executors.<name>.max-concurrent 16 Named workload-lane concurrency

@DubboService(executes = N) has the highest priority. Otherwise, executor = "name" uses the matching named property. If neither is present, the global default is used.

Safe Tuning Order

  1. Measure provider execution time, DB pool wait, CPU throttling, p99, rejection count, and RSS.
  2. Fix slow business code and database access first.
  3. Match provider executor limits to downstream capacity.
  4. Match consumer max-in-flight to work the provider can finish before timeout.
  5. Add connections only when socket saturation or Kubernetes distribution is proven.
  6. Increase queues last. Queues store waiting work; they do not add capacity.

Small Glossary

Term Meaning
RSS Physical memory currently attributed to the process or container
p99 Latency below which 99% of measured requests complete
In-flight A request that started but has not completed yet
Backpressure Rejecting or slowing new work when bounded capacity is full
Queue Work waiting for an available worker or connection
Heartbeat Small periodic message used to keep and verify an idle connection
Idempotent Safe to repeat without applying the business effect twice

Troubleshooting

Symptom Check
Maven returns 401 Token exists, has read:packages, and server ID is exactly github
Maven returns 403 or 404 Token owner can access the repository and package
Native library cannot load Exactly one native artifact matching Windows x64, Linux x64, or macOS ARM64 is present
macOS reports an unsupported platform Use an ARM64 Java 21 JDK; an x86_64 JDK runs through Rosetta and is not compatible
Startup reports provider unavailable Provider address, port, readiness, and startup timeout are correct
Build rejects an annotation option Use only the supported annotation subset listed above
Generated client or injection is missing Annotation processor and enhancer plugin both ran during mvn package
DTO decode fails Consumer and provider use the same contract package, class names, and compatible fields
p99 and RSS rise under load Inspect DB/provider capacity before increasing workers, connections, or queues
Traffic reaches too few provider pods Measure and then raise connections-per-endpoint gradually

Integrity And License

Release assets include Windows DLL, Linux SO, Apple Silicon macOS dylib, SHA-256 checksums, and CycloneDX SBOMs. NATIVE_SHA256SUMS pins the distributed artifacts. Third-party native components are listed in THIRD_PARTY_NOTICES and the SBOM files.

The public Java source is licensed under Apache License 2.0. See Releases for published packages and native assets.

About

Reflection-free Spring Boot integration for Dubbo consumers and providers, backed by a Rust native data plane without Netty or ZooKeeper dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages