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

标签 swift vapor

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

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 {}

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

我怎样才能在 vapor 中处理这个问题?

最佳答案

我现在解决了这个问题,感谢来自 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。在我的例子中,我使用并发的,在 token 读取、获取(如果需要)和 token 写入发生的关键部分,我使用了一个屏障。

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

希望对遇到同样问题的人有所帮助

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 {}

关于swift - Vapor:如何协调多个请求,直到完成一个中央请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57499340/

相关文章:

rabbitmq - 消息队列/RabbitMQ 的 Vapor Swift 客户端

ios - QueryEndingAtValue 函数在 Swift firebase 中无法正常工作

ios - Swift 2.2 语法错误 : Cannot call value of non-function type 'UITableView!'

json - 解码 Vapor 中的对象数组

swift - 如何处理 Vapor 3.0 中一对多关系的 POST 请求?

Swift Vapor 向自定义响应添加附加信息

swift - 数据库不存在 - 使用 Vapor 3 和 Fluent 的服务器端 Swift 中的 PostgreSQL

swift - Collection View 单元格自动布局每个单元格的不同高度

ios - 从 UITableView 加载 UISplitViewController

swift - 我可以在 SKCamera 中制作无限滚动背景吗?