# Inter-service Communication Patterns in Java Microservices: REST, RPC, and Async Messaging Explained

> Explore Java microservice inter-service communication patterns including REST, RPC, and asynchronous messaging. Understand how to choose the right pattern for your application.

- Repository: [Doocs/advanced-java](https://github.com/doocs/advanced-java)
- Tags: tutorial
- Published: 2026-02-28

---

**Java microservices use three fundamental inter-service communication patterns: synchronous REST HTTP for stateless request-response, synchronous RPC over TCP for high-performance binary calls, and asynchronous message brokers for event-driven decoupling.**

Inter-service communication patterns in Java microservices determine how distributed components exchange data while maintaining loose coupling and high availability. The **doocs/advanced-java** repository provides concrete implementations demonstrating RESTful HTTP, custom socket-based RPC, and message broker integration strategies. Understanding these patterns helps architects choose the right protocol for specific latency requirements, payload sizes, and consistency models.

## Synchronous Communication Patterns

Synchronous patterns block the caller until the callee returns a response, making them suitable for real-time queries and immediate consistency requirements.

### REST HTTP Communication

**REST over HTTP** is the most common pattern, using standard verbs (GET, POST, PUT, DELETE) and JSON payloads. In `docs/micro-services/what's-microservice-how-to-communicate.md` (lines 27-34), the repository demonstrates a basic provider service:

```java
@RestController
@RequestMapping("/communication")
public class RestControllerDemo {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

```

The consumer uses **Spring Boot's** `RestTemplate` to invoke the endpoint synchronously. The example at lines 40-50 shows:

```java
@RestController
@RequestMapping("/demo")
public class RestDemo {
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/hello2")
    public String hello2() {
        String result = restTemplate.getForObject(
            "http://localhost:9013/communication/hello", String.class);
        return result;
    }
}

```

**Key implementation details:**
- The caller blocks on `getForObject()` until the HTTP response arrives.
- Spring Boot automatically handles `String` or JSON conversion via `HttpMessageConverters`.
- Production deployments typically replace hardcoded URLs (`localhost:9013`) with service discovery (Eureka) and client-side load balancing (Spring Cloud LoadBalancer).

### RPC over TCP Communication

**RPC (Remote Procedure Call)** hides network complexity behind Java interfaces, serializing method invocations over binary TCP connections for lower latency than HTTP/JSON.

The repository provides a minimal socket-based RPC framework in `docs/micro-services/what's-microservice-how-to-communicate.md` (lines 75-176) to illustrate the concept.

**Server-side implementation:**
The `RPCServer` class registers interface implementations and listens on a specific port:

```java
public class RPCServer {
    private static final ExecutorService executor = Executors.newFixedThreadPool(10);
    private static final ConcurrentHashMap<String, Class> serviceRegister = new ConcurrentHashMap<>();

    public void register(Class service, Class impl) {
        serviceRegister.put(service.getSimpleName(), impl);
    }

    public void start(int port) {
        try (ServerSocket server = new ServerSocket(port)) {
            while (true) {
                Socket socket = server.accept();
                executor.execute(new ServiceTask(socket, serviceRegister));
            }
        }
    }
}

```

**Client-side dynamic proxy:**
The `RPCclient` class (lines 81-130) creates a proxy that serializes the method call over the socket:

```java
public class RPCclient<T> {
    public static <T> T getRemoteProxyObj(final Class<T> service,
                                           final InetSocketAddress addr) {
        return (T) Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[]{service},
            (proxy, method, args) -> {
                try (Socket socket = new Socket();
                     ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
                     ObjectInputStream in = new ObjectInputStream(socket.getInputStream())) {
                    socket.connect(addr);
                    out.writeUTF(service.getSimpleName());
                    out.writeUTF(method.getName());
                    out.writeObject(method.getParameterTypes());
                    out.writeObject(args);
                    return in.readObject();
                }
            });
    }
}

```

**Service contract and test:**
The interface and implementation (lines 38-48) define the business logic:

```java
public interface Tinterface {
    String send(String msg);
}

public class TinterfaceImpl implements Tinterface {
    @Override
    public String send(String msg) {
        return "send message " + msg;
    }
}

```

The test driver (lines 58-70) wires everything together on port 10000:

```java
public class RunTest {
    public static void main(String[] args) {
        new Thread(() -> {
            RPCServer rpcServer = new RPCServer();
            rpcServer.register(Tinterface.class, TinterfaceImpl.class);
            rpcServer.start(10000);
        }).start();

        Tinterface proxy = RPCclient.getRemoteProxyObj(
                Tinterface.class, new InetSocketAddress("localhost", 10000));
        System.out.println(proxy.send("rpc 测试用例"));
    }
}

```

**Key implementation details:**
- The framework uses **Java dynamic proxies** (`Proxy.newProxyInstance`) to intercept interface methods.
- Serialization relies on standard Java `ObjectOutputStream` and `ObjectInputStream`, transmitting the service name, method name, parameter types, and arguments.
- The server dispatches to the registered implementation using a thread pool (`ExecutorService`) to handle concurrent requests.

## Asynchronous Messaging Patterns

Asynchronous communication decouples services in time, allowing producers to continue processing without waiting for consumers.

### Message Broker Integration

While the repository focuses on synchronous examples, it documents that production Java microservices typically adopt **message brokers** such as **Apache Kafka**, **RabbitMQ**, **RocketMQ**, or **ActiveMQ** for asynchronous flows.

A typical Spring Boot implementation uses **Spring Kafka**:

```java
@KafkaListener(topics = "order-events")
public void handleOrderEvent(String payload) {
    // Process event asynchronously
}

```

And the corresponding producer:

```java
@Component
public class OrderPublisher {
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    public void publish(String orderJson) {
        kafkaTemplate.send("order-events", orderJson);
    }
}

```

**Key characteristics:**
- **Temporal decoupling**: Producers and consumers operate independently; consumers can be down without affecting producers.
- **Scalability**: Consumer groups allow parallel processing by adding instances.
- **Durability**: Brokers persist messages, enabling replay and recovery.
- **Protocols**: Java clients typically use **AMQP** (RabbitMQ), **Kafka protocol**, or **MQTT** for IoT scenarios.

## Summary

- **REST HTTP** provides the simplest inter-service communication pattern for Java microservices, using standard Spring Boot annotations (`@RestController`, `RestTemplate`) and JSON payloads over port 80/443.
- **RPC over TCP** offers higher performance and type safety by serializing Java method calls through binary sockets, as demonstrated by the custom `RPCServer` and `RPCclient` implementation in `docs/micro-services/what's-microservice-how-to-communicate.md`.
- **Asynchronous messaging** via Kafka, RabbitMQ, or RocketMQ decouples services temporally, enabling event-driven architectures that handle traffic spikes and consumer failures gracefully.
- Choose **REST** for external APIs and simple CRUD, **RPC** for internal high-throughput Java-to-Java calls, and **messaging** for eventual consistency and event sourcing.

## Frequently Asked Questions

### What is the difference between REST and RPC in Java microservices?

**REST** uses HTTP verbs and JSON over port 80/443, optimizing for interoperability and caching, while **RPC** (such as the socket-based example in the repository) transmits binary method invocations over TCP for lower latency and stronger type safety. REST is better for public-facing APIs, whereas RPC suits internal service meshes where both endpoints run Java.

### When should I use asynchronous messaging instead of synchronous calls?

Use **asynchronous messaging** (Kafka, RabbitMQ) when services must remain available independently, when handling traffic spikes that could overwhelm synchronous consumers, or when implementing **event sourcing** and **eventual consistency** models. Synchronous REST or RPC creates temporal coupling that causes cascading failures if the downstream service is slow or unavailable.

### How does the custom RPC implementation in doocs/advanced-java handle serialization?

The `RPCclient` class uses **Java dynamic proxies** to intercept interface methods, then serializes the service name, method name, parameter types, and arguments using `ObjectOutputStream` over a raw TCP socket. The `RPCServer` deserializes these components via `ObjectInputStream`, looks up the implementation in a `ConcurrentHashMap` registry, and returns the result object back through the socket stream.

### Can I combine multiple communication patterns in the same microservice architecture?

**Yes**, production Java architectures typically mix patterns: **REST** for edge services exposing APIs to mobile or web clients, **RPC** (gRPC, Dubbo, or the custom socket example) for high-frequency internal calls between backend services, and **asynchronous messaging** for background processing, audit trails, and cross-domain event publishing. The choice depends on the latency, coupling, and consistency requirements of each specific interaction.