Groovy中的批处理请求?

Groovy中的批处理请求?,groovy,concurrency,Groovy,Concurrency,我是Groovy新手,对于如何将请求成批提交到服务器(而不是像我目前所做的那样单独提交到服务器)有点迷茫: class Handler { private String jobId // [...] void submit() { // [...] // client is a single instance of Client used by all Handlers jobId = client.add(args)

我是Groovy新手,对于如何将请求成批提交到服务器(而不是像我目前所做的那样单独提交到服务器)有点迷茫:

class Handler {
    private String jobId
    // [...]
    void submit() {
        // [...]
        // client is a single instance of Client used by all Handlers
        jobId = client.add(args)
    }
}

class Client {
    //...
    String add(String args) {
        response = postJson(args)
        return parseIdFromJson(response)
    }
}
现在,有东西调用Client.add,它将发布到RESTAPI并返回解析结果

我遇到的问题是,add方法可能会被快速连续调用数千次,收集传入add的所有arg将更加有效,等待add调用停止,然后为该批处理向restapi发布一次,一次性发送所有arg


这可能吗?add可能会立即返回一个假id,只要批处理发生,提交发生,客户端可以稍后知道假id和来自REST API的id之间的查找,REST API将按照发送给它的参数对应的顺序返回id。

如注释中所述,对于GPAR来说,这可能是一个很好的例子,它在这类场景中非常出色

这实际上不是关于groovy,而是关于java和jvm中的异步编程

如果您想坚持使用java并发习惯用法,我将提供一段代码片段,您可以将其用作潜在的起点。这尚未经过测试,也未考虑边缘情况。我写这篇文章是为了好玩,因为这是异步编程,我还没有花适当的时间考虑它,所以我怀疑里面有足够大的洞可以让坦克通过

也就是说,下面是一些试图批量处理请求的代码:

import java.util.concurrent.* 
import java.util.concurrent.locks.* 

// test code 
def client = new Client()

client.start()
def futureResponses = []
1000.times { 
  futureResponses << client.add(it as String)
}
client.stop()

futureResponses.each { futureResponse ->
  // resolve future...will wait if the batch has not completed yet
  def response = futureResponse.get()
  println "received response with index ${response.responseIndex}"
}
// end of test code 

class FutureResponse extends CompletableFuture<String> {
  String args
}

class Client {
  int minMillisLullToSubmitBatch = 100
  int maxBatchSizeBeforeSubmit   = 100
  int millisBetweenChecks        = 10
  long lastAddTime               = Long.MAX_VALUE

  def batch = []
  def lock = new ReentrantLock()
  boolean running = true

  def start() {
    running = true
    Thread.start { 
      while (running) {
        checkForSubmission()
        sleep millisBetweenChecks
      }
    }
  }

  def stop() {
    running = false
    checkForSubmission()
  }

  def withLock(Closure c) {
    try { 
      lock.lock()
      c.call()
    } finally { 
      lock.unlock()
    }    
  }

  FutureResponse add(String args) {
    def future = new FutureResponse(args: args)

    withLock { 
      batch << future
      lastAddTime = System.currentTimeMillis()
    }

    future
  }

  def checkForSubmission() {
    withLock { 
      if (System.currentTimeMillis() - lastAddTime > minMillisLullToSubmitBatch ||
          batch.size() > maxBatchSizeBeforeSubmit) { 
        submitBatch()
      }
    }
  }

  def submitBatch() {
    // here you would need to put the combined args on a format 
    // suitable for the endpoint you are calling. In this 
    // example we are just creating a list containing the args
    def combinedArgs = batch.collect { it.args }

    // further there needs to be a way to map one specific set of 
    // args in the combined args to a specific response. If the 
    // endpoint responds with the same order as the args we submitted
    // were in, then that can be used otherwise something else like 
    // an id in the response etc would need to be figured out. Here 
    // we just assume responses are returned in the order args were submitted
    List<String> combinedResponses = postJson(combinedArgs)
    combinedResponses.indexed().each { index, response -> 
      // here the FutureResponse gets a value, can be retrieved with 
      // futureResponse.get()
      batch[index].complete(response)
    }

    // clear the batch
    batch = []
  }

  // bogus method to fake post
  def postJson(combinedArgs) {
    println "posting json with batch size: ${combinedArgs.size()}"
    combinedArgs.collect { [responseIndex: it] }
  }
}

它应该可以工作并打印出从我们虚假的json提交中收到的响应

一切都是可能的,但你必须编写它…如果在相当长的时间内没有暂停,等待调用添加暂停可能会导致问题&你必须批处理1000个请求,可能需要根据时间和可配置的限制进行批处理。另外,如果您向呼叫者返回一个假id,那么会发生什么&其余的呼叫失败?猜测您将不得不撤销一堆刚刚标记为已完成的内容。也许可以看看GPAR和多线程技术。谢谢。我最终选择了另一种方式,因为我没有意识到在我的头顶上发生了并发,但如果我需要做类似的事情,这肯定会有助于理解。
~> groovy code.groovy
posting json with batch size: 153
posting json with batch size: 234
posting json with batch size: 243
posting json with batch size: 370
received response with index 0
received response with index 1
received response with index 2
...
received response with index 998
received response with index 999

~>