首页  /  Tool Reviews

工具测评

一半是协议基础的学习笔记,一半是家里那台小服务器上真在跑的容器。评分只代表我自己用得开不开心。

全部 协议 容器 开源项目

按评分排序  ·  共 8 篇

Protocol   协议基础

啃了几遍的协议

每次面试前都要重新看一遍,索性写下来。

TCP/IP

三次握手与四次挥手,到底在握什么

TCP 是面向连接的,所谓"连接"其实只是双方各自维护的一份状态。握手的过程,就是双方确认彼此的初始序列号和接收窗口。

三次握手

客户端                              服务端
  |--- SYN, seq=x        ----------->|
  |<-- SYN+ACK, seq=y, ack=x+1 ------|
  |--- ACK, ack=y+1      ----------->|
  • 第一次:客户端说"我要连你,我的起始序号是 x";
  • 第二次:服务端说"收到,我的起始序号是 y,你的 x 我确认";
  • 第三次:客户端说"你的 y 我也收到了"。

为什么不是两次?两次握手只能确认客户端到服务端这一条路是通的,服务端无法确认自己的序列号被对端收到了。少了第三次,服务端可能会为一个早已失效的连接请求白白建立连接——这就是 SYN 洪泛的由来之一。

四次挥手

关闭要四次,是因为 TCP 是全双工的,两个方向要分别关:

主动方 --- FIN ---->  被动方      (我不再发了)
主动方 <-- ACK -----  被动方      (知道了)
主动方 <-- FIN -----  被动方      (我也不发了)
主动方 --- ACK ---->  被动方      (好,再见)

主动关闭方发完最后的 ACK 后会进入 TIME_WAIT,等 2MSL(通常 60 秒)。等这么久有两个原因:一是确保最后的 ACK 能到对端,二是让这次连接的残留报文在网络中消散,避免和新连接串台。

线上如果看到大量 TIME_WAIT,通常是短连接太多。可以考虑长连接、连接池,或者调 net.ipv4.tcp_tw_reuse——但改内核参数之前,先确认是不是应用层的连接策略有问题。

HTTP/2

HTTP/2 解决了什么:多路复用与队头阻塞

HTTP/1.1 有个著名的毛病:队头阻塞。一个 TCP 连接上,请求必须排队,前一个响应没回来,后面的就卡着。浏览器为此开了 6~8 个并发连接,但这只是缓解,不是解决。

HTTP/2 的三个关键改动

  • 二进制分帧:不再用纯文本,报文被拆成 HEADERS 帧和 DATA 帧;
  • 多路复用:同一个 TCP 连接上可以并行跑多个流(Stream),每个流有独立 ID,互不阻塞;
  • HPACK 头部压缩:前后两个请求的头部差别很小,用索引表去重,省掉了大量重复传输。

加了这三点之后,一个域名只需要一个连接就够了,也不需要再拼雪碧图、合并 JS 来省请求数——反而是拆得细一点对缓存更友好。

但它没有彻底解决队头阻塞

HTTP/2 解决的是应用层的队头阻塞,传输层还在。因为所有流共用一条 TCP,一旦丢包,TCP 的重传会让所有流一起等。这正是 HTTP/3 改用 QUIC(基于 UDP)的原因。

怎么确认站点开了 HTTP/2

curl -I --http2 https://example.com -s | head -1
# 看到 HTTP/2 200 就说明生效了

注意:HTTP/2 在浏览器里基本只在 HTTPS 下用。所以把证书配好,往往比纠结协议版本更实际。

gRPC

gRPC 和 REST:什么时候该换

gRPC 用 Protobuf 做接口定义和序列化,跑在 HTTP/2 之上。很多人以为它只是"更快的 REST",其实真正的差别在接口契约上。

一个最小的 proto

syntax = "proto3";

service UserService {
  rpc GetUser (GetUserReq) returns (User);
  rpc ListUsers (ListReq) returns (stream User);
}

message GetUserReq { int64 id = 1; }
message User { int64 id = 1; string name = 2; }

有了这个文件,服务端和客户端的代码都可以生成,字段类型、编号、是否可选全都写死。改接口时不再靠口头约定,编号冲突会有工具查出来。

四种调用方式

  • Unary:一问一答,最常见;
  • Server streaming:一次请求,服务端持续推送(如订阅);
  • Client streaming:客户端持续上传(如大文件分片);
  • Bidirectional streaming:双向流,像一条长连接的管道。

什么时候不该用

需要浏览器直接调用的场景就别硬上了——浏览器没法直接发 gRPC(要 gRPC-Web 加一层代理)。对外开放的 Open API 用 REST + JSON 更友好,调试门槛低。gRPC 更适合内部服务之间、调用频繁、对延迟和带宽敏感的场景。

Self-hosted   家庭服务器

NAS / 软路由上在跑的几个容器

都是装上去就忘了、但一直有用的那类。命令可以直接复制。

Docker  ·  管理

Portainer

给容器一个网页界面。看日志、重启、看资源占用,不用再 ssh 进去敲 docker ps。

docker run -d --name portainer \
  -p 9000:9000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest
评分 9.0 9.0

Docker  ·  网关

Nginx Proxy Manager

反向代理加自动证书。把内网十几个服务各自挂一个子域名,证书自动续期,再也不用手动改 nginx.conf。

docker run -d --name npm \
  -p 80:80 -p 443:443 -p 81:81 \
  -v npm_data:/data -v npm_letsencrypt:/etc/letsencrypt \
  jc21/nginx-proxy-manager:latest
评分 9.2 9.2

Docker  ·  文件

copyparty

一个单文件、一条命令就能跑起来的文件服务器。上传下载、在线预览、断点续传都有,是我用过最省心的自建网盘方案。

docker run -d --name copyparty \
  -p 3923:3923 \
  -v /srv/share:/w \
  copyparty/ac:latest
评分 8.8 8.8

Docker  ·  媒体

Jellyfin

自建影音库。刮削海报、转码、多端播放。家里电视、平板都能直接看,不用再开会员。

docker run -d --name jellyfin \
  -p 8096:8096 \
  -v jellyfin_config:/config \
  -v jellyfin_cache:/cache \
  -v /srv/media:/media \
  jellyfin/jellyfin:latest
评分 8.6 8.6

Docker  ·  自动化

Watchtower

定时检查镜像更新并自动重建容器。装完之后就再也不用手动 docker pull 了,代价是偶尔会半夜把服务重启一下。

docker run -d --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --cleanup --schedule "0 0 4 * * *"
评分 7.5 7.5

GitHub  ·  工具

lazydocker

终端里的 Docker 面板。比 Portainer 轻,ssh 到机器上直接敲 lazydocker 就能看状态、进容器、跟日志。

# 安装
curl -sSL https://raw.githubusercontent.com/\
jesseduffield/lazydocker/master/\
scripts/install_update_linux.sh | bash
评分 8.4 8.4

Ops · 建议

自建服务的三条避坑经验

一、数据卷一定要挂宿主机。用匿名卷(-v /data)短期没问题,一旦重建容器就找不回来了。我现在一律写绝对路径,比如 -v /srv/portainer:/data。

二、不要暴露不必要的端口。数据库、Redis 默认只监听容器网络就够了。真要外网访问,走反向代理加一层鉴权,别直接 -p 6379:6379。

三、先想好怎么回滚。升级前记下当前镜像的 digest,出问题能一键退回去。自动更新工具很香,但也要给自己留一条后路。