Chapter 25Lesson 03~210 minutes

Distributed Testing, Remote Engines, Network Topology, and Synchronization: Configuration, Design Patterns, and Trade-Offs

Native JMeter remote mode is not the only way to scale generators. At larger scales many teams run independent CLI workers under an orchestrator because it reduces controller result-transfer pressure and makes per-worker artifacts explicit. The right design depends on how much central control, live telemetry, file distribution, failure isolation and networking complexity you can operate reliably.

Remote vs workersCentral vs local resultsSharded dataFixed RMI portsBackend telemetry

Learning objectives

  • Choose native remote mode or independent workers based on failure/capacity boundaries.
  • Choose central aggregate results or per-engine artifacts deliberately.
  • Choose shared logical data versus engine-specific shards without collisions.
  • Engineer RMI SSL and fixed port topology rather than disabling security.
  • Choose controller-mediated sample results or backend telemetry from evidence needs.
  • Keep core/JVM/OS/SUT/plugin/CI/container configuration boundaries explicit.

1. Mandatory learning remains local/free

All executable examples remain on loopback/private isolated infrastructure; the mandatory local fallback uses 127.0.0.1. Paid load clouds, managed Kubernetes, shared performance farms, enterprise identity and commercial orchestration are optional contexts only. The chapter's independent-worker simulation is the stable local fallback. Never substitute a public/shared/production endpoint for these executable examples.

2. Native remote mode versus independent CLI workers

Native JMeter remote mode Independent CLI workers / orchestrator
One controller sends JMX/commands to engines and can collect central results. Each worker starts JMeter CLI independently with explicit local inputs/outputs.
Convenient single start/stop surface. Natural failure isolation and per-worker logs/JTL.
RMI SSL/firewall/reverse callback/sample-transfer topology required. Orchestrator/SSH/CI/container mechanism distributes files/commands instead.
Controller/network can become a result-processing bottleneck. No central sample-return bottleneck; aggregation happens later/backend.
JMX is automatically sent, data/plugins are not. Orchestrator must deploy JMX + all files explicitly.
Good small trusted fleet pattern. Often easier to scale to many ephemeral workers if orchestration exists.

3. Central versus per-engine result collection

Central controller JTL: easy aggregate count/report, but every selected remote engine returns samples through controller/network. Default StrippedBatch reduces result payload but does not eliminate cost.

Per-engine JTL: workers write locally and artifacts are merged later. This reduces controller pressure but requires reliable artifact collection, synchronized schemas/clocks and duplicate-safe run/engine IDs.

Do not accidentally do both at scale unless the duplicate write/transfer cost is intentional.

4. Remote sample sender modes

Mode Behavior / trade-off
StrippedBatch (default) Strips successful responseData and sends batches; strong general remote-load default.
Standard Synchronous every-sample return; can limit engine throughput.
Batch Batches without stripping; bigger payload if responseData exists.
Asynch / StrippedAsynch Worker queue smooths peaks; if generation outruns sending, queue fills and sampler thread blocks.
Hold Keeps all samples until test end; high engine memory risk and discouraged.
Statistical Returns summaries only; loses per-sample fields and changes forensic result semantics.

Changing sender mode changes the measurement pipeline. Preserve it in the run manifest and do not compare runs with different modes as if result-transfer cost were identical.

5. One shared logical dataset versus sharded files

A copied identical CSV on every engine does not become globally coordinated. Each JVM opens its local copy independently. If every shard contains user001 first, multiple engines can reuse the same account.

Use one of these:

  • pre-shard unique records per engine (mandatory lab);
  • derive non-overlapping ranges from engine ID only when the data semantics allow it;
  • use an external concurrency-safe allocator only if its own latency/capacity/failure behavior is acceptable and measured.

Never use one writable network CSV as a pretend distributed lock.

6. RMI SSL and fixed ports

Private firewall rules are easier to audit with fixed ports. Explicitly set registry/server and controller callback ranges and restrict them to trusted generator networks. RMI SSL stays enabled.

The current docs have two descriptions of the server engine port: the remote-testing text warns about dynamic firewall behavior, while the current properties reference lists server.rmi.localport=4000 as a default. The operational resolution is simple: set it explicitly and record the actual listening sockets.

7. Shared lab keystore versus production trust design

JMeter's helper creates a convenient common RMI keystore. For a disposable lab, one copied file is practical. In production, certificate lifetime, password handling, private-key distribution, host authorization and rotation should follow organizational PKI/secrets policy. The fact that JMeter can share one key pair does not make a default-password shared private key a general security architecture.

8. Controller-mediated results versus Backend Listener telemetry

Chapter 24's Backend Listener runs as part of the test plan on each executing engine. A distributed design can therefore send live aggregate metrics from engines directly to InfluxDB/Graphite while retaining smaller per-engine/central forensic JTL.

This can reduce the need to use the controller as the only live aggregation point, but it creates multiple backend clients and can multiply metric series/writes. Add an engine_id tag only if cardinality/retention supports it.

9. -R, -r, -G design choices

Mechanism Use
-Rhost1,host2 Explicit per-run engine list; useful in manifests/CI and overrides remote_hosts.
-r Runs hosts from controller remote_hosts; useful for a managed stable fleet.
-Gname=value Same JMeter property to every remote server.
-Gfile.properties Bulk global remote properties from a controller-side property file.
Engine-local startup property Unique engine ID, data-file path, local backend identity/port as needed.

10. Missing engine policy changes total load

client.continue_on_fail defaults to false. Keep that fail-closed behavior for capacity/regression tests unless you have a very explicit degraded-capacity policy. If a two-engine run silently continues with one engine, the configured total load is halved; a “successful” target latency comparison becomes invalid.

11. Configuration-layer boundaries

Layer Examples Do not confuse with
JMeter core/test plan remote hosts, -G, sender mode, CSV paths, Thread Groups Java heap/GC.
Java/JVM same Java version, heap, GC, RMI JVM properties SUT latency.
OS/network RMI/firewall/NAT/DNS/routes/NIC/socket limits/clock JMeter property scope.
SUT server capacity, rate limits, source-IP behavior controller result-transfer saturation.
Plugin/driver third-party samplers/listeners/JAR versions core JMeter remote transport.
CI/container/orchestrator worker inventory, artifact/file deployment, service networking target capacity unless independently proven.

12. Worked scenario

You need 600 configured threads and have three generator VMs. Two VMs comfortably support 250 threads each; one supports 100. Native remote mode with one global -Gthreads=200 would force all engines to 200 and overload the small VM.

Better options:

  • three separately configured independent workers: 250 +250 +100;
  • separate plans/groups/engine-specific startup properties only if the design remains maintainable and exact;
  • never claim JMeter will “balance 600” based on host capacity automatically.

13. Decision table

Need Preferred design Validity evidence
Two-to-five trusted private engines Native remote mode + SSL + fixed ports + StrippedBatch controller/engine CPU/network + central JTL + engine logs.
Many ephemeral/unequal workers Independent CLI workers/orchestrator per-worker manifest/JTL/log + later aggregate/backend metrics.
Unique synthetic users Pre-sharded data per engine hash manifest + target collision detection.
Live fleet view Backend Listener per engine + low-cardinality engine/run tags backend overhead/cardinality + JTL fallback.
Strong per-sample forensic trail Per-engine/central CSV JTL with stable schema result transfer/disk/artifact capacity measured.
Strict capacity regression fail if any engine missing inventory/version/start success + exact aggregate count.

14. Configured versus achieved load

Distributed configured load is the sum of engine configurations. Achieved load must be checked per engine and in aggregate. If engine-b starts late, runs out of CSV, loses RMI connectivity, saturates CPU, or is skipped after initialization failure, the target does not receive the planned experiment.

Preserve controller aggregate JTL, matching controller jmeter.log, every engine log, engine inventory/version/file hashes, target event counts, generator CPU/GC/network and sender mode before making a capacity/regression claim.

Knowledge check

Why can identical CSV copies on two engines create duplicate users?

When is -R preferable to -r?

Why keep client.continue_on_fail=false for a capacity test?

How does StrippedBatch help remote mode?

Why might independent workers scale better than native remote mode?

Next lesson

Diagnose distributed failures causally

Lesson 4 covers replicated-load mistakes, version/plugin drift, missing files, public/insecure RMI, controller/network saturation and a deliberate wrong-shard engine configuration.

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against current Apache JMeter primary documentation on 2026-09-05. The course baseline remains Apache JMeter 5.6.3 with a Java 17 JDK; JMeter 5.6.3 requires Java 8+. JMeter remote mode sends the test plan to every remote server, but each engine runs the entire test plan; workload is not divided automatically. All controller/server nodes should run exactly the same JMeter version, and JMeter discourages mixing Java versions. External data files are not sent by the controller and must exist on every server in the expected path; plugins/user JARs likewise need an explicit identical deployment. CLI -r starts servers listed in remote_hosts; -Rhost1,host2 explicitly selects/overrides the server list; -Gname=value or -Gpropertyfile sends JMeter properties to remote servers. Since JMeter 4.0, RMI uses SSL by default. JMeter ships create-rmi-keystore, whose generated test certificate is documented as valid for seven days and uses default alias rmi/passphrase changeit; these defaults are suitable only for a disposable private lab. server.rmi.ssl.disable defaults to false and this chapter never disables it. The remote-testing manual describes dynamic server-engine ports conceptually, while the current properties reference lists server.rmi.localport default 4000; production/private labs should set registry/server/callback ports explicitly instead of relying on defaults. The controller reverse callback client.rmi.localport defaults to 0 (random); when set non-zero JMeter can use up to three consecutive ports. Current default remote sample sender mode is StrippedBatch: successful response bodies are stripped and results are batched. Remote mode can consume more resources than equivalent independent CLI workers, and the controller/client or its network can become the bottleneck.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.