推文
@realchendahuang · 2026-10-09 21:27
翻开家里无线路由器的背面,底部的贴纸上印着一行数字:192.168.1.1。你在浏览器里输入这串数字,就能打开路由器的管理后台。 在社交平台上发帖,底部会显示你的省份属地;有时候家里连不上网,客服还会让你在手机设置里查查分配到的 IP 是多少。 但如果真要停下来想一想:这些由点号隔开的四段数字,究竟是个什么东西?为什么有时候看到的是 192.168 开头,有时候看到的又是完全不同的数字? 如果把整个互联网看成一座巨大的城市,联网的电脑和服务器就是城市里的一栋栋建筑,IP 地址就是每台设备在网络地图上的法定门牌号。 但在现实中,门牌号分成了三种不同的范围。 第一种是公网 IP,也就是真正的城市法定大门牌号。各大网站的服务器,或者运营商分配给你家光猫对外的大门,都拥有一个在全世界独一无二的公网 IP。不管是北京、纽约还是东京的电脑,顺着这个门牌号,数据包在光纤和路由器里穿梭,就能在茫茫网海里精准定位到这台机器接在哪根网线上。 第二种是局域网 IP,也就是关起门来的内部房间编号。路由器背面的 192.168.1.1,以及手机连上 WiFi 后拿到的 192.168.1.5,都属于局域网 IP。这就像你在自己家里给书房编了个一号房、卧室编了个二号房。隔壁邻居在他自己家里也可以有一号房和二号房,大家关起门来各叫各的,互不干扰。但出了你家的大门,外人拿着 192.168.1.5 这个内部房间号,在公网上根本找不到你。 第三种是本地回环,最常见的代表是 127.0.0.1,在电脑里通常也叫 localhost。这就像是你在日记本上写备忘录,或者一个人对着镜子自言自语。当电脑里的一个软件去访问 127.0.0.1 时,数据包根本不会流出网线,甚至连网卡都不出,在操作系统肚子里转一圈又送回给了自己。很多程序员在自己电脑上开发测试软件时,最喜欢用这个地址。 不管是公网门牌号、局域网房间号,还是本地回环的自言自语,光有 IP 地址,通信其实还是无法完成。 设想一个很现实的生活场景:你给远方的朋友寄快递,包裹顺利送到了对方公司的大楼门口,但收件人姓名那一栏完全是空的。 大楼里有几百号人同时在办公,前台拿着包裹,根本不知道这盒东西到底该交给谁。 电脑里的情况完全一样。一台电脑接入网络后,系统里往往同时跑着几十个不同的软件:浏览器在看网页,微信在收发消息,后台还挂着音乐播放器和下载工具。 远端的数据包顺着 IP 地址敲开网卡的大门,操作系统如果只看门牌号,根本不知道这串数据到底该交给微信还是交给浏览器。 为了解决这批数据该由哪个软件来认领的问题,网络协议在门牌号后面,又加了一个数字编号。这个编号,就是端口。 如果说 IP 地址是一栋大楼的门牌号,端口就是大楼内部每一个独立办公室的房间号。 端口是一个从 0 到 65535 的整数编号。任何一个软件想要在网络里收发数据,启动时都必须向操作系统申请并监听一个属于自己的端口编号。 微信占一个房间号,浏览器占一个房间号,后台的下载软件又占一个房间号。 在网络协议里,IP 地址加上端口号,组合在一起才构成了一个完整的网络通信终点,工程师通常叫它套接字,英文原名叫 Socket。 Socket 在英语里的原意就是电源插座。大楼地址配上办公室房间号,就像在墙上接好了一个专属的插座。两边的软件把插头插上去,一条双向对话的管道才算真正通了电。 理解了 IP 和端口,很多人可能会好奇一个日常现象: 既然每个软件都要靠具体的端口房间号来区分,为什么我们平时在浏览器里打开百度或者各种网站,从来没有手动输入过端口号? 这里面藏着两个大家天天在用、却很少注意到的网络潜规则。 第一个潜规则是域名。比如 http://baidu.com 这种网址,本质上就是网络世界的电话本备注。因为人类的大脑记不住枯燥的长串数字门牌号,所以大家给服务器起了好记的名字。你在浏览器里敲下名字按下回车,网络里的域名解析系统(也就是 DNS)会在千分之一秒内,悄悄把这个名字翻译成背后的真实 IP 数字。 第二个潜规则是端口默认值。全球 Web 标准在几十年前就定下了一条固定规则:只要是普通的网页访问,浏览器默认只找服务器的 80 号端口房间;如果是加密的网页访问,浏览器默认只找 443 号端口房间。 平时你在浏览器地址栏输入一个网址按下回车,浏览器在底层已经悄悄帮你把这两件事全办妥了:先查出 IP 门牌号,再自动补上 80 或者 443 这个房间号。 这就引出了一个很现实的物理冲突: 很多人在云服务商那里买了一台服务器,但一台服务器通常只分配到一个公网 IP 地址,也就是整栋大楼只有一个法定门牌号。 偏偏这栋大楼对外最核心的两个黄金大门,也就是 80 和 443 端口,按照操作系统的规则,同一时间只能租给某一个软件独占使用。 假设你在这台服务器里放了三个不同的服务:一个展示个人文章的博客,一个存照片的云盘,还有一个处理数据的后台接口。 如果博客软件占用了 80 和 443 端口,网盘和接口就只能被分到五位数的高位端口里,比如 8080 或者 9000。 这样一来,朋友想打开你的网盘,就不能只输入网址,必须在网址后面额外带上端口号,比如 http://pan.yourdomain.com:8080。 如果忘了输入后面的 8080,浏览器默认去找 80 端口,打开的就会是博客首页,根本找不到网盘。 为了让同一台机器里的多个软件,都能共用标准大门对外开放,反向代理诞生了。 平常大家听说的代理,通常是替我们个人跑腿去联系外部;而反向代理正好反了过来,它是替内部的一堆服务挡在外网前面。 理解反向代理,最贴切的生活场景就是打大公司的总机客服电话。 一家大公司可能有几百个部门,但在对外宣传时,永远只会留一个统一的总机号码。 客户不管是要办业务、找技术还是咨询售后,全都先拨通这个总机号码。电话接通后,总机系统听你报出具体部门,再帮你自动转接到内部的各个分机号。 反向代理扮演的,就是这个总机接线员的角色。 在服务器的世界里,对外的 80 和 443 黄金大门,全权交给反向代理这一个软件来把守。 你在这台机器上跑的个人博客、云盘和数据接口,全都不对外直接露面,而是安安心心躲在内部不同的端口房间里。 这时候很多人会好奇:反向代理怎么知道访客到底是要找博客还是找网盘? 秘密就藏在网络请求的信封封皮上。 虽然博客和网盘的域名都指向同一个服务器 IP,但当访客的浏览器敲响 80 大门时,递过来的通信数据包封皮上,用纯文本清清楚楚写着一行域名信息,也就是 Host 字段。 反向代理不需要拆开信件看具体内容,它只要低头看一眼信封抬头:写着博客域名的,顺手转交给跑着博客的 8081 房间;写着网盘域名的,顺手转交给跑着网盘的 8082 房间。 内部软件把内容处理完之后,交还给反向代理,再由反向代理统一递给外网访客。 外面的访客只需要认准对外公开的总机大门,根本不需要知道大楼内部到底开了多少个分机端口,网址后面也就不需要再额外挂端口号了。这就是反向代理的核心心智。 在过去十几年里,站在这个总机位置最知名的软件,是 Nginx。 Nginx 是二十年前由俄罗斯工程师用纯 C 语言编写的开源项目。在它诞生之前,主流的服务器软件每来一个访客,就单独派一个服务员去专门等待对接。当同时在线的访问量达到上万级别时,系统的 CPU 和内存光是在不同任务之间来回切换就消耗殆尽,这就是当年著名的 C10K 并发难题。 Nginx 换了一种思路,采用了高效的事件驱动模型。就像一个眼疾手快的服务员,大厅里哪张桌子的客人举手了才过去倒一次水,不用给每一桌都配专职服务员。单台机器用极低的内存就能抗住海量并发,成了互联网基础设施里的中流砥柱。 但在日常维护中,配置 Nginx 经常会遇到让人头疼的细节暗坑。 它的配置语法相对古老,尤其是负责把不同网址转发到不同内部端口的规则。 在配置转发路径时,目标地址末尾多写一个斜杠,或者少写一个斜杠,系统的物理转发逻辑完全是两码事。 末尾带了斜杠,系统会把原本的网址前缀切除掉再替换;末尾没带斜杠,系统会把整串网址原封不动拼在后面转发。 比如访客原本想获取一张头像图片,请求路径是 api/avatar.png。 如果不小心漏掉了一个斜杠,Nginx 会把整串路径连皮带肉拼过去,在后台变成了 api/api/avatar.png。后台程序压根找不到这个重复嵌套的路径,网页上的图片和图标当场全变成裂开的 404 错误。每次修改完配置,工程师都得小心翼翼地在终端里敲 nginx -t 检查语法,生怕写错一个字符导致整个总机瘫痪。 把反向代理的路径理顺之后,第二道关卡随之而来:HTTPS 安全证书。 在早期的互联网时代,网络传输没有任何保护。你在网页上输入的账号密码,在公网链路上就像写在一张透明的明信片上到处传递。沿途经过的路由器甚至网络运营商,都可以随便偷看,甚至在网页里强行加塞弹窗广告。 为了防偷看和防篡改,现代网络全面推行 HTTPS 加密。 它的原理是在通信两端给数据套上加密密码箱,并在建立连接时,出示一张由权威数字证书机构(也就是 CA)签发的电子凭据。 这就像去官方公证处盖了公章的数字身份证。浏览器只信任全球受认可的 CA 机构盖章的证书。如果没有这张合法的证书,现代浏览器会直接弹出一整个鲜红色的不安全警告页面,把访客拦截在外。 在 2015 年之前,商业安全证书的价格普遍偏高。 一张最普通的单域名证书一年要收几百上千块,如果是支持多个子域名的通配符证书,一年往往要收好几千甚至上万块钱。对于只是想挂个小网站自用的个人或者小团队来说,这笔开销并不低。 扭转这个局面的,是由开源基金会和科技巨头联合发起的 Let's Encrypt。 它做成了两件事:第一是把证书费用降到了零,向全球免费签发证书;第二是主导制定了一套全自动的通信协议,也就是 ACME 协议。 既然核心目的是验证申请人是不是真的拥有这个域名,根本不需要人工提交证件和漫长审核。 服务器上的程序可以直接向证书机构发起申请。对方会出一道校验暗号,比如要求在网站特定目录下放一个临时的密文文件。程序把暗号摆好,对方回查确认无误,几秒钟内就能签发出一张合法的安全证书。 但免费证书在普及的同时,也定下了一条硬性规则:为了保障网络安全,免费证书的最长有效期只有 90 天。 这意味着如果依靠人工手动更新,每隔两三个月就得重新折腾一次。 为了实现自动化续期,官方推出了配套的独立客户端 Certbot。 很多开发者以为装上这个办证工具配合 Nginx 就能省下力气,但在实际运行中,这套拼装起来的组合经常在后台静悄悄地出问题。 办证工具是用 Python 编写的独立脚本,而 Nginx 是用 C 语言编写的独立服务器。两套软件互相独立,平时互不相识,全靠系统的定时闹钟硬连在一起。 一个是把守大门的总机门卫,一个是跑外勤去办证的办事员。办证员每隔一阵子要去网上申请续期,证书机构要求在 80 大门临时亮出暗号查验身份。但办证员一伸手,发现 80 大门早就被 Nginx 门卫反锁独占了,两边在门口直接打起架来。 更隐蔽的坑是热重载。 所谓热重载,就是软件在不关机、不中断服务的前提下,在内存里悄悄把新的证书换上。 哪怕办证员把新证书办回来放进了抽屉,门卫不知道,手里举着的依然是三个月前的旧证件。必须在系统里额外写一行通知指令,专门提醒门卫重新看一眼抽屉换上新证件。 只要中间哪一环出了岔子——比如办证脚本环境报错、定时闹钟没响、或者通知指令没有触发——续期就会在后台默默失败。 很多人直到某天早晨点开手机,发现浏览器突然弹出红色的证书过期警告,才发现证书早在一个礼拜前就失效了。 面对 Nginx、办证脚本、定时任务和重启指令的繁琐拼装,用现代语言编写的 Web 服务器 Caddy 给出了另一种思路。 在现代网络架构里,把守总机大门和自动办安全证书,本来就是每个网站最基础的需求,它们根本就不应该拆成两个软件。 Caddy 的做法是把两者合二为一:它既是前台总机,又是随身带着公章和自动化流程的办证专员。 你想把一个网站转发到内部的某个端口,在配置文件 Caddyfile 里不需要研究复杂的路径规则,写清域名和目标端口三行就能跑通: http://blog.yourdomain.com { reverse_proxy localhost:8080 } 更重要的是它内部自带了全套自动化的证书管家。 当它启动时,不仅负责接管大门,还会自己去向证书机构对暗号、领证、配置加密,几秒内一次性跑完,不需要在配置里手动指定任何证书路径。 到了生命周期管理阶段,Caddy 的守护进程常驻在后台,发现证书还有不到一个月到期时,它会自动在后台发起静默续期。新证办下来之后,直接在内存里悄悄换上,不需要外部定时闹钟,不跟自己的 80 端口冲突,也根本不需要任何人手动发重启通知。 当然,任何技术选型都有其物理边界。 Nginx 作为二十年的工业老将,纯 C 语言编写带来了极低的内存占用和底层控制力。在面对单机数十万海量并发、需要极限榨干服务器硬件资源的大型商业网关里,Nginx 依然不可替代。 Caddy 因为采用现代高级语言编写,自带运行时的垃圾回收机制,系统需要定期在后台清理多余内存,因此它的常驻内存占用通常在几十兆到上百兆,比 Nginx 稍微多消耗一点硬件资源。 但对于今天动辄拥有上千兆内存的云服务器来说,多消耗这几十兆内存,其代价连发几张高清手机照片的体积都不到。 在个人项目、中小型团队或者独立出海工具场景下,多花这几十兆内存,换来的是再也不用对着反向代理斜杠反复排错,也再也不用半夜爬起来处理过期的安全证书。 项目地址: https://github.com/caddyserver/caddy
曝光 1173 · 评论 3 · 点赞 3 · 书签 4 · 曝光/时 884.5022228488506