本节摘要:当多条流共享一个出口端口,排队顺序决定谁先走。OpenFlow 的 QoS 模型是:队列挂在端口之下(由队列配置协议管理),流条目用 SET_QUEUE 动作把包映射进队列,调度器按队列属性(最小速率、最大速率)裁决发送顺序。本节讲清这套分工,并把 Meter、队列、DSCP 三个工具拼成完整的 QoS 设计。
阅读完本节,你应当能够:
OpenFlow 标准刻意只管"中间一层":SET_QUEUE 动作把包分进队列。队列本身的创建与属性由配套机制完成(OVS 用其自身的队列配置,硬件交换机各有配置通道),端口属性消息则让控制器能读到队列的最小/最大速率。这样设计的原因是调度器深植于硬件,标准化它的行为不现实。

OVS 上给端口 2 配两个队列(0 号保底 5 Mbps、1 号保底 10 Mbps),再让不同流量走不同队列:
# 建队列(OVS 的 QoS 配置走其自身接口) sudo ovs-vsctl set port s1-eth2 qos=@q -- \ --id=@q create qos type=linux-htb queues=0=@q0,1=@q1 -- \ --id=@q0 create queue other-config:min-rate=5000000 -- \ --id=@q1 create queue other-config:min-rate=10000000 # 流表分流:TCP 走高保障队列 1,其余走队列 0 sudo ovs-ofctl -O OpenFlow13 add-flow s1 \ "tcp,in_port=1,actions=set_queue:1,output:2" sudo ovs-ofctl -O OpenFlow13 add-flow s1 \ "ip,in_port=1,actions=set_queue:0,output:2"
同时从 h1 起一个 TCP 与一个 UDP 大流量打向 h2,拥塞时 UDP 吞吐被压到保底值附近,TCP 拿到它的保底——分层效果即刻可见。队列统计用 Multipart 的 QUEUE 类型拉取,含每队列的收发字节数。
单靠队列或单靠 Meter 都不完整。工程上的惯用拼法:
| 工具 | 管什么 | 典型用法 |
|---|---|---|
| Meter(4.2) | 入向速率 | 超速 DROP 或 DSCP 降级,入口执法 |
| DSCP_REMARK | 打标 | 违规流量降优先级,而非直接丢弃 |
| SET_QUEUE + 队列 | 出向调度 | 高优先业务保底,扫描流量进惩罚队列 |
一个语音网络的例子:入向 Meter 把每路通话限在 100 kbps;语音流匹配 DSCP EF 进保底队列;背景流量被 remark 成尽力而为;端口扫描流量额外进 max_rate 极小的队列。四条流表规则加两个 Meter,全部用已学过的报文机制实现,没有一行设备 CLI。
⚠️ 常见坑:以为 SET_QUEUE 万能——队列数量与调度算法是硬件能力,虚拟环境(Mininet 的 veth)与真实 ASIC 的表现差异很大,方案要在目标硬件上重测。
⚠️ 常见坑:只设 max_rate 不设 min_rate,拥塞时所有队列一起饿死,保底才是重点。
💡 关键直觉:QoS 的本质是"在拥塞发生前定好谁让谁"。队列定相对顺序,Meter 定绝对上限,DSCP 是两者之间传递信号的信使。
正文讲了三层分工,OVS 的队列属性要走它自己的接口(OpenFlow 协议只管 SET_QUEUE 映射,不管属性怎么来):
# 在 s1-eth2 口上建 QoS,含两个队列:保底 8Mbps 与尽力而为 sudo ovs-vsctl set port s1-eth2 qos=@q1 -- \ --id=@q1 create qos type=linux-htb queues=0=@q0,1=@q1 -- \ --id=@q0 create queue other-config:min-rate=8000000 -- \ --id=@q1 create queue other-config:max-rate=2000000 # 流表把 TCP 映射进高保障队列 0,其余进队列 1 sudo ovs-ofctl -O OpenFlow13 add-flow s1 \ "table=0,priority=200,tcp,nw_src=10.0.0.1,actions=set_queue:0,normal" sudo ovs-ofctl -O OpenFlow13 add-flow s1 \ "table=0,priority=100,ip,actions=set_queue:1,normal" # 观察:h1(队列 0)稳在约 8Mbps,h3(队列 1)挤剩余带宽 sudo ovs-ofctl -O OpenFlow13 dump-queues s1 2 # 输出:queue 0: tx_bytes=... tx_packets=... 两队列计数分离增长
这个实验的关键观察点不是绝对速率,而是"拥塞时的分配行为":把两个 iperf 的总需求调到超出端口容量,看保底是否兑现。若 h1 的吞吐被压到保底以下,先查内核 qdisc(linux-htb 需要内核支持)、再查 min-rate 的单位(OVS 用 bit/s)与端口实际速率的匹配。同样值得一看的是队列统计的读取路径——dump-queues 走的是 Multipart 的 QUEUE 统计,这正是 6.2 节监控三口井之一,此处先埋一个入口。
实现层面 min_rate 在很多平台上是按比例分配的权重而非硬性预留:两个队列各配 min-rate 5M,总带宽 10M 时各得 5M;总带宽 8M 时按 5:5 比例各得 4M。也就是说"保底"只在拥塞仲裁里按权重兑现,不等于带宽预留。要硬保证得把保底队列的最大速率也封住(min 加 max 夹紧),避免它空闲时吞掉全部剩余带宽。这些细节在 datasheet 里通常一句"支持 8 队列"带过,真正的语义只有实验和 Multipart 回读能说清——这也是本教程反复强调"信报文不信宣传"的又一处体现。
最后一块拼图:协议之外的扩展与定制,下一节收束本章。