Java Distributed Systems: A Comprehensive Guide
Introduction to Distributed Systems
Distributed systems are collections of independent computers that work together to appear as a single coherent system. This guide covers essential concepts and implementation patterns for building distributed systems in Java.
Microservices Architecture
Service Implementation
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
private final NotificationService notificationService;
@Autowired
public OrderService(OrderRepository orderRepository,
PaymentService paymentService,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
@Transactional
public Order createOrder(OrderRequest request) {
// Process order
Order order = orderRepository.save(createOrderFromRequest(request));
// Process payment
PaymentResult paymentResult = paymentService.processPayment(order);
// Send notification
notificationService.sendOrderConfirmation(order);
return order;
}
}
Service Discovery
Eureka Client Implementation
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@Configuration
public class EurekaConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Load Balancing
Ribbon Configuration
@Configuration
public class RibbonConfig {
@Bean
public IRule ribbonRule() {
return new RoundRobinRule();
}
@Bean
public IPing ribbonPing() {
return new NoOpPing();
}
@Bean
public ServerList ribbonServerList() {
return new ConfigurationBasedServerList();
}
}
Circuit Breaker Pattern
Hystrix Implementation
@Service
public class PaymentService {
@HystrixCommand(fallbackMethod = "processPaymentFallback",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
})
public PaymentResult processPayment(Order order) {
// Payment processing logic
}
public PaymentResult processPaymentFallback(Order order) {
// Fallback logic
return new PaymentResult(PaymentStatus.FAILED);
}
}
Distributed Caching
Redis Implementation
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
@Service
public class CacheService {
private final RedisTemplate redisTemplate;
@Autowired
public CacheService(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void setValue(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public Object getValue(String key) {
return redisTemplate.opsForValue().get(key);
}
}
Distributed Transactions
Saga Pattern Implementation
public class OrderSaga {
private final List steps;
public OrderSaga() {
this.steps = Arrays.asList(
new ValidateOrderStep(),
new ProcessPaymentStep(),
new UpdateInventoryStep(),
new SendNotificationStep()
);
}
public void execute(Order order) {
SagaContext context = new SagaContext(order);
for (SagaStep step : steps) {
try {
step.execute(context);
} catch (Exception e) {
compensate(context);
throw new SagaExecutionException(e);
}
}
}
private void compensate(SagaContext context) {
for (int i = context.getCurrentStep() - 1; i >= 0; i--) {
steps.get(i).compensate(context);
}
}
}
Best Practices for Distributed Systems
- Design for failure
- Implement proper error handling
- Use circuit breakers and fallbacks
- Implement proper monitoring and logging
- Use appropriate consistency models
- Implement proper security measures
Monitoring and Observability
Prometheus Integration
@Configuration
public class PrometheusConfig {
@Bean
public MeterRegistry registry() {
return new SimpleMeterRegistry();
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
@Service
public class OrderService {
@Timed(value = "order.processing.time")
public Order processOrder(OrderRequest request) {
// Order processing logic
}
}
Conclusion
Building distributed systems requires careful consideration of various factors including scalability, reliability, and consistency. By following best practices and using appropriate patterns and tools, you can create robust and maintainable distributed applications.