我应该使用 Hazelcast 来检测对 REST 服务的重复请求吗

Should I use Hazelcast to detect duplicate requests to a REST service

我有一个简单的用例。我有一个系统,其中不允许重复请求 REST 服务(有几十个实例)。但是,由于复杂的数据存储配置和下游服务也很难预防。

所以我可以防止重复的唯一方法 "transactions" 是有一些集中的地方,我写一个请求数据的唯一散列。每个 REST 端点首先检查新请求的哈希值是否已经存在,并且仅在不存在此类哈希值时才继续。

出于此问题的目的,假设无法使用数据库约束来执行此操作。

一个解决方案是在我存储请求哈希的数据库中创建一个 table,并在继续请求之前始终写入此 table。但是,我想要比这更轻的东西。

另一种解决方案是使用 Redis 之类的东西,并在处理请求之前将我的唯一哈希值写入 redis。但是,我不想启动 Redis 集群并维护它等。

我正在考虑将 Hazelcast 嵌入到我的每个应用程序实例中,并在其中写入我的唯一哈希值。理论上,所有实例都将在内存网格中看到哈希,并且能够检测到重复请求。这解决了我的问题,即拥有比数据库更轻便的解决方案以及不必维护 Redis 集群的其他要求。

现在终于可以回答我的问题了。在这个用例中使用 Hazelcast 是个好主意吗? hazelcast 是否足够快以检测相隔几毫秒或几微秒的重复请求?

如果请求 1 进入实例 1 和请求 2 进入实例相隔 2 微秒。实例 1 向 hazelcast 写入请求的哈希值,实例 2 仅在几毫秒后检查 hazelcast 是否存在哈希值,是否会检测到哈希值? hazelcast 是否会及时在集群中传播数据?它甚至需要这样做吗?

在此先感谢,欢迎所有想法。

Hazelcast 绝对是这种用例的不错选择。特别是如果您只使用 Map<String, Boolean> 并使用 Map::containsKey 进行测试,而不是检索元素并检查 null。放元素的时候也要放个TTL,这样才不会运行内存不足。但是,与 Redis 一样,我们建议将 Hazelcast 与独立集群一起用于 "bigger" 数据集,因为缓存元素的生命周期通常会干扰应用程序的其余部分并使 GC 优化复杂化。 运行 Hazelcast embedded 是一个选择,只有在 运行 时间对您的应用程序进行认真考虑和测试后才应该做出选择。

是的,您可以使用 Hazelcast 分布式地图来检测对 REST 服务的重复请求,因为只要在 hazelcast 地图数据中有 put 操作,所有其他集群实例都将可用。

根据我在测试中阅读和看到的内容,它实际上并没有复制。它使用数据网格将主要数据均匀分布在所有节点上,而不是每个节点都保留所有内容的完整副本并进行复制以同步数据。这样做的好处是没有数据滞后,这是任何复制策略所固有的。

每个节点的数据都有一个备份副本存储在另一个节点上,这显然依赖于复制,但备份副本仅在节点崩溃时使用。

查看下面创建两个 hazelcast 集群实例并获取分布式地图的代码。一个 hazelcast 实例将数据放入分布式 IMap,另一个实例从 IMap 获取数据。

import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;

public class TestHazelcastDataReplication {

    //Create 1st Instance
    public static final HazelcastInstance instanceOne = Hazelcast
            .newHazelcastInstance(new Config("distributedFisrtInstance"));
    //Create 2nd Instance
    public static final HazelcastInstance instanceTwo = Hazelcast
            .newHazelcastInstance(new Config("distributedSecondInstance"));
    //Insert in distributedMap using instance one 
    static IMap<Long, Long> distributedInsertMap = instanceOne.getMap("distributedMap");
    //Read from distributedMap using instance two
    static IMap<Long, Long> distributedGetMap = instanceTwo.getMap("distributedMap");

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (long i = 0; i < 100000; i++) {
                    //Inserting data in distributedMap using 1st instance
                    distributedInsertMap.put(i, System.currentTimeMillis());
                    //Reading data from distributedMap using 2nd instance
                    System.out.println(i + " : " + distributedGetMap.get(i));
                }
            }
        }).start();
    }

}