Repository navigation
[Bug] Script routing bypassed with two service references #16545
Description
Pre-check
- I am sure that all the content I provide is in English.
Search before asking
- I had searched in the issues and found no similar issues.
I also checked reports concerning AppStateRouterFactory, nextRouter, script routing, and multiple service references; none described this cross-service chain behavior.
Apache Dubbo Component
Java SDK (apache/dubbo)
Affected module: dubbo-cluster.
Dubbo Version
- Apache Dubbo Java 3.3.6 from Maven Central. The consumer printed
AppStateRouterFactory source=.../dubbo-cluster/3.3.6/dubbo-cluster-3.3.6.jar. - Amazon Corretto JDK 1.8.0_442, which supplies Nashorn, on Windows 11 build 26200.
- ZooKeeper: embedded Curator
TestingServer(Curator 5.9.0), used only as the service registry. - Nacos 2.5.2 standalone, used only as the configuration center.
For an isolated Nacos instance with its gRPC port mapped, I used:
docker run -d --name dubbo-route-chain-repro-nacos -e MODE=standalone -e NACOS_AUTH_ENABLE=false -p 127.0.0.1:18848:8848 -p 127.0.0.1:19848:9848 nacos/nacos-server:v2.5.2
$env:NACOS_SERVER_ADDR = '127.0.0.1:18848'Steps to reproduce this issue
-
Start two independent Dubbo provider JVMs exposing
OrderProbeService.instance()under provider applicationroute-chain-repro-order:order-v1on port23981andorder-v2on port23982. Each method simply returns its instance ID as aString. -
Start a third provider JVM exposing
UserProbeService.ping()under applicationroute-chain-repro-user, port23983. -
Register the providers in ZooKeeper. Publish the following rule to Nacos, group
dubbo, Data IDroute-chain-repro-order.script-router:configVersion: v3.0 scope: application key: route-chain-repro-order runtime: true enabled: true force: true type: javascript script: | (function route(invokers, invocation) { var result = new java.util.ArrayList(); for (var i = 0; i < invokers.size(); i++) { if (invokers.get(i).getUrl().getPort() == 23982) { result.add(invokers.get(i)); } } return result; })(invokers, invocation);
-
In one consumer JVM and one DubboBootstrap/default ModuleModel, register both references before
start(). The relevant code is:ReferenceConfig<OrderProbeService> orderRef = new ReferenceConfig<>(); orderRef.setInterface(OrderProbeService.class); orderRef.setLoadbalance("roundrobin"); orderRef.setRetries(0); ReferenceConfig<UserProbeService> userRef = new ReferenceConfig<>(); userRef.setInterface(UserProbeService.class); userRef.setRetries(0); ConfigCenterConfig nacosConfigCenter = new ConfigCenterConfig(); nacosConfigCenter.setAddress("nacos://127.0.0.1:18848"); DubboBootstrap bootstrap = DubboBootstrap.getInstance() .application(new ApplicationConfig("route-chain-repro-consumer")) .configCenter(nacosConfigCenter) .registry(new RegistryConfig("zookeeper://127.0.0.1:23281")) .reference(orderRef) .reference(userRef) .start(); OrderProbeService order = bootstrap.getCache().get(orderRef); UserProbeService user = bootstrap.getCache().get(userRef); user.ping(); // Confirm the second reference is initialized and usable. Map<String, Integer> counts = new LinkedHashMap<>(); for (int i = 0; i < 20; i++) { counts.merge(order.instance(), 1, Integer::sum); } System.out.println(counts);
-
Repeat step 4 in five fresh consumer JVMs. Use a single-reference consumer (
.reference(orderRef)only) as a control. Both provider instances remain online throughout. The RPC methods only returnString; there is no application serialization issue, andretries=0prevents retry effects.
The local reproducer uses Maven dependencies dubbo-cluster, dubbo-config-api, dubbo-rpc-dubbo, dubbo-remoting-netty4, dubbo-registry-zookeeper, dubbo-configcenter-nacos, dubbo-metadata-report-zookeeper, and dubbo-serialization-hessian2, all at version 3.3.6, plus curator-test:5.9.0. A PowerShell runner starts embedded ZooKeeper and all three providers, publishes the rule, then launches each consumer in a new JVM. With the source bundle, the command is ./run-router-chain-repro.ps1 -StartupReferences -Requests 20 -Repeat 5 from PowerShell, with JAVA_HOME pointing to JDK 8. The bundle includes a Maven wrapper.
What you expected to happen
The script accepts only port 23982, so every Order RPC should return order-v2, whether or not the same consumer also references UserProbeService.
Actual result
Single-reference control: order-v2=20, order-v1=0
Two references, startup run 1: order-v2=10, order-v1=10
Two references, startup run 2: order-v2=10, order-v1=10
Two references, startup run 3: order-v2=10, order-v1=10
Two references, startup run 4: order-v2=10, order-v1=10
Two references, startup run 5: order-v2=10, order-v1=10
Standalone source-bundle run: order-v2=9, order-v1=11
The exact split between the two providers can shift with the round-robin starting position. The failure condition is that any Order RPC reaches order-v1 despite the script returning only the order-v2 invoker. The standalone bundle was extracted into a new directory, built with its included Maven wrapper, and run independently of the original checkout.
The same consumer also gives four consecutive order-v2 responses before adding the User reference at runtime; after initializing that reference, 20 further Order RPCs split 10/10. This second sequence is a diagnostic control; the five results above used the normal pre-start registration of both references.
As a causal control, I built a JDK 8 classpath override for AppStateRouterFactory that extends CacheableStateRouterFactory and creates one AppStateRouter per service key. With both references registered before startup, three fresh consumer JVMs each returned order-v2=20.
Anything else
Root cause and code pointers
- In the 3.3.6
AppStateRouterFactory, oneprivate volatile StateRouter routeris returned for every service key in the module. RouterChain.buildSingleChainobtains routers for each service.SingleRouterChain.initWithStateRoutersmutates each router'snextRouterfield. Building the second service's chain therefore changes the sharedAppStateRouter.nextRouterin the first service's chain.- Address notifications iterate the first service's
stateRouterslist, so its ownAppScriptStateRoutercan receive and parse the rule. Actual routing starts atheadStateRouterand follows the mutablenextRouterpointers. It can therefore reach the other service'sAppScriptStateRouter, which has no script rule, and pass through all Order candidates.
The relevant 3.3.6 factory code is:
private volatile StateRouter router;
public <T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url) {
if (router != null) {
return router;
}
synchronized (this) {
if (router == null) {
router = createRouter(url);
}
}
return router;
}When another service chain is built, SingleRouterChain.initWithStateRouters calls nextStateRouter.setNextRouter(stateRouter) on this same returned object. The observed 10/10 split is consistent with the Order script router being bypassed and round-robin choosing between both Order providers.
Could maintainers confirm whether sharing one AppStateRouter across different service keys is intended? If this is a bug, is per-service caching (as used by other state-router factories) the preferred fix, or should the chain topology be made independent of reusable router objects? The regression test should cover two references in one ModuleModel, both construction orders, and the main/backup router-chain switch on address updates.
The proposed fix in PR #16546 caches AppStateRouter per service key and adds regression coverage for both chain construction orders.
Do you have a (mini) reproduction demo?
- Yes, I have a minimal reproduction demo to help resolve this issue more effectively!
The complete runnable real-RPC source is included below. It starts separate provider and consumer JVMs, uses ZooKeeper for registration and Nacos for rule delivery, and checks the returned provider IDs.
Are you willing to submit a pull request to fix on your own?
- Yes I am willing to submit a pull request on my own!
PR #16546 has been submitted.
Code of Conduct
- I agree to follow this project's Code of Conduct.
Complete runnable reproduction source
The following files are the exact minimal project used for the real-RPC reproduction. A normal Maven installation can build them; my local source bundle also includes the standard Maven wrapper. The runner writes detailed logs under target/repro-*.log.
Expand the exact POM, Java sources, rule, and PowerShell runner
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dubbo.samples</groupId>
<artifactId>dubbo-demo-script-router</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-api</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-configcenter-nacos</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-zookeeper</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<version>5.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>src/main/java/org/apache/dubbo/samples/script/OrderProbeService.java
package org.apache.dubbo.samples.script;
public interface OrderProbeService {
String instance();
}src/main/java/org/apache/dubbo/samples/script/UserProbeService.java
package org.apache.dubbo.samples.script;
public interface UserProbeService {
String ping();
}src/main/java/org/apache/dubbo/samples/script/RouterChainReproMain.java
package org.apache.dubbo.samples.script;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import org.apache.curator.test.TestingServer;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ConfigCenterConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.rpc.cluster.router.condition.config.AppStateRouterFactory;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
/** Real RPC reproduction of a shared AppStateRouter.nextRouter across two service chains. */
public final class RouterChainReproMain {
private static final String REGISTRY_URL = "zookeeper://127.0.0.1:23281";
private static final String ORDER_APP = "route-chain-repro-order";
private static final String USER_APP = "route-chain-repro-user";
private static final String RULE_DATA_ID = ORDER_APP + ".script-router";
private RouterChainReproMain() {}
public static void main(String[] args) throws Exception {
if (args.length == 0) {
throw new IllegalArgumentException("zookeeper | order-provider <id> <port> | user-provider | publish | consumer <single|multi> <count>");
}
switch (args[0]) {
case "zookeeper":
try (TestingServer server = new TestingServer(23281)) {
System.out.println("ZooKeeper ready: " + server.getConnectString());
new CountDownLatch(1).await();
}
return;
case "order-provider":
requireArgs(args, 3);
runOrderProvider(args[1], Integer.parseInt(args[2]));
return;
case "user-provider":
runUserProvider();
return;
case "publish":
publishRule();
return;
case "consumer":
requireArgs(args, 3);
if (!"single".equals(args[1]) && !"multi".equals(args[1])
&& !"fixed".equals(args[1]) && !"startup".equals(args[1])
&& !"fixed-startup".equals(args[1])) {
throw new IllegalArgumentException("Unknown consumer mode: " + args[1]);
}
runConsumer(args[1], Integer.parseInt(args[2]));
return;
default:
throw new IllegalArgumentException("Unknown role: " + args[0]);
}
}
private static void requireArgs(String[] args, int count) {
if (args.length != count) {
throw new IllegalArgumentException("Expected " + count + " arguments, got " + args.length);
}
}
private static void runOrderProvider(String id, int port) {
ServiceConfig<OrderProbeService> service = new ServiceConfig<>();
service.setInterface(OrderProbeService.class);
service.setRef(() -> id);
DubboBootstrap.getInstance()
.application(new ApplicationConfig(ORDER_APP))
.registry(new RegistryConfig(REGISTRY_URL))
.protocol(new ProtocolConfig("dubbo", port))
.service(service)
.start()
.await();
}
private static void runUserProvider() {
ServiceConfig<UserProbeService> service = new ServiceConfig<>();
service.setInterface(UserProbeService.class);
service.setRef(() -> "user-provider");
DubboBootstrap.getInstance()
.application(new ApplicationConfig(USER_APP))
.registry(new RegistryConfig(REGISTRY_URL))
.protocol(new ProtocolConfig("dubbo", 23983))
.service(service)
.start()
.await();
}
private static void publishRule() throws Exception {
InputStream input = RouterChainReproMain.class.getClassLoader().getResourceAsStream("route-chain-repro.yaml");
if (input == null) {
throw new IllegalStateException("route-chain-repro.yaml is missing");
}
String rule;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
rule = reader.lines().collect(Collectors.joining("\n"));
}
ConfigService config = NacosFactory.createConfigService(nacosProperties());
try {
if (!config.publishConfig(RULE_DATA_ID, "dubbo", rule)) {
throw new IllegalStateException("Nacos rejected " + RULE_DATA_ID);
}
} finally {
config.shutDown();
}
System.out.println("Published " + RULE_DATA_ID + " to Nacos group dubbo");
}
private static Properties nacosProperties() {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosAddress());
String username = System.getenv("NACOS_USERNAME");
String password = System.getenv("NACOS_PASSWORD");
if (username != null && !username.isEmpty()) {
properties.setProperty(PropertyKeyConst.USERNAME, username);
}
if (password != null && !password.isEmpty()) {
properties.setProperty(PropertyKeyConst.PASSWORD, password);
}
return properties;
}
private static String nacosAddress() {
String address = System.getenv("NACOS_SERVER_ADDR");
return address == null || address.isEmpty() ? "127.0.0.1:18848" : address;
}
private static void runConsumer(String mode, int requests) {
boolean multi = !"single".equals(mode);
boolean startup = "startup".equals(mode) || "fixed-startup".equals(mode);
boolean fixed = "fixed".equals(mode) || "fixed-startup".equals(mode);
if (requests < 2) {
throw new IllegalArgumentException("At least two requests are needed to observe round-robin leakage");
}
System.out.println("Java version=" + System.getProperty("java.version"));
System.out.println("AppStateRouterFactory source="
+ AppStateRouterFactory.class.getProtectionDomain().getCodeSource().getLocation());
ConfigCenterConfig configCenter = new ConfigCenterConfig();
configCenter.setAddress("nacos://" + nacosAddress());
String username = System.getenv("NACOS_USERNAME");
String password = System.getenv("NACOS_PASSWORD");
if (username != null && !username.isEmpty()) {
configCenter.setUsername(username);
}
if (password != null && !password.isEmpty()) {
configCenter.setPassword(password);
}
ReferenceConfig<OrderProbeService> orderRef = new ReferenceConfig<>();
orderRef.setInterface(OrderProbeService.class);
orderRef.setLoadbalance("roundrobin");
orderRef.setRetries(0);
ReferenceConfig<UserProbeService> userRef = null;
if (multi) {
userRef = new ReferenceConfig<>();
userRef.setInterface(UserProbeService.class);
userRef.setRetries(0);
}
DubboBootstrap bootstrap = DubboBootstrap.getInstance()
.application(new ApplicationConfig("route-chain-repro-consumer"))
.configCenter(configCenter)
.registry(new RegistryConfig(REGISTRY_URL))
.reference(orderRef);
if (startup) {
bootstrap.reference(userRef);
}
bootstrap.start();
try {
OrderProbeService order = bootstrap.getCache().get(orderRef);
if (!startup) {
for (int i = 0; i < 4; i++) {
String result = order.instance();
if (!"order-v2".equals(result)) {
throw new IllegalStateException("Order rule did not work before second service was referenced: " + result);
}
}
System.out.println("Before second service: 4/4 Order RPCs reached order-v2");
}
if (multi) {
if (!startup) {
bootstrap.reference(userRef);
}
UserProbeService user = bootstrap.getCache().get(userRef);
if (!"user-provider".equals(user.ping())) {
throw new IllegalStateException("Second service is not reachable");
}
System.out.println("Second service reference initialized and invoked in the same JVM");
}
Map<String, Integer> counts = new LinkedHashMap<>();
for (int i = 0; i < requests; i++) {
String id = order.instance();
counts.merge(id, 1, Integer::sum);
}
System.out.println("Mode=" + mode + " Order RPC results=" + counts);
if (!multi && !counts.keySet().equals(java.util.Collections.singleton("order-v2"))) {
throw new IllegalStateException("Single-service control failed: " + counts);
}
if (!fixed && multi && !counts.containsKey("order-v1")) {
throw new IllegalStateException("The multi-service bug did not reproduce: " + counts);
}
if (fixed && !counts.keySet().equals(java.util.Collections.singleton("order-v2"))) {
throw new IllegalStateException("The router factory patch did not restore Order routing: " + counts);
}
System.out.println(fixed ? "PATCHED CONTROL PASSED"
: multi ? "BUG REPRODUCED" : "SINGLE-SERVICE CONTROL PASSED");
} finally {
bootstrap.stop();
}
}
}src/main/resources/route-chain-repro.yaml
configVersion: v3.0
scope: application
key: route-chain-repro-order
runtime: true
enabled: true
force: true
type: javascript
script: |
(function route(invokers, invocation) {
var result = new java.util.ArrayList();
for (var i = 0; i < invokers.size(); i++) {
if (invokers.get(i).getUrl().getPort() == 23982) {
result.add(invokers.get(i));
}
}
return result;
})(invokers, invocation);run-router-chain-repro.ps1
param(
[ValidateRange(2, 1000)][int]$Requests = 20,
[ValidateRange(1, 10)][int]$Repeat = 5,
[string]$HotfixJar = '',
[switch]$StartupReferences
)
$ErrorActionPreference = 'Stop'
$projectDirectory = $PSScriptRoot
$repositoryDirectory = (Resolve-Path (Join-Path $projectDirectory '..\..')).Path
$projectMavenWrapper = Join-Path $projectDirectory 'mvnw.cmd'
$repositoryMavenWrapper = Join-Path $repositoryDirectory 'mvnw.cmd'
$maven = if (Test-Path -LiteralPath $projectMavenWrapper) {
$projectMavenWrapper
} elseif (Test-Path -LiteralPath $repositoryMavenWrapper) {
$repositoryMavenWrapper
} else {
$command = Get-Command mvn.cmd,mvn -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $command) {
throw 'Maven is required when mvnw.cmd is not present.'
}
$command.Source
}
$pom = Join-Path $projectDirectory 'pom.xml'
$classpathFile = Join-Path $projectDirectory 'target\classpath.txt'
$mainClass = 'org.apache.dubbo.samples.script.RouterChainReproMain'
$java = (Get-Command java -ErrorAction Stop).Source
$processes = [System.Collections.Generic.List[System.Diagnostics.Process]]::new()
if (-not $env:NACOS_SERVER_ADDR) {
$env:NACOS_SERVER_ADDR = '127.0.0.1:18848'
}
$endpoint = $env:NACOS_SERVER_ADDR.Split(':')
if ($endpoint.Length -ne 2) {
throw 'NACOS_SERVER_ADDR must be host:port.'
}
function Wait-ForPort {
param([string]$HostName, [int]$Port, [System.Diagnostics.Process]$Process)
for ($attempt = 0; $attempt -lt 90; $attempt++) {
if ($Process -and $Process.HasExited) {
throw "Process exited before $($HostName):$Port opened. Inspect target/repro-*.stderr.log."
}
$client = [System.Net.Sockets.TcpClient]::new()
try {
$connection = $client.BeginConnect($HostName, $Port, $null, $null)
if ($connection.AsyncWaitHandle.WaitOne(500)) {
$client.EndConnect($connection)
return
}
} catch {
} finally {
$client.Close()
}
Start-Sleep -Milliseconds 500
}
throw "Timed out waiting for $($HostName):$Port."
}
function Start-Role {
param([string]$Name, [string[]]$RoleArguments)
$stdout = Join-Path $projectDirectory "target\repro-$Name.stdout.log"
$stderr = Join-Path $projectDirectory "target\repro-$Name.stderr.log"
$arguments = @('-cp', ('"' + $classpath + '"'), $mainClass) + $RoleArguments
$process = Start-Process -FilePath $java -ArgumentList $arguments -PassThru -WindowStyle Hidden `
-RedirectStandardOutput $stdout -RedirectStandardError $stderr
$processes.Add($process)
return $process
}
function Invoke-Role {
param([string]$Name, [string[]]$RoleArguments)
$stdout = Join-Path $projectDirectory "target\repro-$Name.stdout.log"
$stderr = Join-Path $projectDirectory "target\repro-$Name.stderr.log"
$arguments = @('-cp', ('"' + $classpath + '"'), $mainClass) + $RoleArguments
$process = Start-Process -FilePath $java -ArgumentList $arguments -Wait -PassThru -WindowStyle Hidden `
-RedirectStandardOutput $stdout -RedirectStandardError $stderr
$output = Get-Content -LiteralPath $stdout
$output | Select-String 'Before second service|Second service reference|Mode=|BUG REPRODUCED|SINGLE-SERVICE|Published' |
ForEach-Object { Write-Host $_.Line }
if ($process.ExitCode -ne 0) {
$errorText = (Get-Content -LiteralPath $stderr -Tail 40) -join [Environment]::NewLine
throw "Role $Name failed with exit code $($process.ExitCode). $errorText"
}
}
Wait-ForPort -HostName $endpoint[0] -Port ([int]$endpoint[1])
$ready = $false
for ($attempt = 0; $attempt -lt 90; $attempt++) {
try {
$health = Invoke-WebRequest -Uri "http://$($env:NACOS_SERVER_ADDR)/nacos/v1/console/health/readiness" -TimeoutSec 2 -UseBasicParsing
if ($health.StatusCode -eq 200) {
$ready = $true
break
}
} catch {
}
Start-Sleep -Seconds 1
}
if (-not $ready) {
throw "Nacos did not become ready at $env:NACOS_SERVER_ADDR."
}
Push-Location -LiteralPath $projectDirectory
try {
& $maven -f $pom -q -DskipTests package dependency:build-classpath "-Dmdep.outputFile=$classpathFile"
$mavenExitCode = $LASTEXITCODE
} finally {
Pop-Location
}
if ($mavenExitCode -ne 0) {
throw 'Maven build failed.'
}
$dependencyClasspath = (Get-Content -Raw -LiteralPath $classpathFile).Trim()
$classpath = (Join-Path $projectDirectory 'target\classes') + ';' + $dependencyClasspath
if ($HotfixJar) {
$classpath = (Resolve-Path -LiteralPath $HotfixJar).Path + ';' + $classpath
}
try {
$zookeeper = Start-Role -Name 'zookeeper' -RoleArguments @('zookeeper')
Wait-ForPort -HostName '127.0.0.1' -Port 23281 -Process $zookeeper
$v1 = Start-Role -Name 'order-v1' -RoleArguments @('order-provider', 'order-v1', '23981')
Wait-ForPort -HostName '127.0.0.1' -Port 23981 -Process $v1
$v2 = Start-Role -Name 'order-v2' -RoleArguments @('order-provider', 'order-v2', '23982')
Wait-ForPort -HostName '127.0.0.1' -Port 23982 -Process $v2
$user = Start-Role -Name 'user' -RoleArguments @('user-provider')
Wait-ForPort -HostName '127.0.0.1' -Port 23983 -Process $user
Invoke-Role -Name 'publish' -RoleArguments @('publish')
Invoke-Role -Name 'single' -RoleArguments @('consumer', 'single', [string]$Requests)
$mode = if ($HotfixJar -and $StartupReferences) { 'fixed-startup' } `
elseif ($HotfixJar) { 'fixed' } elseif ($StartupReferences) { 'startup' } else { 'multi' }
for ($run = 1; $run -le $Repeat; $run++) {
Write-Host "Multi-service fresh JVM run $run/$Repeat, mode=$mode"
Invoke-Role -Name "multi-$run" -RoleArguments @('consumer', $mode, [string]$Requests)
}
Write-Host "Mode $mode verified across $Repeat fresh consumer JVMs."
} finally {
foreach ($process in $processes) {
if (-not $process.HasExited) {
Stop-Process -Id $process.Id -Force
}
}
}Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
xiaobaicai66695 commentedon Oct 26, 2026
if this is a true bug that needs to fix,Im glad to provide a PR.
xiaobaicai66695 commentedon Oct 26, 2026
ask for assign
[-][Bug] Script routing bypassed with two service references / 双服务引用时脚本路由失效[/-][+][Bug] Script routing bypassed with two service references[/+]