skip to content

Which JMeter properties fix the RMI ports of a distributed run so a firewall can allow them?

level: middleimportance: must knowfreq 62%

answer

  1. Count the TCP legs, not the hosts
  2. One direction carries the plan out
  3. The other direction carries results home
  4. Two of the properties end in localport
  5. Zero means RMI chooses for you

basics

~10 s

Three ports, three properties. server.rmi.port pins the engine's RMI registry, default 1099. server.rmi.localport pins the engine object itself, dynamic by default. client.rmi.localport pins the controller's two result-callback ports, also dynamic by default.

solid answer

~40 s

A JMeter distributed run opens three TCP legs and only the first has a memorable default. The controller reaches each engine's RMI registry on `server.rmi.port` (default `1099`; `bin/jmeter-server` also passes `server_port=1099`). Every later call — configure the tree, run it, stop it — goes to the exported engine object on `server.rmi.localport`, whose code default is `0`, meaning RMI picks an anonymous high port at export. Results travel the other way: the engine calls back into the controller's `RemoteThreadsListenerImpl` and `RemoteSampleListenerImpl`, exported on `client.rmi.localport` plus 1 and plus 2, and also anonymous while that property is `0`. Pin both `localport` properties, then open the registry and engine ports inbound on each generator and the two callback ports inbound on the controller.

code

properties · 6 lines
properties
# user.properties on each jmeter-server generator
server.rmi.port=1099
server.rmi.localport=4000

# user.properties on the controller
client.rmi.localport=4100

go deeper

for a junior

Remember that a JMeter remote run is not one connection on one port. The registry default is 1099, and two more ports are chosen dynamically unless you pin them.

for a middle

Explain each leg: registry lookup, calls to the exported engine object on server.rmi.localport, and the engine's callback into the controller on client.rmi.localport plus one and plus two.

for a senior

Be able to write the firewall rules from memory, including the inbound rule on the controller, and to prove from the startup log lines which port each side actually exported.

for a principal

Own the standard: a fixed, documented port block per generator role so network requests are written once, rather than a per-run ticket for whatever ephemeral port RMI happened to pick.

JMeter's distributed mode is Java RMI end to end, and RMI does not use one port. A run opens three distinct TCP legs — two outbound from the controller, one back from every engine — and only the first of them has a default worth memorising. ## Leg 1 - the controller finds the engine: the RMI registry Each `jmeter-server` process creates its own RMI registry inside the server JVM (`server.rmi.create`, default `true`) and binds one object into it under the name `JMeterEngine`. The port comes from `RmiUtils.getRmiRegistryPort()`: it is `server_port` when that is non-zero, and otherwise `server.rmi.port`, whose default is `1099`. The controller opens a TCP connection to that port and calls `lookup("JMeterEngine")`. This is the only leg most people ever open. ## Leg 2 - the controller drives the engine: server.rmi.localport The registry hands back a *stub*, not the engine. Every subsequent call goes to the exported `RemoteJMeterEngineImpl` object on its own port, and that port is `server.rmi.localport`. `RmiUtils.DEFAULT_LOCAL_PORT` reads the property with a default of **`0`**, and `0` tells RMI to pick a free anonymous port at export time. The `4000` you see in `bin/jmeter.properties` is a commented-out *example*, not the default — the properties reference page lists `4000` as the default and contradicts the code there. A port that changes on every restart is exactly what a corporate firewall has no rule for. ## Leg 3 - the engine answers back: client.rmi.localport The controller does not poll for results; the engine calls *into* the controller. The controller exports two objects of its own and ships their stubs inside the test tree. Both read `client.rmi.localport` (default `0`) and add a fixed offset: - `RemoteThreadsListenerImpl` uses base **+ 1** - `RemoteSampleListenerImpl` uses base **+ 2** - either object falls back to port `0` (anonymous) whenever the base is `0` So `client.rmi.localport=4100` puts the controller's listeners on **4101 and 4102**, not on 4100. The manual phrases it loosely as "up to three ports beginning with the port defined", which is why the safe firewall rule is the three-port range starting at the base rather than the two ports the code actually uses. ## What to open, and where | Leg | Direction | Port | Property | |---|---|---|---| | Registry lookup | controller to engine | `1099` | `server.rmi.port` (or `server_port`) | | Engine calls | controller to engine | you choose | `server.rmi.localport` | | Result callbacks | engine to controller | you choose | `client.rmi.localport` (+1, +2) | The row people miss is the last one, because it is the only rule that has to be applied **on the controller's own host**, inbound from each generator. Opening ports on the load generators alone gives you a run that starts cleanly and then delivers nothing. ## Making the settings stick 1. Put both properties in `user.properties` rather than editing `jmeter.properties`; the shipped file is meant to stay untouched and `user.properties` is layered over it. 2. `server.rmi.localport` has to be set on each generator *before* `jmeter-server` starts — `RmiUtils` reads it into a static field at class load. 3. `client.rmi.localport` belongs on the controller only. Setting it on the generators changes nothing about where results are delivered. 4. Only one `jmeter-server` fits on a host at the stock ports; a second one needs its own `server.rmi.port` **and** `server.rmi.localport`. ## Confirming the ports actually took Both ends print the endpoint they exported. The generator prints `Using local port: <n>` at startup whenever `server.rmi.localport` is non-zero, then `Created remote object: ` followed by the RMI reference for the engine object; the controller prints `Using remote object: ` with the reference it looked up. Those references carry the host and port RMI really chose. If the number changes between restarts, the property never took effect — usually because it was set on the wrong host or in a file that process did not load. Pinning ports is only half of a first distributed run. The same sockets carry TLS unless `server.rmi.ssl.disable` is changed from its default of `false`, so a fresh setup normally has to solve the port problem and the keystore problem before it sees a single sample.

  • If client.rmi.localport is left at its default of 0, what does the JMeter controller listen on?
    Both exported callback objects get an anonymous port. The offset helper returns 0 when the base is 0, so `UnicastRemoteObject` is exported on port 0 and the JVM takes a free high port at export time. That port changes on every controller start, so no static firewall rule can ever match it.
  • Why can only one jmeter-server run on a host without extra configuration?
    The registry port is fixed at 1099 by default, so a second server process cannot create or bind into its own registry there. The manual states there can only be one JMeter server per node unless different RMI ports are used, so give the second process its own `server.rmi.port` and `server.rmi.localport`.

saying these in an interview costs you the question

  • Thinks opening port 1099 is enough
  • Assumes the engine only ever receives connections
  • Believes RMI keeps one port for the whole run
  • Sets client.rmi.localport on the generators instead
  • Opens ports on the generators but not the controller