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
[xml][/xml]
If using Eureka:
[xml][/xml]
2. Enable Feign Clients
In your main Spring Boot application class:
[java]@SpringBootApplication
@EnableFeignClients
public <span>class <span>UserServiceApplication { … }
[/java]
3. Define Your Feign Interface
[java]
@FeignClient(name = "order-service")
public interface OrderClient { @GetMapping("/orders/{id}")
OrderDTO getOrder(@PathVariable("id") Long id); }
[/java]
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:
[java]
@FeignClient(name = "order-service", fallback = OrderClientFallback.class)
public interface OrderClient {
@GetMapping("/orders/{id}") OrderDTO getOrder(@PathVariable Long id);
}[/java]
The fallback:
[java]
@Component
public class OrderClientFallback implements OrderClient {
@Override public OrderDTO getOrder(Long id) {
return new OrderDTO(id, "Fallback Order", LocalDate.now());
}
}[/java]
⚙️ Configuration Tweaks
Customize Feign timeouts in application.yml:
feign:
client:
config:
default:
connectTimeout:3000
readTimeout:500
[/yml]
Enable retry:
[xml]
feign:
client:
config:
default:
retryer:
maxAttempts: 3
period: 1000
maxPeriod: 2000
[/xml]
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:
[xml]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency[/xml]
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
[java]
@Scheduled(fixedDelay = 60000)
void doSomeThing(){…}
[/java]
.
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
:
[java]
@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(); }
}
[/java]
In application.yml
, add:
[xml]
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
[/xml]
(note the cron expression: leaving it empty may prevent SpringBoot from starting)
In application.yml
, add:
[xml]
jobScheduler:
# noinspection GrazieInspection
scheduling:
type: cron
cron: 0 0 1 * * ?
[/xml]
It should work now ;-).