YoWuwuuuw opened a new issue, #7547: URL: https://github.com/apache/incubator-seata/issues/7547
### Check Ahead - [x] I have searched the [issues](https://github.com/seata/seata/issues) of this repository and believe that this is not a duplicate. - [x] I am willing to try to implement this feature myself. ### Why you need it? To enhance Apache Seata's routing intelligence and flexibility in multi-node, cross-region, and multi-environment deployments, this proposal designs and implements: - A pluggable routing filter mechanism based on the Chain of Responsibility pattern; - **Expression-based routing configuration** using service metadata such as region, env, version, etc.; - Support for **configuration-driven**, **dynamically extensible**, and **hot-pluggable** router modules; - Features such as **primary-backup routing**, **snapshot debugging**, and **performance optimizations like `BitList`**. ### How it could be? #### 1. Core Routing Flow Request routing flow: ``` Client Request ↓ Service instance discovery from registry (with metadata) ↓ RouterChain execution: - RegionRouter (geolocation) - MetadataMatchRouter (version, environment, etc.) - Custom routers (via SPI) ↓ Filtered candidate Server list ↓ Load Balance selects the final Server ↓ Request sent to selected Server ``` #### 2. Supported Routing Filter Types | Router Type | Description | Metadata Field(s) | Notes | |---------------------|------------------------------------|-------------------|--------------------------------------------------------------------------------------------| | RegionRouter | Matches geographic region or distance | region | Geo-aware filtering: **calculates distance** from client via coordinates and selects the nearest N Server instances (e.g., regionTopN = 5) to achieve optimal latency. | | MetadataMatchRouter | Matches arbitrary metadata expressions | version, env | General-purpose string match with support for **logical expressions** ("AND", "OR") and **conditional operators** (e.g., >=, <=, <, >). | | xxxRouter | Custom rule via SPI | — | User-implemented filters via **SPI**. Only sequence-based, not combinable with expression-based routers. | #### 3. Routing Rule Configuration (YAML) ``` registry: route: enabled: true chain: - RegionRouter - MetadataRouter RegionRouter: enabled: true regionTopN: 5 MetadataRouter: expression: "version >= 2.3 | env = dev" fallbackToAny: true snapshot-log: enabled: true ``` #### 4. Fallback Mechanism > To ensure high availability, the routing filter chain supports a fallback mechanism. Scenarios: - When any router (e.g., RegionRouter or MetadataRouter) filters out all service instances; - To prevent transaction failures or unavailability, fallback is triggered; - The system reverts to using the full list of available Seata Server instances for downstream routing and load balancing. Example: `registry.route.fallbackToAny: true` #### 5. Future Extensibility - Hot-reload of routing expressions with visual UI editor - Dynamic lifecycle management of filter chain (add/remove/disable routers at runtime) ### Other related information #### Module Design ##### 1. StateRouter<T> Interface ``` public interface StateRouter<T> { BitList<T> route(BitList<T> servers, RoutingContext ctx, boolean debugMode, Holder<RouterSnapshotNode<T>> snapshotHolder); boolean isRuntime(); boolean isForce(); String buildSnapshot(); void setNext(StateRouter<T> next); // chain-of-responsibility } ``` ##### 2. RegionRouter<T> – Geographic Distance Filter > Filters server instances based on the geographic distance from the client (from metadata coordinates), selecting the top-N closest instances. ``` public class RegionRouter<T extends ServerInstance> extends AbstractStateRouter<T> { private final int regionTopN; @Override protected BitList<T> doRoute(BitList<T> servers, RoutingContext ctx) { GeoLocation clientLocation = getClientLocation(ctx); List<ServerWithDistance<T>> sorted = servers.stream() .map(s -> new ServerWithDistance<>(s, calculateDistance(clientLocation, s))) .sorted(Comparator.comparingDouble(ServerWithDistance::getDistance)) .limit(regionTopN) .collect(Collectors.toList()); return BitList.fromList(sorted.stream() .map(ServerWithDistance::getServer) .collect(Collectors.toList())); } } ``` ##### 3. MetadataRouter<T> – Metadata Expression Matcher > Supports advanced expression-based filtering (e.g., version >= 2.0 & env = dev). ``` public class MetadataRouter<T extends ServerInstance> extends AbstractStateRouter<T> { private volatile String expression; @Override protected BitList<T> doRoute(BitList<T> servers, RoutingContext ctx) { List<ConditionMatcher> matchers = ExpressionParser.parse(expression); return servers.filter(server -> matchers.stream() .allMatch(m -> m.match(server, ctx))); } } ``` ##### 4. RouterChain<T> – Filter Chain Controller > Implements the chain-of-responsibility routing pipeline, where each router processes and passes down a filtered BitList. ``` class RoutingFilterChain { List<RoutingFilter> filters; BitList<Server> filterAll(BitList<Server> origin, MetadataContext ctx) { for (RoutingFilter f : filters) { origin = f.filter(origin, ctx); } return origin; } } ``` ##### 5. ExpressionParser – Expression Engine Parses metadata-based filtering expressions. Expressions follow: - Exact match: key = value - Comparison operators: >=, <=, <, > - Logical operators: & (AND), | (OR) - Parentheses for precedence Example Expressions: version = 1.2 & env = dev region = cn-bj | region = cn-hz // plain string matching, not geo-based version >= 2.0 | (env = staging & region != us-west) Features: - Case-sensitive matching - Optional fallback: If no match is found, fallback to all instances if fallback = true - Configurable router priorities or disable via enabled = false ##### 6. Primary-Backup Routing Chain > Supports active-failover between routing chains. Typically used in multi-region deployments, where a primary region is preferred, with automatic fallback to backup. ``` public class PrimaryBackupRouterChain<T> { private volatile RouterChain<T> primaryChain; private volatile RouterChain<T> backupChain; private volatile RouterChain<T> current; public synchronized void updateServers(List<T> servers, Runnable onSwitch) { current = backupChain; primaryChain.setServers(servers); onSwitch.run(); current = primaryChain; backupChain.setServers(servers); } } ``` #### Optimization ##### 1. BitList<T> – High-Performance Filtering Structure A wrapper around BitSet to represent valid service candidates efficiently. Each filter updates only the active bitset, avoiding full array copies. ``` BitList<ServerInstance> origin = new BitList<>(servers); BitList<ServerInstance> filtered = origin.filter(s -> s.region.equals("cn")); ``` Text Diagram: ``` Original list of service instances (indexed): Index: 0 1 2 3 4 Servers: A B C D E Initial BitSet (all 1s, meaning all valid): Bits: 1 1 1 1 1 After RegionRouter filtering (keep only region = "cn"): Bits: 0 1 1 0 1 Servers: B C E After MetadataRouter filtering (keep only version >= 2.0): Bits: 0 1 0 0 1 Servers: B E ``` ##### 2. SnapshotLogger – Router Debugging Output > Logs each routing step's filter results and decision-making process. Example Output: ``` [RoutingChain] Service = 'default', Total servers = 5 [RegionRouter] config: regionTopN = 3, matched = 3, selected = [192.168.1.1, 192.168.1.3, 192.168.1.6] [MetadataMatchRouter] expression = "version >= 1.2 & env = dev", matched = 2, selected = [192.168.1.3, 192.168.1.6] Final candidates: [192.168.1.3, 192.168.1.6] ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For additional commands, e-mail: notifications-h...@seata.apache.org