Different Ways to Establish Communication Between Spring Microservices

Last Updated : 29 Apr, 2026

In a Spring Boot–based microservices architecture, applications are divided into multiple independent services. These services often need to communicate with each other to fulfill business requirements. Depending on the use case, this communication can be synchronous or asynchronous.

Synchronous Communication

In synchronous communication, the calling service waits for a response from the called service before continuing execution. Spring provides multiple ways to implement this.

1. REST Template

RestTemplate is a Spring class used for making synchronous HTTP calls between microservices. It simplifies communication by providing built-in methods to send requests and receive responses. It is commonly used in traditional Spring applications for REST API consumption.

  • Offers methods like getForEntity() and postForObject() to interact with REST APIs.
  • Allows customization of requests using headers, parameters, and request entities.
Java
@RestController
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/user/{id}/orders")
    public String getUserOrders(@PathVariable String id) {
        return restTemplate.getForObject(
            "http://order-service/orders/" + id, String.class
        );
    }
}

Explanation:

  • @Autowired RestTemplate: Injects the RestTemplate bean into UserController, enabling it to make synchronous HTTP calls to another microservice.
  • @GetMapping("/user/{id}/orders"): Exposes an endpoint that accepts a user ID and triggers an inter-service call to fetch order details for that user.
  • restTemplate.getForObject(...): Sends a GET request to the order-service URL, converts the HTTP response directly into a String, and returns it to the client; any error response results in an exception.

Note: RestTemplate is now in maintenance mode, and Spring recommends using WebClient for newer applications.

2. Feign Client

Feign is a declarative HTTP client that simplifies inter-service communication by letting developers define REST calls using Java interfaces instead of boilerplate code. It integrates seamlessly with Spring Cloud, making microservice communication cleaner, scalable, and production-ready.

  • No need to hard-code service URLs
  • Supports service discovery using Eureka Client IDs
  • Built-in support for load balancing
  • Enables fault tolerance when integrated with Spring Cloud (e.g., Resilience4j/Hystrix)
Java
@FeignClient(name = "order-service", fallback = OrderClientFallback.class)
public interface OrderClient {
    @GetMapping("/orders/{id}")
    String getOrder(@PathVariable("id") String id);
}

@Component
class OrderClientFallback implements OrderClient {
    @Override
    public String getOrder(String id) {
        return "Order service unavailable, try again later";
    }
}

Explanation:

  • The first line of code declares a Feign client that connects to the order-service; if the service is unavailable, the fallback class is triggered.
  • @GetMapping("/orders/{id}"): Maps the getOrder method to the /orders/{id} endpoint of order-service, automatically handling the HTTP request and response conversion.
  • OrderClientFallback implementation: Provides an alternative response when the remote service fails, ensuring graceful error handling instead of service failure propagation.

3. gRPC

gRPC is a high-performance, open-source RPC framework developed by Google that enables efficient communication between distributed services. It uses HTTP/2 for transport and Protocol Buffers for fast, compact data serialization, making it ideal for low-latency microservices.

Java
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090)
                                              .usePlaintext()
                                              .build();
OrderServiceGrpc.OrderServiceBlockingStub stub = OrderServiceGrpc.newBlockingStub(channel);

OrderResponse response = stub.getOrder(OrderRequest.newBuilder().setId("123").build());
System.out.println("Order Details: " + response.getDetails());

Explanation:

  • ManagedChannelBuilder.forAddress("localhost", 9090): Creates a gRPC communication channel to connect the client with the Order Service running on port 9090.
  • OrderServiceGrpc.newBlockingStub(channel): Generates a blocking (synchronous) gRPC client stub that uses the channel to invoke remote methods.
  • stub.getOrder(...): Sends a request containing order ID 123 to the remote gRPC service, receives the OrderResponse, and prints the order details to the console.

Asynchronous Communication

In asynchronous communication, services do not wait for an immediate response. Instead, messages are exchanged through an intermediary system, allowing services to remain loosely coupled.

1. Message Broker Approach

Message brokers are middleware systems that enable applications or services to communicate by sending and receiving messages in a reliable and asynchronous way. They help decouple producers and consumers, improving scalability and fault tolerance.

  • Act as an intermediary to route, queue, and deliver messages between systems
  • Support asynchronous communication, reducing system dependency
  • Provide features like message persistence, buffering, and retries
  • Commonly used in distributed systems and microservices architectures
Java
@EnableBinding(Sink.class)
public class OrderConsumer {

    @StreamListener(Sink.INPUT)
    public void handleOrderEvent(Order order) {
        System.out.println("Processing order: " + order.getId());
    }
}

Explanation:

  • @EnableBinding(Sink.class) binds this class to the input channel (Sink.INPUT) so it can consume messages from a message broker.
  • OrderConsumer acts as a message consumer, listening for incoming order events.
  • @StreamListener(Sink.INPUT) tells Spring Cloud Stream to invoke handleOrderEvent() whenever a message arrives on the input channel.
  • The received message is automatically deserialized into an Order object, and the order ID is printed while processing.

2. Spring Cloud Bus

Spring Cloud Bus is a framework used to link multiple microservices with a lightweight message broker to share configuration and state changes. It helps propagate events across distributed systems.

  • Uses message brokers like RabbitMQ or Kafka to broadcast events
  • Commonly used to refresh configuration across services dynamically
  • Helps synchronize state changes among multiple instances
  • Reduces the need for manual restarts in microservice environments
Java
@RestController
public class BusRefreshController {

    @Autowired
    private ApplicationEventPublisher publisher;

    @PostMapping("/refresh")
    public String refreshConfig() {
        publisher.publishEvent(new RefreshRemoteApplicationEvent(
                this, "config-service", "**"));
        return "Refresh event published";
    }
}

Explanation:

  • ApplicationEventPublisher is injected to publish events across services using Spring Cloud Bus.
  • @PostMapping("/refresh") exposes an endpoint to trigger a bus refresh event.
  • RefreshRemoteApplicationEvent sends a configuration refresh event to all connected services ("**").
  • When this event is published, all listening microservices receive and act on the refresh automatically.

Comparison: Synchronous vs Asynchronous Communication

Feature

Synchronous (REST/Feign/gRPC)

Asynchronous (Message Brokers/Bus)

Coupling

Tighter

Looser

Latency

Caller waits for response

Caller continues immediately

Complexity

Lower

Higher

Use Case

Request-response workflows

Event-driven, decoupled workflows

Reliability

Dependent on service uptime

More resilient with retries/buffering

Comment