|
1
|
# Web Clients — Local Brokers, Host Order, and `map.clients-to-backends`
|
|
2
|
|
|
3
|
This note explains how per-broker web-client port ranges are described in the
|
|
4
|
directory, how the **host order / host index** is derived from the directory
|
|
5
|
(no `hosts.txt` file is involved), and how `ClientsToPortsGenerator` produces the
|
|
6
|
`map.clients-to-backends` file consumed by a reverse proxy.
|
|
7
|
|
|
8
|
It is written against the broker example below and the code in
|
|
9
|
`com.goldencode.p2j.main.{BrokerDiscovery, Broker, ClientsToPortsGenerator,
|
|
10
|
HostsManager, WebClientsManager}`.
|
|
11
|
|
|
12
|
## 0. Two modes
|
|
13
|
|
|
14
|
`ClientsToPortsGenerator.generate(String host)` picks one of two modes based on
|
|
15
|
whether the directory's `brokers` container has any broker nodes:
|
|
16
|
|
|
17
|
| Mode | Condition | Hosts | Host index |
|
|
18
|
|------|-----------|-------|------------|
|
|
19
|
| **Broker mode** | `brokers` container is **non-empty** | one entry per ports-restricted broker | the broker's **position in the directory order** (loopback = 1, brokers = 2, 3, …) |
|
|
20
|
| **Local mode** | `brokers` container is **empty** | a single host | always **1** |
|
|
21
|
|
|
22
|
In **local mode** the single host is the `host` argument passed to `generate`
|
|
23
|
(the actual representer of the local server, e.g. its LAN address
|
|
24
|
`192.168.100.3`), or `localhost` when no host is supplied. **No `hosts.txt` input
|
|
25
|
is required in either mode** — the host order comes entirely from the directory
|
|
26
|
(broker mode) or from the single host argument (local mode).
|
|
27
|
|
|
28
|
> **Host order is directory-based.** The host index is *not* stored in a file and
|
|
29
|
> is *not* an attribute on any node. In broker mode it is the enumeration order
|
|
30
|
> of the broker nodes under the `brokers` container; the FWD server's loopback is
|
|
31
|
> index `1` and brokers are `2, 3, …`. Because the generator and the running
|
|
32
|
> server both derive the index from that same ordered list, their indices agree
|
|
33
|
> by construction.
|
|
34
|
|
|
35
|
## 1. The directory data (broker mode)
|
|
36
|
|
|
37
|
A broker is a node under the `brokers` container (which itself lives under
|
|
38
|
`/server/<serverId>/`, e.g. `/server/default/`):
|
|
39
|
|
|
40
|
```xml
|
|
41
|
<node class="container" name="brokers">
|
|
42
|
<node class="broker" name="broker1">
|
|
43
|
<node-attribute name="account" value="sbi"/>
|
|
44
|
<node-attribute name="broker_account" value="broker1_process"/>
|
|
45
|
<node-attribute name="host" value="192.168.100.28"/>
|
|
46
|
<node class="container" name="portsRange">
|
|
47
|
<node class="string" name="namePrefix">
|
|
48
|
<node-attribute name="value" value="client"/>
|
|
49
|
</node>
|
|
50
|
<node class="integer" name="from">
|
|
51
|
<node-attribute name="value" value="7449"/>
|
|
52
|
</node>
|
|
53
|
<node class="integer" name="to">
|
|
54
|
<node-attribute name="value" value="7459"/>
|
|
55
|
</node>
|
|
56
|
</node>
|
|
57
|
</node>
|
|
58
|
</node>
|
|
59
|
```
|
|
60
|
|
|
61
|
The `broker` object-class (see `dir_schema.xml`) is **non-leaf** and carries:
|
|
62
|
|
|
63
|
| Attribute | Mandatory | Multiple | Meaning |
|
|
64
|
|------------------|-----------|----------|---------|
|
|
65
|
| `host` | yes | no | The host name / IP address on which this broker's client (agent) runs. This is the broker's **identity for host ordering**. |
|
|
66
|
| `broker_account` | yes | no | The FWD process account the broker client must authenticate as to register with the server. |
|
|
67
|
| `account` | no | yes | The list of FWD user accounts served by this broker. |
|
|
68
|
|
|
69
|
The child `portsRange` container holds the **web-client port range** for this
|
|
70
|
broker:
|
|
71
|
|
|
72
|
| Node | Meaning |
|
|
73
|
|-----------------------|---------|
|
|
74
|
| `portsRange/from` | First web-client port (inclusive). |
|
|
75
|
| `portsRange/to` | Last web-client port (inclusive). |
|
|
76
|
| `portsRange/namePrefix` | **Not read per broker** — see the caveat in §6. |
|
|
77
|
|
|
78
|
In the example, `broker1` runs on `192.168.100.28` and owns the web-client port
|
|
79
|
range `7449..7459` (11 ports).
|
|
80
|
|
|
81
|
### How the directory data is read
|
|
82
|
|
|
83
|
`BrokerDiscovery.readBrokerConfigs(ds)`:
|
|
84
|
|
|
85
|
1. Locates the `brokers` container via `Utils.findDirectoryNodePath(ds,
|
|
86
|
"brokers", "container", false)`.
|
|
87
|
2. Enumerates the broker child nodes with `ds.enumerateNodes(path)` and, **in
|
|
88
|
that enumeration order**, builds a `LinkedHashMap<String, Broker>` (broker
|
|
89
|
name → `Broker`). **This ordered map is what defines the host order** — see
|
|
90
|
§2.
|
|
91
|
3. For each broker node, `getBroker(...)` reads the `account` / `broker_account`
|
|
92
|
/ `host` attributes, then reads the two port-range values **relative to the
|
|
93
|
broker node** using `ConfigItem.RELATIVE_TO_PARENT`:
|
|
94
|
|
|
95
|
```java
|
|
96
|
int from = ConfigItem.<Integer>createConfigItemOfType(
|
|
97
|
ConfigItem.CLIENT_PORT_RANGE_FROM, Type.RELATIVE_TO_PARENT)
|
|
98
|
.setContainerNodeSearchPath("brokers/broker1/") // "brokers/<name>/"
|
|
99
|
.read(0);
|
|
100
|
// ... same for CLIENT_PORT_RANGE_TO ("portsRange/to")
|
|
101
|
```
|
|
102
|
|
|
103
|
`CLIENT_PORT_RANGE_FROM`/`_TO` have node name `portsRange/from` /
|
|
104
|
`portsRange/to`, so the resolved directory path is
|
|
105
|
`brokers/broker1/portsRange/from` and `…/to`.
|
|
106
|
|
|
107
|
4. The resulting `Broker` reports `isPortsRestricted()` as
|
|
108
|
`(to > from) && (from > 0)`. For `broker1` that is
|
|
109
|
`(7459 > 7449) && (7449 > 0)` → **true**. A broker whose range is absent or
|
|
110
|
invalid is **not** ports-restricted: it still occupies a host index (§2) but
|
|
111
|
produces no lines in the map.
|
|
112
|
|
|
113
|
## 2. Host order — derived from the broker enumeration order
|
|
114
|
|
|
115
|
There is no host-index file and no stored index attribute. In broker mode the
|
|
116
|
host index is the **position of a broker in the ordered broker list**:
|
|
117
|
|
|
118
|
* Index **1** is reserved for the FWD server's own loopback host
|
|
119
|
(`localhost` / `127.0.0.1`). The server is always "host 1".
|
|
120
|
* **Every** broker node consumes the next index in the order it appears under the
|
|
121
|
`brokers` container — the first broker → `2`, the second → `3`, and so on. This
|
|
122
|
is true even for a broker that is not ports-restricted: it still takes its slot
|
|
123
|
so that the numbering stays aligned with the runtime, which registers one host
|
|
124
|
per broker regardless of its port configuration (§7).
|
|
125
|
|
|
126
|
So for the example (one broker, `broker1` on `192.168.100.28`):
|
|
127
|
|
|
128
|
```
|
|
129
|
index 1 : localhost / 127.0.0.1 (the FWD server)
|
|
130
|
index 2 : 192.168.100.28 (broker1, first broker node)
|
|
131
|
```
|
|
132
|
|
|
133
|
Add a `broker2` after `broker1` and it becomes index `3`, etc. The ordering is
|
|
134
|
the directory enumeration order — the same order `BrokerDiscovery` returns and
|
|
135
|
the same "well-defined host order" the comment in `BrokerManager.registerBroker`
|
|
136
|
refers to. **To renumber a host, reorder the broker nodes** in the directory.
|
|
137
|
|
|
138
|
Each broker declares a unique `host` (mandatory, single-valued), and
|
|
139
|
`BrokerManager` rejects a second broker that resolves to a host already claimed,
|
|
140
|
so the host → index mapping is one-to-one.
|
|
141
|
|
|
142
|
> **Design decision — count every broker.** The host index advances for *every*
|
|
143
|
> broker node, including brokers that are not ports-restricted, rather than only
|
|
144
|
> for the brokers that emit lines. This is deliberate, for consistency with the
|
|
145
|
> runtime: `BrokerManager.registerBroker` registers one host per broker
|
|
146
|
> (`HostsManager.addHost`) regardless of its port configuration, so a
|
|
147
|
> non-ports-restricted broker still consumes a host slot at runtime. Counting it
|
|
148
|
> in the generator too keeps the offline indices and the runtime indices in
|
|
149
|
> lockstep. The trade-off is that the position of *all* broker nodes affects the
|
|
150
|
> numbering — adding, removing, or reordering any broker shifts the indices of
|
|
151
|
> the brokers after it.
|
|
152
|
|
|
153
|
## 3. Mode selection in `generate(String host)`
|
|
154
|
|
|
155
|
```
|
|
156
|
generate(host):
|
|
157
|
brokers = BrokerDiscovery.readBrokerConfigs(ds)
|
|
158
|
if (!brokers.isEmpty()):
|
|
159
|
→ generateWithBrokerRanges(brokers, fileName) # broker mode (directory-ordered)
|
|
160
|
else:
|
|
161
|
→ local mode: single host = host or "localhost", at index 1
|
|
162
|
```
|
|
163
|
|
|
164
|
The switch is simply **whether any broker node exists**. (Note this differs from
|
|
165
|
an earlier draft that switched on "any broker is ports-restricted"; the mode is
|
|
166
|
now decided purely by the presence of broker nodes.)
|
|
167
|
|
|
168
|
## 4. Broker mode — `generateWithBrokerRanges`
|
|
169
|
|
|
170
|
The generator walks the brokers **in directory order**, advancing the host index
|
|
171
|
for every broker (starting at `1` for the server loopback, so the first broker
|
|
172
|
host is `2`), and emits lines only for ports-restricted brokers:
|
|
173
|
|
|
174
|
```
|
|
175
|
prefix = resolvePrefix(...) # see §6
|
|
176
|
hostIndex = 1 # 1 == server loopback (localhost)
|
|
177
|
|
|
178
|
for (broker : brokers.values()) { # directory enumeration order
|
|
179
|
hostIndex++; # every broker takes a slot: 2, 3, ...
|
|
180
|
if (!broker.isPortsRestricted()) continue; # but only restricted ones emit lines
|
|
181
|
|
|
182
|
for (port = broker.getFrom(); port <= broker.getTo(); port++) {
|
|
183
|
name = getPortName(prefix, broker.getFrom(), hostIndex, port);
|
|
184
|
write(name + " " + broker.getHost() + ":" + port);
|
|
185
|
}
|
|
186
|
}
|
|
187
|
```
|
|
188
|
|
|
189
|
The output file is `map.clients-to-backends` by default (the user may enter
|
|
190
|
another name at the prompt). Because the index comes from the broker order, there
|
|
191
|
is no `hosts.txt` lookup, creation, or write-back.
|
|
192
|
|
|
193
|
### Port name format
|
|
194
|
|
|
195
|
`getPortName` (shared by the generator and the runtime) is:
|
|
196
|
|
|
197
|
```
|
|
198
|
«prefix»-«hostIndex»-«port - from + 1»
|
|
199
|
```
|
|
200
|
|
|
201
|
i.e. `prefix`, the **host index**, and the **1-based offset of the port within
|
|
202
|
the range**, joined by hyphens.
|
|
203
|
|
|
204
|
## 5. Local mode — single host
|
|
205
|
|
|
206
|
When `brokers` is empty, the generator uses the **global**
|
|
207
|
`webClient/portsRange` (`from` / `to`, prompting and persisting them in the
|
|
208
|
directory if absent, exactly as before) and maps a **single host at index 1**:
|
|
209
|
|
|
210
|
```
|
|
211
|
localHost = (host == null || host.isEmpty()) ? "localhost" : host
|
|
212
|
for (port = from; port <= to; port++) {
|
|
213
|
write(getPortName(prefix, from, 1, port) + " " + localHost + ":" + port)
|
|
214
|
}
|
|
215
|
```
|
|
216
|
|
|
217
|
`host` is the `generate(...)` argument — supplied on the command line as the
|
|
218
|
optional second parameter (see §8). Use it to name the local server by the
|
|
219
|
address a reverse proxy must reach (e.g. `192.168.100.3`); omit it to fall back
|
|
220
|
to `localhost`.
|
|
221
|
|
|
222
|
## 6. Caveat — `namePrefix` under the broker node is ignored
|
|
223
|
|
|
224
|
`resolvePrefix(...)` reads `ConfigItem.PROXY_CLIENT_NAME_PREFIX`, which is a
|
|
225
|
`Type.WEB_CLIENT` item with node name `portsRange/namePrefix` — i.e. it resolves
|
|
226
|
to the **global** `webClient/portsRange/namePrefix`, *not* the
|
|
227
|
`portsRange/namePrefix` placed under the broker node.
|
|
228
|
|
|
229
|
Consequences:
|
|
230
|
|
|
231
|
* The `<node class="string" name="namePrefix" value="client"/>` inside
|
|
232
|
`broker1/portsRange` in the example is **not consulted** by the generator.
|
|
233
|
* The prefix comes from `webClient/portsRange/namePrefix`; if that is empty the
|
|
234
|
generator prompts for it (interactive) and defaults to `client` if nothing is
|
|
235
|
entered.
|
|
236
|
* Only `portsRange/from` and `portsRange/to` are read per broker
|
|
237
|
(`BrokerDiscovery` does not read a per-broker `namePrefix`).
|
|
238
|
|
|
239
|
If a per-broker prefix is intended, it would require reading
|
|
240
|
`brokers/<name>/portsRange/namePrefix` via a `RELATIVE_TO_PARENT` item in
|
|
241
|
`BrokerDiscovery`/`generateWithBrokerRanges`; today it is a single global value.
|
|
242
|
|
|
243
|
## 7. Worked examples
|
|
244
|
|
|
245
|
### Broker mode
|
|
246
|
|
|
247
|
Directory: a single ports-restricted broker `broker1` →
|
|
248
|
`host=192.168.100.28`, `from=7449`, `to=7459`, with `prefix=client`.
|
|
249
|
|
|
250
|
`broker1` is the first broker node, so its host order is `2` (index `1` is the
|
|
251
|
server loopback). The generator emits 11 lines:
|
|
252
|
|
|
253
|
```
|
|
254
|
client-2-1 192.168.100.28:7449
|
|
255
|
client-2-2 192.168.100.28:7450
|
|
256
|
client-2-3 192.168.100.28:7451
|
|
257
|
client-2-4 192.168.100.28:7452
|
|
258
|
client-2-5 192.168.100.28:7453
|
|
259
|
client-2-6 192.168.100.28:7454
|
|
260
|
client-2-7 192.168.100.28:7455
|
|
261
|
client-2-8 192.168.100.28:7456
|
|
262
|
client-2-9 192.168.100.28:7457
|
|
263
|
client-2-10 192.168.100.28:7458
|
|
264
|
client-2-11 192.168.100.28:7459
|
|
265
|
```
|
|
266
|
|
|
267
|
The left column is the proxy **route name**; the right column is the **backend**
|
|
268
|
the reverse proxy forwards that route to. If a `broker2` were added next, it
|
|
269
|
would produce `client-3-*` lines for its own host and range.
|
|
270
|
|
|
271
|
### Local mode
|
|
272
|
|
|
273
|
No brokers configured, global `webClient/portsRange` = `7449..7459`,
|
|
274
|
`prefix=client`, and the generator invoked with host `192.168.100.3`. The single
|
|
275
|
host is index `1`:
|
|
276
|
|
|
277
|
```
|
|
278
|
client-1-1 192.168.100.3:7449
|
|
279
|
client-1-2 192.168.100.3:7450
|
|
280
|
...
|
|
281
|
client-1-11 192.168.100.3:7459
|
|
282
|
```
|
|
283
|
|
|
284
|
Invoked with no host argument, the backend column would read `localhost:74xx`.
|
|
285
|
|
|
286
|
## 8. Command line
|
|
287
|
|
|
288
|
```
|
|
289
|
java com.goldencode.p2j.main.ClientsToPortsGenerator <directory.xml> [host]
|
|
290
|
```
|
|
291
|
|
|
292
|
* `<directory.xml>` — required; the directory the brokers / webClient config is
|
|
293
|
read from.
|
|
294
|
* `[host]` — optional; the local-server host for **local mode** (defaults to
|
|
295
|
`localhost`). **Ignored in broker mode**, where hosts come from the broker
|
|
296
|
nodes. There is no `hosts.txt` argument.
|
|
297
|
|
|
298
|
## 9. The runtime side — the same order, no second source of truth
|
|
299
|
|
|
300
|
The generated `map.clients-to-backends` only routes correctly if the **host
|
|
301
|
index the proxy was configured with equals the host index the running server
|
|
302
|
uses**. Both sides derive that index from the **same directory data**, so they
|
|
303
|
agree by construction:
|
|
304
|
|
|
305
|
* `HostsManager` pins the server loopback at construction: `localhost → 1`,
|
|
306
|
`127.0.0.1 → 1`, and the host counter starts at `1`.
|
|
307
|
* As brokers register (`BrokerManager.registerBroker` → `HostsManager.addHost`),
|
|
308
|
each host is assigned `broker.getIndex() + 1` — i.e. its **directory position**
|
|
309
|
(`BrokerDiscovery` sets `index = i + 1`), not a running registration-time
|
|
310
|
counter. Keying the index off the directory position is what keeps the runtime
|
|
311
|
aligned with the generator regardless of the order in which brokers actually
|
|
312
|
register, or whether some brokers are skipped.
|
|
313
|
* `HostsManager.getPortName(prefix, from, host, port)` looks up that host's index
|
|
314
|
directly by the `host` literal (`hosts.get(host)`) — **no DNS resolution at
|
|
315
|
route-name time** — and calls the same `ClientsToPortsGenerator.getPortName(...)`.
|
|
316
|
`WebClientsManager` uses this to build the proxy web-root for a web client.
|
|
317
|
|
|
318
|
```
|
|
319
|
directory `brokers` order ──► host index ─┬─► generator: "client-2-7" (map.clients-to-backends)
|
|
320
|
└─► runtime : "client-2-7" (proxy web-root)
|
|
321
|
```
|
|
322
|
|
|
323
|
* **Local mode / single-host**: the host is index `1` on both sides; nothing to
|
|
324
|
diverge.
|
|
325
|
* **Broker mode / multiple hosts**: the index is the broker's position in the
|
|
326
|
directory order on both sides. Reordering the broker nodes (or the order in
|
|
327
|
which brokers are expected to register) changes the index consistently for the
|
|
328
|
generator and the server, so the proxy map and the runtime stay aligned without
|
|
329
|
a flat file.
|
|
330
|
|
|
331
|
### Host literal form
|
|
332
|
|
|
333
|
A broker's `host` attribute is used verbatim as its identity. The running server
|
|
334
|
keys the host index by that exact literal: `HostsManager.addHost` stores the
|
|
335
|
index under the registered `host` string, and `getPortName` looks it up by the
|
|
336
|
same string (`hosts.get(host)`) — there is **no DNS resolution at route-name
|
|
337
|
time**. The host registered (`BrokerManager.registerBroker`) and the host used to
|
|
338
|
build the route name (`WebClientsManager`) both originate from the broker's
|
|
339
|
`host` config value, so they match by construction. Keep the `host` literal in a
|
|
340
|
single, consistent form across the directory and any proxy configuration derived
|
|
341
|
from these route names — a different spelling of the same address (e.g. a DNS
|
|
342
|
name vs. its IP) is treated as a distinct host because the lookup is now purely
|
|
343
|
string-keyed.
|
|
344
|
|
|
345
|
## 10. Summary
|
|
346
|
|
|
347
|
1. `generate(host)` runs **broker mode** when the `brokers` container has any
|
|
348
|
broker nodes, **local mode** otherwise. Neither mode reads a `hosts.txt` file.
|
|
349
|
2. **Broker mode**: each broker node declares its `host`, `broker_account`, and a
|
|
350
|
`portsRange/{from,to}`. The **host index is the broker's position in the
|
|
351
|
directory order** — loopback `1`, brokers `2, 3, …`, counting every broker.
|
|
352
|
Ports-restricted brokers emit `«prefix»-«hostIndex»-«offset» «host»:«port»`
|
|
353
|
lines into `map.clients-to-backends`.
|
|
354
|
3. **Local mode**: a single host (the `host` argument, or `localhost`) is mapped
|
|
355
|
at index `1` using the global `webClient/portsRange`.
|
|
356
|
4. The running server (`HostsManager`) assigns indices from the same ordered
|
|
357
|
broker registrations (loopback pinned to `1`), so the offline-generated proxy
|
|
358
|
map and the runtime web-roots agree by construction.
|