This guide is for the people who operate, integrate, extend, and reason about Apache Dubbo: platform engineers, SREs, architects, and contributors. Where the User Guide shows how to build with Dubbo, this guide explains how Dubbo is built — its layered architecture, the SPI extension model that makes every component swappable, the scope-model domain that governs lifecycle, the deployment topology, the security surface, and the operational levers you pull in production.
It is intentionally exhaustive. The goal is that an engineer (or an AI) could understand, operate, harden, and contribute to a Dubbo-based system from this document alone.
Table of Contents
- Executive Summary
- Architecture Overview
- The SPI Extension Mechanism
- The Scope-Model Domain
- Repository & Module Map
- The Invocation Pipeline
- Service Discovery Internals
- Requirements & Dependencies
- Deployment
- Security
- Operations
- Observability
- Performance Tuning
- Known Issues & Tech Debt
- Contributing
- Quick Reference
1. Executive Summary
Apache Dubbo is a Java microservices framework delivered as a large Maven multi-module project (roughly 4,800 files across ~150 modules). It is not an application you run; it is a library set your applications depend on. Its defining architectural property is pervasive extensibility: almost every behavior — protocol, transport, serialization, registry, load balancer, cluster strategy, router, filter, proxy factory — is a named SPI extension resolved at runtime from the classpath. This is what lets one codebase support Triple/HTTP-2, classic TCP, REST, Zookeeper, Nacos, multiple serializers, and a service mesh without conditionals.
| Aspect | Summary |
|---|---|
| Language / build | Java (source level 1.8, runs on JDK 8–25), Maven multi-module, ${revision} = 3.3.7-SNAPSHOT (this analysis); current stable line 3.3.x |
| Core idea | Interface-based RPC + automatic service discovery + traffic management, all behind swappable SPI extensions |
| Domain model | Three-tier scope hierarchy: FrameworkModel → ApplicationModel → ModuleModel |
| Config carrier | The URL object — every component is parameterized by a URL |
| Default stack | Protocol dubbo (TCP), serialization hessian2, cluster failover, loadbalance random, proxy javassist |
| Discovery | Application-level service discovery (Dubbo 3) with interface-level compatibility |
| Transport | Netty 4 (primary), plus HTTP/1.2/3 and WebSocket remoting modules |
| Operations | A built-in QOS port (telnet + HTTP) for runtime inspection and control |
Note
This guide is generated from source at commit
ee9c190. Because AST extraction does not parse Java, all reference data here (config fields, extension names, command names, SPI defaults) was read directly from the framework's source and SPI declaration files, not inferred.
2. Architecture Overview
Dubbo is organized as a stack of layers. A call enters at the top (your interface proxy) and descends through cluster, protocol, exchange, transport, and serialization before hitting the wire.
┌──────────────────────────────────────────────────────────┐
config │ ServiceConfig / ReferenceConfig / @DubboService / Bootstrap│ ← you configure here
├──────────────────────────────────────────────────────────┤
proxy │ ProxyFactory (javassist) — interface ⇄ Invoker │
├──────────────────────────────────────────────────────────┤
registry│ RegistryFactory / ServiceDiscovery / ServiceNameMapping │ ← discovery + metadata
├──────────────────────────────────────────────────────────┤
cluster│ Cluster (failover…) → Directory → Router → LoadBalance │ ← fault tolerance + routing
├──────────────────────────────────────────────────────────┤
monitor │ Filter chain (validation, auth, metrics, tracing, cache…) │ ← cross-cutting
├──────────────────────────────────────────────────────────┤
protocol│ Protocol (dubbo / tri / rest2 / grpc / injvm) │ ← Invoker ⇄ Exporter
├──────────────────────────────────────────────────────────┤
exchange│ Exchanger / HeaderExchange — request/response semantics │
├──────────────────────────────────────────────────────────┤
transport│ Transporter (Netty4) — Channel, Server, Client │
├──────────────────────────────────────────────────────────┤
serialize│ Serialization (hessian2 / fastjson2) │
└──────────────────────────────────────────────────────────┘
Three properties tie the layers together:
-
The
URLis the contract. Every layer reads the parameters it cares about from a sharedorg.apache.dubbo.common.URL. A registry URL likeregistry://zk:2181/...?registry=zookeeperis rewritten as it descends —RegistryProtocolturns it into adubbo://provider:20880/...URL for the actual call. This uniform carrier is why the layers compose without bespoke glue. -
Every layer is an SPI. The boundary between layers is a Java interface annotated
@SPI, and the concrete implementation is selected by a name in the URL. See §3. -
Wrappers add cross-cutting behavior. When the SPI loader instantiates, say, a
Protocol, it automatically wraps it inProtocolFilterWrapper,ProtocolListenerWrapper,ProtocolSerializationWrapper, andProtocolSecurityWrapper. This is how filters, listeners, and security are woven in without the core protocol knowing about them.
3. The SPI Extension Mechanism
This is the single most important thing to understand about Dubbo. Everything else follows from it.
3.1 @SPI, ExtensionLoader, and named extensions
An extension point is a Java interface annotated @SPI, located in org.apache.dubbo.common.extension. Implementations are declared in META-INF/dubbo/internal/<fully.qualified.InterfaceName> files as name=implementationClass lines — Dubbo's enhanced version of the JDK ServiceLoader.
@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol { /* ... */ }
# META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol
grpc=org.apache.dubbo.rpc.protocol.tri.GrpcProtocol
rest2=org.apache.dubbo.rpc.protocol.tri.RestProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
mock=org.apache.dubbo.rpc.support.MockProtocol
Code obtains an extension by name:
Protocol dubbo = applicationModel.getExtensionLoader(Protocol.class).getExtension("dubbo");
The @SPI value ("dubbo") is the default used when the URL doesn't name one.
3.2 Adaptive extensions
Most layers don't know which implementation to use until call time, when the protocol is in the URL. The @Adaptive mechanism generates a dispatcher that reads the relevant URL parameter and delegates to the right concrete extension:
protocolSPI = getExtensionLoader(Protocol.class).getAdaptiveExtension();
// at call time, protocolSPI.refer(type, url) picks DubboProtocol or TripleProtocol
// based on url.getProtocol()
This is why ReferenceConfig holds a single Protocol protocolSPI yet transparently routes dubbo:// to DubboProtocol and tri:// to TripleProtocol.
3.3 Activate (conditional auto-wiring)
@Activate marks extensions (especially filters and routers) that should be auto-included when certain conditions hold (provider vs consumer side, a URL parameter present, etc.). The cluster and filter chains are assembled by collecting all @Activate extensions that match the current URL — which is how the validation filter, access-log filter, metrics filter, and tracing filter all insert themselves without explicit registration.
3.4 Extension scopes
ExtensionScope controls how widely an extension instance is shared:
| Scope | Shared across | Examples |
|---|---|---|
FRAMEWORK |
The whole JVM | Protocol, ProxyFactory, Serialization |
APPLICATION |
One application | RegistryFactory |
MODULE |
One module | ConfigInitializer, ConfigPostProcessor |
Important
When you extend Dubbo, you do not fork it. You implement the SPI interface, drop a
META-INF/dubbo/<InterfaceName>file in your jar, and select your extension by name in config. This is the intended extension path for protocols, registries, serializers, filters, routers, and load balancers. Thedubbo-compileranddubbo-nativemodules support build-time stub generation and GraalVM native-image hints for this model.
4. The Scope-Model Domain
Dubbo 3 replaced global static state with an explicit, three-tier scope-model hierarchy rooted in org.apache.dubbo.rpc.model. This is what allows multiple isolated Dubbo "applications" in one JVM (multi-tenancy, test isolation, library embedding).
FrameworkModel (one per JVM by default; shared framework services)
└── ApplicationModel (one per application; owns config, registries, deployers)
└── ModuleModel (one per module; owns a set of services/references)
| Model | Owns | Lifecycle driver |
|---|---|---|
FrameworkModel |
Framework-scoped extensions, the shared executor repository | FrameworkModelCleaner (destroy listener) |
ApplicationModel |
ConfigManager (all config beans), application deployer, service repository, bean factory |
DefaultApplicationDeployer |
ModuleModel |
The module's exported services and references | DefaultModuleDeployer |
Each model carries a ScopeBeanFactory (a lightweight DI container) and a Deployer that drives startup/shutdown. ConfigScopeModelInitializer registers the deployers and the DefaultConfigValidator when the models are created.
Note
The deployer is the orchestration heart.
DefaultApplicationDeployer.start()initializes config, starts the config center and metadata report, exports the internal metadata service, registers services, and starts the QOS server. When Dubbo is embedded in Spring, modules are markedisLifeCycleManagedExternally()so Spring drives their lifecycle and Dubbo's shutdown hook waits for Spring before tearing down (seeDubboShutdownHook).
4.1 Why this matters operationally
- Graceful shutdown is coordinated at the
ApplicationModellevel: the shutdown hook flips all services to read-only, drains in-flight calls up toshutwait, and only then destroys the application. - Multiple applications in one JVM each get their own
ApplicationModel, so their registries, config, and services don't collide — useful for gateways and test harnesses. - Config resolution is per-
ApplicationModelvia itsConfigManager, which is why the precedence rules in the User Guide are evaluated per application.
5. Repository & Module Map
Dubbo's ~150 Maven modules group into functional families. The table lists the families and the headline modules in each; the suffix convention is dubbo-<area>-<impl>.
| Family | Key modules | Responsibility |
|---|---|---|
| Core | dubbo-common |
URL, SPI/ExtensionLoader, scope models, config bean classes, utilities, constants |
| Config | dubbo-config-api, dubbo-config-spring, dubbo-config-spring6 |
ServiceConfig/ReferenceConfig, DubboBootstrap, Spring annotations & XML, autowiring |
| RPC / protocols | dubbo-rpc-api, dubbo-rpc-dubbo, dubbo-rpc-triple, dubbo-rpc-injvm |
Invoker/Exporter/Protocol, the dubbo and Triple protocols, in-JVM calls |
| Cluster | dubbo-cluster |
Cluster strategies, directories, routers (condition/tag/script/affinity/mesh), load balancers |
| Registry | dubbo-registry-api, -zookeeper, -nacos, -multicast, -multiple |
Service registration & discovery, app-level discovery, migration |
| Remoting | dubbo-remoting-api, -netty4, -netty, -http12, -http3, -websocket, -zookeeper-curator5 |
Transports, channels, the exchange layer |
| Serialization | dubbo-serialization-api, -hessian2, -fastjson2 |
Wire serializers + the security wrapper |
| Metadata | dubbo-metadata-api, -report-zookeeper, -report-nacos, -definition-protobuf |
Service metadata, name mapping, metadata centers |
| Config center | dubbo-configcenter-zookeeper, -nacos, -apollo, -file |
External dynamic configuration sources |
| Metrics / tracing | dubbo-metrics-api, -default, -prometheus, -otlp, dubbo-tracing |
Observability collection and export |
| Spring Boot | dubbo-spring-boot-autoconfigure, -3-autoconfigure, -actuator, -starters/* |
Auto-config and the user-facing starters |
| Plugins | dubbo-qos, dubbo-auth, dubbo-security, dubbo-spring-security, dubbo-filter-*, dubbo-rest-*, dubbo-mcp, dubbo-native, dubbo-compiler, dubbo-maven-plugin |
Operations port, auth, REST/OpenAPI, MCP, native image, codegen |
| Compatibility | dubbo-compatible |
com.alibaba.dubbo.* shims for migrating from Dubbo 2.x |
| Distribution | dubbo-distribution/*, dubbo-bom, dubbo-dependencies-bom |
Shaded "all" jar, BOMs |
| Demo / test | dubbo-demo/*, dubbo-test/* |
Runnable examples and the test harness |
Tip
When wiring a production app, depend on the leaf modules you need (one protocol, one registry, one serializer, the starters) rather than the umbrella
dubboartifact, to keep the classpath minimal and the attack surface small.
5.1 The com.alibaba.dubbo.* compatibility layer
dubbo-compatible ships deprecated shims under the old com.alibaba.dubbo package (e.g. com.alibaba.dubbo.config.annotation.Service, com.alibaba.dubbo.remoting.Server). They exist only to ease migration from Dubbo 2.x. Do not use them in new code — they are marked @Deprecated and delegate to the org.apache.dubbo.* equivalents.
6. The Invocation Pipeline
A consumer call traverses this sequence; understanding it is essential for debugging latency, retries, and routing.
- Proxy — The
javassistProxyFactorygenerated a dynamic proxy for your interface. Calling a method produces anRpcInvocation. - Cluster invoker — The proxy's
Invokeris a cluster invoker (defaultFailoverClusterInvoker). It owns fault tolerance. - Directory — Supplies the current candidate
Invokerlist (aRegistryDirectoryfor registry-backed references, kept live by registry pushes; aStaticDirectoryfor direct URLs). - Router chain — Filters the candidate list by condition/tag/script/affinity/mesh rules. Routers are dynamic — rules push from the config center.
- LoadBalance — Picks one invoker from the survivors (default
RandomLoadBalance). - Filter chain —
@Activatefilters run in order (validation, auth, access log, metrics, tracing, generic, …). This is where cross-cutting concerns live. - Protocol invoker — The protocol-specific invoker (
DubboInvoker, Triple invoker) serializes and sends over the transport. - Exchange + transport —
HeaderExchangeradds request/response correlation; Netty4 carries the bytes.
On the provider side the mirror image runs: transport → codec/serialization → exchange → protocol Exporter → provider-side filter chain → your implementation, dispatched on a configurable thread pool.
Note
The
ZoneAwareClusterInvoker → FailoverClusterInvoker(RegistryDirectory)nesting you see in logs is the multi-registry case: the zone-aware layer picks a registry, the failover layer picks a provider within it. Single-registry references collapse to one cluster layer.
6.1 Where retries happen — and the danger
failover retries at step 2 (cluster), re-running steps 3–8 on a different invoker. Because retries re-enter the full pipeline, a non-idempotent operation can execute more than once. The framework cannot know which methods are idempotent — operators and developers must set cluster=failfast or retries=0 for writes. This is the most common production correctness pitfall.
7. Service Discovery Internals
7.1 Application-level discovery (the Dubbo 3 model)
In Dubbo 2, every interface registered one node per instance — a 100-instance app exposing 50 interfaces created 5,000 registry entries. Dubbo 3 registers one node per application instance and stores the interface → application mapping separately as metadata. The pieces:
ServiceDiscovery— registers/queries application instances (ServiceDiscoveryRegistryFactory, registry nameservice-discovery-registry).ServiceNameMapping— resolves which application(s) provide a given interface, published at export time.- Metadata report (
dubbo-metadata-report-*) — stores per-revision service metadata (methods, parameters, config) so consumers can reconstruct the full provider URL from a lightweight instance registration. - Migration (
dubbo-registry-api .../client/migration) — lets a cluster run interface-level and application-level discovery simultaneously and shift between them without downtime, controlled by a migration rule.
7.2 Empty-protection
enable-empty-protection (on application and registry config) guards against a registry pushing an empty provider list (e.g. during a registry hiccup) and wiping a consumer's working address list. When enabled, an empty push is ignored and the last-known-good list is retained. Keep this on in production.
8. Requirements & Dependencies
8.1 Runtime requirements
| Requirement | Value |
|---|---|
| JDK | 8 minimum (source level 1.8); supported through 25 on the 3.3.x line |
| Build | Maven 3.x; dubbo-maven-plugin for Protobuf/Triple stub generation |
| Registry | One of Zookeeper, Nacos (production); Multicast for dev only |
| Optional infra | Config center, metadata center (often the same Zookeeper/Nacos), Prometheus, Zipkin/OTLP collector |
8.2 Key third-party dependencies (from dubbo-dependencies-bom)
| Dependency | Version | Used by |
|---|---|---|
| Netty 4 | 4.x | Primary transport (dubbo-remoting-netty4) |
| Spring Framework | 5.3.39 (and legacy 4.3.30) | dubbo-config-spring |
| Spring Boot | 2.7.18 (3.x via -3-autoconfigure) |
Starters |
| Curator | 5.9.0 | Zookeeper client |
| Zookeeper | 3.7.2 | Zookeeper registry |
| Nacos client | 2.5.2 | Nacos registry |
| Hessian | 4.0.66 | Default serialization |
| Fastjson2 | 2.0.62 | Alternative serialization |
| gRPC | 1.78.0 | Triple/gRPC interop |
| Jackson | 2.21.3 | JSON/REST |
| Micrometer / OpenTelemetry / Zipkin | (BOM-managed) | Metrics & tracing |
Caution
Dubbo deliberately gives consumers control over transitive versions: the starters use
<exclusions>heavily (e.g. excludingspring-boot-starter-loggingin favor of log4j2, and Netty/logback from the Zookeeper client). When you upgrade Dubbo, re-verify your effective dependency tree (mvn dependency:tree) — version skew between Dubbo's expected client libraries (Curator, Nacos client, Netty) and yours is a frequent source ofNoSuchMethodErrorat runtime.
9. Deployment
Dubbo deploys as part of your application — there is no Dubbo server to run. The deployment concerns are about topology, process model, and the supporting infrastructure.
9.1 Topology
┌─────────────┐ register/heartbeat ┌──────────────┐
│ Provider 1 │ ───────────────────────────────▶ │ │
│ :20880(tcp)│ │ Registry │
│ :50051(tri)│ ◀── subscribe (provider list) ──── │ (ZK/Nacos) │
└─────────────┘ │ │
▲ ▲ │ + Config │
RPC call │ │ heartbeat │ + Metadata │
│ └──────────────┐ └──────────────┘
┌─────────────┐ │ ▲
│ Consumer │ ────────┘ discover + invoke │
│ │ ──────────────────────────────────────────►│ publish metadata
└─────────────┘
- Providers open one port per protocol (e.g.
20880fordubbo,50051fortri), register in the registry, and publish metadata. - Consumers subscribe, cache the address list locally (
dubbo.registry.file), and call providers directly — the registry is not in the data path. - The registry/config/metadata roles are often the same Zookeeper or Nacos cluster (
use-as-config-center,use-as-metadata-center).
9.2 Containers & Kubernetes
- Expose each protocol port and the QOS port (
22222). - Wire the QOS HTTP probes (
/live,/ready,/startup) into liveness/readiness/startup probes. - For service mesh, set
mesh-enable: trueand use Triple; Dubbo resolves providers via Kubernetes DNS (<svc>.<ns>.svc.cluster.local) and lets Envoy/Istio route. The default cluster domain iscluster.localand mesh port80. - The
dubbo-nativemodule provides GraalVM native-image hints and a sampleDockerfile(GraalVM CE) for AOT-compiled, fast-start, low-footprint deployments.
9.3 Graceful rollout
offline(orofflineApp) via QOS to deregister the instance — consumers stop routing to it within a refresh cycle.- Wait for in-flight calls to drain (
shutwait). - Deploy the new version.
onlineto re-register.
Tip
Combine the QOS
offline/onlinecommands with your orchestrator'spreStophook so rolling deploys drain cleanly without dropped requests.
10. Security
Dubbo's security surface spans the wire, the serializer, authentication, and the operations port. Harden all four.
10.1 The QOS operations port — the highest-risk surface
The QOS port (22222) can offline, shutdown, switchLogLevel, and invoke arbitrary methods. Treat it like a management interface:
- It binds localhost-only by default (
qos-accept-foreign-ip: false). Keep it that way. - If remote access is unavoidable, set
qos-accept-foreign-ip-whitelistto explicit IPs and lowerqos-anonymous-access-permission-level/ restrictqos-anonymous-allow-commands. - Never route the QOS port through an ingress or expose it publicly.
Caution
Exposing the QOS port to an untrusted network is equivalent to handing out a remote shutdown and arbitrary-invoke button for your service. This is the #1 Dubbo misconfiguration to audit.
10.2 Serialization allow-listing
Java deserialization is a classic RCE vector. Dubbo defends with a serialization security wrapper and a class allow-list:
serialize-check-status(STRICT/WARN/DISABLE) controls enforcement.auto-trust-serialize-class+trust-serialize-class-levelgovern which classes may be deserialized.serializeCheckStatusandserializeWarnedClassesQOS commands report what's being blocked or warned.
Warning
Two real serialization hazards from the issue tracker: (1) classes with a
writeReplace()method can bypass the Hessian2Serializablesecurity check (#16287) — audit such classes; (2) Hessian2 may skip field-initialization on deserialization, yielding nulls/defaults (#15805). Preferfastjson2for affected payloads and keepserialize-check-statusatSTRICTin production. Run with the allow-list enabled; do not set it toDISABLEto "fix" a deserialization error — fix the allow-list entry instead.
10.3 Authentication & transport security
- Token auth (
token = "true"on a service) requires consumers to present a registry-distributed token; blocks unauthorized direct callers. dubbo-authprovides request signing/authentication filters;dubbo-spring-security/dubbo-spring6-securityintegrate Spring Security context propagation across RPC calls.- TLS via
dubbo.ssl.*(SslConfig) andssl-enabledper protocol encrypts the wire;dubbo.registry.securesecures the registry connection.
10.4 Disclosure process
Security issues are reported through the Apache process documented in the repository's SECURITY.md. Do not file security bugs as public GitHub issues.
11. Operations
11.1 The QOS command catalog
Connect via telnet localhost 22222 or HTTP (curl http://localhost:22222/<command>). Full command set (read directly from the @Cmd declarations):
| Category | Commands |
|---|---|
| Inspection | ls, ps, pwd, cd, getConfig, getAddress, count, version, help, greeting |
| Service rotation | online, offline, onlineApp, offlineApp, onlineInterface, offlineInterface |
| Lifecycle | live, ready, startup, shutdown, gracefulShutdown, quit |
| Invocation | invoke, select |
| Metadata | publishMetadata, getOpenAPI |
| Logging | loggerInfo, switchLogger, switchLogLevel |
| Profiler | enableSimpleProfiler, enableDetailProfiler, disableSimpleProfiler, disableDetailProfiler, setProfilerWarnPercent |
| Router debug | enableRouterSnapshot, disableRouterSnapshot, getRouterSnapshot, getEnabledRouterSnapshot, getRecentRouterSnapshot |
| Serialization | serializeCheckStatus, serializeWarnedClasses |
| Metrics | metrics_default |
11.2 Runtime diagnosis playbook
| Symptom | QOS commands to run |
|---|---|
| "Consumer can't find provider" | ls (both sides), getAddress <service> |
| "Calls routed to the wrong instance" | enableRouterSnapshot then getRecentRouterSnapshot |
| "Some calls are slow" | enableDetailProfiler, setProfilerWarnPercent, then inspect logs |
| "Need more log detail without restart" | switchLogLevel DEBUG, revert with switchLogLevel INFO |
| "Deserialization blocked" | serializeCheckStatus, serializeWarnedClasses |
| "Draining for deploy" | offline, wait, deploy, online |
11.3 Logging
Dubbo abstracts logging via dubbo.application.logger (slf4j, log4j2, jcl, …). The demos use log4j2 and exclude spring-boot-starter-logging. Error logs carry stable error codes (e.g. CLUSTER_NO_VALID_PROVIDER, CONFIG_FAILED_SHUTDOWN_HOOK) defined in LoggerCodeConstants — grep logs by code for precise diagnosis.
12. Observability
Dubbo ships first-class metrics and tracing, enabled through starters and dubbo.metrics.* / dubbo.tracing.*.
- Metrics (
dubbo-metrics-*): Micrometer-based, exportable to Prometheus (dubbo-metrics-prometheus) or OTLP (dubbo-metrics-otlp). Covers RPC QPS/RT/error rates, thread pools, registry events, and JVM metrics. Adddubbo-observability-spring-boot-starter. - Tracing (
dubbo-tracing): Micrometer Tracing bridges to Brave/Zipkin or OpenTelemetry (OTLP or Zipkin export). Starters:dubbo-tracing-brave-zipkin-spring-boot-starter,dubbo-tracing-otel-zipkin-spring-boot-starter,dubbo-tracing-otel-otlp-spring-boot-starter. Configure sampling withdubbo.tracing.sampling.probability. - Actuator:
dubbo-spring-boot-actuatorexposes Dubbo health/info endpoints.
dubbo:
metrics:
protocol: prometheus
enable-jvm-metrics: true
tracing:
enabled: true
sampling: { probability: 0.1 }
tracing-exporter:
otlp-config: { endpoint: http://otel-collector:4318/v1/traces }
13. Performance Tuning
| Lever | Where | Guidance |
|---|---|---|
| Protocol | dubbo.protocol.name |
dubbo (TCP) for max throughput between Java services; tri for HTTP/2/mesh |
| Thread pool | dubbo.protocol.threadpool (fixed/cached/limited/eager) + threads, corethreads, queues |
Size to provider workload; eager grows before queuing |
| Thread isolation | dubbo.application.executor-management-mode: isolation |
Give heavy services their own pool so one can't starve others |
| IO threads | dubbo.protocol.iothreads |
Netty event-loop sizing (default = cores) |
| Connections | connections / dubbo.consumer.shareconnections |
Share or dedicate TCP connections per reference |
| Payload cap | dubbo.protocol.payload |
Max message size; raise carefully (memory) |
| Serialization | dubbo.protocol.serialization |
hessian2 default; fastjson2 can be faster for some shapes |
| Load balance | loadbalance |
leastactive/shortestresponse smooth out slow nodes; adaptive is load-aware |
| Concurrency limits | actives (consumer), executes (provider) |
Bound in-flight calls to prevent overload/OOM (see #16294) |
Tip
Use the QOS profiler (
enableDetailProfiler+setProfilerWarnPercent) in a canary to find slow methods before tuning blindly. Pairleastactiveload balancing withactiveslimits so a degraded provider sheds load instead of accumulating it.
14. Known Issues & Tech Debt
Drawn from the project's own issue tracker at this commit — useful context for operators choosing versions.
| Issue | Area | Operator action |
|---|---|---|
| #16295 / #16296 | JDK 25 + ServiceLoader/Gson NoClassDefFoundError |
Use a 3.3.x build with JDK 25 support, or pin JDK 21; ensure Gson present if needed |
| #16294 | Large consumer.timeout → OOM |
Keep timeouts realistic; bound concurrency with actives/executes |
| #16178 | Provider start fails when ZK (weak dep) is down | dubbo.registry.check: false |
| #15805 | Hessian2 skips field init on deserialize | Set fields explicitly or use fastjson2 |
| #16287 | writeReplace() bypasses Hessian2 Serializable security check |
Audit such classes; keep allow-list STRICT |
| #16308 | NetUtils.matchIpExpression mis-parses IPv4+port |
Separate host/port in config; upgrade |
| #16117 | CompositeInputStream.close() may leak streams |
Tracking; upgrade when fixed |
| #16214 | Spring Boot 4 compatibility not yet available | Stay on supported Spring Boot 2.7/3.x lines |
Structural debt worth knowing: the com.alibaba.dubbo.* compatibility layer is dead weight for new projects (don't depend on it); the source level is still 1.8, so the codebase cannot use modern Java language features internally even while supporting JDK 25 at runtime; and the SPI metadata files (META-INF/dubbo/internal/*) are the source of truth for extension wiring but are easy to miss when reviewing — a missing line silently disables an extension.
15. Contributing
- Build:
mvn clean install(Maven multi-module). The${revision}property sets the version across all modules — do not hard-code versions in child POMs. - Code style: enforced via the
codestylemodule and the Maven compiler config; run the formatter before submitting. - Adding an extension: implement the
@SPIinterface, add aMETA-INF/dubbo/<InterfaceName>mapping line, and a test. Do not add conditionals to core — the SPI is the extension seam. - Spring config metadata: config fields live in
dubbo-common'sorg.apache.dubbo.config.*classes; their Javadoc is harvested to generatespring-configuration-metadata.jsonfor IDE auto-completion (a dedicated compatible module copies the classes and substitutes@NestedConfigurationProperty). Follow the Javadoc conventions ("The xxx" for nouns, "Whether to xxx, default value istrue" for booleans) so the generated metadata is correct. - Conduct & security: see
CODE_OF_CONDUCT.md,CONTRIBUTING.md, andSECURITY.mdat the repo root.
Note
Because Java AST tooling does not parse this codebase for extraction, the authoritative reference for any contributor question is the source itself — start from the
@SPIinterface for the layer you're changing and theMETA-INF/dubbo/internal/<interface>file that wires its implementations.
16. Quick Reference
Default extensions
| Layer | Default | Alternatives |
|---|---|---|
| Protocol | dubbo |
tri, grpc, rest2, injvm, mock |
| Cluster | failover |
failfast, failsafe, failback, forking, broadcast, available, mergeable, zone-aware |
| LoadBalance | random |
roundrobin, leastactive, shortestresponse, consistenthash, adaptive |
| Serialization | hessian2 |
fastjson2 |
| Proxy factory | javassist |
jdk |
| Registry | (none) | zookeeper, nacos, multicast, multiple, service-discovery-registry |
Default ports
| Port | Purpose |
|---|---|
20880 |
dubbo (TCP) protocol — conventional default |
50051 |
Triple (tri) — conventional example port |
22222 |
QOS operations (telnet + HTTP) |
Scope-model hierarchy
FrameworkModel → ApplicationModel → ModuleModel
(JVM/global) (per application) (per module's services/refs)
Configuration precedence (highest first)
-D system properties → external config center → environment variables → application.yml/dubbo.properties → annotation/XML attributes → built-in defaults.
Essential QOS commands
ls · getAddress · online / offline · gracefulShutdown · enableDetailProfiler · getRecentRouterSnapshot · switchLogLevel · serializeCheckStatus