Posts Tagged ‘SpringBoot’
Efficient Inter-Service Communication with Feign and Spring Cloud in Multi-Instance Microservices
In a world where systems are becoming increasingly distributed and cloud-native, microservices have emerged as the de facto architecture. But as we scale
microservices horizontally—running multiple instances for each service—one of the biggest challenges becomes inter-service communication.
How do we ensure that our services talk to each other reliably, efficiently, and in a way that’s resilient to failures?
Welcome to the world of Feign and Spring Cloud.
The Challenge: Multi-Instance Microservices
Imagine you have a user-service that needs to talk to an order-service, and your order-service runs 5 instances behind a
service registry like Eureka. Hardcoding URLs? That’s brittle. Manual load balancing? Not scalable.
You need:
- Service discovery to dynamically resolve where to send the request
- Load balancing across instances
- Resilience for timeouts, retries, and fallbacks
- Clean, maintainable code that developers love
The Solution: Feign + Spring Cloud
OpenFeign is a declarative web client. Think of it as a smart HTTP client where you only define interfaces — no more boilerplate REST calls.
When combined with Spring Cloud, Feign becomes a first-class citizen in a dynamic, scalable microservices ecosystem.
✅ Features at a Glance:
- Declarative REST client
- Automatic service discovery (Eureka, Consul)
- Client-side load balancing (Spring Cloud LoadBalancer)
- Integration with Resilience4j for circuit breaking
- Easy integration with Spring Boot config and observability tools
Step-by-Step Setup
1. Add Dependencies
If using Eureka:
2. Enable Feign Clients
In your main Spring Boot application class:
@SpringBootApplication @EnableFeignClients public <span>class <span>UserServiceApplication { ... }
3. Define Your Feign Interface
@FeignClient(name = "order-service") public interface OrderClient { @GetMapping("/orders/{id}") OrderDTO getOrder(@PathVariable("id") Long id); }
Spring will automatically:
- Register this as a bean
- Resolve order-service from Eureka
- Load-balance across all its instances
4. Add Resilience with Fallbacks
You can configure a fallback to handle failures gracefully:
@FeignClient(name = "order-service", fallback = OrderClientFallback.class) public interface OrderClient { @GetMapping("/orders/{id}") OrderDTO getOrder(@PathVariable Long id); }
The fallback:
@Component public class OrderClientFallback implements OrderClient { @Override public OrderDTO getOrder(Long id) { return new OrderDTO(id, "Fallback Order", LocalDate.now()); } }
⚙️ Configuration Tweaks
Customize Feign timeouts in application.yml:
feign:
client:
config:
default:
connectTimeout:3000
readTimeout:500
[/yml]
Enable retry:
feign: client: config: default: retryer: maxAttempts: 3 period: 1000 maxPeriod: 2000
What Happens Behind the Scenes?
When user-service calls order-service:
- Spring Cloud uses Eureka to resolve all instances of order-service.
- Spring Cloud LoadBalancer picks an instance using round-robin (or your chosen strategy).
- Feign sends the HTTP request to that instance.
- If it fails, Resilience4j (or your fallback) handles it gracefully.
Observability & Debugging
Use Spring Boot Actuator to expose Feign metrics:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency
And tools like Spring Cloud Sleuth + Zipkin for distributed tracing across Feign calls.
Beyond the Basics
To go even further:
- Integrate with Spring Cloud Gateway for API routing and external access.
- Use Spring Cloud Config Server to centralize configuration across environments.
- Secure Feign calls with OAuth2 via Spring Security and OpenID Connect.
✨ Final Thoughts
Using Feign with Spring Cloud transforms service-to-service communication from a tedious, error-prone task into a clean, scalable, and cloud-native solution.
Whether you’re scaling services across zones or deploying in Kubernetes, Feign ensures your services communicate intelligently and resiliently.
SpringBatch: How to have different schedules, per environment, for instance: keep the fixedDelay=60000 in prod, but schedule with a Cron expression in local dev?
Case
In SpringBatch, a batch is scheduled in a bean JobScheduler
with
@Scheduled(fixedDelay = 60000) void doSomeThing(){...}
.
How to have different schedules, per environment, for instance: keep the fixedDelay=60000
in prod, but schedule with a Cron expression in local dev?
Solution
Add this block to the <JobScheduler
:
@Value("${jobScheduler.scheduling.enabled:true}") private boolean schedulingEnabled; @Value("${jobScheduler.scheduling.type:fixedDelay}") private String scheduleType; @Value("${jobScheduler.scheduling.fixedDelay:60000}") private long fixedDelay; @Value("${jobScheduler.scheduling.initialDelay:0}") private long initialDelay; @Value("${jobScheduler.scheduling.cron:}") private String cronExpression; @Scheduled(fixedDelayString = "${jobScheduler.scheduling.fixedDelay:60000}", initialDelayString = "${jobScheduler.scheduling.initialDelay:0}") @ConditionalOnProperty(name = "jobScheduler.scheduling.type", havingValue = "fixedDelay") public void scheduleFixedDelay() throws Exception { if ("fixedDelay".equals(scheduleType) || "initialDelayFixedDelay".equals(scheduleType)) { doSomething(); } } @Scheduled(cron = "${jobScheduler.scheduling.cron:0 0 1 * * ?}") @ConditionalOnProperty(name = "jobScheduler.scheduling.type", havingValue = "cron", matchIfMissing = false) public void scheduleCron() throws Exception { if ("cron".equals(scheduleType)) { doSomething(); } }
In application.yml
, add:
jobScheduler: # noinspection GrazieInspection scheduling: enabled: true type: fixedDelay fixedDelay: 60000 initialDelay: 0 cron: 0 0 1 31 2 ? # every 31st of February... which means: never
(note the cron expression: leaving it empty may prevent SpringBoot from starting)
In application.yml
, add:
jobScheduler: # noinspection GrazieInspection scheduling: type: cron cron: 0 0 1 * * ?
It should work now ;-).