skip to content

What does JMeter's bin/jmeter-server script actually start on a load generator?

level: middleimportance: must knowfreq 68%

answer

  1. The script itself is only one command
  2. A single letter flag does all the work
  3. Nothing about the plan lives on this host
  4. It registers under a fixed lookup name

basics

~10 s

It launches JMeter with -s, which starts a long-lived engine process that registers itself for remote calls and then waits. It holds no test plan until a controller sends one.

solid answer

~40 s

The script is a thin wrapper around `jmeter -Dserver_port=${SERVER_PORT:-1099} -s -j jmeter-server.log`. The `-s` (`--server`) flag makes JMeter skip both the GUI and the plan-loading path and start a remote engine instead. By default that engine creates its own RMI registry — `server.rmi.create` has defaulted to `true` since JMeter 2.3.1 — and binds itself there under the fixed name `JMeterEngine`, then idles. It holds no test plan: at run time the controller ships the parsed tree over RMI, so the `.jmx` never has to exist on the generator. Because the tree crosses the wire as serialized Java objects, every node must run the same JMeter version. While a test is active the engine refuses a second controller with `Engine is busy - please try later`.

code

bash · 11 lines
bash
# gen1..gen3: default port
jmeter-server

# gen4: second server on a host that already runs one
SERVER_PORT=1234 jmeter-server

# what either command really runs
# jmeter -Dserver_port=<port> -s -j jmeter-server.log

# multi-homed generator: pin the address the controller must call back on
RMI_HOST_DEF=-Djava.rmi.server.hostname=10.0.0.24 jmeter-server

go deeper

for a junior

Remember that a generator runs bin/jmeter-server and then just waits, and that you never copy the .jmx to it. Knowing the script is a wrapper around jmeter -s is enough at this level.

for a middle

Explain that -s starts a remote engine, that the engine creates its own RMI registry and binds under a fixed name, and that the plan is serialized to it per run rather than installed.

for a senior

Show that you read jmeter-server.log on the generator first, recognise the loopback and site-local address warnings, and treat a version skew across nodes as a deserialization problem rather than a network one.

for a principal

Own the rule that the generator image is built once and rolled out identically. Version drift between controller and engines is the failure mode that costs a whole test window, and it is prevented by provisioning, not by documentation.

## The script is a two-line wrapper `bin/jmeter-server` contains no logic of its own. Stripped of comments it is: ```bash DIRNAME=`dirname $0` ${DIRNAME}/jmeter ${RMI_HOST_DEF} -Dserver_port=${SERVER_PORT:-1099} -s -j jmeter-server.log "$@" ``` Three things are happening, and all of them matter when a run misbehaves: - **`-s`** (`--server`) is the flag that does the work. Everything else is convenience. - **`-Dserver_port=…`** fixes the port the server listens on, defaulting to `1099`, and honours a `SERVER_PORT` environment variable so you can run more than one server on a host. - **`-j jmeter-server.log`** sends the engine's own log somewhere other than `jmeter.log`, which is the first file to read when a generator refuses to accept a plan. There is also a commented-out `RMI_HOST_DEF` line in the script for pinning `java.rmi.server.hostname`, left there because a multi-homed generator otherwise advertises an address the controller cannot reach. ## What -s actually starts With `-s` present, JMeter never loads a plan and never enters the GUI branch. It creates a remote engine object, and by default that engine also **creates its own RMI registry** — the `server.rmi.create` property has defaulted to `true` since JMeter 2.3.1, so you do not start `rmiregistry` yourself. The engine then binds itself into that registry under the fixed name `JMeterEngine` and the process simply stays up. At this point the generator holds: | Thing | Present on the server before a run? | |---|---| | The JMeter installation and its plugins | yes, and it must match the controller's version | | A registry entry named `JMeterEngine` | yes, created at startup | | The test plan | **no** — it arrives per run, over RMI | | Any results | no | ## The plan arrives, it is not installed When the controller starts a run it calls `rconfigure` on each engine, handing over the already parsed and cloned test tree, plus the script name and the base directory the plan was loaded from. That is why a distributed run needs no `.jmx` on the generators at all: driving `gen1` through `gen4` from a laptop, `plan.jmx` exists only on the laptop. The server then builds a fresh `StandardJMeterEngine` for that tree, receives any properties the controller wants to push, and is told to run. Because the tree crosses the wire as serialized Java objects, every node has to be running **the same JMeter version**, and the same Java version is strongly advised. There is no version handshake in the protocol — a mismatch shows up as a deserialization failure or a missing class on the generator, not as a friendly message. ## One engine, one test at a time The engine guards itself. If a second controller calls `rconfigure` while a test is still active on that host, JMeter logs `Engine is busy - cannot create JMeter engine` and throws back `Engine is busy - please try later`. In practice: 1. One `jmeter-server` process per host is the norm; a second one on the same host needs its own port, via `SERVER_PORT`. 2. Two people cannot share a generator pool by accident — the second run fails fast instead of quietly blending its threads into the first. 3. After a run ends the engine goes idle again and is immediately reusable, unless the run was started with `-X` or the server was configured with `server.exitaftertest=true`. ## The failure you will actually see If the server logs `Cannot start. <hostname> is a loopback address.` it resolved its own name to `127.0.0.1`; if it logs that the address is site-local, the controller may not be able to route back. Both are fixed on the server side, by defining `java.rmi.server.hostname` — which is exactly what the commented `RMI_HOST_DEF` line in the script is for.

  • Which .jmx files have to be present on gen1 through gen4 before the laptop can start the run?
    None. The controller parses and clones the plan locally and passes the resulting tree to each engine's rconfigure call, so the plan crosses the wire per run. Only the JMeter installation itself, including any plugin jars the plan needs, has to be in place on the generators.
  • A generator's jmeter-server.log says 'Cannot start. <hostname> is a loopback address.' What is wrong?
    The server resolved its own name to a loopback address, so the address it would advertise to the controller is unusable. JMeter refuses to start rather than publish it. Fix it on the server by defining java.rmi.server.hostname — the commented RMI_HOST_DEF line in bin/jmeter-server exists for exactly this.
  • Why must every node run the same JMeter version?
    The controller sends the test tree as serialized Java objects and there is no version negotiation in the protocol. A generator on a different build may lack a class or read a changed field layout, and the run fails during deserialization on that host rather than with a message that names the real cause.

A jmeter-server process is a rented kitchen that keeps no recipes. It stands fully equipped and idle, listed under one name, until a chef arrives with the recipe for exactly one dinner service; while that service is running the door is locked to anyone else.

saying these in an interview costs you the question

  • Thinks the .jmx has to be copied to every server before a run.
  • Believes you must start rmiregistry by hand at JMeter 6.0.0.
  • Says jmeter-server loads and validates the plan when it starts.
  • Assumes one server process can serve two controllers at once.
  • Claims mixed JMeter versions across nodes are fine as long as Java matches.