Приращение Atomi c с данными Spring для AWS ElastiCache (Redis) - PullRequest
0 голосов
/ 19 февраля 2020

У нас есть несколько экземпляров одного и того же приложения, развернутого за ELB (Load Balancer). Всякий раз, когда выполняется определенная работа, мы подсчитываем некоторые элементы и затем хотим увеличить значение счетчика.

Мы используем ElastiCache для хранения этих метрик в памяти. Мы настроили его как экземпляры Cluster of Redis.

У меня возникают проблемы с пониманием того, как правильно взаимодействовать с ElastiCache, чтобы счетчик никогда не пропускал никакого приращения (то есть операция atomi c). Я знаю, что INCRBY, кажется, путь к go, но я не уверен, как настроить Spring Data, чтобы я мог выполнить команду Redis для моего Master. На самом деле, наш подход даже не ориентирован на многопоточность, но вот код:

@Slf4j
@Service
@RequiredArgsConstructor
public class MetricServiceImpl implements MetricService {

    private final IntegerMetricRepository integerMetricRepository;

    private static final BigInteger ZERO = BigInteger.ZERO;


    @Override
    public long countRealJobs(List<Job> newJobs) {
        return newJobs.stream()
                .filter(job -> !job.isFake())
                .count();
    }

    @Override
    public long countRealDrafts(List<Draft> drafts) {
        return drafts.stream()
                .filter(draft -> !draft.getString(JsonFields.TITLE.getValue())
                        .contains("FAKE"))
                .count();
    }

    @Override
    public IntegerMetric increment(IntegerMetricType integerMetricType, long amount) {
        IntegerMetric metric = getOrInitialize(integerMetricType);
        BigInteger newValue = metric.getValue().add(BigInteger.valueOf(amount));
        metric.setValue(newValue.max(ZERO)); // smallest possible value is 0
        return integerMetricRepository.save(metric);
    }

    @Override
    public BigInteger getValue(IntegerMetricType integerMetricType) {
        return getOrInitialize(integerMetricType).getValue();
    }

    @Override
    public IntegerMetric setValue(IntegerMetricType integerMetricType, long amount) {
        IntegerMetric metric = getOrInitialize(integerMetricType);

        if (amount < 0) { // negatives not allowed
            log.info("Tried to set a negative value for an IntegerMetric.");
            return metric;
        }

        metric.setValue(BigInteger.valueOf(amount));
        return integerMetricRepository.save(metric);
    }

    /**
     * @param integerMetricType the desired Entity
     * @return either the Entity which already existed, or a new one initialized to {@code ZERO}.
     */
    private IntegerMetric getOrInitialize(IntegerMetricType integerMetricType) {
        return integerMetricRepository.findById(integerMetricType).orElseGet(
                () -> integerMetricRepository.save(new IntegerMetric(integerMetricType, ZERO)));
    }
}

Для моего Repository кажется, что единственные релевантные операции, которые я могу выполнить, это эквиваленты get и set. Как мне настроить мой код так, чтобы я мог выдавать фактическую команду Redis для моего Кластера, таким образом используя преимущества атома c природы примитивов (здесь INCRBY), которые я хочу использовать?

1 Ответ

0 голосов
/ 06 марта 2020

Решение заключается в использовании RedisTemplate. С этим классом становится возможным использовать AtomicCounter, который Redis изначально поддерживает (через такие операции, как INCRBY).

...