路由与寻址:从IP数据报到Nginx反向代理,一个Java开发者的网络通关笔记

张开发
2026/4/16 8:59:48 15 分钟阅读

分享文章

路由与寻址:从IP数据报到Nginx反向代理,一个Java开发者的网络通关笔记
简历里写着“熟悉TCP/IP”可线上环境一条错误的路由规则就让整个微服务链路超时——这是很多后端人踩过的坑。今天我们从IP协议、路由表、最长前缀匹配一路聊到Nginx反向代理和Docker网络模式把计网八股变成真正能帮你排查问题的工程思维。写在前面在开发智荟知识库系统和知识汇教育平台的过程中我踩过不少“网络层”的坑用SpringCloud开发微服务时网关路由配置错误导致服务之间调不通用Nginx做反向代理因为不熟悉proxy_pass末尾的斜杠规则静态资源404用Docker部署Python的FastAPI和Java的SpringBoot容器间通信全靠--link和自定义网络。回头再看408里那些枯燥的IP、路由表、ARP忽然觉得它们全都“活”了。所以这篇博客不背概念而是从一个后端开发者的视角重新理解IP协议、路由与寻址并且告诉你它们在Nginx、Docker、微服务网关里到底是怎么体现的。一、IP协议不只是“门牌号”1.1 为什么我们离不开IPIP协议的核心职责就两件事编址封装。编址给每个联网设备一个唯一的IP地址比如你的服务器内网IP172.17.0.2。封装把上层数据TCP/UDP报文塞进IP数据报再扔给链路层。面试时常问IP和MAC地址都唯一为什么不能只用一个我的理解MAC地址像身份证号一辈子不变但无法“寻路”IP地址像你现在的住址可以随网络位置变化并且路由器根据IP前缀就能快速决定往哪个方向扔。快递员不会拿着你的身份证号送货他看的是地址。1.2 IPv4 / IPv6 与子网掩码在实际开发中你配过最多的就是192.168.x.x和10.x.x.x这类私有地址。子网掩码决定了一个IP的“网络部分”和“主机部分”敲黑板同一个子网内的设备可以不经过路由器直接通信通过ARP协议找MAC地址。不同子网之间通信必须经过网关通常是路由器。工程关联Docker容器默认的桥接网络模式会给每个容器分配一个172.17.0.0/16网段的IP容器之间可以直接通信但要从宿主机访问容器端口需要做-p 8080:8080端口映射——这就是一种网络地址转换。你在application.yml里配置的server.address如果不小心填错了网段可能导致服务只在某个虚拟网卡上监听外部死活连不上。1.3 IP数据报里藏着什么一个IP包长这样头部20~60字节后面是数据| 版本 | 首部长度 | 服务类型 | 总长度 | | 标识 | 标志 | 片偏移 | | TTL | 协议 | 首部校验和 | | 源IP地址 | | 目的IP地址 | | 选项如果有 | | 数据TCP/UDP报文等 |对开发有用的两个字段TTL生存时间每经过一个路由器减1变成0就丢弃。ping命令返回的ttl64说明目标大概率是Linux系统默认初始TTL 64如果ttl128则是Windows。协议字段值为6表示上层是TCP17表示UDP。抓包时靠这个区分。一个小实验在Linux上traceroute baidu.com能看到每一跳的路由器IP原理就是利用TTL递增来触发ICMP超时报文。二、路由与寻址路由器是怎么“问路”的2.1 路由表每台设备的决策依据不只是路由器有路由表你的电脑、你的云服务器也都有。查看本机路由表# Linux / macOS ip route show # 示例输出 default via 192.168.1.1 dev eth0 proto static metric 100 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.105 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1解释一下default via 192.168.1.1默认路由所有不在直连网段的目标IP都交给192.168.1.1你的家用路由器/网关。192.168.1.0/24直连路由发往这个网段的包直接通过eth0发出不需要网关。172.17.0.0/16Docker网桥宿主机和容器通信走这条路。开发中的实际场景你在云服务器上配了VPN或者用Docker启动了多个容器网络路由表会动态变化。有一次我的SpringBoot应用无法访问外部的Redis排查半天发现是容器路由被覆盖所有流量被指向了错误的网关。2.2 最长前缀匹配路由器怎么选“最精确”的路假设路由表里有这两条192.168.1.0/24 via 10.0.0.1 192.168.1.128/25 via 10.0.0.2现在要转发目标IP为192.168.1.130的包。两条规则都匹配但第二条的掩码是/25255.255.255.128比第一条/24更精确所以选择10.0.0.2。工程隐喻这就像你在微服务网关里配置路由——/order/**和/order/query/**同时存在时更具体的路径会优先匹配。2.3 路由是如何一步步完成的简化版主机A发出一个IP包目标IP为8.8.8.8。主机A查自己的路由表发现不在任何直连网段于是交给默认网关192.168.1.1。网关路由器收到包查自己的路由表可能包含去往8.0.0.0/8的边界路由转发给下一跳。每一跳重复这个过程直到到达目标主机所在的子网。最后一跳路由器通过ARP找到目标主机的MAC地址把IP包封装成帧发送过去。这就是“路由”的本质逐跳转发每个路由器只知道下一跳不需要知道整条路径。三、这些计网知识在Java后端开发中到底有什么用3.1 Nginx 路由与反向代理你每天都在配置的路由表Nginx 的location指令本质上就是一个基于URI的路由表匹配规则和IP路由的最长前缀匹配如出一辙server { listen 80; # 精确匹配 location /health { return 200 OK; } # 前缀匹配类似 /24 location /api/ { proxy_pass http://backend_java_app:8080/; } # 正则匹配更具体 location ~* ^/api/v2/order { proxy_pass http://order_service:8081/; } }反向代理的“反向”是什么意思传统正向代理是代理客户端比如你翻墙反向代理是代理服务器——客户端不知道真正提供服务的是哪台机器。Nginx收到请求后根据location规则类似于路由表把请求转发到后端的Java应用、Python服务或者静态文件目录。坑点提醒proxy_pass末尾的斜杠很重要。proxy_pass http://backend/会把/api/user代理到backend/api/userproxy_pass http://backend无斜杠会把/api/user代理到backend//api/user多一个斜杠可能导致路径错误这不就是字符串拼接版的“路由前缀匹配”吗3.2 Docker 网络模式IP和路由的容器化演绎Docker默认的bridge网络模式会在宿主机创建一个docker0虚拟网桥每个容器分配一个172.17.x.x的IP。容器间通信靠的是宿主机的路由转发和iptables规则。你可以通过docker network create --subnet10.0.0.0/24 mynet自定义一个网段然后让容器加入这个网络——这就相当于在宿主机内部建了一个小的“局域网”。实战经验在知识汇教育平台项目中我用Docker Compose同时启动了SpringCloud微服务、Elasticsearch、MinIO和RabbitMQ。如果不在同一个自定义网络里服务之间只能通过暴露端口宿主机IP访问非常别扭。把它们都加入同一个backend_net比如172.20.0.0/16就可以直接用容器名作为主机名互相访问——背后的原理就是Docker内置的DNS解析和路由规则。3.3 微服务网关企业级路由和寻址在SpringCloud Gateway中路由配置更加声明式spring: cloud: gateway: routes: - id: order_route uri: lb://order-service predicates: - Path/order/** - id: user_route uri: lb://user-service predicates: - Path/user/**这里的Path匹配规则类似于IP路由中的前缀匹配而lb://负载均衡则是在多个服务实例之间做“下一跳选择”——这不就是一个七层的路由器吗3.4 一个真实的排错故事之前做企业级AI知识库系统Java后端SpringBoot调用Python的FastAPI嵌入服务。一开始直接用的http://localhost:8000一切正常。后来我把两个服务都放进Docker容器Java容器里访问localhost:8000就变成了访问Java容器自己显然不对。排查后发现容器之间的通信必须通过容器IP或容器名并且要在同一个自定义网络里。我执行docker network inspect看了路由表和IP分配最终用docker-compose的links和自定义网络解决。那一刻我突然明白了计网书上讲的“不同子网之间通信需要路由器”在容器世界里就是“不同网络之间的容器无法直接通信需要加入同一个网络或者做端口映射”。四、总结把八股变成肌肉记忆下次你再配置Nginx反向代理、调试Docker容器网络、或者写微服务网关路由的时候不妨想一想“我正在写的就是一张七层的路由表而IP层的那张表正在我的Linux内核里默默地为我工作。” 互动思考题你在 Linux 服务器上用 Docker 部署了两个容器容器 AJava SpringBoot 应用端口 8080容器 BRedis端口 6379两个容器都在默认的bridge网络中IP 分别为172.17.0.2和172.17.0.3。现在容器 A 需要通过redis://172.17.0.3:6379访问容器 B一切正常。问题如果你在宿主机上执行ip route show会看到一条关于172.17.0.0/16的路由。请问这条路由的作用是什么如果手动删除这条路由容器 A 还能 ping 通容器 B 吗为什么欢迎在评论区留下你的答案和排查思路 —— 下一篇我会专门讲讲 Docker 网络模式与 Linux 路由表的关系。

更多文章