package com.rinha.backend.service; import com.rinha.backend.model.PaymentModel; import com.rinha.backend.repository.PaymentRepository; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Service public class PaymentService { private static String processorDefault = "http://payment-processor-default:8080/payments"; private static String processorFallback = "http://payment-processor-fallback:8080/payments"; private final HttpClient httpClient = HttpClient.newHttpClient(); private static final Random random = new Random(); private final ExecutorService dbExecutor = Executors.newFixedThreadPool(2); private final BlockingQueue paymentQueue = new ArrayBlockingQueue(65536); private final PaymentRepository paymentRepository; public PaymentService(PaymentRepository paymentRepository) { this.paymentRepository = paymentRepository; this.startWorker(); } public void addQueue(PaymentModel p) { p.setData(OffsetDateTime.now(ZoneOffset.UTC)); paymentQueue.add(p); } public void startWorker() { for(int i = 1; i <= 4; ++i) { Thread worker = new Thread(() -> { while (true) { try { PaymentModel p = paymentQueue.take(); processPayment(p); } catch (InterruptedException e) { } } }); worker.setName("worker-" + i); worker.start(); } } public void processPayment(PaymentModel p) { //System.out.println("Processando pagamento " + p.getCorrelationId() + // " no valor de " + p.getAmount() + // " às " + p.getData()); int ok = sendToProcessor(p); if(ok > 0) { p.setProcessor(ok); saveDB(p); }else paymentQueue.add(p); } public int sendToProcessor(PaymentModel p) { try { String body = String.format(""" { "correlationId": "%s", "amount": %.2f, "requestedAt": "%s" } """, p.getCorrelationId(), p.getAmount(), p.getData()); int n = random.nextInt(100); String url; if(n < 60) url = processorDefault; else url = processorFallback; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); //System.out.println("Resposta servidor: " + response.statusCode() + " - " + response.body()); if(response.statusCode() == 200 && url.equals(processorDefault)) return 1; if(response.statusCode() == 200 && url.equals(processorFallback)) return 2; return 0; } catch (Exception e) { throw new RuntimeException(e); } } public void saveDB(PaymentModel p){ //paymentRepository.save(p); dbExecutor.submit(() -> { try { paymentRepository.save(p); } catch (Exception e) { } }); } }