Configuração Inicial do Projeto
Para implementar bolqueio distribuído em uma aplicação Spring Boot, utilize a biblioteca Redisson. Adicione a dependência correspondente ao seu arquivo pom.xml:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version>
</dependency>
Configure a conexão com o cluster Redis no arquivo application.yml:
spring:
redis:
cluster:
nodes: cache-server1:7000,cache-server2:7001,cache-server3:7002
password: senha_segura123
Implementação do Serviço de Bloqueio
Crie um serviço dedicado para gerenciar bloqueios. Esta abordagem encapsula a lógica de aquisição e liberação de bloqueios.
package com.example.distributed.service;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class ResourceLockService {
private final RedissonClient redissonClient;
public ResourceLockService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public boolean acquireLockWithTimeout(String resourceIdentifier, long timeoutSeconds) {
RLock lockHandle = redissonClient.getLock(resourceIdentifier);
try {
return lockHandle.tryLock(timeoutSeconds, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public void releaseLock(String resourceIdentifier) {
RLock lockHandle = redissonClient.getLock(resourceIdentifier);
if (lockHandle != null && lockHandle.isHeldByCurrentThread()) {
lockHandle.unlock();
}
}
public void executeProtectedOperation(String resourceId, Runnable operation) {
RLock lock = redissonClient.getLock(resourceId);
lock.lock();
try {
operation.run();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Exemplo de Uso no Controlador
Demonstre a utilização do bloqueio em um cenário concorrente:
package com.example.distributed.controller;
import com.example.distributed.service.ResourceLockService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataProcessorController {
private final ResourceLockService lockService;
public DataProcessorController(ResourceLockService lockService) {
this.lockService = lockService;
}
@PostMapping("/process-data")
public String processData() {
String lockKey = "data:processing:task-001";
for (int i = 0; i < 3; i++) {
new Thread(() -> performTask(lockKey)).start();
}
return "Tasks submitted";
}
private void performTask(String lockKey) {
boolean acquired = lockService.acquireLockWithTimeout(lockKey, 30);
if (!acquired) {
System.out.println("Failed to acquire lock - exiting");
return;
}
try {
System.out.println(Thread.currentThread().getName() + " started processing");
Thread.sleep(8000);
System.out.println(Thread.currentThread().getName() + " completed processing");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lockService.releaseLock(lockKey);
}
}
}
Diretrizes para Implementação Segura
- Especificidade das chaves: Use identificadores únicos para cada recurso protegido, como
order:creation:4567, em vez de chaves genéricas. - Prevenção de erros: Verifique sempre se a thread atual é proprietária do bloqueio antes de liberá-lo, evitando exceções de monitoramanto ilegal.
- Gerenciamento de tempo: Defina tempos de espera adequados para evitar bloqueios indefinidos e timeouts excessivos.
- Recuperação de falhas: Implemente tratamento adequado para interrupções de thread durante a espera do bloqueio.