前言
在服务器开发中,许多高性能服务如 Redis、MySQL、Nginx 默认监听 127.0.0.1 进行本机通信。这种通信方式究竟与跨机通信有何不同?数据是否真的走了物理网卡?
本文将深入剖析 Linux 内核中 Loopback(回环)设备的工作原理,揭示本机网络通信 "左手倒右手" 的精妙设计。
第一章 跨机通信回顾
1.1 完整接收路径概览
在深入本机通信之前,我们需要回顾跨机通信的完整流程:
┌─────────────────────────────────────────────────────────────┐
│ 跨机通信完整路径 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 阶段一:硬件接收 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 网卡通过 DMA 将数据写入 Ring Buffer │ │
│ │ │ │ │
│ │ │ 硬中断 (IRQ) │ │
│ │ ▼ │ │
│ │ igb_msix_ring() → napi_schedule() │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ 阶段二:软中断处理 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ksoftirqd 执行 net_rx_action() │ │
│ │ │ │ │
│ │ ├──► igb_poll() 从 RingBuffer 取包 │ │
│ │ │ │ │
│ │ └──► netif_receive_skb() │ │
│ │ │ │ │
│ │ ├──► ip_rcv() (IP层) │ │
│ │ └──► tcp_v4_rcv() (TCP层) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 数据入队 sk_receive_queue │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ 阶段三:用户态接收 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ recvfrom() 被唤醒 │ │
│ │ │ │ │
│ │ └──► tcp_recvmsg() │ │
│ │ │ │ │
│ │ ├──► 从 Socket 接收队列取数据 │ │
│ │ └──► 拷贝到用户缓冲区 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 跨机通信的核心开销
跨机通信涉及多个昂贵的操作:
┌─────────────────────────────────────────────────────────────┐
│ 跨机通信的性能开销 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 开销类型 │ 原因 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ DMA 内存映射 │ 需要分配一致性内存 │ │
│ │ 硬件中断 (IRQ) │ 网卡通知 CPU 有数据到达 │ │
│ │ 上下文切换 │ 用户态 → 内核态 → 用户态 │ │
│ │ PCIe 总线传输 │ 数据在总线上来回搬运 │ │
│ │ 物理信号传输 │ 电信号/光信号在线路上传播 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
第二章 本机通信的核心问题
2.1 一个经典的误解
当我们访问本机服务时,直觉上会有两种选择:
| 访问方式 | 代码示例 | 直觉判断 |
|---|---|---|
| Loopback 地址 | telnet 127.0.0.1 6379 |
走虚拟设备 |
| 本机局域网 IP | telnet 192.168.1.100 6379 |
可能走物理网卡 |
然而事实是:两者都不走物理网卡,都会走虚拟的 Loopback 设备。
2.2 实验验证
有一个精彩的实验来证明这一点:
┌─────────────────────────────────────────────────────────────┐
│ tcpdump 实验验证 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 实验一:监听物理网卡 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ $ tcpdump -i eth0 │ │
│ │ $ telnet 192.168.1.100 6379 │ │
│ │ │ │
│ │ 结果:没有任何数据包 │ │
│ │ 结论:数据没有经过物理网卡 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 实验二:监听 Loopback 设备 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ $ tcpdump -i lo │ │
│ │ $ telnet 192.168.1.100 6379 │ │
│ │ │ │
│ │ 结果:抓到了完整的 TCP 握手包 │ │
│ │ 结论:数据走了 Loopback 设备 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
第三章 路由表的判决机制
3.1 路由查找的优先级
Linux 内核维护多张路由表,查找顺序如下:
┌─────────────────────────────────────────────────────────────┐
│ 路由表查找顺序 │
├─────────────────────────────────────────────────────────────┤
│ │
│ fib_lookup() 执行顺序: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. RT_TABLE_LOCAL (本地路由表) │ │
│ │ 优先级最高,匹配本机 IP 和 127.0.0.0/8 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 如果匹配 → res.type == RTN_LOCAL │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ dev_out = net->loopback_dev // 强制指定 lo │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2. RT_TABLE_MAIN (主路由表) │ │
│ │ 匹配外部目标,确定下一跳网关 │ │
│ │ 匹配成功 → dev_out = eth0 (物理网卡) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 关键代码解析
// __ip_route_output_key 函数核心逻辑
int ip_route_output_ports(...)
{
// 查找路由
fib_lookup(&fl, &res);
// 关键判决
if (res.type == RTN_LOCAL) {
// 目标为本机,强制使用 Loopback 设备
dev_out = net->loopback_dev;
} else {
// 目标为外部,使用物理网卡
dev_out = fib_dev(&res);
}
}
核心结论 :无论目标是 127.0.0.1 还是本机配置的局域网 IP,只要内核判断目标是本机地址,都会强制使用 loopback_dev。
第四章 Loopback 设备详解
4.1 Loopback 设备的本质
Loopback(lo)是一个纯软件实现的虚拟网卡:
┌─────────────────────────────────────────────────────────────┐
│ Loopback 设备 vs 物理网卡 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 物理网卡 (eth0) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 有真实硬件 (网卡芯片) │ │
│ │ 有 DMA 控制器 │ │
│ │ 有 PCIe 总线连接 │ │
│ │ 有物理中断 (IRQ) │ │
│ │ 需要驱动程序 │ │
│ │ 数据发送到网线 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 虚拟网卡 (lo) │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 纯软件实现,无任何硬件 │ │
│ │ 无 DMA 控制器 │ │
│ │ 无 PCIe 总线 │ │
│ │ 无物理中断 │ │
│ │ 简单的内核函数实现 │ │
│ │ "发送"即"接收" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4.2 MTU 的巨大差异
┌─────────────────────────────────────────────────────────────┐
│ MTU 对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 物理网卡 MTU │ │
│ │ 通常为 1500 字节 │ │
│ │ 受以太网标准限制 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Loopback 设备 MTU │ │
│ │ 通常为 65535 字节 │ │
│ │ 虚拟设备,无物理限制 │ │
│ │ 可以传输巨大的数据包而无需分片 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 影响: │
│ 本机通信极少触发 IP 分片,除非发送超过 64KB 的单次请求 │
│ │
└─────────────────────────────────────────────────────────────┘
第五章 Loopback 发送流程
5.1 物理网卡 vs Loopback 的发送对比
┌─────────────────────────────────────────────────────────────┐
│ 发送流程对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理网卡发送 (igb_xmit_frame): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 获取 TX 描述符 │ │
│ │ 2. DMA 映射 (dma_map_single) │ │
│ │ 3. 填写描述符 │ │
│ │ 4. 写寄存器"踢门铃" │ │
│ │ 5. 网卡通过 DMA 取走数据 │ │
│ │ 6. 网卡发送电信号到网线 │ │
│ │ 复杂度:极高 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Loopback 发送 (loopback_xmit): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. skb_orphan(skb) 剥离 Socket 关联 │ │
│ │ 2. netif_rx(skb) 直接进入接收流程 │ │
│ │ 复杂度:极低 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
5.2 loopback_xmit 核心实现
// Loopback 设备的发送函数
static netdev_tx_t loopback_xmit(struct sk_buff *skb,
struct net_device *dev)
{
// 步骤 1:剥离 Socket 关联
// 节省开销,不再需要维护发送方的 Socket 状态
skb_orphan(skb);
// 步骤 2:直接进入接收流程
// 没有硬件操作,直接调用网络核心的接收函数
netif_rx(skb);
return NETDEV_TX_OK;
}
关键点 :loopback_xmit 并没有真正 "发送" 任何东西,而是直接调用了接收函数。
第六章 软中断触发机制
6.1 netif_rx 的内部处理
当 loopback_xmit 调用 netif_rx() 后,内核执行以下操作:
┌─────────────────────────────────────────────────────────────┐
│ netif_rx 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ netif_rx(struct sk_buff *skb) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ local_bh_disable() │ │
│ │ 禁用本地软中断,防止嵌套处理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 放入 CPU 输入队列 │ │
│ │ softnet_data.input_pkt_queue │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 触发软中断 │ │
│ │ __napi_schedule(&softnet_data.backlog) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ __raise_softirq_irqoff(NET_RX_SOFTIRQ) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
6.2 enqueue_to_backlog 函数解析
static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
unsigned int *qtail)
{
// 获取目标 CPU 的 softnet_data
struct softnet_data *sd = &softnet_data[cpu];
// 将 SKB 加入输入队列
__skb_queue_tail(&sd->input_pkt_queue, skb);
// 触发 NET_RX_SOFTIRQ 软中断
// 通知内核:"有数据包需要处理"
__napi_schedule(&sd->backlog);
return NET_RX_SUCCESS;
}
6.3 软中断处理的特殊性
┌─────────────────────────────────────────────────────────────┐
│ Loopback 的软中断处理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理网卡场景: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 硬中断 → 软中断 → net_rx_action() │ │
│ │ │ │ │ │
│ │ │ ├──► 调用驱动 poll 函数 │ │
│ │ │ │ 从 RingBuffer 取包 │ │
│ │ │ │ │ │
│ │ │ └──► __netif_receive_skb() │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Loopback 场景: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 直接触发软中断 (无硬中断) │ │
│ │ │ │ │
│ │ │ │ │ │
│ │ │ ├──► process_backlog() │ │
│ │ │ │ 从 input_pkt_queue 取包 │ │
│ │ │ │ │ │
│ │ │ └──► __netif_receive_skb() │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
6.4 process_backlog 函数
static int process_backlog(struct napi_struct *napi, int quota)
{
struct softnet_data *sd = container_of(napi, struct softnet_data,
backlog);
int work = 0;
// 循环处理队列中的数据包
while (work < quota) {
struct sk_buff *skb;
// 从输入队列取出数据包
skb = __skb_dequeue(&sd->input_pkt_queue);
if (!skb)
break;
// 送入协议栈处理
// 后续流程与物理网卡完全相同
netif_receive_skb(skb);
work++;
}
return work;
}
第七章 完整数据流对比
7.1 跨机通信完整流程
┌─────────────────────────────────────────────────────────────┐
│ 跨机通信完整路径 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 发送方: │
│ 应用 send() → TCP 层 → IP 层 → 路由查找 → 邻居 ARP │
│ → QDisc → 驱动层 → DMA 映射 → 写网卡寄存器 │
│ │
│ 物理传输: │
│ 网卡 DMA 取数据 → 串行化 → 电信号/光信号 → 发送至网线 │
│ │
│ 接收方: │
│ 硬中断 (IRQ) → 软中断 → 驱动 Poll → IP 层 → TCP 层 │
│ → Socket 接收队列 → 用户 recv() │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 涉及:硬件中断、PCIe 总线、物理传输、上下文切换 │ │
│ │ 延迟:微秒级到毫秒级 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
7.2 本机通信完整流程
┌─────────────────────────────────────────────────────────────┐
│ 本机通信完整路径 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 发送方: │
│ 应用 send() → TCP 层 → IP 层 → 路由查找 (RTN_LOCAL) │
│ → Loopback 设备 → loopback_xmit() │
│ │
│ 软中断触发: │
│ netif_rx() → 入 input_pkt_queue → NET_RX_SOFTIRQ │
│ │
│ 接收方: │
│ 软中断处理 → process_backlog() → IP 层 → TCP 层 │
│ → Socket 接收队列 → 用户 recv() │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 涉及:纯软中断处理,无硬件交互 │ │
│ │ 延迟:亚微秒级 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
7.3 对比总结
┌─────────────────────────────────────────────────────────────┐
│ 跨机通信 vs 本机通信 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 特性 │ 跨机 │ 本机 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 硬件中断 │ ✓ │ ✗ │ │
│ │ DMA 映射 │ ✓ │ ✗ │ │
│ │ PCIe 总线传输 │ ✓ │ ✗ │ │
│ │ 物理信号传输 │ ✓ │ ✗ │ │
│ │ 软中断处理 │ ✓ │ ✓ │ │
│ │ IP 分片 │ 可能发生 │ 极少发生 │ │
│ │ 延迟 │ 高 │ 低 │ │
│ │ 吞吐量 │ 中 │ 高 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
第八章 "左手倒右手" 的本质
8.1 Loopback 的核心设计
┌─────────────────────────────────────────────────────────────┐
│ Loopback "左手倒右手" │
├─────────────────────────────────────────────────────────────┤
│ │
│ 应用层发送 │
│ │ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ 协议栈处理 │ │
│ │ TCP 头、IP 头封装 │ │
│ └───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ 路由判决 │ │
│ │ RT_TABLE_LOCAL → RTN_LOCAL │ │
│ │ dev_out = loopback_dev │ │
│ └───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ loopback_xmit() │ │
│ │ skb_orphan() 剥离 Socket │ │
│ │ netif_rx() 直接进接收 │ │
│ └───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ input_pkt_queue │ ◄── "左口袋" │
│ │ 数据包暂存队列 │ │
│ └───────────────────────────────┘ │
│ │ │
│ │ 触发 NET_RX_SOFTIRQ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ process_backlog() │ │
│ │ 从队列取出数据包 │ │
│ └───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────┐ │
│ │ 协议栈接收 │ │
│ │ IP 层 → TCP 层 → Socket 队列 │ │
│ └───────────────────────────────┘ │
│ │ │
│ ▼ │
│ 应用层接收 │
│ │
└─────────────────────────────────────────────────────────────┘
8.2 skb_orphan 的意义
// 为什么需要 skb_orphan()?
//
// 发送时的 SKB 与发送方 Socket 有关联:
// - skb->sk 指向发送方的 struct sock
// - 需要维护发送状态、序列号等信息
//
// 进入 Loopback 接收流程后:
// - SKB 不再需要发送方的状态
// - skb_orphan() 剥离这个关联
// - skb->sk = NULL
//
// 好处:
// - 节省内存,不再需要维护发送方状态
// - 简化处理,不需要追踪发送上下文
8.3 性能差异的本质原因
┌─────────────────────────────────────────────────────────────┐
│ 本机通信性能高的原因 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 省去了物理网卡的所有开销: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 无 DMA 内存映射 │ │
│ │ 不需要分配一致性内存 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 2. 无硬件中断 │ │
│ │ 不需要 CPU 响应网卡 IRQ │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 3. 无 PCIe 总线传输 │ │
│ │ 数据不经过总线 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 4. 无物理信号传输 │ │
│ │ 没有电信号/光信号的转换和传输 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 5. 无需 ARP 解析 │ │
│ │ 本机通信不需要 MAC 地址解析 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 6. 大 MTU │ │
│ │ 65535 字节,极少分片 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 唯一开销:软中断处理 │
│ │
└─────────────────────────────────────────────────────────────┘
第九章 统一性与差异性
9.1 协议栈的统一性
Linux 网络栈最精妙的设计之一是统一性:无论数据来自物理网卡还是 Loopback 设备,最终都走相同的协议栈代码:
┌─────────────────────────────────────────────────────────────┐
│ 协议栈的统一处理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理网卡接收: │
│ igb_poll() → netif_receive_skb() → ip_rcv() → tcp_v4_rcv()
│ │
│ Loopback 接收: │
│ process_backlog() → netif_receive_skb() → ip_rcv() → tcp_v4_rcv()
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 从 __netif_receive_skb() 开始, │ │
│ │ 两条路径完全相同 │ │
│ │ │ │
│ │ 内核无需关心数据来自哪里 │ │
│ │ 统一走 IP 层 → TCP 层 → Socket │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
9.2 差异仅在入口
┌─────────────────────────────────────────────────────────────┐
│ 入口点的差异 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据来源 │ 入口函数 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 物理网卡 │ 驱动 poll 函数 │ │
│ │ (igb_poll, e1000_poll) │ │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ VLAN 设备 │ vlan_dev_poll │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Bonding 设备 │ bonding_poll │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Bridge 设备 │ br_handle_frame │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Loopback 设备 │ process_backlog │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Tunnel 设备 │ ip_tunnel_rcv │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 共同点:最终都调用 netif_receive_skb() 进入协议栈 │
│ │
└─────────────────────────────────────────────────────────────┘
9.3 路由决定一切
┌─────────────────────────────────────────────────────────────┐
│ 路由是分流的起点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ip_route_output_ports() 决定数据包的出口设备: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 目的地址匹配 RT_TABLE_LOCAL │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ res.type == RTN_LOCAL │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ dev_out = net->loopback_dev ──► Loopback 设备 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 目的地址匹配 RT_TABLE_MAIN │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ fib_dev(res) ──► 物理网卡 (eth0) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
第十章 实际应用场景
10.1 为什么 Redis 默认监听 127.0.0.1
┌─────────────────────────────────────────────────────────────┐
│ Redis 本机通信优化 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 配置: │
│ bind 127.0.0.1 │
│ │
│ 优势: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 极致性能 │ │
│ │ 省去所有物理层开销 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 2. 安全隔离 │ │
│ │ 仅本机进程可访问,外部无法连接 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 3. 简化网络配置 │ │
│ │ 不依赖物理网卡状态 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 注意: │
│ - 同一台机器上的应用服务(如 PHP-FPM、Nginx)访问 Redis │
│ 使用 127.0.0.1 可获得最高性能 │
│ │
└─────────────────────────────────────────────────────────────┘
10.2 本机通信的延迟测量
┌─────────────────────────────────────────────────────────────┐
│ 典型延迟对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 通信类型 │ 典型延迟 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Loopback (127.0.0.1) │ 0.5 - 2 微秒 │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 本机物理网卡 │ 5 - 20 微秒 │ │
│ │ (同一子网) │ │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 跨路由器 │ 1 - 10 毫秒 │ │
│ │ (同城网络) │ │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ 跨地域 │ 20 - 200 毫秒 │ │
│ │ (不同城市) │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 结论:Loopback 比物理网卡快 10-100 倍 │
│ │
└─────────────────────────────────────────────────────────────┘
10.3 连接本机服务的正确方式
┌─────────────────────────────────────────────────────────────┐
│ 应用层访问方式选择 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 推荐做法: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # Redis / MySQL / Memcached │ │
│ │ # 配置监听 127.0.0.1 │ │
│ │ bind 127.0.0.1 │ │
│ │ │ │
│ │ # 客户端连接字符串 │ │
│ │ redis-cli -h 127.0.0.1 -p 6379 │ │
│ │ │ │
│ │ # 或使用 localhost │ │
│ │ redis-cli -h localhost │ │
│ │ # /etc/hosts 中 localhost 解析为 127.0.0.1 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 避免做法: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 不要使用机器的公网/内网 IP │ │
│ │ redis-cli -h 192.168.1.100 -p 6379 │ │
│ │ │ │
│ │ 原因: │ │
│ │ - 虽然最终也走 Loopback,但多了路由判决的开销 │ │
│ │ - 需要确保防火墙允许该端口 │ │
│ │ - 可能有安全风险 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘