skip to content

What exactly happens when you POST to /actuator/busrefresh, and which event is broadcast?

level: middleimportance: must knowfreq 50%

answer

  1. Publishes RefreshRemoteApplicationEvent
  2. originService + destinationService (**=all)
  3. ServiceMatcher vs spring.cloud.bus.id
  4. Same as ContextRefresher rebind
  5. No JVM/context restart

basics

~20 s

The instance you call publishes a RefreshRemoteApplicationEvent onto the shared broker topic. Every instance on the Bus receives it and does a local refresh — re-reading config and rebinding @RefreshScope / @ConfigurationProperties beans, exactly like calling /actuator/refresh on each one.

solid answer

~40 s

POST /actuator/busrefresh makes the receiving instance publish a RefreshRemoteApplicationEvent to the shared Bus destination (default topic 'springCloudBus') via Spring Cloud Stream. Each instance subscribed to the Bus consumes the event and performs the same work as a local /actuator/refresh: it re-fetches configuration (typically from the Config Server), diffs it against the current Environment, and for any changed keys rebinds @ConfigurationProperties beans and disposes @RefreshScope bean instances so they're recreated lazily with new values. The instance that originated the call also refreshes itself. It does NOT restart the app or reload the whole context — only refresh-scoped and properties-bound beans see new values; ordinary singletons keep their startup-time state. You can target a subset with ?destination=service:port using the ServiceMatcher against each instance's spring.cloud.bus.id.

code

java · 19 lines
java
// Beans that actually pick up refreshed values:

@ConfigurationProperties(prefix = "feature")
public class FeatureProps {          // rebound in place on refresh
    private boolean darkMode;
    // getters/setters...
}

@RefreshScope                        // instance destroyed & recreated lazily on refresh
@Component
public class PricingClient {
    private final String endpoint;
    public PricingClient(@Value("${pricing.endpoint}") String endpoint) {
        this.endpoint = endpoint;    // re-read after busrefresh
    }
}

// Cluster-wide:   curl -X POST http://any:8080/actuator/busrefresh
// One app only:   curl -X POST 'http://any:8080/actuator/busrefresh?destination=pricing:**'

go deeper

for a junior

Know that busrefresh broadcasts a refresh so every instance re-reads config from one call.

for a middle

Name RefreshRemoteApplicationEvent, the springCloudBus destination, and that only @RefreshScope/@ConfigurationProperties beans update.

for a senior

Explain destinationService/ServiceMatcher targeting and why a plain singleton keeps stale values.

for a principal

Reason about broadcast-vs-consumer-group semantics and why each instance needs a distinct spring.cloud.bus.id.

**The endpoint.** `busrefresh` is an Actuator endpoint contributed by Spring Cloud Bus (in Spring Cloud 2.0+; the old id was `bus-refresh`). It must be exposed via `management.endpoints.web.exposure.include=busrefresh`. It accepts `POST` with no body. **Step by step.** 1. You `POST /actuator/busrefresh` to **one** instance. 2. That instance constructs a **`RefreshRemoteApplicationEvent`** — a subclass of `RemoteApplicationEvent` carrying an `originService` (the sender's Bus id) and a `destinationService` (defaults to `**`, meaning everyone). 3. The event is serialized and published to the shared broker destination, default **`springCloudBus`**, through the **Spring Cloud Stream** binder (Kafka or RabbitMQ). 4. Every Bus-connected instance consumes the message. Each checks whether the `destinationService` matches its own id using the **`ServiceMatcher`**; if it matches, it fires a local refresh. 5. A local refresh = what `ContextRefresher` / `/actuator/refresh` does: re-load the `Environment` (re-contact the Config Server, re-read property sources), compute the set of changed keys, publish an `EnvironmentChangeEvent`, rebind `@ConfigurationProperties` beans, and destroy `@RefreshScope` beans so the next access rebuilds them with fresh values. **What it does NOT do.** It does not restart the JVM, does not rebuild the whole `ApplicationContext`, and does not change beans that aren't `@RefreshScope` or `@ConfigurationProperties`. A plain singleton that captured a value in a constructor keeps the old value until restart. That's a classic gotcha. **Targeted refresh.** Append `?destination=` to refresh a subset: - `?destination=customers:**` — all instances of the `customers` app. - `?destination=customers:9000` — the instance whose id is `customers:9000`. The id comes from **`spring.cloud.bus.id`**, whose conventional format is `app:index:id` (derived from `spring.application.name`, an index, and a unique id). The `ServiceMatcher` does Ant-style matching against it. **Ordering / delivery.** Delivery guarantees are the broker's. With Kafka, all instances in the **same consumer group would compete** — so Bus assigns each instance a distinct group/id (via `spring.cloud.bus.id`) to ensure the event is **broadcast** (every instance gets its own copy) rather than load-balanced to one. This is why a proper unique Bus id matters at scale. **Related events on the same channel.** `EnvironmentChangeRemoteApplicationEvent` (pushed by `/actuator/busenv` to set a single property cluster-wide) and, if enabled, `AckRemoteApplicationEvent` (acknowledgements) travel over the same `springCloudBus` destination.

  • Why doesn't a plain @Component with a @Value field pick up the new value after busrefresh?
    Because refresh only rebinds @ConfigurationProperties and recreates @RefreshScope beans. An ordinary singleton was constructed once at startup and its @Value was injected then; without @RefreshScope it's never recreated, so it keeps the old value until a restart.
  • How do you refresh only one service and not the whole cluster?
    Use the destination parameter: POST /actuator/busrefresh?destination=orders:** targets all instances of the 'orders' app; orders:8081 targets one instance. Bus matches it against each instance's spring.cloud.bus.id via ServiceMatcher.

saying these in an interview costs you the question

  • Saying busrefresh restarts instances or reloads the whole ApplicationContext
  • Claiming all beans get new values (only @RefreshScope / @ConfigurationProperties do)
  • Confusing RefreshRemoteApplicationEvent with a plain Spring RefreshEvent/ContextRefreshedEvent
  • Thinking you must call busrefresh on every instance (the point is you call it once)

context