Vapor:如何协调多个请求,直到完成一个中心请求

Posted

技术标签:

【中文标题】Vapor:如何协调多个请求,直到完成一个中心请求【英文标题】:Vapor: How to coordinate multiple requests until one central one is done 【发布时间】:2019-08-14 17:21:53 【问题描述】:

我有一个中间件,如果在 Redis 中找不到令牌,它会获取令牌。

struct TokenMiddleware: Middleware, TokenAccessor 

  func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> 
    guard let _ = request.http.headers.firstValue(name: HTTPHeaderName("Client-ID")) else 
      throw Abort(.badRequest, reason: "missing 'Client-ID' in header")
    
    guard request.clientID.isEmpty == false else 
      throw Abort(.badRequest, reason: "'Client-ID' in header is empty")
    
    guard let _ = request.http.headers.firstValue(name: HTTPHeaderName("Client-Secret")) else 
      throw Abort(.badRequest, reason: "missing 'Client-Secret' in header")
    
    /// getToken fetches a new Token and stores it in Redis for the controller to use
    return try self.getToken(request: request).flatMap(to: Response.self)  token in
      return try next.respond(to: request)
    

  



extension TokenMiddleware: Service 

但这会导致多个进程自行获取新令牌,从而导致竞争条件。

我该如何处理这个问题?

【问题讨论】:

【参考方案1】:

我现在解决了这个问题,感谢来自http://khanlou.com/2017/09/dispatch-on-the-server/ 的 Soroush,他向我提示了正确的方向。更多关于 DispatchQueues 的信息可以在https://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2的一篇优秀文章中找到

所以:

在服务器上的 ios 和 Vapor 中,我们都可以创建一个DispatchQueue。就我而言,我使用的是并发的,在令牌读取、获取(如果需要)和令牌写入发生的关键部分,我使用了一个屏障。

屏障只允许一个人进入,因此在这一部分中,一切都像串行队列一样执行。

希望这对可能遇到相同问题的任何人有所帮助

import Vapor

protocol TokenAccessor: RedisAccessor 



extension TokenAccessor 

  ///   Main convenience function that handles expiry, refetching etc
  ///
  /// - Check if token was saved before
  /// - We store the token in redis
  /// - We use redis TTL feature to handle token expiry
  ///
  func getToken(request: Request) throws -> Future<Token> 
    print(":getToken(request:)")
    let promise = request.eventLoop.newPromise(Token.self)
    return request.withNewConnection(to: .redis)  redis in
      let concurrentQueue = DispatchQueue(label: "com.queuename.gettoken",
                                          attributes: .concurrent)
      /// Making the concurrent queue serial because only one is allowed to fetch a new token at a time
      concurrentQueue.async(flags: .barrier) 
        let _ = redis.get(request.clientIdLastDigits, as: String.self).map(to: Void.self)  tokenOpt in
          guard let accessToken = tokenOpt else 
            try self.fetchNewToken(forRequest: request).do  newToken in
              print("fetched a new token")
              promise.succeed(result: newToken)
              .catch  error in
                print("failed fetching a new token")
                promise.fail(error: error)
            
            return
          
          print("got a valid token from redis")
          let token = Token(client: request.clientIdLastDigits, token: accessToken, expiresIn: Date())
          // return request.future(token)
          promise.succeed(result: token)
        
      
      return promise.futureResult
    
  
...

这是通过中间件在我的方法前面触发的(所以我不需要考虑)

import Vapor

struct TokenMiddleware: Middleware, TokenAccessor 

  func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> 
    guard let _ = request.http.headers.firstValue(name: HTTPHeaderName("Client-ID")) else 
      throw Abort(.badRequest, reason: "missing 'Client-ID' in header")
    
    guard request.clientID.isEmpty == false else 
      throw Abort(.badRequest, reason: "'Client-ID' in header is empty")
    
    guard let _ = request.http.headers.firstValue(name: HTTPHeaderName("Client-Secret")) else 
      throw Abort(.badRequest, reason: "missing 'Client-Secret' in header")
    
    return try self.getToken(request: request).flatMap(to: Response.self)  token in
      return try next.respond(to: request)
    

  



extension TokenMiddleware: Service 

【讨论】:

以上是关于Vapor:如何协调多个请求,直到完成一个中心请求的主要内容,如果未能解决你的问题,请参考以下文章

同时多个php请求,第二个请求直到第一个完成才完成

Swift Vapor 服务器:如何在获取请求中返回嵌套字典?

Swift 3 如何使用 Vapor 发送多部分发布请求

为啥在 URL 中处理 Vapor GET 请求

如何通过 Vapor 获取远程 IP

如何在一个请求中等待,直到另一个请求完成 nodeJS 中相同功能的执行