适用于AWS ElastiCache(Redis)的Spring Data的原子增量 [英] Atomic increment with Spring Data for AWS ElastiCache (Redis)

查看:154
本文介绍了适用于AWS ElastiCache(Redis)的Spring Data的原子增量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们在ELB(负载均衡器)后面部署了同一应用程序的多个实例.每当完成某项工作时,我们都会对一些元素进行计数,然后想要增加计数器的值.

We have multiple instances of the same application deployed behind an ELB (Load Balancer). Whenever a certain job is done, we count some elements and then want to increment a counter's value.

我们使用ElastiCache将这些指标保存在内存中.我们已将其设置为Redis实例的群集.

We use ElastiCache to hold these metrics in memory. We have set it up as a Cluster of Redis instances.

我在理解如何与ElastiCache进行正确交互方面遇到困难,因此计数器永远不会丢失任何增量(即原子操作).我知道INCRBY似乎是行之有效的方法,但是我不确定如何设置Spring Data,以便可以向Master发出Redis命令.实际上,我们的方法甚至不是线程安全的,但是下面是代码:

I'm having trouble understanding how to properly interact with ElastiCache so that the counter never misses any increment (i.e. an atomic operation). 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. 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)));
    }
}

对于我的Repository,似乎我可以执行的唯一相关操作是getset的等效项.如何设置代码,以便可以向集群发出实际的Redis命令,从而利用我要使用的原语的原子性(在这里为INCRBY)?

For my Repository, it seems like the only relevant operations I can issue are the equivalents of get and set. 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?

推荐答案

解决方案在于使用RedisTemplate.通过该类,可以使用Redis原生支持的"AtomicCounter"(通过诸如INCRBY之类的操作).

The solution resides in the usage of RedisTemplate. With that class, it becomes possible to use the "AtomicCounter" that Redis natively supports (through operations such as INCRBY).

这篇关于适用于AWS ElastiCache(Redis)的Spring Data的原子增量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆