Skip to content

Conversation

@YoWuwuuuw
Copy link
Contributor

@YoWuwuuuw YoWuwuuuw commented Dec 21, 2025

Ⅰ. Describe what this PR did

1. Core Capabilities and New Configuration Items

1.1. Server Side: Metadata Registration
  • Seata-Server can carry custom metadata when registering with the registry center.
# server: application.yaml, common metadata is provided, custom configuration items can be configured.

# Common metadata for routing, custom metadata key-value pairs can be configured.

seata.registry.metadata.weight: 1

seata.registry.metadata.env:

seata.registry.metadata.region:

seata.registry.metadata.version:
1.2. Client Side: Metadata-Driven Instance Selection

Client instance selection is broken down into two stages: route filtering and load balancing, the process is as follows

All Instances

│
▼
Metadata Routing (0 ~ N routers)

│
▼
Filtered Instances

│
▼
LoadBalance Strategy (random / round-robin / Advanced Load Balancing Based on Metadata

│
▼
Target Server Instance

Configuration items are as follows:

# Add load balancing strategy: Weighted Random

seata.client.load-balance.type=WeightedRandomLoadBalance

client.routing.enabled=false # Enable route filtering mode

client.routing.debug=false # Debug mode, displaying detailed logs of the route filtering handler

client.routing.routers= # Declare routers to load router configuration

# 1. Single router: use metadata-router-1

client.routing.routers = metadata-router-1

client.routing.metadata-router-1.enabled = true

client.routing.metadata-router-1.expression = version >= 2.3 && env == prod

# 2. Multiple routers: use metadata-router-1, metadata-router-2, ...

client.routing.routers = metadata-router-1, metadata-router-2, spi-cunstom

client.routing.metadata-router-1.enabled = true

client.routing.metadata-router-1.expression = version >= 2.0 || env == dev

client.routing.metadata-router-2.enabled = true

client.routing.metadata-router-2.expression = weight >= 1

client.routing.spi-cunstom.enabled = true # Configurable SPI router, custom configuration and filtering logic, name must start with 'spi-'

client.routing.spi-cunstom.xxx = ...

2. Expression Syntax:

2.1. Basic Format

key operator value, three parts separated by spaces: key name, operator, value

2.2. Supported Operators
Operator Description Supported Types
== / = Equal to String, Number
!= Not equal to String, Number
> / >= / < / <= Comparison Number only

<、>、>=、<= Only support numbers. If the instance metadata is not a number, it will return false and filter the instance.

2.3. Expression Examples
  1. Single expression: version >= 2.3

  2. OR expression: (version >= 2.3) || (env == dev)

  3. AND expression: (version >= 2.3) && (env == prod)

2.4. Expression Limitations

Limitation: A single expression does not support mixing && and ||

Solution: Use multiple || expressions in the router to implement complex logic

# Implement the logic of (A || B) && (C || D)

client.routing.metadata-router-1.expression= A || B
client.routing.metadata-router-2.expression= C || D

Or use the distributive law: (A && B) || (C && D) = (A || C) && (A || D) && (B || C) && (B || D)

3. Metadata Enhancement for Each Registry

3.1. Enhancement Methods
Registry Metadata Implementation Description
Consul Directly call the API's setMeta() / getMeta() Consul natively supports metadata fields, and the API handles them automatically
Nacos Directly call the API's setMetadata() / getMetadata() Nacos natively supports metadata fields, and the API handles them automatically
Eureka Through instanceConfig.setMetadata() / instance.getMetadata() Eureka natively supports metadata fields;
getMetadataMap() converts them to a Map<String, String>
Namingserver Based on the original API, metadata is part of the Instance object The Instance object itself contains a metadata field,
passed via JSON serialization/deserialization, which the API handles automatically
Zookeeper Manually serialize metadata to string format (key=value\n) Stored in the data of the ZNode, requiring manual serialization/deserialization
Redis As shown below
3.2. To be implemented
Registry Status
Etcd The original logic is not working; the existing implementation logic should be fixed first.
Sofa Cannot be built; a separate pull request will be made to enhance it in the future.
3.3. Redis Enhancement Description

Implementation method: Manually build a hash structure to store metadata.

Core design:

  • Maintain the original live keys. Unchanged: registry.redis.${cluster}_${serverAddr} (used for service discovery and heartbeat detection)

  • Added metadata key: registry.redis.${cluster}.meta_${serverAddr} (Hash structure, storing metadata)

Backward compatibility:

  • When a new server registers, it creates both a liveness key and a metadata key (if metadata exists).

  • Existing clients only scan the liveness key and can discover new servers normally (ignoring the metadata key).

  • After scanning the liveness key, new clients attempt to retrieve the metadata key; if it does not exist or is empty, a ServiceInstance without metadata is created.

Technical details:

  • Metadata is stored in a separate hash structure, decoupled from liveness detection.

  • The TTL of both keys is refreshed synchronously (5 seconds) to ensure data consistency.

4. Compatibility Notes

  • Enabling routing mode:

    • seata-server without metadata configuration:

      • When route filtering is enabled, instances without metadata will be filtered.

      • When using a weighted random load balancing strategy, a default weight of weight=1 will be assigned.

    • seata-client does not enable routing mode: logical consistency backwards.

Ⅱ. Does this pull request fix one issue?

fixes #7886

Ⅲ. Why don't you add test cases (unit test/integration test)?

Ⅳ. Describe how to verify it

Refer to #7550, use demo, modify the corresponding configurations for verification.

Ⅴ. Special notes for reviews

# Conflicts:
#	changes/en-us/2.x.md
#	changes/zh-cn/2.x.md
#	common/pom.xml
#	common/src/main/java/org/apache/seata/common/util/HttpClientUtil.java
#	compressor/seata-compressor-zstd/src/test/java/org/apache/seata/compressor/zstd/ZstdUtilTest.java
#	config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosConfigurationTest.java
#	config/seata-config-zk/src/test/java/org/apache/seata/config/zk/ZkConfigurationTest.java
#	console/pom.xml
#	console/src/main/resources/static/console-fe/package-lock.json
#	core/src/main/java/org/apache/seata/core/rpc/netty/http/filter/HttpRequestParamWrapper.java
#	core/src/test/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClientTest.java
#	core/src/test/java/org/apache/seata/core/rpc/netty/http/Http2HttpHandlerTest.java
#	core/src/test/java/org/apache/seata/core/rpc/netty/http/HttpDispatchHandlerTest.java
#	core/src/test/java/org/apache/seata/core/rpc/netty/http/filter/HttpRequestFilterManagerTest.java
#	core/src/test/java/org/apache/seata/core/rpc/netty/http/filter/HttpRequestParamWrapperTest.java
#	dependencies/pom.xml
#	discovery/seata-discovery-etcd3/src/test/java/org/apache/seata/discovery/registry/etcd3/EtcdRegistryServiceImplTest.java
#	discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java
#	discovery/seata-discovery-namingserver/src/test/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImplTest.java
#	discovery/seata-discovery-sofa/src/main/java/org/apache/seata/discovery/registry/sofa/SofaRegistryServiceImpl.java
#	mock-server/pom.xml
#	namingserver/pom.xml
#	rm-datasource/src/main/java/org/apache/seata/rm/datasource/util/XAUtils.java
#	rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/JacksonUndoLogParserTest.java
#	rm-datasource/src/test/java/org/apache/seata/rm/datasource/util/XAUtilsTest.java
#	rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/DataSourceProxyXATest.java
#	saga/seata-saga-statemachine-designer/package-lock.json
#	seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/properties/registry/RegistryMetadataPropertiesTest.java
#	server/src/test/java/org/apache/seata/server/cluster/raft/RaftServerTest.java
#	server/src/test/java/org/apache/seata/server/controller/ClusterControllerTest.java
#	server/src/test/java/org/apache/seata/server/storage/raft/store/RaftVGroupMappingStoreManagerTest.java
#	test-suite/test-new-version/src/test/java/org/apache/seata/saga/annotation/AnnotationConflictTest.java
#	test-suite/test-new-version/src/test/java/org/apache/seata/saga/annotation/DualParserIntegrationTest.java
#	test-suite/test-new-version/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationAction.java
#	test-suite/test-new-version/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationActionImpl.java
# Conflicts:
#	common/src/test/java/org/apache/seata/common/loader/EnhancedServiceLoaderTest.java
Copilot AI review requested due to automatic review settings December 21, 2025 08:33
@YoWuwuuuw YoWuwuuuw added the module/discovery discovery module label Dec 21, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces a comprehensive metadata-based service registration, routing, and load balancing system for Seata. The feature enables server instances to register with custom metadata and clients to perform metadata-driven instance selection through configurable routing filters and weighted load balancing.

Key changes include:

  • Metadata support added to all registry implementations (Nacos, Eureka, Zookeeper, Redis, etc.)
  • New routing framework with expression-based metadata filtering
  • Weighted random load balancing strategy
  • Extensive test coverage for new routing and expression parsing logic

Reviewed changes

Copilot reviewed 112 out of 112 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
application.example.yml, application.raft.example.yml Added metadata configuration fields for server registration
config.txt, application.yml, application.properties Added client routing configuration examples
RegistryMetadataProperties.java Enhanced to support dynamic metadata map with common fields
RoutingProperties.java, MetadataRouterConfig.java New configuration classes for client-side routing
StarterConstants.java Added routing prefix constant
Registry test files Updated to use ServiceInstance instead of InetSocketAddress
Registry implementation files Enhanced to support metadata in registration/discovery
Routing test files Comprehensive test coverage for expression parsing, routers, and chains
Load balance tests Enhanced to test weighted random load balancing

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Dec 21, 2025

Codecov Report

❌ Patch coverage is 70.16908% with 247 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.25%. Comparing base (2deed88) to head (f9303c8).
⚠️ Report is 2 commits behind head on 2.x.

Files with missing lines Patch % Lines
...discovery/routing/expression/ExpressionParser.java 70.75% 9 Missing and 22 partials ⚠️
...ta/discovery/routing/chain/DefaultRouterChain.java 60.56% 23 Missing and 5 partials ⚠️
...scovery/registry/raft/RaftRegistryServiceImpl.java 7.69% 24 Missing ⚠️
...overy/registry/redis/RedisRegistryServiceImpl.java 65.45% 13 Missing and 6 partials ⚠️
...scovery/loadbalance/WeightedRandomLoadBalance.java 62.50% 8 Missing and 10 partials ⚠️
.../namingserver/NamingserverRegistryServiceImpl.java 71.66% 11 Missing and 6 partials ⚠️
...very/registry/zk/ZookeeperRegisterServiceImpl.java 75.00% 8 Missing and 7 partials ⚠️
.../apache/seata/common/metadata/ServiceInstance.java 74.50% 8 Missing and 5 partials ⚠️
...scovery/registry/sofa/SofaRegistryServiceImpl.java 0.00% 12 Missing ⚠️
...ta/core/rpc/netty/AbstractNettyRemotingClient.java 23.07% 9 Missing and 1 partial ⚠️
... and 14 more
Additional details and impacted files
@@             Coverage Diff              @@
##                2.x    #7887      +/-   ##
============================================
+ Coverage     71.07%   71.25%   +0.17%     
  Complexity      797      797              
============================================
  Files          1294     1312      +18     
  Lines         49528    50172     +644     
  Branches       5871     5990     +119     
============================================
+ Hits          35201    35748     +547     
- Misses        11418    11428      +10     
- Partials       2909     2996      +87     
Files with missing lines Coverage Δ
...ava/org/apache/seata/common/ConfigurationKeys.java 0.00% <ø> (ø)
...eata/core/rpc/netty/NettyClientChannelManager.java 84.61% <100.00%> (-0.48%) ⬇️
...che/seata/core/rpc/netty/NettyServerBootstrap.java 75.96% <100.00%> (+1.73%) ⬆️
...scovery/loadbalance/ConsistentHashLoadBalance.java 84.61% <100.00%> (ø)
.../discovery/loadbalance/LeastActiveLoadBalance.java 100.00% <100.00%> (ø)
...eata/discovery/loadbalance/LoadBalanceFactory.java 66.66% <ø> (ø)
...seata/discovery/loadbalance/RandomLoadBalance.java 100.00% <ø> (ø)
...a/discovery/loadbalance/RoundRobinLoadBalance.java 66.66% <ø> (ø)
...he/seata/discovery/loadbalance/XIDLoadBalance.java 94.11% <100.00%> (ø)
...ta/discovery/registry/FileRegistryServiceImpl.java 86.66% <100.00%> (+28.04%) ⬆️
... and 34 more

... and 22 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module/discovery discovery module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce Metadata-based Service Registration, Routing and Load Balancing for Seata

1 participant