简体   繁体   English

如何在 2 个不同的 API 调用之间共享数据?

[英]How to share data between 2 different API calls?

I am trying to create a framework in which I would receive requests over REST API and would wait for another service (which works over gRPC) to poll and execute the request.我正在尝试创建一个框架,在该框架中我将通过 REST API 接收请求,并等待另一个服务(通过 gRPC 工作)轮询和执行请求。 This is needed cause the "other" service is very deeply embedded into the network and I can't directly call it.这是必需的,因为“其他”服务非常深入地嵌入到网络中,我无法直接调用它。 At the same time, I would like to buffer the output of the other service back to the request origin.同时,我想将其他服务的输出缓冲回请求源。

Any ideas how I can share this data between 2 different asynchronous API requests?有什么想法可以在 2 个不同的异步 API 请求之间共享这些数据吗? Using the file system is a way... but I was thinking can I do it better via channels or something...?使用文件系统是一种方式......但我在想我可以通过渠道或其他方式做得更好......?

Kind of pseudo code below:伪代码如下:

func RestHandler(payload string) (string, error){
    respChan := make(chan string)
    workId := placeWorkInQueue(payload)
    // Start polling in the background
    go pollForResult(respChan, workId)
    // wait for result in the channel
    var result string
    select {
    case result = <-respChan:
        // implement your timeout logic as a another case: here
    }
    return result, nil
}

// This is poller for just the workId given to it.
func pollForResult(respChan chan string, workId string) {
    // Do the polling for workId result 
    /// Write response to respChan when available 
    // You may have to implement a timeout to give up polling.

}

func placeWorkInQueue(s string) string {
    // Place the job in queue and return a unique workId
    return "unique-id"
}

Use Redis queues in both directions.在两个方向上使用 Redis 队列。 The API endpoint writes request and unique id to queue, registers Go channel with unique id as key with central reader in process, and waits on Go channel. API 端点将请求和唯一 id 写入队列,将唯一 id 作为键的 Go 通道注册到正在处理的中央读取器,并在 Go 通道上等待。

Queue reader gets responses with id from Redis queue and sends response to appropriate Go channel.队列读取器从 Redis 队列获取带有 id 的响应,并将响应发送到适当的 Go 通道。

// Response represents the response type from the
// remote service.
type Response struct {
    ID string
    // ... more fields as needed
}

// Request represents request to remote serivce.
type Request struct {
    ID string
    // ... more fields as needed
}

// enqueueRequest writes request to Redis queue.
// Implementation TBD by application.
func enqueueRequest(r *Request) error {
    return nil
}

// dequeueResponse reads a response from a Redis queue.
// Implementation TBD by application.
func dequeueResponse() (*Response, error) {
    return nil, nil
}

Use sync.Map to register waiting Go channels from API request handlers.使用 sync.Map 从 API 请求处理程序注册等待的 Go 通道。 The key is the unique id for the request and the value is a chan *Response .键是请求的唯一 ID,值是chan *Response

  var responseWaiters sync.Map

Run queuePump in a single goroutine to dequeue results from Redis queue and send to appropriate channel.在单个 goroutine 中运行 queuePump 以将结果从 Redis 队列中取出并发送到适当的通道。 Start the gorountine before serving HTTP requests.在处理 HTTP 请求之前启动 goroutine。

func queuePump() {
    for {
        response, err := dequeueResponse()
        if err != nil {
            // handle error
        }
        v, ok := responseWaiters.Load(response.ID)
        if ok {
            c := v.(chan *Response)
            c <- response

            // Remove cahnel from map to ensure that pump never sends 
            // twice to same channel. The pump will black forever if
            // this happens. 
            responseWaiters.Delete(response.ID)
        }
    }
}

The API endpoint allocates a unique id for request, registers a channel with the queue pump, enqueues the request and waits for the response. API 端点为请求分配一个唯一的 id,向队列泵注册一个通道,将请求加入队列并等待响应。

func apiEndpoint(w http.ResponseWriter, r *http.Request) {
    id := generateUniqueID()

    c := make(chan *Response, 1) // capacity 1 ensures that queue pump will not block
    responseWaiters.Store(id, c)
    defer responseWaiters.Delete(id)

    req := &Request{
        ID: id,
        // fill in other fields as needed
    }

    if err := enqueueRequest(req); err != nil {
        // handle error
    }

    select {
    case resp := <-c:
        // process response
        fmt.Println(resp)
    case <-time.After(10 * time.Second):
        // handle timeout error
    }
}

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

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