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);
    }
}
                

Message Queues

RabbitMQ Implementation


@Configuration
public class RabbitMQConfig {
    @Bean
    public Queue orderQueue() {
        return new Queue("order-queue");
    }
    
    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("order-exchange");
    }
    
    @Bean
    public Binding binding(Queue orderQueue, TopicExchange exchange) {
        return BindingBuilder.bind(orderQueue)
                           .to(exchange)
                           .with("order.*");
    }
}

@Service
public class OrderMessageService {
    private final RabbitTemplate rabbitTemplate;
    
    @Autowired
    public OrderMessageService(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }
    
    public void sendOrder(Order order) {
        rabbitTemplate.convertAndSend("order-exchange",
                                    "order.create",
                                    order);
    }
}
                

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.