• 【译】StackExchange.Redis 中文文档(三)流水线与多路复用


    流水线与多路复用

    延迟很糟糕。 现代计算机以惊人的速度处理数据,高速网络(通常在重要服务器之间具有多个并行链接)提供了巨大的带宽,但是延迟意味着计算机要花费大量时间等待数据,这就是 continuation-based programming 变得越来越流行的几个原因之一。让我们看一下以下代码:

    string a = db.StringGet("a");
    string b = db.StringGet("b");
    

    执行过程如下所示:

    [req1]                         # client: the client library constructs request 1
         [c=>s]                    # network: request one is sent to the server
              [server]             # server: the server processes request 1
                     [s=>c]        # network: response one is sent back to the client
                          [resp1]  # client: the client library parses response 1
                                [req2]
                                     [c=>s]
                                          [server]
                                                 [s=>c]
                                                      [resp2]
    

    在客户端角度,操作过程如下所示:

    [req1]
         [====waiting=====]
                          [resp1]
                                [req2]
                                     [====waiting=====]
                                                      [resp2]
    

    请谨记,这不是按比例缩放的。

    流水线(Pipelining)

    许多 redis 客户端都允许使用流水线。通过流水线发送多个消息,无需等待每个消息的回复,通常消息回复会在后续进行处理。在 .NET 中,可以通过基于 TPLTask / Task<T> APIs 创建未执行的 Task。然后:

    • 可调用 .Wait() 等待 Task 完成。
    • 可调用 .ContinueWith(...) 或者 await 安排 Task 的完成后执行的 Task

    例如,通过流水线获取两个字符串:

    var aPending = db.StringGetAsync("a");
    var bPending = db.StringGetAsync("b");
    var a = db.Wait(aPending);
    var b = db.Wait(bPending);
    

    在这里使用 db.Wait,会自动使用配置里的同步超时。也可以使用 aPending.Wait()Task.WaitAll(aPending,bPending)。流水线可以让我们同时发送多个请求,从而减轻延迟。

    此外,它还有助于减少数据包碎片:单独发送20个请求(等待每个响应)将至少需要20个数据包,但是在流水线中发送的20个请求使用更少的数据包(可能只用一个)。

    Fire and Forget

    流水线的一种特殊情况是我们明确不需要操作结果,不阻塞主程序,由后台排队处理。

    // sliding expiration
    db.KeyExpire(key, TimeSpan.FromMinutes(5), flags: CommandFlags.FireAndForget);
    var value = (string)db.StringGet(key);
    

    FireAndForget 标志让操作不阻塞主程序,并且立即返回默认值(无需理会)。这也适用于 *Async 方法:使用默认值返回一个已经完成的 Task<T>

    多路复用(Multiplexing)

    无论是单连接同步调用还是多连接并发调用,都会产生很多等待时间。而 StackExchange.Redis 通过多路复用把多个连接合并,利用流水线进行传输,有效利用空闲时间。无论是阻塞访问还是异步访问,都将通过流水线传输。

    StackExchange.Redis 唯一没有实现 redis 特性:"blocking pops"(BLPOPBRPOPBRPOPLPUSH)。因为这允许单个调用者停止整个多路复用,从而阻塞 所有其他调用者。

    StackExchange.Redis 唯一需要保留工作的时间是验证事务的前提条件,这就是为什么 StackExchange.Redis 将此类条件封装到内部托管的 Condition 实例中的原因。 查看更多事务(transactions)。 如果想要 "blocking pops",建议使用 pub/sub:

    sub.Subscribe(channel, delegate {
        string work = db.ListRightPop(key);
        if (work != null) Process(work);
    });
    //...
    db.ListLeftPush(key, newWork, flags: CommandFlags.FireAndForget);
    sub.Publish(channel, "");
    

    这样就可以达到相同的目的,而无需阻塞操作。

    • 数据不是通过 pub/sub 发送的;pub/sub API 仅用于通知工作程序检查更多工作。
    • 如果没有工作程序,新工作保留在列表中;工作将不会执行。
    • 只有一个工作程序可以 pop 一个值;当消费者比生产者多时,将通知一些消费者没有工作处理。
    • 当您重新启动工作程序时,应该假设有工作,以便您处理所有积压工作。
    • 除此之外,其语义与 "blocking pops" 相同

    StackExchange.Redis 的多路复用在单个连接上达到极高的吞吐量。

    并发(Concurrency)

    流水线 / 多路复用 / 延迟值在 continuation-based asynchronous code 中也可以很好地发挥作用。 例如:

    string value = await db.StringGetAsync(key);
    if (value == null) {
        value = await ComputeValueFromDatabase(...);
        db.StringSet(key, value, flags: CommandFlags.FireAndForget);
    }
    return value;
    

    原文地址:Pipelines and Multiplexers

  • 相关阅读:
    有效管理时间的十八种方法
    针对某个块下面的按钮注册点击事件
    vs2015运行项目时出现“编译器失败,错误代码为 1”的解决方案
    淘宝API调用 申请 获取session key
    中小型研发团队架构实践:生产环境诊断利器WinDbg帮你快速分析异常情况Dump文件
    中小型研发团队架构实践:如何规范公司所有应用分层?
    中小型研发团队架构实践:电商如何做企业总体架构?
    中小型研发团队架构实践:高效率、低风险,一键发布并测试的持续集成工具Jenkins
    ASP.NET全栈开发验证模块之在Vue中使用前端校验
    计算机基础存储结构
  • 原文地址:https://www.cnblogs.com/liang24/p/13847075.html
Copyright © 2020-2023  润新知