Java Microservices - Synchronous Communication (REST/gRPC)



Introduction

Microservices architecture involves breaking down applications into independently deployable, loosely coupled services. For these services to work cohesively, they must communicate with each other-either synchronously or asynchronously.

This article focuses on the Synchronous Communication pattern, where services interact in real time, expecting immediate responses. The two most widely used technologies for synchronous communication are −

  • REST (Representational State Transfer)

  • gRPC (Google Remote Procedure Call)

We will explore both in detail-understanding their use cases, trade-offs, implementation techniques, and how they compare.

What Is Synchronous Communication?

Definition

Synchronous communication in microservices refers to a communication pattern where one service sends a request to another and waits for a response before proceeding.

This is akin to traditional function calls: Service A calls Service B, and waits for the result to continue its execution.

Characteristics of Synchronous Communication

Sr.No. Feature Description
1 Real-time interaction The client waits until the response is received
2 Simple error handling Built-in status codes, retries, and fallbacks
3 Tightly coupled timing Both services must be available during communication
4 Serialization Data is serialized into formats like JSON (REST) or Protobuf (gRPC)

Why Use Synchronous Communication?

Ideal for −

  • Real-time data requirements (e.g., payments, user authentication)

  • CRUD operations (e.g., read user profile)

  • Predictable and consistent APIs

Not Ideal for −

  • High-volume or event-driven scenarios

  • Long-running processes

  • Systems requiring decoupling and fault tolerance

Technology Options

Sr.No. Protocol Description Common Usage
1 REST HTTP-based API using JSON/XML Web, mobile, HTTP clients
2 gRPC Binary protocol over HTTP/2 using Protobuf Internal microservices, low-latency systems

Architecture Overview

Synchronous Microservices Architecture
  • Service A makes a synchronous request to Service B

  • Service B processes and responds instantly

  • If B fails, A must retry or handle the failure

REST-Based Synchronous Communication with Spring Boot

Project Setup

Dependencies (Maven)

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId> <!-- Optional for async REST -->
</dependency>

Service B: Profile Service

@RestController
@RequestMapping("/profiles")
public class ProfileController {

   @GetMapping("/{id}")
   public Profile getProfile(@PathVariable String id) {
      return new Profile(id, "Alice", "alice@example.com");
   }
}

Service A: User Service (REST Client)

@Service
public class ProfileClient {

   @Autowired
   private RestTemplate restTemplate;

   public Profile getProfile(String userId) {
      return restTemplate.getForObject("http://profile-service/profiles/" + userId, Profile.class);
   }
}

Enable LoadBalanced RestTemplate

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
   return new RestTemplate();
}

Configuration (application.yml)

spring:
  application:
    name: user-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

gRPC-Based Synchronous Communication in Spring Boot

Why gRPC?

Feature REST gRPC Format JSON / XML Protocol Buffers (binary) Performance Moderate Very high Streaming Limited Full-duplex supported Language Support Wide Also wide HTTP Version HTTP/1.1 HTTP/2

gRPC is ideal for internal service communication requiring low latency.

Setup: Add gRPC Dependencies

Use yidongnan's Spring Boot starter for gRPC −

Maven

<dependency>
   <groupId>net.devh</groupId>
   <artifactId>grpc-server-spring-boot-starter</artifactId>
   <version>2.14.0.RELEASE</version>
</dependency>
<dependency>
   <groupId>net.devh</groupId>
   <artifactId>grpc-client-spring-boot-starter</artifactId>
   <version>2.14.0.RELEASE</version>
</dependency>

Define Proto File

profile.proto
syntax = "proto3";

package profile;

service ProfileService {
  rpc GetProfile (ProfileRequest) returns (ProfileResponse);
}

message ProfileRequest {
  string userId = 1;
}

message ProfileResponse {
  string userId = 1;
  string name = 2;
  string email = 3;
}

Compile with the Protobuf plugin to generate Java classes.

Implement the gRPC Server

@GrpcService
public class ProfileServiceImpl extends ProfileServiceGrpc.ProfileServiceImplBase {

   @Override
   public void getProfile(ProfileRequest request, StreamObserver<ProfileResponse> responseObserver) {
      ProileResponse response = ProfileResponse.newBuilder()
         .setUserId(request.getUserId())
         .setName("Alice")
         .setEmail("alice@example.com")
         .build();
      responseObserver.onNext(response);
      responseObserver.onCompleted();
   }
}

gRPC Client

@Service
public class ProfileGrpcClient {

   @GrpcClient("profile-service")
   private ProfileServiceGrpc.ProfileServiceBlockingStub stub;

   public ProfileResponse getProfile(String userId) {
      return stub.getProfile(ProfileRequest.newBuilder().setUserId(userId).build());
   }
}

Synchronous Communication Best Practices

Sr.No. Practice Description
1 Circuit Breakers Use Resilience4j or Hystrix to avoid cascading failures
2 Timeouts Set request timeouts to avoid hanging requests
3 Retries Automatically retry transient failures
4 Load Balancing Use Ribbon, Eureka, or Kubernetes for distributing traffic
5 Monitoring & Tracing Use Sleuth, Zipkin, Prometheus for observability
6 Fallback Mechanisms Provide alternative responses if a service fails

Pros and Cons of Synchronous Communication

Sr.No. Pros Cons
1 Simpler to implement and debug Coupling in availability
2 Easier data consistency Not suitable for large-scale, event-driven systems
3 Familiar request/response model Latency increases with each network hop
4 Ideal for chained workflows Prone to cascading failures

Use Cases Comparison: REST vs. gRPC

Sr.No. Use Case Recommended Approach
1 Internal microservice communication gRPC (performance critical)
2 Mobile/Web communication REST (browser/client friendly)
3 Streaming large datasets gRPC with streaming
4 Public APIs REST (easy integration)

Real-World Example: Netflix

Netflix uses gRPC extensively for internal communications between services like recommendation engines and playback servers, due to its high performance and contract-first development.

However, for public APIs, Netflix still uses REST with GraphQL for client flexibility.

When to Use Synchronous Communication

Use When

  • Real-time responses are required

  • Workflow depends on sequential execution

  • Systems are under control in terms of scale

Avoid When

  • Services are frequently unavailable

  • High-volume traffic or long processing is involved

  • Decoupling and resilience are key priorities

Conclusion

Synchronous communication is a core pattern in microservices that enables real-time, request-response interaction between services. With REST and gRPC as the leading technologies, you can choose based on −

  • Performance needs (gRPC)

  • Interoperability (REST)

  • Use case complexity

For mission-critical, performance-sensitive applications, gRPC is highly effective. For client-facing and public APIs, REST remains the default choice.

Design your system based on communication patterns that align with business and technical requirements.

Advertisements