如果 2+ 个前端执行相同的请求,我怎样才能使后端只允许第一个请求而忽略其余的请求?

If 2+ front-ends execute the same request, how can I make it so the back-end only allows the first request and ignores the rest?

我想做什么?

当 2+ 个前端通过 socket 发出请求时,我只希望后端处理其中 1 个请求并拒绝其他请求。

目前尝试这样做的代码是什么?

后端代码。

// Original code.

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();

  // Thinking of putting some "locking" code around this if statement?
  // Something like "If this function is already in process, deny other requests and remove them"?
  if (request instanceof AdventureIoMessage) {
    createRequest = (AdventureIoMessage) request;
    DatabaseManager databaseManager = Application.getDatabaseManager();
    Adventure adventure = Adventure.read(databaseManager);
    if (adventure == null || adventure.getStartTime() == 0 || !adventure.isActive()) {
      createAdventure(adventures, createRequest, message);
    } else {
      message.append("New adventure already exists and hasn't ended yet!");
    }
  } else {
    message.append("Request formatting is invalid!");
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}
// My suggested code 1 using a Boolean.

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  Boolean isRequestAlreadyRunning = false;
  
  if (!isRequestAlreadyRunning) {
    isRequestAlreadyRunning = true;
    if (request instanceof AdventureIoMessage) {
      createRequest = (AdventureIoMessage) request;
      DatabaseManager databaseManager = Application.getDatabaseManager();
      Adventure adventure = Adventure.read(databaseManager);
      if (adventure == null || !adventure.isActive()) {
        createAdventure(adventures, createRequest, message);
      } else {
        message.append("New adventure already exists and hasn't ended yet!");
      }
    } else {
      message.append("Request formatting is invalid!");
    }
    isRequestAlreadyRunning = false; // Done processing.
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}
// My suggested code 2 using AtomicBoolean and CountDownLatch.

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  AtomicBoolean updateStarted = new AtomicBoolean();
  CountDownLatch updateFinished = new CountDownLatch(1);
  
  if (updateStarted.compareAndSet(false, true) {
    if (request instanceof AdventureIoMessage) {
      createRequest = (AdventureIoMessage) request;
      DatabaseManager databaseManager = Application.getDatabaseManager();
      Adventure adventure = Adventure.read(databaseManager);
      if (adventure == null || !adventure.isActive()) {
        createAdventure(adventures, createRequest, message);
      } else {
        message.append("New adventure already exists and hasn't ended yet!");
      }
    } else {
      message.append("Request formatting is invalid!");
    }
    updateFinished.countDown();
  } else {
    updateFinished.await();
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}

对于建议的代码 2,也只尝试了 AtomicBoolean

我期望结果是什么?

我考虑过保留某种变量来检查 create() 是否仍会发生,希望只有 1 个请求通过。

实际结果如何?

当 2+ 个前端通过 socket emit 发出请求时,create() 中的 2 个发生,尽管内部有检查逻辑。

我认为可能是什么问题?

一个计时问题,因为 2 个以上的前端可以在没有适当检查的情况下同时访问此 create() 方法。代码的原始逻辑在它们不同时触发的过程中起作用。我希望这是有道理的,因为很难用这个词来形容 Java.

编辑:

我最终确定的工作解决方案:

// Within a class...

AtomicBoolean isRequestAlreadyRunning = new AtomicBoolean();

public IoMessage create(IoMessage request) {
  Adventure[] adventures = null;
  AdventureIoMessage createRequest = null;
  StringBuilder message = new StringBuilder();
  
  if (updateStarted.compareAndSet(false, true) {
    try {
      if (request instanceof AdventureIoMessage) {
        createRequest = (AdventureIoMessage) request;
        DatabaseManager databaseManager = Application.getDatabaseManager();
        Adventure adventure = Adventure.read(databaseManager);
        if (adventure == null || !adventure.isActive()) {
          createAdventure(adventures, createRequest, message);
        } else {
          message.append("New adventure already exists and hasn't ended yet!");
        }
      } else {
        message.append("Request formatting is invalid!");
      }
    } finally {
      isRequestAlreadyRunning.set(false);
    }
  }

  return new AdventureIoMessage(adventures, createRequest, message); 
}

问题:在Java中,每个方法都有自己的堆栈,所以在方法内部分配的所有变量都是唯一的。

解决方案:

  • 把你的isRequestAlreadyRunning作为成员变量,而不是方法变量
  • 成为 boolean isRequestAlreadyRunning,而不是 Boolean isRequestAlreadyRunning
  • 使其成为 volatile,如 private volatile boolean isRequestAlreadyRunning

那么您的第一次尝试应该会奏效。

由于同样的原因,您的第二次尝试将失败。

或者,搜索 Java mutex,如“互斥”,您会发现许多 类 和技术专门用于此, 除了 sychronized(myLockObj) 语句。