简体   繁体   English

使用 Spring Data for AWS ElastiCache (Redis) 的原子增量

[英]Atomic increment with Spring Data for AWS ElastiCache (Redis)

We have multiple instances of the same application deployed behind an ELB (Load Balancer).我们在 ELB(负载均衡器)后面部署了同一应用程序的多个实例。 Whenever a certain job is done, we count some elements and then want to increment a counter's value.每当完成某项工作时,我们都会计算一些元素,然后想要增加计数器的值。

We use ElastiCache to hold these metrics in memory.我们使用 ElastiCache 将这些指标保存在内存中。 We have set it up as a Cluster of Redis instances.我们已将其设置为 Redis 实例集群。

I'm having trouble understanding how to properly interact with ElastiCache so that the counter never misses any increment (ie an atomic operation).我无法理解如何与 ElastiCache 正确交互,以便计数器不会错过任何增量(即原子操作)。 I know INCRBY seems to be the way to go, but I'm not sure how to set up Spring Data so that I can issue a Redis command to my Master .我知道INCRBY似乎是要走的路,但我不确定如何设置 Spring Data 以便我可以向我的Master发出 Redis 命令。 As it is, our approach isn't even thread-safe, but here is the code:事实上,我们的方法甚至不是线程安全的,但这里是代码:

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

For my Repository , it seems like the only relevant operations I can issue are the equivalents of get and set .对于我的Repository ,似乎我可以发出的唯一相关操作是getset的等效项。 How do I set up my code so that I can issue actual Redis command to my Cluster, thus taking advantage of the atomic nature of the primitives (here, INCRBY ) I want to use?我如何设置我的代码,以便我可以向我的集群发出实际的 Redis 命令,从而利用我想要使用的原语(这里是INCRBY )的原子性质?

The solution resides in the usage of RedisTemplate .解决方案在于使用RedisTemplate With that class, it becomes possible to use the "AtomicCounter" that Redis natively supports (through operations such as INCRBY ).有了这个类,就可以使用 Redis 原生支持的“AtomicCounter”(通过诸如INCRBY操作)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM