Communication Questions

Synchronous vs asynchronous communication: when do you use each?

Synchronous (REST, gRPC, Feign)Asynchronous (Kafka, RabbitMQ)
Caller waits?yes, needs the answer nowno, fire an event and move on
Couplingtemporal: both must be updecoupled: consumer can be down and catch up
Use forqueries, "get price", "validate user"side effects: send email, update inventory, analytics
Failure handlingtimeouts, retries, circuit breakerretries, DLQ, idempotent consumers

Tip: Rule of thumb: query synchronously, notify asynchronously. Long chains of synchronous calls multiply latency and failure probability.

How do you call another service with OpenFeign? How do you handle errors and timeouts?

@FeignClient(name = "product-service", path = "/api/products")
public interface ProductClient {
    @GetMapping("/{id}")
    ProductDto getProduct(@PathVariable Long id);
}

@Component
class FeignErrorDecoder implements ErrorDecoder {
    public Exception decode(String method, Response r) {
        return switch (r.status()) {
            case 404 -> new ProductNotFoundException();
            case 503 -> new RetryableException(503, "unavailable", r.request().httpMethod(), (Long) null, r.request());
            default  -> new ErrorDecoder.Default().decode(method, r);
        };
    }
}
spring:
  cloud:
    openfeign:
      client:
        config:
          product-service:
            connect-timeout: 1000
            read-timeout: 2000
      circuitbreaker:
        enabled: true

name is the Eureka service id, so Spring Cloud LoadBalancer picks an instance. Always set timeouts; the defaults are too long for user-facing calls.

Feign vs RestClient vs WebClient?

OpenFeignRestClient (Spring 6.1+)WebClient
Styledeclarative interfacefluent, blockingfluent, reactive (non-blocking)
Best formany endpoints of one servicesimple calls in MVC appsWebFlux apps, high fan-out
Load balancingbuilt in with name@LoadBalanced RestClient.Builder@LoadBalanced WebClient.Builder

Note: Spring Cloud OpenFeign is now feature-complete (maintenance mode). For new code the Spring team suggests the HTTP interface (@HttpExchange) backed by RestClient, which gives the same declarative style.

Explain Kafka basics: topic, partition, consumer group, offset.

  • Topic: named stream of events, e.g. order-placed.
  • Partition: a topic is split into ordered logs; ordering is guaranteed only within a partition. The message key (e.g. orderId) decides the partition.
  • Consumer group: each partition is read by exactly one consumer in a group; add consumers (up to the partition count) to scale.
  • Offset: position of a consumer in a partition; committed after processing, so a restart resumes from there.
// producer (order-service)
kafkaTemplate.send("order-placed", order.id().toString(), new OrderPlaced(order.id(), order.total()));

// consumer (inventory-service)
@KafkaListener(topics = "order-placed", groupId = "inventory")
void on(OrderPlaced event) { inventory.reserve(event.orderId()); }

Kafka vs RabbitMQ?

KafkaRabbitMQ
Modeldistributed append-only logmessage broker with queues and exchanges
Retentionkeeps messages (days), consumers can replaydeleted after ack
Throughputvery high (millions/sec)high, lower than Kafka
Routingby topic + partition keyrich: direct, topic, fanout, headers
Use forevent streaming, event sourcing, analytics, CDCtask queues, RPC-like work, complex routing

What delivery guarantees does Kafka give and how do you get "exactly once"?

By default at-least-once: if a consumer processes a message and crashes before committing the offset, it processes it again. Producers can enable enable.idempotence=true (default since Kafka 3.0) and transactions for exactly-once within Kafka. For side effects outside Kafka (DB writes, payments), you still need idempotent consumers: store the processed event id and skip duplicates.

What is a dead letter topic and how do you configure retries for a Kafka consumer?

@Bean
DefaultErrorHandler errorHandler(KafkaTemplate<Object, Object> template) {
    var recoverer = new DeadLetterPublishingRecoverer(template); // sends to "order-placed.DLT"
    var backoff = new ExponentialBackOff(1000, 2.0);
    backoff.setMaxElapsedTime(10_000);
    var handler = new DefaultErrorHandler(recoverer, backoff);
    handler.addNotRetryableExceptions(ValidationException.class); // bad data: no point retrying
    return handler;
}

Poison messages go to the DLT after retries so the partition is not blocked. Monitor the DLT and replay after a fix. Spring Kafka also offers @RetryableTopic for non-blocking retries via retry topics.