This is an automated email from the ASF dual-hosted git repository. lhotari pushed a commit to branch lh-support-webservice-bindaddresses-and-advertised-listeners in repository https://gitbox.apache.org/repos/asf/pulsar.git
commit 8d1a4d034b0bcc382d20838867460612fdf386ac Author: Lari Hotari <[email protected]> AuthorDate: Thu May 21 11:12:22 2026 +0300 [improve][broker] Clarify listener-related config docs Tighten and align the docs for the eight listener-related properties across `ServiceConfiguration.java`, `conf/broker.conf`, and `conf/standalone.conf` so the configuration model is explicit: - `*Port` properties (`brokerServicePort` / `brokerServicePortTls` / `webServicePort` / `webServicePortTls`) now say that each port number is used both for the local socket binding (with `bindAddress`) and for the advertised URL (with `advertisedAddress`), so no entry in `advertisedListeners` or `bindAddresses` is required for the internal listener. - `advertisedAddress` notes that it supplies the hostname for the internal listener's advertised URLs (e.g. `pulsar://<advertisedAddress>:<port>`). - `advertisedListeners` is reframed so its primary purpose comes first (declaring additional, typically external, listeners). The auto-configured internal listener and the discouraged-override caveat come after the format, and the legacy fallback (`internalListenerName=` blank ⇒ first listener parsed here becomes the internal one) is called out as its own short paragraph. - `internalListenerName` opens with the purpose + default, then the "when set (the default)" branch covering auto-configuration and the avoid-routing- cluster-traffic-through-an-external-LB warning, and finally the legacy fallback sentence. - "lookup redirects, replication, admin forwarding" → "lookup redirects, admin forwarding to owner or leader broker" — geo-replication may legitimately use an external listener, so it doesn't belong in the internal-listener list. - Implementation details (Jetty connectors, `AddListenerAttributeFilter`) removed from user-facing docs. All six shared property comment blocks (`brokerServicePort`, `webServicePort`, `bindAddress`, `bindAddresses`, `advertisedAddress`, `internalListenerName`) are now byte-identical between `broker.conf` and `standalone.conf`. --- conf/broker.conf | 79 +++++++++++------- conf/standalone.conf | 57 ++++++++----- .../apache/pulsar/broker/ServiceConfiguration.java | 95 ++++++++++++++-------- 3 files changed, 145 insertions(+), 86 deletions(-) diff --git a/conf/broker.conf b/conf/broker.conf index 3d5ad632b42..0038cb22571 100644 --- a/conf/broker.conf +++ b/conf/broker.conf @@ -38,16 +38,22 @@ configurationMetadataSyncEventTopic= # The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl configurationMetadataStoreUrl= -# Broker data port +# Port for the Pulsar binary protocol of the internal listener. +# The same port number is used both for the local socket binding (with bindAddress) and for the +# advertised URL (with advertisedAddress), so no entry in advertisedListeners or bindAddresses +# is required for the internal listener. brokerServicePort=6650 -# Broker data port for TLS - By default TLS is disabled +# Port for the Pulsar binary protocol TLS endpoint of the internal listener. +# Used both for the local socket binding and the advertised URL. By default TLS is disabled. brokerServicePortTls= -# Port to use to server HTTP request +# Port for the HTTP admin/REST endpoint of the internal listener. +# Used both for the local socket binding and the advertised URL. webServicePort=8080 -# Port to use to server HTTPS request - By default TLS is disabled +# Port for the HTTPS admin/REST endpoint of the internal listener. +# Used both for the local socket binding and the advertised URL. By default TLS is disabled. webServicePortTls= # Specify the tls protocols the broker's web service will use to negotiate during TLS handshake @@ -62,46 +68,61 @@ webServiceTlsProtocols= # webServiceTlsCiphers=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 webServiceTlsCiphers= -# Hostname or IP address the service binds on, default is 0.0.0.0. +# Local network interface IP for the internal listener's port bindings. +# Use a specific local IP to bind to a single interface, or 0.0.0.0 to bind on all interfaces. bindAddress=0.0.0.0 -# Bind addresses for the broker. -# Comma-separated list of <listener_name>:<scheme>://<ip>:<port> entries. +# Per-listener socket bindings. +# The internal listener's bindings are derived automatically from bindAddress plus the port +# properties (brokerServicePort / brokerServicePortTls / webServicePort / webServicePortTls) +# and do not need to be repeated here. +# PIP-95 smart listener selection routes a connection to the listener whose port it arrived on. +# For the Pulsar binary protocol (pulsar / pulsar+ssl) this is optional — clients can also pass +# an explicit listenerName. For the HTTP/HTTPS Admin API (http / https) it is the only routing +# mechanism, so every HTTP/HTTPS advertised listener reachable from outside the cluster needs a +# dedicated entry here on a unique port. This lets a layer-4 TCP load balancer serve the Admin +# API directly per listener, without an HTTP reverse proxy. +# Format: comma-separated <listener_name>:<scheme>://<ip>:<port> entries. # Supported schemes: pulsar, pulsar+ssl, http, https. # The <ip> part selects which local network interface the port binds to: use a specific local # IP, or 0.0.0.0 to bind on all interfaces. A local hostname is also accepted but not # recommended. -# At runtime, the legacy brokerServicePort, brokerServicePortTls, webServicePort, and -# webServicePortTls properties are migrated into the bind address list (using bindAddress, -# default 0.0.0.0, as the IP) under the listener named by internalListenerName, and merged with -# the entries declared here. -# Each ip:port may be bound by exactly one (listener, scheme) pair — a TCP socket cannot serve -# two protocol schemes at once. An entry here that exactly matches a migrated legacy binding -# (same listener:scheme://ip:port) is tolerated; assigning the same ip:port to a different -# listener or to a different scheme fails validation. -# Binding the same listener to multiple ports of the same scheme is allowed but rarely useful. +# Each ip:port may be bound by exactly one (listener, scheme) pair. An entry that exactly +# matches the auto-derived internal-listener binding is tolerated; assigning the same ip:port +# to a different listener or scheme fails validation. bindAddresses= -# Hostname or IP address the service advertises to the outside world. -# If not set, the value of InetAddress.getLocalHost().getCanonicalHostName() is used. +# Hostname or IP advertised to clients for the internal listener. +# Combined with the *Port properties it forms the internal listener's advertised URLs +# (e.g. pulsar://<advertisedAddress>:<brokerServicePort>). +# If not set, defaults to InetAddress.getLocalHost().getCanonicalHostName(). advertisedAddress= -# Advertised listeners for the broker. -# Comma-separated list of <listener_name>:<scheme>://<host>:<port> entries. +# Declares additional advertised listeners — typically external listeners that complement the +# internal one. +# Format: comma-separated <listener_name>:<scheme>://<host>:<port> entries. # Supported schemes: pulsar, pulsar+ssl, http, https. # A listener name may be repeated to declare multiple schemes for the same listener. -# URLs declared here for the listener named by internalListenerName take precedence; any URL -# slots left undeclared are filled in from brokerServicePort, brokerServicePortTls, -# webServicePort, and webServicePortTls, so existing deployments keep working after upgrade. +# The internal listener is auto-configured from advertisedAddress plus the *Port properties +# (brokerServicePort / brokerServicePortTls / webServicePort / webServicePortTls) so it does +# not need an entry here. URLs declared here under internalListenerName do override that +# auto-configured listener, but this is not recommended because it can route cluster-internal +# traffic through an external endpoint (for example, an external load balancer). +# Legacy fallback: when internalListenerName is left blank, the first listener parsed from this +# property is used as the internal listener (so in that case its entry here is required). # advertisedListeners= # Name of the listener used for cluster-internal broker-to-broker communication (lookup -# redirects, replication, admin forwarding). -# The internal listener must advertise an address reachable from other brokers in the same -# cluster. It can also serve external traffic when the same DNS names and IPs are routable from -# outside the cluster; this is typically not the case with Kubernetes, where the broker pod IP -# is only reachable in-cluster — configure a separate non-internal listener for external clients -# in that situation. +# redirects, admin forwarding to owner or leader broker). Defaults to "internal". +# When set (the default), the internal listener is auto-configured from advertisedAddress plus +# the *Port properties. The internal listener must advertise addresses reachable from other +# brokers in the same cluster; avoid overriding its URLs in advertisedListeners to point at an +# external load balancer because that would route cluster-internal traffic outside the cluster. +# For external clients, declare a separate non-internal listener in advertisedListeners +# instead. The default name "internal" also keeps the port-derived internal listener distinct +# from any user-declared listener names. +# Setting this to an empty string restores the legacy fallback: the first listener parsed from +# advertisedListeners is used as the internal listener. internalListenerName=internal # Enable or disable the HAProxy protocol. diff --git a/conf/standalone.conf b/conf/standalone.conf index 8d4c07e7f01..69011aeb77e 100644 --- a/conf/standalone.conf +++ b/conf/standalone.conf @@ -32,42 +32,57 @@ metadataStoreConfigPath= # The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl configurationMetadataStoreUrl= +# Port for the Pulsar binary protocol of the internal listener. +# The same port number is used both for the local socket binding (with bindAddress) and for the +# advertised URL (with advertisedAddress), so no entry in advertisedListeners or bindAddresses +# is required for the internal listener. brokerServicePort=6650 -# Port to use to server HTTP request +# Port for the HTTP admin/REST endpoint of the internal listener. +# Used both for the local socket binding and the advertised URL. webServicePort=8080 -# Hostname or IP address the service binds on, default is 0.0.0.0. +# Local network interface IP for the internal listener's port bindings. +# Use a specific local IP to bind to a single interface, or 0.0.0.0 to bind on all interfaces. bindAddress=0.0.0.0 -# Bind addresses for the broker. -# Comma-separated list of <listener_name>:<scheme>://<ip>:<port> entries. +# Per-listener socket bindings. +# The internal listener's bindings are derived automatically from bindAddress plus the port +# properties (brokerServicePort / brokerServicePortTls / webServicePort / webServicePortTls) +# and do not need to be repeated here. +# PIP-95 smart listener selection routes a connection to the listener whose port it arrived on. +# For the Pulsar binary protocol (pulsar / pulsar+ssl) this is optional — clients can also pass +# an explicit listenerName. For the HTTP/HTTPS Admin API (http / https) it is the only routing +# mechanism, so every HTTP/HTTPS advertised listener reachable from outside the cluster needs a +# dedicated entry here on a unique port. This lets a layer-4 TCP load balancer serve the Admin +# API directly per listener, without an HTTP reverse proxy. +# Format: comma-separated <listener_name>:<scheme>://<ip>:<port> entries. # Supported schemes: pulsar, pulsar+ssl, http, https. # The <ip> part selects which local network interface the port binds to: use a specific local # IP, or 0.0.0.0 to bind on all interfaces. A local hostname is also accepted but not # recommended. -# At runtime, the legacy brokerServicePort, brokerServicePortTls, webServicePort, and -# webServicePortTls properties are migrated into the bind address list (using bindAddress, -# default 0.0.0.0, as the IP) under the listener named by internalListenerName, and merged with -# the entries declared here. -# Each ip:port may be bound by exactly one (listener, scheme) pair — a TCP socket cannot serve -# two protocol schemes at once. An entry here that exactly matches a migrated legacy binding -# (same listener:scheme://ip:port) is tolerated; assigning the same ip:port to a different -# listener or to a different scheme fails validation. -# Binding the same listener to multiple ports of the same scheme is allowed but rarely useful. +# Each ip:port may be bound by exactly one (listener, scheme) pair. An entry that exactly +# matches the auto-derived internal-listener binding is tolerated; assigning the same ip:port +# to a different listener or scheme fails validation. bindAddresses= -# Hostname or IP address the service advertises to the outside world. -# If not set, the value of InetAddress.getLocalHost().getCanonicalHostName() is used. +# Hostname or IP advertised to clients for the internal listener. +# Combined with the *Port properties it forms the internal listener's advertised URLs +# (e.g. pulsar://<advertisedAddress>:<brokerServicePort>). +# If not set, defaults to InetAddress.getLocalHost().getCanonicalHostName(). advertisedAddress= # Name of the listener used for cluster-internal broker-to-broker communication (lookup -# redirects, replication, admin forwarding). -# The internal listener must advertise an address reachable from other brokers in the same -# cluster. It can also serve external traffic when the same DNS names and IPs are routable from -# outside the cluster; this is typically not the case with Kubernetes, where the broker pod IP -# is only reachable in-cluster — configure a separate non-internal listener for external clients -# in that situation. +# redirects, admin forwarding to owner or leader broker). Defaults to "internal". +# When set (the default), the internal listener is auto-configured from advertisedAddress plus +# the *Port properties. The internal listener must advertise addresses reachable from other +# brokers in the same cluster; avoid overriding its URLs in advertisedListeners to point at an +# external load balancer because that would route cluster-internal traffic outside the cluster. +# For external clients, declare a separate non-internal listener in advertisedListeners +# instead. The default name "internal" also keeps the port-derived internal listener distinct +# from any user-declared listener names. +# Setting this to an empty string restores the legacy fallback: the first listener parsed from +# advertisedListeners is used as the internal listener. internalListenerName=internal # Enable or disable the HAProxy protocol. diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java index 9ffdbf57880..3829f6d062c 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java @@ -175,26 +175,32 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_SERVER, - doc = "The port for serving binary protobuf requests." - + " If set, defines a server binding for bindAddress:brokerServicePort." - + " The Default value is 6650." + doc = "Port for the Pulsar binary protocol of the internal listener." + + " The same port number is used both for the local socket binding (with `bindAddress`)" + + " and for the advertised URL (with `advertisedAddress`), so no entry in" + + " `advertisedListeners` or `bindAddresses` is required for the internal listener." + + " Default is 6650." ) private Optional<Integer> brokerServicePort = Optional.of(6650); @FieldContext( category = CATEGORY_SERVER, - doc = "The port for serving TLS-secured binary protobuf requests." - + " If set, defines a server binding for bindAddress:brokerServicePortTls." + doc = "Port for the Pulsar binary protocol TLS endpoint of the internal listener." + + " Used both for the local socket binding and the advertised URL." + + " By default TLS is disabled." ) private Optional<Integer> brokerServicePortTls = Optional.empty(); @FieldContext( category = CATEGORY_SERVER, - doc = "The port for serving http requests" + doc = "Port for the HTTP admin/REST endpoint of the internal listener." + + " Used both for the local socket binding and the advertised URL." ) private Optional<Integer> webServicePort = Optional.of(8080); @FieldContext( category = CATEGORY_SERVER, - doc = "The port for serving https requests" + doc = "Port for the HTTPS admin/REST endpoint of the internal listener." + + " Used both for the local socket binding and the advertised URL." + + " By default TLS is disabled." ) private Optional<Integer> webServicePortTls = Optional.empty(); @@ -220,57 +226,74 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_SERVER, - doc = "Hostname or IP address the service binds on" + doc = "Local network interface IP for the internal listener's port bindings." + + " Use a specific local IP to bind to a single interface, or `0.0.0.0` to bind on all" + + " interfaces. Default is `0.0.0.0`." ) private String bindAddress = "0.0.0.0"; @FieldContext( category = CATEGORY_SERVER, - doc = "Hostname or IP address the service advertises to the outside world." - + " If not set, the value of `InetAddress.getLocalHost().getCanonicalHostName()` is used." + doc = "Hostname or IP advertised to clients for the internal listener." + + " Combined with the *Port properties it forms the internal listener's advertised URLs" + + " (e.g. `pulsar://<advertisedAddress>:<brokerServicePort>`)." + + " If not set, defaults to `InetAddress.getLocalHost().getCanonicalHostName()`." ) private String advertisedAddress; @FieldContext(category = CATEGORY_SERVER, - doc = "Advertised listeners for the broker.\n" - + " Comma-separated list of `<listener_name>:<scheme>://<host>:<port>` entries." + doc = "Declares additional advertised listeners — typically external listeners that" + + " complement the internal one.\n" + + " Format: comma-separated `<listener_name>:<scheme>://<host>:<port>` entries." + " Supported schemes: `pulsar`, `pulsar+ssl`, `http`, `https`." + " A listener name may be repeated to declare multiple schemes for the same listener.\n" - + " URLs declared here for the listener named by `internalListenerName` take precedence;" - + " any URL slots left undeclared are filled in from `brokerServicePort`," - + " `brokerServicePortTls`, `webServicePort`, and `webServicePortTls`, so existing" - + " deployments keep working after upgrade.") + + " The internal listener is auto-configured from `advertisedAddress` plus the *Port" + + " properties (`brokerServicePort` / `brokerServicePortTls` / `webServicePort` /" + + " `webServicePortTls`) so it does not need an entry here. URLs declared here under" + + " `internalListenerName` do override that auto-configured listener, but this is" + + " not recommended because it can route cluster-internal traffic through an external" + + " endpoint (for example, an external load balancer).\n" + + " Legacy fallback: when `internalListenerName` is left blank, the first listener" + + " parsed from this property is used as the internal listener (so in that case its" + + " entry here is required).") private String advertisedListeners; @FieldContext(category = CATEGORY_SERVER, doc = "Name of the listener used for cluster-internal broker-to-broker communication" - + " (lookup redirects, replication, admin forwarding).\n" - + " The internal listener must advertise an address reachable from other brokers in" - + " the same cluster. It can also serve external traffic when the same DNS names and" - + " IPs are routable from outside the cluster; this is typically not the case with" - + " Kubernetes, where the broker pod IP is only reachable in-cluster — configure a" - + " separate non-internal listener for external clients in that situation.\n" - + " Defaults to `internal`.") + + " (lookup redirects, admin forwarding to owner or leader broker). Defaults to" + + " `internal`.\n" + + " When set (the default), the internal listener is auto-configured from" + + " `advertisedAddress` plus the *Port properties. The internal listener must" + + " advertise addresses reachable from other brokers in the same cluster; avoid" + + " overriding its URLs in `advertisedListeners` to point at an external load" + + " balancer because that would route cluster-internal traffic outside the cluster." + + " For external clients, declare a separate non-internal listener in" + + " `advertisedListeners` instead. The default name `internal` also keeps the" + + " port-derived internal listener distinct from any user-declared listener names.\n" + + " Setting this to an empty string restores the legacy fallback: the first listener" + + " parsed from `advertisedListeners` is used as the internal listener.") private String internalListenerName = DEFAULT_INTERNAL_LISTENER_NAME; @FieldContext(category = CATEGORY_SERVER, - doc = "Bind addresses for the broker.\n" - + " Comma-separated list of `<listener_name>:<scheme>://<ip>:<port>` entries." + doc = "Per-listener socket bindings.\n" + + " The internal listener's bindings are derived automatically from `bindAddress`" + + " plus the port properties (`brokerServicePort` / `brokerServicePortTls` /" + + " `webServicePort` / `webServicePortTls`) and do not need to be repeated here.\n" + + " PIP-95 smart listener selection routes a connection to the listener whose port" + + " it arrived on. For the Pulsar binary protocol (`pulsar` / `pulsar+ssl`) this is" + + " optional — clients can also pass an explicit `listenerName`. For the HTTP/HTTPS" + + " Admin API (`http` / `https`) it is the only routing mechanism, so every" + + " HTTP/HTTPS advertised listener reachable from outside the cluster needs a" + + " dedicated entry here on a unique port. This lets a layer-4 TCP load balancer" + + " serve the Admin API directly per listener, without an HTTP reverse proxy.\n" + + " Format: comma-separated `<listener_name>:<scheme>://<ip>:<port>` entries." + " Supported schemes: `pulsar`, `pulsar+ssl`, `http`, `https`." + " The `<ip>` part selects which local network interface the port binds to:" + " use a specific local IP, or `0.0.0.0` to bind on all interfaces." + " A local hostname is also accepted but not recommended.\n" - + " At runtime, the legacy `brokerServicePort`, `brokerServicePortTls`," - + " `webServicePort`, and `webServicePortTls` properties are migrated into the bind" - + " address list (using `bindAddress`, default `0.0.0.0`, as the IP) under the listener" - + " named by `internalListenerName`, and merged with the entries declared here.\n" - + " Each `ip:port` may be bound by exactly one (listener, scheme) pair — a TCP socket" - + " cannot serve two protocol schemes at once. An entry here that exactly matches a" - + " migrated legacy binding (same `listener:scheme://ip:port`) is tolerated; assigning" - + " the same `ip:port` to a different listener or to a different scheme fails" - + " validation.\n" - + " Binding the same listener to multiple ports of the same scheme is allowed but rarely" - + " useful.") + + " Each `ip:port` may be bound by exactly one (listener, scheme) pair. An entry" + + " that exactly matches the auto-derived internal-listener binding is tolerated;" + + " assigning the same `ip:port` to a different listener or scheme fails validation.") private String bindAddresses; @FieldContext(category = CATEGORY_SERVER,
