0%

全面解析 CI/CD 流水线设计,涵盖 Jenkins、GitLab CI、GitHub Actions 等平台,包含完整配置示例、最佳实践和实战案例

阅读全文 »

精选 20 个实用 Python 自动化脚本,涵盖文件处理、数据分析、网络请求、系统运维、Web scraping 等场景,所有代码可直接运行

阅读全文 »

Nginx 配置最佳实践:从入门到精通

本文全面讲解 Nginx 的配置语法、常用场景、性能优化和安全加固,帮助运维和开发人员构建高性能、高可用的 Web 服务。

一、Nginx 基础架构

1.1 Nginx 工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
graph TB
A[客户端请求] --> B[Master 进程]
B --> C[Worker 进程 1]
B --> D[Worker 进程 2]
B --> E[Worker 进程 N]
C --> F[处理请求]
D --> F
E --> F
F --> G[返回响应]

style B fill:#ff6b6b
style C fill:#4ecdc4
style D fill:#4ecdc4
style E fill:#4ecdc4

核心概念:

  • Master 进程:管理 Worker 进程,处理配置加载、日志打开等
  • Worker 进程:处理实际请求,多进程模型
  • 事件驱动:非阻塞 I/O,高并发性能
  • 连接复用:Keepalive 减少连接开销

1.2 安装 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Ubuntu/Debian
apt update
apt install nginx -y

# CentOS/RHEL
yum install epel-release -y
yum install nginx -y

# 从源码编译
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -zxvf nginx-1.24.0.tar.gz
cd nginx-1.24.0
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_gzip_static_module
make && make install

# 验证安装
nginx -v
nginx -V # 查看详细配置

# 管理 Nginx
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx # 平滑重载
systemctl status nginx

# 测试配置
nginx -t
nginx -T # 显示完整配置

1.3 目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Nginx 目录结构
/etc/nginx/
├── nginx.conf # 主配置文件
├── mime.types # MIME 类型配置
├── conf.d/ # 额外配置文件
│ ├── default.conf
│ └── example.com.conf
├── sites-available/ # 可用站点配置(Debian)
├── sites-enabled/ # 启用站点配置(Debian)
├── modules-available/ # 可用模块
├── modules-enabled/ # 启用模块
├── ssl/ # SSL 证书
├── logs/ # 日志目录
│ ├── access.log
│ └── error.log
└── conf/ # 其他配置
├── fastcgi.conf
├── koi-utf
└── win-utf

# 网站目录
/var/www/
└── html/
└── index.html

# 日志目录(自定义)
/var/log/nginx/
├── access.log
└── error.log

二、配置文件详解

2.1 nginx.conf 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 主配置文件结构
user nginx; # 运行用户
worker_processes auto; # Worker 进程数
worker_rlimit_nofile 65535; # 文件描述符限制

error_log /var/log/nginx/error.log warn; # 错误日志
pid /var/run/nginx.pid; # PID 文件

events {
worker_connections 10240; # 每个 Worker 的最大连接数
use epoll; # 使用 epoll 模型(Linux)
multi_accept on; # 一次接受多个连接
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json
application/javascript application/xml;

# 包含其他配置
include /etc/nginx/conf.d/*.conf;
}

2.2 核心指令详解

worker_processes

1
2
3
4
5
6
7
8
# 设置 Worker 进程数
worker_processes auto; # 自动(推荐,等于 CPU 核数)
worker_processes 4; # 固定数量
worker_processes 1; # 单进程(调试用)

# 绑定 CPU(提高性能)
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;

worker_connections

1
2
3
4
5
6
7
events {
# 每个 Worker 的最大连接数
worker_connections 10240;

# 最大客户端连接数 = worker_processes × worker_connections
# 例如:4 × 10240 = 40960
}

keepalive_timeout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http {
# Keepalive 超时时间
keepalive_timeout 65;

# Keepalive 请求数上限
keepalive_requests 100;

# 与上游服务器的 Keepalive
upstream backend {
server 127.0.0.1:8080;
keepalive 32; # 保持 32 个空闲连接
}

server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}

三、常用场景配置

3.1 静态文件服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
server {
listen 80;
server_name example.com www.example.com;

root /var/www/example.com;
index index.html index.htm;

# 访问日志
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;

# 静态文件
location / {
try_files $uri $uri/ =404;
}

# 图片缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}

# CSS/JS 缓存
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
access_log off;
}

# 字体文件
location ~* \.(woff|woff2|ttf|eot|otf)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}

# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}

# 禁止访问备份文件
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
}

3.2 反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server {
listen 80;
server_name api.example.com;

location / {
# 代理到后端服务
proxy_pass http://127.0.0.1:8080;

# 传递客户端信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

# 缓冲区
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;

# 临时文件
proxy_temp_file_write_size 64k;
}

# WebSocket 支持
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}

3.3 负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 上游服务器组
upstream backend {
# 轮询(默认)
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;

# 权重
# server 192.168.1.10:8080 weight=3;
# server 192.168.1.11:8080 weight=2;
# server 192.168.1.12:8080 weight=1;

# IP Hash(会话保持)
# ip_hash;

# 最少连接
# least_conn;

# 备用服务器
# server 192.168.1.13:8080 backup;

# 故障服务器
# server 192.168.1.14:8080 down;

# 健康检查参数
# server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;

# Keepalive 连接
keepalive 32;
}

server {
listen 80;
server_name app.example.com;

location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

# 超时
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;

# 重试
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}

3.4 HTTPS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
server {
listen 443 ssl http2;
server_name example.com www.example.com;

# SSL 证书
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;

# SSL 优化
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# 现代 SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;

root /var/www/example.com;
index index.html;

location / {
try_files $uri $uri/ =404;
}
}

# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}

3.5 动静分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}

server {
listen 80;
server_name example.com;
root /var/www/example.com;

# 静态文件
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
try_files $uri =404;
}

# 动态请求
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
}

# API 接口
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

四、性能优化

4.1 基础优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
worker_connections 10240;
use epoll;
multi_accept on;
}

http {
# 基础优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;

# Keepalive
keepalive_timeout 65;
keepalive_requests 100;

# 缓冲区
client_body_buffer_size 10M;
client_max_body_size 10M;

# 超时
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;

# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
font/woff
font/woff2
text/css
text/javascript
text/plain
text/xml;
}

4.2 缓存优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 代理缓存配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:100m
max_size=10g inactive=60m use_temp_path=off;

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://backend;

# 启用缓存
proxy_cache my_cache;

# 缓存键
proxy_cache_key $scheme$request_method$host$request_uri;

# 缓存有效期
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;

# 缓存条件
proxy_cache_methods GET HEAD;

# 缓存头
add_header X-Cache-Status $upstream_cache_status;

# 绕过缓存
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_pragma $http_authorization;
}

# 静态文件缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
}

4.3 连接优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
http {
# 打开文件缓存
open_file_cache max=100000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

# 重置超时连接
reset_timedout_connection on;

# 服务器名称哈希
server_names_hash_bucket_size 128;
server_names_hash_max_size 4096;

# 类型哈希
types_hash_max_size 2048;
types_hash_bucket_size 64;

# 限制请求
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
listen 80;
server_name example.com;

# 限制请求频率
location / {
limit_req zone=one burst=20 nodelay;
limit_conn addr 10;

proxy_pass http://backend;
}

# API 限流
location /api/ {
limit_req zone=one burst=5 nodelay;
limit_conn addr 5;

proxy_pass http://backend;
}
}
}

4.4 FastCGI 优化(PHP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.php index.html;

location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

# FastCGI 缓存
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
keys_zone=php_cache:100m max_size=1g
inactive=60m;

fastcgi_cache php_cache;
fastcgi_cache_key $request_method$host$fastcgi_script_name$query_string;
fastcgi_cache_valid 200 30m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;

# FastCGI 参数
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;

# 超时
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;
}
}

五、安全加固

5.1 基础安全配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
server {
listen 80;
server_name example.com;

# 隐藏 Nginx 版本
server_tokens off;

# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

# 禁止访问敏感文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}

location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}

location ~* \.(git|svn|htaccess|htpasswd|ini|log|sh|sql|conf|bak|swp)$ {
deny all;
access_log off;
log_not_found off;
}

# 禁止访问备份文件
location ~* \.(bak|backup|old|orig|save|tmp|temp)$ {
deny all;
access_log off;
log_not_found off;
}

# 限制请求方法
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}

# 限制请求体大小
client_max_body_size 10M;

# 限制请求头大小
large_client_header_buffers 4 16k;
}

5.2 DDoS 防护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
http {
# 限制连接数
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $server_name zone=server:10m;

# 限制请求频率
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;

# 限制请求体
client_body_buffer_size 10M;
client_max_body_size 10M;

# 超时设置
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 15s;
send_timeout 10s;

server {
listen 80;
server_name example.com;

# 应用限制
location / {
limit_conn addr 10;
limit_conn server 1000;
limit_req zone=req burst=20 nodelay;

proxy_pass http://backend;
}

# API 严格限制
location /api/ {
limit_conn addr 5;
limit_req zone=api burst=5 nodelay;

proxy_pass http://backend;
}

# 登录接口更严格
location /login {
limit_req zone=api burst=3 nodelay;

proxy_pass http://backend;
}

# 静态文件宽松限制
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
limit_conn addr 20;
limit_req zone=req burst=50;

expires 30d;
access_log off;
}
}
}

# 自定义错误页面
http {
limit_req_status 429;
limit_conn_status 429;

server {
error_page 429 /429.html;
location = /429.html {
root /var/www/error_pages;
internal;
}
}
}

5.3 SSL/TLS 安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server {
listen 443 ssl http2;
server_name example.com;

# 证书
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;

# 协议
ssl_protocols TLSv1.2 TLSv1.3;

# 加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# 会话
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# OCSP
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# 安全头
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

5.4 访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
server {
listen 80;
server_name example.com;

# IP 白名单
location /admin/ {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;

proxy_pass http://backend;
}

# IP 黑名单
# 在 http 块中
# geo $block_ip {
# default 0;
# 192.168.1.100 1;
# 10.0.0.50 1;
# }

# server {
# if ($block_ip) {
# return 403;
# }
# }

# 基本认证
location /private/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;

proxy_pass http://backend;
}

# 基于 User-Agent 的限制
if ($http_user_agent ~* (curl|wget|scrapy|python-requests)) {
return 403;
}

# 基于 Referer 的限制
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) {
return 403;
}
}
}

# 生成 .htpasswd 文件
# htpasswd -c /etc/nginx/.htpasswd username

六、监控与日志

6.1 日志配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
http {
# 自定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';

log_format json escape=json '{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status": $status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';

# 访问日志
access_log /var/log/nginx/access.log main;
# access_log /var/log/nginx/access.log json;

# 错误日志
error_log /var/log/nginx/error.log warn;

# 按域名分离日志
server {
listen 80;
server_name example.com;

access_log /var/log/nginx/example.com.access.log main;
error_log /var/log/nginx/example.com.error.log warn;
}

# 条件日志
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /var/log/nginx/errors.log main if=$loggable;
}

6.2 日志分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看访问日志
tail -f /var/log/nginx/access.log

# 统计访问最多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 统计访问最多的 URL
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 统计状态码
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 统计 404 错误
grep " 404 " /var/log/nginx/access.log

# 统计慢请求(>1s)
awk '($10 > 1) {print $7, $10}' /var/log/nginx/access.log

# 使用 GoAccess 分析
goaccess /var/log/nginx/access.log -o report.html --log-format=COMBINED

# 使用 awk 分析
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10

6.3 状态监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 启用状态模块
http {
server {
listen 80;
server_name localhost;

# Nginx 状态
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}

# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}

# 查看状态
curl http://localhost/nginx_status

# 输出示例:
# Active connections: 100
# server accepts handled requests
# 1000000 1000000 5000000
# Reading: 10 Writing: 50 Waiting: 40

6.4 Prometheus 监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用 nginx-prometheus-exporter
# 安装 exporter
docker run -p 9113:9113 nginx/nginx-prometheus-exporter:latest \
-nginx.scrape-uri="http://localhost/nginx_status"

# Prometheus 配置
# prometheus.yml
scrape_configs:
- job_name: 'nginx'
static_configs:
- targets: ['localhost:9113']

# Grafana Dashboard
# 导入 Nginx Dashboard (ID: 12708)

七、故障排查

7.1 常见问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 测试配置
nginx -t
nginx -T

# 重载配置
nginx -s reload

# 查看进程
ps aux | grep nginx

# 查看端口
netstat -tlnp | grep nginx
ss -tlnp | grep nginx

# 查看日志
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

# 查看打开的文件
lsof -p $(cat /var/run/nginx.pid)

# 查看系统资源
top -p $(cat /var/run/nginx.pid)

# 测试连接
curl -I http://localhost
curl -v http://localhost

7.2 错误码排查

错误码 含义 常见原因 解决方案
400 错误请求 请求格式错误 检查客户端请求
403 禁止访问 权限不足 检查文件权限、IP 限制
404 未找到 文件不存在 检查路径、root 配置
405 方法不允许 HTTP 方法错误 检查允许的请求方法
413 请求体过大 超过限制 调整 client_max_body_size
414 URI 过长 URL 太长 调整 large_client_header_buffers
499 客户端关闭 客户端超时 优化后端响应时间
500 服务器错误 配置错误 检查 error.log
502 错误网关 后端服务异常 检查后端服务状态
503 服务不可用 后端过载 检查后端负载
504 网关超时 后端响应超时 增加 proxy_read_timeout

7.3 调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 增加日志详细程度
error_log /var/log/nginx/error.log debug;

# 调试特定模块
error_log /var/log/nginx/error.log debug_http;
error_log /var/log/nginx/error.log debug_upstream;

# 返回调试信息
location /debug {
return 200 "
remote_addr: $remote_addr
remote_user: $remote_user
request: $request
uri: $uri
args: $args
request_method: $request_method
content_type: $content_type
content_length: $content_length
host: $host
http_host: $http_host
server_protocol: $server_protocol
";
add_header Content-Type text/plain;
}

八、总结

Nginx 配置的关键要点:

  1. 基础配置:worker_processes、worker_connections、keepalive
  2. 常用场景:静态文件、反向代理、负载均衡、HTTPS
  3. 性能优化:缓存、压缩、连接优化
  4. 安全加固:隐藏版本、安全头、访问控制、DDoS 防护
  5. 监控日志:日志格式、状态监控、故障排查

记住:配置要简洁,优化要适度,安全要优先


参考资料:

微服务架构设计模式:从理论到实践

本文系统讲解微服务架构的核心设计模式,包括服务拆分、通信、数据管理、容错等关键领域,帮助架构师构建可扩展、高可用的分布式系统。

一、微服务架构概述

1.1 什么是微服务?

微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务:

  • 运行在独立的进程中
  • 通过轻量级机制(通常是 HTTP)通信
  • 围绕业务能力构建
  • 可独立部署和扩展
  • 使用不同的技术栈

1.2 单体 vs 微服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
graph TB
subgraph 单体架构
A[Web 层] --> B[业务逻辑层]
B --> C[数据访问层]
C --> D[(数据库)]
end

subgraph 微服务架构
E[API Gateway] --> F[用户服务]
E --> G[订单服务]
E --> H[商品服务]
E --> I[支付服务]
F --> J[(用户 DB)]
G --> K[(订单 DB)]
H --> L[(商品 DB)]
I --> M[(支付 DB)]
end
维度 单体架构 微服务架构
开发效率 高(本地调试) 中(需要服务协调)
部署复杂度 低(一次部署) 高(多次部署)
可扩展性 低(整体扩展) 高(按需扩展)
技术多样性 低(统一技术栈) 高(各服务独立)
故障隔离 低(单点故障) 高(服务隔离)
数据一致性 高(本地事务) 低(分布式事务)

1.3 何时使用微服务?

适合场景:

  • 团队规模大(>10 人)
  • 业务复杂度高
  • 需要快速迭代
  • 不同模块有不同扩展需求
  • 需要技术多样性

不适合场景:

  • 初创项目(快速验证)
  • 团队规模小
  • 业务逻辑简单
  • 对一致性要求极高

二、服务拆分模式

2.1 基于业务能力拆分

按业务领域划分服务边界:

1
2
3
4
5
6
7
graph LR
subgraph 电商系统
A[用户服务] --> B[认证/注册/个人信息]
C[商品服务] --> D[商品管理/库存/分类]
E[订单服务] --> F[下单/支付/物流]
G[营销服务] --> H[优惠券/活动/推荐]
end

拆分原则:

  • 高内聚:相关功能放在一起
  • 低耦合:服务间依赖最小化
  • 单一职责:每个服务做好一件事
  • 自治性:服务可独立运行

2.2 基于子域拆分(DDD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 领域驱动设计示例

// 核心域 - 订单
public class Order {
private OrderId id;
private CustomerId customerId;
private List<OrderItem> items;
private Money totalAmount;
private OrderStatus status;

public void place() {
// 业务逻辑
this.status = OrderStatus.PLACED;
DomainEvents.publish(new OrderPlacedEvent(this));
}
}

// 支撑域 - 库存
public class Inventory {
private ProductId productId;
private int quantity;

public void reserve(int quantity) {
if (this.quantity < quantity) {
throw new InsufficientInventoryException();
}
this.quantity -= quantity;
}
}

// 通用域 - 通知
public class NotificationService {
public void sendEmail(String to, String subject, String body) {
// 发送邮件
}

public void sendSms(String phone, String message) {
// 发送短信
}
}

2.3 拆分陷阱与避免

陷阱 1:分布式单体

1
2
3
4
5
6
7
8
9
10
11
graph TB
A[服务 A] --> B[服务 B]
B --> C[服务 C]
C --> A
A --> D[服务 D]
D --> B

style A fill:#ff6b6b
style B fill:#ff6b6b
style C fill:#ff6b6b
style D fill:#ff6b6b

问题:服务间紧密耦合,一个服务故障导致级联故障。

解决方案:

  • 引入异步通信
  • 添加熔断器
  • 设计容错机制

陷阱 2:过度拆分

1
2
3
4
5
6
7
8
❌ 错误示例:
- UserService(用户基础信息)
- UserProfileService(用户 profile)
- UserPreferenceService(用户偏好)
- UserAuthService(用户认证)

✅ 正确示例:
- UserService(所有用户相关功能)

三、服务通信模式

3.1 同步通信(REST/gRPC)

REST API 设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// RESTful API 示例
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

@Autowired
private OrderService orderService;

// 创建订单
@PostMapping
public ResponseEntity<OrderDTO> createOrder(@RequestBody CreateOrderRequest request) {
Order order = orderService.create(request);
OrderDTO dto = orderMapper.toDTO(order);
return ResponseEntity.created(URI.create("/orders/" + order.getId()))
.body(dto);
}

// 查询订单
@GetMapping("/{orderId}")
public ResponseEntity<OrderDTO> getOrder(@PathVariable Long orderId) {
Order order = orderService.getById(orderId);
return ResponseEntity.ok(orderMapper.toDTO(order));
}

// 取消订单
@PostMapping("/{orderId}/cancel")
public ResponseEntity<Void> cancelOrder(@PathVariable Long orderId) {
orderService.cancel(orderId);
return ResponseEntity.noContent().build();
}
}

gRPC 高性能通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// order.proto
syntax = "proto3";

package order;

service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (OrderResponse);
rpc GetOrder(GetOrderRequest) returns (OrderResponse);
rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse);
}

message CreateOrderRequest {
int64 user_id = 1;
repeated OrderItem items = 2;
string address = 3;
}

message OrderItem {
int64 product_id = 1;
int32 quantity = 2;
}

message OrderResponse {
int64 order_id = 1;
string status = 2;
double total_amount = 3;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// gRPC 服务实现
@GrpcService
public class OrderGrpcService extends OrderServiceGrpc.OrderServiceImplBase {

@Override
public void createOrder(CreateOrderRequest request,
StreamObserver<OrderResponse> responseObserver) {
Order order = orderService.create(request);

OrderResponse response = OrderResponse.newBuilder()
.setOrderId(order.getId())
.setStatus(order.getStatus().name())
.setTotalAmount(order.getTotalAmount().doubleValue())
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();
}
}

REST vs gRPC 对比:

特性 REST gRPC
协议 HTTP/1.1 HTTP/2
数据格式 JSON Protobuf
性能 高(2-10 倍)
浏览器支持 需要代理
生态成熟度
适用场景 对外 API 内部服务通信

3.2 异步通信(消息队列)

事件驱动架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sequenceDiagram
participant O as 订单服务
participant MQ as 消息队列
participant I as 库存服务
participant P as 支付服务
participant N as 通知服务

O->>MQ: 发布 OrderCreated 事件
MQ->>I: 消费事件
MQ->>P: 消费事件
MQ->>N: 消费事件
I->>I: 扣减库存
P->>P: 创建支付
N->>N: 发送通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 事件发布
@Component
public class OrderEventPublisher {

@Autowired
private RabbitTemplate rabbitTemplate;

public void publishOrderCreated(Order order) {
OrderCreatedEvent event = new OrderCreatedEvent();
event.setOrderId(order.getId());
event.setUserId(order.getUserId());
event.setAmount(order.getTotalAmount());
event.setTimestamp(System.currentTimeMillis());

rabbitTemplate.convertAndSend(
"order.events",
"order.created",
event
);
}
}

// 事件消费
@Component
public class InventoryEventListener {

@RabbitListener(queues = "inventory.queue")
public void handleOrderCreated(OrderCreatedEvent event) {
try {
inventoryService.reserveStock(event.getOrderId(), event.getItems());
} catch (Exception e) {
// 失败处理:重试或进入死信队列
throw new AmqpRejectAndDontRequeueException(e);
}
}
}

3.3 API Gateway 模式

1
2
3
4
5
6
7
8
9
graph TB
A[客户端] --> B[API Gateway]
B --> C[认证/鉴权]
B --> D[限流]
B --> E[路由]
C --> F[用户服务]
D --> G[订单服务]
E --> H[商品服务]
B --> I[支付服务]

Spring Cloud Gateway 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- Authentication
- RateLimiter=100

- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- Authentication
- CircuitBreaker

- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**

四、数据管理模式

4.1 数据库按服务拆分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
graph TB
subgraph 用户服务
A[User Service] --> B[(User DB)]
end

subgraph 订单服务
C[Order Service] --> D[(Order DB)]
end

subgraph 商品服务
E[Product Service] --> F[(Product DB)]
end

subgraph 支付服务
G[Payment Service] --> H[(Payment DB)]
end

原则:

  • 每个服务独享数据库
  • 禁止跨库 JOIN
  • 通过 API 或事件访问其他服务数据

4.2 CQRS(命令查询职责分离)

1
2
3
4
5
6
7
graph TB
A[命令端] --> B[Write Model]
B --> C[(写数据库)]
C --> D[事件处理器]
D --> E[Read Model]
E --> F[(读数据库)]
G[查询端] --> F
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 命令端
@Service
public class OrderCommandService {

@Autowired
private OrderRepository orderRepository;

@Transactional
public OrderId createOrder(CreateOrderCommand command) {
Order order = new Order(command.getUserId(), command.getItems());
orderRepository.save(order);

// 发布领域事件
eventPublisher.publish(new OrderCreatedEvent(order));

return order.getId();
}
}

// 查询端
@Service
public class OrderQueryService {

@Autowired
private OrderReadRepository orderReadRepository;

public OrderDTO getOrder(Long orderId) {
OrderReadModel model = orderReadRepository.findById(orderId);
return orderMapper.toDTO(model);
}

public List<OrderDTO> getUserOrders(Long userId) {
List<OrderReadModel> models = orderReadRepository.findByUserId(userId);
return models.stream()
.map(orderMapper::toDTO)
.collect(Collectors.toList());
}
}

4.3 事件溯源(Event Sourcing)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 事件存储
@Entity
public class DomainEvent {
@Id
private Long id;

private String aggregateType;
private String aggregateId;
private String eventType;
private String eventData; // JSON
private Long timestamp;
private Integer version;
}

// 聚合根
public class Order {
private OrderId id;
private List<OrderEvent> events = new ArrayList<>();

public void apply(OrderEvent event) {
events.add(event);
// 根据事件更新状态
if (event instanceof OrderCreatedEvent) {
// 更新订单状态
}
}

public List<OrderEvent> getUncommittedEvents() {
return events;
}

public void clearUncommittedEvents() {
events.clear();
}
}

// 事件存储库
@Repository
public class EventStoreRepository {

public void save(List<DomainEvent> events) {
eventRepository.saveAll(events);
}

public List<DomainEvent> getEvents(String aggregateId) {
return eventRepository.findByAggregateIdOrderByVersion(aggregateId);
}
}

4.4 Saga 分布式事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sequenceDiagram
participant O as 订单服务
participant I as 库存服务
participant P as 支付服务
participant S as Saga 协调器

O->>S: 开始 Saga
S->>I: 预留库存
I-->>S: 成功
S->>P: 创建支付
P-->>S: 失败
S->>I: 补偿:取消预留
I-->>S: 补偿成功
S->>O: Saga 失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Saga 实现
@Service
public class OrderSaga {

@Autowired
private InventoryService inventoryService;

@Autowired
private PaymentService paymentService;

@Autowired
private OrderService orderService;

@Transactional
public Order createOrder(CreateOrderRequest request) {
try {
// 1. 创建订单(待支付状态)
Order order = orderService.createPending(request);

// 2. 预留库存
inventoryService.reserveStock(order.getId(), order.getItems());

// 3. 创建支付
Payment payment = paymentService.createPayment(order);

// 4. 确认订单
orderService.confirm(order.getId(), payment.getId());

return order;

} catch (Exception e) {
// 补偿操作
compensate(request, e);
throw e;
}
}

private void compensate(CreateOrderRequest request, Exception cause) {
// 补偿逻辑
if (cause instanceof PaymentException) {
inventoryService.cancelReservation(request.getOrderId());
}
orderService.cancel(request.getOrderId());
}
}

五、容错与弹性模式

5.1 熔断器(Circuit Breaker)

1
2
3
4
5
6
7
8
9
graph LR
A[CLOSED] -->|失败率>阈值 | B[OPEN]
B -->|休眠时间到 | C[HALF_OPEN]
C -->|成功 | A
C -->|失败 | B

style A fill:#4ecdc4
style B fill:#ff6b6b
style C fill:#ffe66d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Resilience4j 配置
@Configuration
public class ResilienceConfig {

@Bean
public CircuitBreakerConfig circuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值 50%
.waitDurationInOpenState(Duration.ofSeconds(30)) // 打开状态等待时间
.slidingWindowSize(10) // 滑动窗口大小
.minimumNumberOfCalls(5) // 最小调用次数
.build();
}

@Bean
public RetryConfig retryConfig() {
return RetryConfig.custom()
.maxAttempts(3) // 最大重试次数
.waitDuration(Duration.ofSeconds(1)) // 重试间隔
.retryExceptions(SocketTimeoutException.class) // 重试异常类型
.build();
}
}

// 使用熔断器
@Service
public class OrderService {

@Autowired
private CircuitBreakerRegistry circuitBreakerRegistry;

@CircuitBreaker(name = "paymentService", fallbackMethod = "createOrderFallback")
public Order createOrder(CreateOrderRequest request) {
// 调用支付服务
Payment payment = paymentClient.create(request);
return orderRepository.save(new Order(payment));
}

// 降级方法
public Order createOrderFallback(CreateOrderRequest request, Exception e) {
log.warn("支付服务不可用,使用降级逻辑", e);
// 返回缓存数据或默认值
return Order.createPending(request);
}
}

5.2 超时与重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 超时配置
@Configuration
public class HttpClientConfig {

@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 连接超时 5 秒
factory.setReadTimeout(10000); // 读取超时 10 秒
return new RestTemplate(factory);
}
}

// 重试机制
@Service
public class PaymentService {

@Retryable(
value = {ConnectTimeoutException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public Payment createPayment(Order order) {
return paymentClient.create(order);
}

@Recover
public Payment recover(ConnectTimeoutException e, Order order) {
log.error("支付服务重试失败", e);
return Payment.createPending(order);
}
}

5.3 限流(Rate Limiting)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 令牌桶限流
@Service
public class RateLimiterService {

private final RateLimiter rateLimiter = RateLimiter.create(
RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(1))
.limitForPeriod(100) // 每秒 100 个请求
.timeoutDuration(Duration.ofMillis(500))
.build()
);

public void execute(Runnable task) {
Boolean permission = rateLimiter.acquirePermission();
if (permission) {
task.run();
} else {
throw new RateLimitExceededException("请求过于频繁");
}
}
}

// 网关层限流
@Configuration
public class GatewayConfig {

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order-service", r -> r
.path("/api/orders/**")
.filters(f -> f
.requestRateLimiter(config -> {
config.setRateLimiter(redisRateLimiter());
})
)
.uri("lb://order-service")
)
.build();
}

@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(100, 200); // 每秒 100 个请求,令牌桶容量 200
}
}

5.4 舱壁模式(Bulkhead)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 线程池隔离
@Configuration
public class BulkheadConfig {

@Bean
public ThreadPoolBulkheadRegistry bulkheadRegistry() {
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
.maxThreadPoolSize(50)
.coreThreadPoolSize(10)
.queueCapacity(100)
.build();

return ThreadPoolBulkheadRegistry.of(config);
}
}

@Service
public class OrderService {

@Bulkhead(name = "paymentBulkhead", type = Bulkhead.Type.THREADPOOL)
public Order createOrder(CreateOrderRequest request) {
// 使用独立线程池执行
return paymentClient.create(request);
}
}

六、实战案例

案例 1:电商平台微服务架构

系统架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
graph TB
A[客户端] --> B[API Gateway]
B --> C[认证服务]
B --> D[用户服务]
B --> E[商品服务]
B --> F[订单服务]
B --> G[支付服务]
B --> H[物流服务]

C --> I[(用户 DB)]
D --> I
E --> J[(商品 DB)]
F --> K[(订单 DB)]
G --> L[(支付 DB)]
H --> M[(物流 DB)]

subgraph 消息队列
N[RabbitMQ]
end

F --> N
G --> N
H --> N

服务依赖关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 订单服务 - 依赖其他服务
@Service
public class OrderServiceImpl implements OrderService {

@Autowired
private ProductService productService;

@Autowired
private InventoryService inventoryService;

@Autowired
private PaymentService paymentService;

@Autowired
private UserService userService;

@Override
@Transactional
public Order create(CreateOrderRequest request) {
// 1. 验证用户
User user = userService.getById(request.getUserId());

// 2. 验证商品并计算价格
List<Product> products = productService.getByIds(request.getProductIds());
Money total = calculateTotal(products, request.getQuantities());

// 3. 创建订单
Order order = new Order(user, products, total);
orderRepository.save(order);

// 4. 扣减库存(异步)
inventoryService.reserveAsync(order.getId(), request.getItems());

return order;
}
}

案例 2:服务迁移策略

从单体到微服务的演进:

1
2
3
4
5
6
7
8
graph LR
A[单体应用] --> B[模块化单体]
B --> C[垂直拆分]
C --> D[微服务]

A -->|阶段 1| B
B -->|阶段 2| C
C -->|阶段 3| D

迁移步骤:

  1. 识别边界:分析代码库,识别业务边界
  2. 提取模块:将模块拆分为独立项目
  3. 数据拆分:按服务拆分数据库
  4. 服务化:实现服务间通信
  5. 独立部署:建立 CI/CD 流水线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 绞杀者模式 - 逐步替换
@Configuration
public class StranglerConfig {

@Bean
public RouterFunction<ServerResponse> routingFunction(
OldService oldService,
NewService newService) {

return RouterFunctions.route()
// 新功能走新服务
.path("/api/v2", request ->
newService.handle(request))
// 旧功能走旧服务
.path("/api/v1", request ->
oldService.handle(request))
.build();
}
}

七、监控与可观测性

7.1 分布式链路追踪

1
2
3
4
5
6
7
8
9
10
11
# SkyWalking 配置
agent:
service_name: order-service
namespace: production

collector:
backend_service: 192.168.1.100:11800

logging:
level: debug
output: logs/skywalking-agent.log

7.2 指标监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Prometheus 指标
@Component
public class OrderMetrics {

private final Counter orderCounter;
private final Timer orderTimer;
private final Gauge pendingOrdersGauge;

public OrderMetrics(MeterRegistry registry) {
orderCounter = Counter.builder("orders.total")
.description("Total orders created")
.register(registry);

orderTimer = Timer.builder("orders.duration")
.description("Order creation duration")
.register(registry);

pendingOrdersGauge = Gauge.builder("orders.pending")
.description("Pending orders count")
.register(registry, this, OrderMetrics::getPendingCount);
}

public void recordOrderCreation(long durationMs) {
orderCounter.increment();
orderTimer.record(durationMs, TimeUnit.MILLISECONDS);
}
}

7.3 日志聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ELK Stack 配置
filebeat:
inputs:
- type: log
paths:
- /var/log/order-service/*.log
fields:
service: order-service
environment: production

output:
elasticsearch:
hosts: ["http://elasticsearch:9200"]
index: "order-service-%{+yyyy.MM.dd}"

八、总结

微服务架构设计的关键要点:

  1. 合理拆分:基于业务能力或 DDD 子域,避免过度拆分
  2. 通信选择:同步(REST/gRPC)+ 异步(消息队列)
  3. 数据管理:数据库按服务拆分,使用 CQRS/事件溯源
  4. 容错设计:熔断、重试、限流、舱壁
  5. 可观测性:链路追踪、指标监控、日志聚合

微服务不是银弹,需要权衡复杂度和收益。记住:合适的架构才是最好的架构


参考资料:

全面解析 Docker 容器化的最佳实践,涵盖镜像构建优化、Dockerfile 编写、多阶段构建、安全加固、网络存储、编排部署等核心主题,包含 3 个架构图和 2 个实战案例

阅读全文 »

Linux 性能排查指南:从入门到精通

本文系统讲解 Linux 系统性能排查的方法论、工具链和实战技巧,帮助运维和开发人员快速定位和解决性能问题。

一、性能排查方法论

1.1 USE 方法

USE(Utilization, Saturation, Errors) 是 Brendan Gregg 提出的性能分析方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
graph TB
A[性能问题] --> B[资源利用率 Utilization]
A --> C[资源饱和度 Saturation]
A --> D[资源错误 Errors]

B --> B1[CPU 使用率]
B --> B2[内存使用率]
B --> B3[磁盘使用率]
B --> B4[网络带宽]

C --> C1[CPU 队列长度]
C --> C2[内存交换]
C --> C3[磁盘 IO 队列]
C --> C4[网络丢包]

D --> D1[硬件错误]
D --> D2[系统错误]
D --> D3[应用错误]

检查清单:

资源 利用率 饱和度 错误
CPU % 使用率 运行队列长度 硬件错误
内存 % 使用率 Swap 使用 OOM 错误
磁盘 % 容量 IO 等待时间 IO 错误
网络 带宽使用 丢包/重传 连接错误

1.2 性能分析步骤

1
2
3
4
5
6
7
8
9
10
graph LR
A[发现问题] --> B[收集指标]
B --> C[定位瓶颈]
C --> D[分析原因]
D --> E[制定方案]
E --> F[验证效果]

style A fill:#ff6b6b
style C fill:#ffe66d
style F fill:#4ecdc4

标准流程:

  1. 发现问题:监控告警、用户反馈、日志异常
  2. 收集指标:使用工具收集系统指标
  3. 定位瓶颈:分析指标,找出瓶颈资源
  4. 分析原因:深入分析瓶颈原因
  5. 制定方案:设计优化方案
  6. 验证效果:实施后验证效果

1.3 性能指标阈值

指标 正常 警告 严重
CPU 使用率 < 70% 70-85% > 85%
内存使用率 < 80% 80-90% > 90%
Swap 使用率 0% < 50% > 50%
磁盘使用率 < 80% 80-90% > 90%
IO 等待 < 10% 10-30% > 30%
负载平均值 < CPU 核数 1-2 倍 > 2 倍
网络丢包 0% < 0.1% > 0.1%

二、CPU 性能排查

2.1 CPU 指标解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看 CPU 信息
lscpu
cat /proc/cpuinfo

# 查看 CPU 使用率
top -bn1 | grep "Cpu(s)"
mpstat -P ALL 1 5

# 查看负载平均值
uptime
cat /proc/loadavg

# 负载平均值解读
# 1 分钟、5 分钟、15 分钟的平均负载
# 单核 CPU:1.00 表示 100% 利用
# 4 核 CPU:4.00 表示 100% 利用

负载平均值含义:

1
2
3
4
5
6
7
8
9
负载 0.00:系统空闲
负载 0.50:系统利用 50%
负载 1.00:系统满载(单核)
负载 2.00:系统过载(单核),有进程等待
负载 4.00:系统严重过载(单核)

多核系统:负载 / CPU 核数 = 实际负载率
4 核系统负载 4.00 = 100% 利用
4 核系统负载 8.00 = 200% 利用(严重过载)

2.2 CPU 排查工具

top 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 基本使用
top

# 批处理模式
top -bn1

# 显示特定进程
top -p 1234,5678

# 按 CPU 使用率排序
top -o %CPU

# 按内存使用率排序
top -o %MEM

# 刷新间隔 2 秒
top -d 2

# 显示线程
top -H -p 1234

# top 界面快捷键
# P: 按 CPU 排序
# M: 按内存排序
# N: 按 PID 排序
# T: 按时间排序
# k: 杀死进程
# r: 调整优先级
# q: 退出

top 输出解读:

1
2
3
4
5
6
7
8
9
top - 14:30:00 up 10 days,  2:30,  1 user,  load average: 0.50, 0.80, 0.90
Tasks: 200 total, 1 running, 199 sleeping, 0 stopped, 0 zombie
%Cpu(s): 20.0 us, 5.0 sy, 0.0 ni, 75.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 7980.0 total, 2000.0 free, 3000.0 used, 2980.0 buff/cache
MiB Swap: 2048.0 total, 2000.0 free, 48.0 used. 4500.0 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 root 20 0 500000 100000 10000 R 50.0 1.3 10:00.00 java
5678 mysql 20 0 800000 200000 20000 S 20.0 2.5 20:00.00 mysqld

字段说明:

  • us (user): 用户空间 CPU 使用率
  • sy (system): 内核空间 CPU 使用率
  • ni (nice): 低优先级进程 CPU 使用率
  • id (idle): 空闲 CPU 使用率
  • wa (iowait): IO 等待 CPU 使用率
  • hi (hardware interrupts): 硬件中断
  • si (software interrupts): 软件中断
  • st (steal): 虚拟机被偷取的时间

htop 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 安装 htop
yum install htop -y
apt install htop -y

# 基本使用
htop

# 显示特定进程
htop -p 1234

# 按进程名过滤
htop -c java

# htop 快捷键
# F1: 帮助
# F2: 设置
# F3: 搜索
# F4: 过滤
# F5: 树状视图
# F6: 排序
# F9: 杀死进程
# F10: 退出

vmstat 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 每秒采样一次,共 5 次
vmstat 1 5

# 显示进程、内存、IO、CPU 等信息
vmstat -a 1 5

# 显示 slab 信息
vmstat -m 1 5

# 显示磁盘 IO
vmstat -d 1 5

# 显示所有统计
vmstat -a -m -d -s -S M 1 5

vmstat 输出解读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 48000 200000 100000 2980000 0 0 10 20 100 200 20 5 75 0 0

procs:
r: 运行队列中的进程数(> CPU 核数表示过载)
b: 等待 IO 的进程数

memory:
swpd: 使用的虚拟内存(KB)
free: 空闲内存(KB)
buff: 缓冲区内存(KB)
cache: 缓存内存(KB)

swap:
si: 从磁盘换入的内存(KB/s)
so: 换出到磁盘的内存(KB/s)

io:
bi: 从块设备读取的块数/s
bo: 发送到块设备的块数/s

system:
in: 每秒中断数
cs: 每秒上下文切换数

cpu:
us: 用户时间
sy: 系统时间
id: 空闲时间
wa: IO 等待时间
st: 被偷取时间

pidstat 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装 pidstat
yum install sysstat -y
apt install sysstat -y

# 查看所有进程的 CPU 使用率
pidstat -u 1 5

# 查看特定进程
pidstat -p 1234 1 5

# 查看进程的线程
pidstat -t -p 1234 1 5

# 查看进程的上下文切换
pidstat -w 1 5

# 查看进程的所有统计
pidstat -u -r -d -w -h -p 1234 1 5

2.3 CPU 问题诊断

场景 1:CPU 使用率高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 查看整体 CPU 使用率
top -bn1 | grep "Cpu(s)"

# 2. 找出占用 CPU 高的进程
top -o %CPU -bn1 | head -20

# 3. 查看进程的线程
top -H -p 1234

# 4. 查看进程的系统调用
strace -c -p 1234

# 5. 查看进程的调用栈
pstack 1234

# 6. 使用 perf 分析
perf top -p 1234
perf record -p 1234 -g -- sleep 30
perf report

场景 2:负载高但 CPU 使用率低

1
2
3
4
5
6
7
8
9
10
11
# 可能是 IO 等待导致
vmstat 1 5 # 查看 wa 列

# 查看 IO 等待高的进程
iotop -o

# 查看磁盘 IO
iostat -x 1 5

# 查看进程状态
ps aux | awk '$8 ~ /D/ {print}' # D 状态表示不可中断睡眠

场景 3:上下文切换频繁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看上下文切换次数
vmstat 1 5 # 查看 cs 列

# 查看进程的上下文切换
pidstat -w 1 5

# 查看中断
cat /proc/interrupts

# 查看软中断
cat /proc/softirqs

# 解决方案:
# 1. 减少进程数量
# 2. 优化锁竞争
# 3. 调整进程优先级
# 4. 使用 CPU 绑定

2.4 CPU 优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 调整进程优先级
nice -n -10 command # 启动时设置
renice -n -10 -p 1234 # 运行时调整

# CPU 绑定(减少上下文切换)
taskset -c 0,1 command # 绑定到 CPU 0,1
taskset -cp 1234 # 查看进程的 CPU 绑定

# 限制进程 CPU 使用率
cpulimit -p 1234 -l 50 # 限制为 50%

# 查看 CPU 频率
cat /proc/cpuinfo | grep "cpu MHz"
cpufreq-info

# 调整 CPU 调度策略
cpufreq-set -g performance # 性能模式
cpufreq-set -g powersave # 省电模式

三、内存性能排查

3.1 内存指标解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看内存使用情况
free -h
free -m

# 详细内存信息
cat /proc/meminfo

# 查看进程的内存使用
ps aux --sort=-%mem | head -20

# 查看内存映射
pmap -x 1234

# 查看 NUMA 信息
numactl --hardware

free 命令输出解读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
              total        used        free      shared  buff/cache   available
Mem: 7.8G 3.0G 2.0G 100M 2.8G 4.5G
Swap: 2.0G 48M 1.9G

total: 总内存
used: 已使用内存(包括 buff/cache)
free: 完全空闲内存
shared: 共享内存
buff/cache: 缓冲区 + 缓存
available: 可用内存(估算值,包括可回收的 cache)

关键指标:
- 可用内存 = available / total
- 内存使用率 = (total - available) / total
- Swap 使用率 = used / total

3.2 内存排查工具

/proc/meminfo 详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看关键内存指标
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree"

# 重要字段说明
MemTotal: 总内存
MemFree: 完全空闲内存
MemAvailable: 可用内存(包括可回收的 cache)
Buffers: 缓冲区内存
Cached: 页面缓存
SwapCached: Swap 缓存
Active: 活跃内存
Inactive: 非活跃内存
Dirty: 待写入磁盘的内存
Writeback: 正在写入磁盘的内存
AnonPages: 匿名页(进程私有内存)
Mapped: 映射文件内存
Slab: 内核 slab 分配器
SReclaimable: 可回收的 slab
SUnreclaim: 不可回收的 slab
SwapTotal: 总 Swap
SwapFree: 空闲 Swap

slabtop 命令

1
2
3
4
5
6
7
8
9
10
11
# 查看内核 slab 缓存
slabtop

# 按对象数量排序
slabtop -so

# 按内存使用排序
slabtop -sc

# 退出
q

smem 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装 smem
yum install smem -y
apt install smem -y

# 查看进程的内存使用
smem -r

# 按进程名查看
smem -n java

# 查看内存映射
smem -m

# 生成报告
smem -t -k

3.3 内存问题诊断

场景 1:内存使用率高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 查看整体内存使用
free -h

# 2. 找出占用内存高的进程
ps aux --sort=-%mem | head -20

# 3. 查看进程的详细内存
cat /proc/1234/status | grep -E "VmSize|VmRSS|VmData|VmStk"

# 4. 查看内存映射
pmap -x 1234 | tail -20

# 5. 检查是否有内存泄漏
watch -n 1 'ps -p 1234 -o pid,rss,vsz,comm'

# 6. 分析进程的堆内存(Java)
jmap -heap 1234
jmap -histo 1234 | head -50

场景 2:Swap 使用率高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 查看 Swap 使用
free -h
cat /proc/swaps

# 2. 查看 Swap 使用率
vmstat 1 5 # 查看 si/so 列

# 3. 找出使用 Swap 的进程
for file in /proc/*/status ; do
awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file
done | sort -k 2 -n -r | head -20

# 4. 查看 Swap 活动
sar -W 1 5

# 5. 解决方案
# - 增加物理内存
# - 减少进程内存使用
# - 调整 swappiness

场景 3:OOM Killer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 查看 OOM 日志
dmesg | grep -i "out of memory"
dmesg | grep -i "killed process"

# 2. 查看 OOM 记录
cat /var/log/messages | grep -i "oom"
journalctl -k | grep -i "oom"

# 3. 查看进程的 OOM 分数
cat /proc/1234/oom_score
cat /proc/1234/oom_score_adj

# 4. 调整 OOM 分数(保护重要进程)
echo -1000 > /proc/1234/oom_score_adj

# 5. 禁用 OOM Killer(不推荐)
echo 1 > /proc/sys/vm/panic_on_oom

3.4 内存优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 调整 swappiness(默认 60)
cat /proc/sys/vm/swappiness
sysctl -w vm.swappiness=10 # 减少 Swap 使用
echo "vm.swappiness=10" >> /etc/sysctl.conf

# 清理页面缓存(谨慎使用)
sync
echo 1 > /proc/sys/vm/drop_caches # 清理页面缓存
echo 2 > /proc/sys/vm/drop_caches # 清理目录项和 inodes
echo 3 > /proc/sys/vm/drop_caches # 清理所有缓存

# 调整透明大页
cat /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 调整 overcommit
cat /proc/sys/vm/overcommit_memory
# 0: 启发式 overcommit(默认)
# 1: 总是 overcommit
# 2: 基于 overcommit_ratio 限制
echo 2 > /proc/sys/vm/overcommit_memory
echo 50 > /proc/sys/vm/overcommit_ratio # 50%

# 增加 Swap
dd if=/dev/zero of=/swapfile bs=1G count=4
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab

四、磁盘 IO 性能排查

4.1 磁盘 IO 指标解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看磁盘使用情况
df -h
df -i # 查看 inodes

# 查看磁盘 IO 统计
iostat -x 1 5

# 查看磁盘 IO 详情
iostat -d -x -m 1 5

# 查看进程的 IO
pidstat -d 1 5

# 查看实时 IO
iotop

iostat 输出解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
20.00 0.00 5.00 10.00 0.00 65.00

Device: r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await r_await w_await
sda 50.00 30.00 2000.00 1500.00 5.00 10.00 80.00 10.00 8.00 12.00

avg-cpu:
%iowait: IO 等待时间百分比(> 30% 表示 IO 瓶颈)

Device:
r/s: 每秒读请求数
w/s: 每秒写请求数
rkB/s: 每秒读取的 KB 数
wkB/s: 每秒写入的 KB 数
rrqm/s: 每秒合并的读请求数
wrqm/s: 每秒合并的写请求数
%util: 设备利用率(> 80% 表示饱和)
await: IO 请求平均等待时间(ms)
r_await: 读请求平均等待时间
w_await: 写请求平均等待时间

4.2 磁盘 IO 排查工具

iotop 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 安装 iotop
yum install iotop -y
apt install iotop -y

# 查看实时 IO(需要 root)
iotop

# 只显示有 IO 的进程
iotop -o

# 批处理模式
iotop -b -n 10

# 查看累计 IO
iotop -a

# 查看特定进程的 IO
iotop -p 1234

# iotop 快捷键
# left/right: 排序
# a: 累计 IO
# o: 只显示有 IO 的进程
# q: 退出

pidstat IO 统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看进程的 IO
pidstat -d 1 5

# 查看特定进程
pidstat -d -p 1234 1 5

# 输出解读
Linux 5.4.0 (hostname) (03/12/2026) _x86_64_ (4 CPU)

02:30:00 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
02:30:01 PM 0 1234 100.00 50.00 0.00 10 java

kB_rd/s: 每秒读取的 KB 数
kB_wr/s: 每秒写入的 KB 数
kB_ccwr/s: 取消的写入 KB 数
iodelay: IO 延迟(块)

ioping 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装 ioping
yum install ioping -y
apt install ioping -y

# 测试磁盘延迟
ioping -c 10 /dev/sda

# 测试文件系统延迟
ioping -c 10 /tmp

# 测试顺序读延迟
ioping -c 10 -L /tmp

# 测试随机读延迟
ioping -c 10 -R /tmp

4.3 磁盘 IO 问题诊断

场景 1:IO 等待高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 查看 IO 等待
vmstat 1 5 # 查看 wa 列

# 2. 查看磁盘利用率
iostat -x 1 5 # 查看 %util 列

# 3. 找出 IO 高的进程
iotop -o

# 4. 查看进程的 IO
pidstat -d 1 5

# 5. 查看磁盘队列
iostat -x 1 5 # 查看 avgqu-sz 列

# 6. 查看 IO 调度算法
cat /sys/block/sda/queue/scheduler

# 7. 解决方案
# - 优化应用 IO 模式
# - 使用 SSD
# - 调整 IO 调度算法
# - 增加磁盘数量(RAID)

场景 2:磁盘空间不足

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 查看磁盘使用
df -h

# 2. 查找大文件
find / -type f -size +1G -exec ls -lh {} \; 2>/dev/null

# 3. 查找大目录
du -sh /* 2>/dev/null | sort -hr | head -20

# 4. 查找最近修改的大文件
find / -type f -mtime -7 -size +100M -exec ls -lh {} \; 2>/dev/null

# 5. 查看已删除但未释放的文件
lsof | grep deleted

# 6. 清理日志
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;

# 7. 清理包管理器缓存
yum clean all # CentOS
apt clean # Ubuntu

场景 3:Inodes 耗尽

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 查看 Inodes 使用
df -i

# 2. 查找文件数量多的目录
for i in /*; do echo $i; find $i | wc -l; done | sort -k2 -n -r | head -20

# 3. 查找小文件
find / -type f -size -1k 2>/dev/null | wc -l

# 4. 解决方案
# - 删除不必要的小文件
# - 合并小文件
# - 增加 Inodes(重新格式化)

4.4 磁盘 IO 优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 调整 IO 调度算法
echo deadline > /sys/block/sda/queue/scheduler # 机械盘
echo none > /sys/block/nvme0n1/queue/scheduler # SSD

# 查看当前调度算法
cat /sys/block/sda/queue/scheduler

# 调整预读大小
blockdev --getra /dev/sda # 查看
blockdev --setra 4096 /dev/sda # 设置

# 调整队列深度
echo 128 > /sys/block/sda/queue/nr_requests

# 使用 noatime 挂载(减少元数据更新)
# /etc/fstab
/dev/sda1 / ext4 defaults,noatime 0 1

# 使用 writeback 模式(性能优先)
tune2fs -o journal_data_writeback /dev/sda1

# 使用 deadline 调度器(推荐)
echo "deadline" > /sys/block/sda/queue/scheduler

五、网络性能排查

5.1 网络指标解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看网络接口
ip addr
ifconfig -a

# 查看网络统计
cat /proc/net/dev

# 查看网络连接
netstat -an
ss -an

# 查看路由表
ip route
netstat -rn

# 查看 DNS 配置
cat /etc/resolv.conf

/proc/net/dev 解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat /proc/net/dev

Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 1000000 10000 0 0 0 0 0 0 500000 5000 0 0 0 0 0 0

关键指标:
- bytes: 字节数
- packets: 数据包数
- errs: 错误数(应为 0)
- drop: 丢弃数(应接近 0)
- fifo: FIFO 缓冲区错误
- frame: 帧错误
- colls: 冲突数(半双工)
- carrier: 载波错误

5.2 网络排查工具

ss 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查看所有连接
ss -an

# 查看 TCP 连接
ss -tan

# 查看监听端口
ss -tlnp

# 查看 UDP 端口
ss -ulnp

# 查看进程信息
ss -tanp

# 查看连接统计
ss -s

# 查看内存使用
ss -tm

# 过滤端口
ss -tan | grep :80

# 过滤状态
ss -tan state established
ss -tan state time-wait

netstat 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看所有连接
netstat -an

# 查看 TCP 连接
netstat -ant

# 查看监听端口
netstat -tlnp

# 查看 UDP 端口
netstat -ulnp

# 查看网络统计
netstat -s

# 查看路由表
netstat -rn

# 查看接口统计
netstat -i

# 查看连接数统计
netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

tcpdump 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 抓包(所有接口)
tcpdump -i any

# 抓包(指定接口)
tcpdump -i eth0

# 抓包(指定端口)
tcpdump -i eth0 port 80

# 抓包(指定主机)
tcpdump -i eth0 host 192.168.1.1

# 抓包(指定协议)
tcpdump -i eth0 tcp

# 保存抓包数据
tcpdump -i eth0 -w capture.pcap

# 读取抓包文件
tcpdump -r capture.pcap

# 详细输出
tcpdump -i eth0 -v
tcpdump -i eth0 -vv
tcpdump -i eth0 -vvv

# 显示内容
tcpdump -i eth0 -X # 十六进制 + ASCII
tcpdump -i eth0 -XX # 十六进制 + ASCII(不含链路层)

ping 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 基本 ping
ping www.google.com

# 指定次数
ping -c 4 www.google.com

# 指定间隔
ping -i 0.5 www.google.com

# 指定包大小
ping -s 1000 www.google.com

# 记录路由
ping -R www.google.com

# 时间戳
ping -D www.google.com

# flood ping(需要 root)
ping -f www.google.com

traceroute 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 追踪路由
traceroute www.google.com

# 使用 ICMP
traceroute -I www.google.com

# 使用 TCP
traceroute -T www.google.com

# 指定端口
traceroute -T -p 80 www.google.com

# 不解析域名
traceroute -n www.google.com

# 最大跳数
traceroute -m 30 www.google.com

# 替代工具:mtr
mtr www.google.com
mtr -r -c 100 www.google.com # 报告模式

5.3 网络问题诊断

场景 1:网络连接失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1. 检查网络接口
ip addr show
ip link show

# 2. 检查路由
ip route show
ip route get 8.8.8.8

# 3. 检查 DNS
cat /etc/resolv.conf
nslookup www.google.com
dig www.google.com

# 4. 检查防火墙
iptables -L -n
firewall-cmd --list-all

# 5. 检查端口监听
ss -tlnp | grep :80

# 6. 检查连接
telnet 192.168.1.1 80
nc -zv 192.168.1.1 80

# 7. 抓包分析
tcpdump -i eth0 port 80 -n

场景 2:网络延迟高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 测试延迟
ping -c 100 www.google.com

# 2. 追踪路由
traceroute www.google.com
mtr www.google.com

# 3. 查看网络错误
netstat -i
cat /proc/net/dev

# 4. 查看 TCP 重传
netstat -s | grep -i retrans
ss -tm

# 5. 抓包分析
tcpdump -i eth0 -w latency.pcap
wireshark latency.pcap

# 6. 解决方案
# - 优化路由
# - 使用 CDN
# - 调整 TCP 参数

场景 3:连接数过多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 查看连接数统计
netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

# 2. 查看 TIME_WAIT 连接
netstat -an | grep TIME_WAIT | wc -l

# 3. 查看进程的 connection
lsof -n | grep TCP | awk '{print $1}' | sort | uniq -c | sort -rn

# 4. 查看端口使用
ss -tan | awk '{print $5}' | cut -d: -f2 | sort | uniq -c | sort -rn

# 5. 解决方案
# - 调整 TIME_WAIT 超时
# - 启用端口复用
# - 增加文件描述符限制

5.4 网络优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 调整 TCP 参数
cat >> /etc/sysctl.conf << EOF
# 启用 SYN Cookie
net.ipv4.tcp_syncookies = 1

# 减少 TIME_WAIT 超时
net.ipv4.tcp_fin_timeout = 30

# 启用端口复用
net.ipv4.tcp_tw_reuse = 1

# 增加本地端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 增加连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# 调整缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 启用 TCP BBR 拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF

sysctl -p

# 增加文件描述符限制
cat >> /etc/security/limits.conf << EOF
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
EOF

# 查看当前限制
ulimit -n

# 启用 BBR
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p

# 验证 BBR
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control

六、综合排查案例

案例 1:Web 服务器响应慢

问题现象: 用户反馈网站访问慢,页面加载时间长。

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1. 检查系统负载
uptime
# 输出:load average: 8.50, 7.20, 6.80(4 核 CPU,负载过高)

# 2. 查看 CPU 使用率
top -bn1 | grep "Cpu(s)"
# 输出:%Cpu(s): 85.0 us, 10.0 sy, 5.0 id(用户空间占用高)

# 3. 找出占用 CPU 的进程
top -o %CPU -bn1 | head -10
# 发现:java 进程占用 300% CPU

# 4. 查看 Java 进程详情
ps aux | grep java
jps -l

# 5. 查看 Java 线程
top -H -p <java_pid>
# 发现:多个线程占用高

# 6. 生成线程 dump
jstack <java_pid> > thread_dump.txt

# 7. 分析线程 dump
# 发现:大量线程处于 RUNNABLE 状态,执行复杂计算

# 8. 查看 GC 情况
jstat -gc <java_pid> 1000 10

# 9. 解决方案
# - 优化代码算法
# - 增加缓存
# - 水平扩展(增加服务器)

案例 2:数据库查询慢

问题现象: 数据库查询响应时间长,应用超时。

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 1. 检查系统资源
vmstat 1 10
# 发现:wa(IO 等待)高达 50%

# 2. 查看磁盘 IO
iostat -x 1 10
# 发现:sda 的 %util 达到 100%,await 达到 100ms

# 3. 找出 IO 高的进程
iotop -o
# 发现:mysqld 进程 IO 高

# 4. 查看 MySQL 慢查询
mysql -e "SHOW PROCESSLIST"
mysql -e "SHOW VARIABLES LIKE 'slow_query_log%'"

# 5. 查看慢查询日志
tail -f /var/log/mysql/slow.log

# 6. 分析慢查询
mysqldumpslow /var/log/mysql/slow.log

# 7. 查看表锁
mysql -e "SHOW OPEN TABLES WHERE In_use > 0"

# 8. 解决方案
# - 优化慢查询 SQL
# - 添加索引
# - 使用 SSD
# - 读写分离

案例 3:内存泄漏

问题现象: 服务器运行一段时间后内存耗尽,服务崩溃。

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. 监控内存使用
watch -n 1 'free -h'

# 2. 查看进程内存
ps aux --sort=-%mem | head -20

# 3. 监控特定进程
watch -n 1 'ps -p <pid> -o pid,rss,vsz,comm'

# 4. 查看内存映射
pmap -x <pid>

# 5. Java 应用:查看堆内存
jmap -heap <pid>
jmap -histo:live <pid> | head -50

# 6. Java 应用:生成 heap dump
jmap -dump:format=b,file=heap.hprof <pid>

# 7. 分析 heap dump
# 使用 MAT (Memory Analyzer Tool) 分析

# 8. 查看 OOM 日志
dmesg | grep -i "out of memory"

# 9. 解决方案
# - 修复内存泄漏代码
# - 增加堆内存限制
# - 定期重启服务

七、监控工具推荐

7.1 系统监控

工具 用途 特点
top/htop 实时监控 简单直观
vmstat 系统统计 轻量级
sar 历史统计 数据持久化
dstat 综合监控 功能全面
glances Web 界面 易于使用

7.2 应用监控

工具 用途 特点
Prometheus 指标监控 云原生标准
Grafana 可视化 丰富的图表
ELK Stack 日志分析 强大的搜索
SkyWalking 链路追踪 APM 功能
Zabbix 企业监控 功能全面

7.3 快速部署监控栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml

grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana

node-exporter:
image: prom/node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($$|/)"'

volumes:
grafana_data:

八、总结

Linux 性能排查的关键要点:

  1. 方法论:USE 方法,从利用率、饱和度、错误三个维度分析
  2. 工具链:top/vmstat/iostat/ss 等工具的组合使用
  3. 指标解读:理解每个指标的含义和阈值
  4. 优化方案:根据瓶颈制定针对性的优化方案

记住:监控是基础,排查是技能,优化是目标


参考资料:

Git 常用命令速查:从入门到精通

本文整理了 Git 开发中最常用的命令和技巧,涵盖基础操作、分支管理、远程协作、问题排查等场景,是开发者的速查手册。

一、Git 基础配置

1.1 用户信息配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 全局配置(所有仓库)
git config --global user.name "John Doe"
git config --global user.email "john@example.com"

# 查看配置
git config --list
git config user.name
git config user.email

# 编辑配置文件
git config --global --edit

# 常用别名配置
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"

1.2 SSH 密钥配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 生成 SSH 密钥
ssh-keygen -t ed25519 -C "your_email@example.com"

# 查看公钥
cat ~/.ssh/id_ed25519.pub

# 添加到 SSH agent
ssh-add ~/.ssh/id_ed25519

# 测试连接 GitHub
ssh -T git@github.com

# 测试连接 GitLab
ssh -T git@gitlab.com

1.3 常用 Git 配置优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 自动修正命令
git config --global help.autocorrect 1

# 默认使用 rebase
git config --global pull.rebase true

# 显示所有分支的图形化日志
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

# 忽略文件权限变化
git config --global core.filemode false

# 使用 VSCode 作为默认编辑器
git config --global core.editor "code --wait"

# 使用 Beyond Compare 作为 difftool
git config --global diff.tool bc3
git config --global difftool.bc3.trustexitcode true

# 使用 Beyond Compare 作为 mergetool
git config --global merge.tool bc3
git config --global mergetool.bc3.trustexitcode true

二、仓库操作

2.1 创建仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 初始化新仓库
git init
git init my-project

# 克隆远程仓库
git clone https://github.com/user/repo.git
git clone git@github.com:user/repo.git

# 克隆指定分支
git clone -b develop https://github.com/user/repo.git

# 克隆深度(只克隆最近 N 次提交)
git clone --depth 1 https://github.com/user/repo.git

# 克隆子模块
git clone --recursive https://github.com/user/repo.git

2.2 查看状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看仓库状态
git status
git status -s # 简短格式

# 查看提交历史
git log
git log --oneline
git log --graph --oneline --all
git log -n 5 # 最近 5 条

# 查看某文件的修改历史
git log --follow path/to/file

# 查看某人的提交
git log --author="John"

# 查看时间范围内的提交
git log --since="2 weeks ago"
git log --until="2024-01-01"

# 查看统计信息
git log --stat
git log --shortstat
git log --numstat

# 查看某次提交的详细内容
git show commit_id
git show --stat commit_id

2.3 差异比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 工作区 vs 暂存区
git diff

# 暂存区 vs 最新提交
git diff --cached
git diff --staged

# 工作区 vs 最新提交
git diff HEAD

# 两个提交之间的差异
git diff commit1 commit2
git diff commit1..commit2

# 两个分支之间的差异
git diff branch1 branch2

# 只比较某个文件
git diff path/to/file

# 忽略空白字符
git diff -w

# 生成补丁文件
git diff > patch.diff
git diff commit1 commit2 > patch.diff

三、文件操作

3.1 添加文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加指定文件
git add file.txt
git add dir/file.txt

# 添加所有文件
git add .
git add -A

# 添加所有修改的文件(不包括新文件)
git add -u

# 交互式添加
git add -p
git add -i

# 查看暂存区内容
git diff --cached

3.2 忽略文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 创建 .gitignore 文件
cat > .gitignore << EOF
# 编译产物
*.class
*.o
*.so
build/
dist/
target/

# 依赖目录
node_modules/
vendor/
__pycache__/

# 配置文件
.env
*.local
config.local.js

# IDE 文件
.idea/
.vscode/
*.swp
*.swo

# 系统文件
.DS_Store
Thumbs.db

# 日志文件
*.log
logs/

# 临时文件
tmp/
temp/
*.tmp
EOF

# 查看忽略规则
git check-ignore -v file.txt

# 测试忽略规则
git check-ignore *.log

3.3 删除文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 删除文件(工作区和暂存区)
git rm file.txt

# 删除目录
git rm -r dir/

# 只从暂存区删除(保留工作区文件)
git rm --cached file.txt

# 删除已修改但未暂存的文件
git checkout -- file.txt

# 恢复误删的文件
git checkout HEAD -- file.txt

3.4 移动/重命名文件

1
2
3
4
5
6
7
8
9
# 重命名文件
git mv old_name.txt new_name.txt

# 移动文件
git mv file.txt dir/file.txt

# 重命名后 Git 未识别
git add -A
git status # Git 会自动识别重命名

四、提交操作

4.1 基本提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 提交暂存区
git commit -m "feat: add user authentication"

# 提交并添加描述
git commit -m "feat: add user authentication" -m "Implemented JWT-based authentication"

# 提交所有修改的文件
git commit -am "fix: resolve login bug"

# 修改最后一次提交
git commit --amend
git commit --amend -m "new commit message"

# 提交时跳过预提交钩子
git commit --no-verify

4.2 提交规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Conventional Commits 格式
<type>(<scope>): <subject>

# type 类型
feat: 新功能
fix: 修复 bug
docs: 文档更新
style: 代码格式(不影响功能)
refactor: 重构
test: 测试相关
chore: 构建过程或辅助工具变动

# 示例
feat(auth): add login functionality
fix(api): resolve null pointer exception
docs(readme): update installation guide
style(format): format code with prettier
refactor(core): simplify user service
test(unit): add unit tests for auth module
chore(deps): upgrade dependencies

4.3 撤销提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 撤销工作区修改
git checkout -- file.txt
git restore file.txt # Git 2.23+

# 撤销暂存区修改
git reset HEAD file.txt
git restore --staged file.txt # Git 2.23+

# 撤销最后一次提交(保留修改)
git reset --soft HEAD~1
git reset --soft commit_id

# 撤销提交并丢弃修改
git reset --hard HEAD~1
git reset --hard commit_id

# 撤销远程已推送的提交(危险!)
git reset --hard HEAD~1
git push --force

# 安全撤销远程提交(推荐)
git revert commit_id
git push

4.4 查看提交历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 简洁的提交历史
git log --oneline

# 图形化提交历史
git log --graph --oneline --all

# 带统计信息的提交历史
git log --stat

# 搜索提交信息
git log --grep="bug fix"
git log --grep="feat" --oneline

# 查看某文件的提交历史
git log --follow -- path/to/file

# 查看某人的提交
git log --author="John" --oneline

# 查看时间范围内的提交
git log --since="2 weeks ago" --until="yesterday"

五、分支管理

5.1 分支基础操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查看分支
git branch
git branch -a # 所有分支(包括远程)
git branch -v # 显示最后一次提交

# 创建分支
git branch feature/login
git branch feature/login commit_id

# 切换分支
git checkout feature/login
git switch feature/login # Git 2.23+

# 创建并切换分支
git checkout -b feature/login
git switch -c feature/login # Git 2.23+

# 删除分支
git branch -d feature/login # 安全删除(已合并)
git branch -D feature/login # 强制删除(未合并)

# 重命名分支
git branch -m old-name new-name
git branch -m new-name # 当前分支

5.2 分支合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 合并分支到当前分支
git checkout main
git merge feature/login

# 快进合并(不创建合并提交)
git merge --ff-only feature/login

# 禁止快进合并(强制创建合并提交)
git merge --no-ff feature/login

# 合并时压缩提交
git merge --squash feature/login

# 查看合并冲突
git diff --name-only --diff-filter=U
git diff # 查看冲突内容

# 解决冲突后标记为已解决
git add file.txt

# 取消合并
git merge --abort

5.3 分支变基(Rebase)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 变基操作
git checkout feature/login
git rebase main

# 交互式变基
git rebase -i HEAD~3
git rebase -i commit_id

# 变基交互命令
# pick: 使用提交
# reword: 使用提交,修改提交信息
# edit: 使用提交,暂停以便修改
# squash: 使用提交,压缩到上一个提交
# fixup: 使用提交,丢弃提交信息
# drop: 删除提交

# 继续变基
git rebase --continue

# 跳过当前提交
git rebase --skip

# 取消变基
git rebase --abort

# 变基到上游分支
git pull --rebase

5.4 分支管理最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看已合并的分支
git branch --merged

# 查看未合并的分支
git branch --no-merged

# 查看分支的提交关系
git log --oneline --graph --all --decorate

# 清理已合并的本地分支
git branch --merged | grep -v "\*\|main\|develop" | xargs -n 1 git branch -d

# 查看远程跟踪分支
git branch -r

# 删除远程已删除的分支
git fetch -p
git remote prune origin

六、远程协作

6.1 远程仓库操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看远程仓库
git remote
git remote -v

# 添加远程仓库
git remote add origin https://github.com/user/repo.git
git remote add origin git@github.com:user/repo.git

# 修改远程仓库 URL
git remote set-url origin https://github.com/user/new-repo.git

# 删除远程仓库
git remote remove origin

# 重命名远程仓库
git remote rename origin upstream

# 获取远程仓库信息
git remote show origin

6.2 拉取和推送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 拉取远程分支
git fetch origin
git fetch origin feature/login

# 拉取并合并
git pull origin main
git pull # 默认当前分支的 upstream

# 拉取并变基
git pull --rebase origin main

# 推送分支
git push origin feature/login
git push # 推送当前分支

# 推送并设置 upstream
git push -u origin feature/login

# 强制推送(危险!)
git push --force
git push --force-with-lease # 更安全

# 推送所有分支
git push --all origin

# 推送所有标签
git push --tags origin

# 删除远程分支
git push origin --delete feature/login
git push origin :feature/login # 旧语法

6.3 标签管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查看标签
git tag
git tag -l "v1.*"

# 创建标签
git tag v1.0.0
git tag v1.0.0 commit_id

# 创建带注释的标签
git tag -a v1.0.0 -m "Release version 1.0.0"

# 查看标签信息
git show v1.0.0

# 推送标签
git push origin v1.0.0
git push --tags

# 删除本地标签
git tag -d v1.0.0

# 删除远程标签
git push origin --delete v1.0.0
git push origin :refs/tags/v1.0.0

# 检出标签
git checkout v1.0.0

6.4 Git Flow 工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 初始化 Git Flow
git flow init

# 开始新功能
git flow feature start login
git flow feature finish login

# 发布新版本
git flow release start 1.0.0
git flow release finish 1.0.0

# 修复生产 bug
git flow hotstart critical-bug
git flow hotfix finish critical-bug

七、高级技巧

7.1 暂存(Stash)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 暂存当前修改
git stash
git stash save "WIP: login feature"

# 查看暂存列表
git stash list

# 应用暂存(不删除)
git stash apply
git stash apply stash@{1}

# 应用并删除暂存
git stash pop
git stash pop stash@{1}

# 删除暂存
git stash drop
git stash drop stash@{1}

# 清空所有暂存
git stash clear

# 从暂存创建分支
git stash branch feature/login stash@{0}

# 暂存未跟踪的文件
git stash -u
git stash --include-untracked

# 暂存所有文件(包括已删除)
git stash -a
git stash --all

7.2 樱桃拣选(Cherry-pick)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 拣选单个提交
git cherry-pick commit_id

# 拣选多个提交
git cherry-pick commit1 commit2 commit3

# 拣选提交范围
git cherry-pick commit1..commit2

# 拣选时不提交
git cherry-pick --no-commit commit_id

# 拣选时编辑提交信息
git cherry-pick --edit commit_id

# 取消樱桃拣选
git cherry-pick --abort

# 继续樱桃拣选(解决冲突后)
git cherry-pick --continue

7.3 二分查找(Bisect)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 开始二分查找
git bisect start

# 标记当前为坏版本
git bisect bad

# 标记某版本为好版本
git bisect good v1.0.0

# Git 自动切换到中间版本,测试后继续标记
git bisect good # 或 git bisect bad

# 自动二分查找
git bisect run ./test-script.sh

# 结束二分查找
git bisect reset

# 查看二分查找日志
git bisect log

7.4 子模块(Submodule)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 添加子模块
git submodule add https://github.com/user/lib.git libs/lib

# 初始化子模块
git submodule init

# 更新子模块
git submodule update
git submodule update --init --recursive

# 查看子模块状态
git submodule status

# 更新子模块到最新版本
git submodule update --remote

# 删除子模块
git submodule deinit libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib

7.5 Git Hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Git Hooks 目录
ls .git/hooks/

# 常用 Hooks
pre-commit # 提交前检查
pre-push # 推送前检查
commit-msg # 提交信息检查
post-merge # 合并后执行

# 启用 Hook
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

# 示例:pre-commit 检查
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 检查是否有调试代码
if git diff --cached | grep -E "console\.log|debugger"; then
echo "Error: Found debug code"
exit 1
fi

# 运行代码检查
npm run lint
EOF

chmod +x .git/hooks/pre-commit

八、问题排查

8.1 查看修改历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 谁修改了某行代码
git blame file.txt
git blame -L 10,20 file.txt # 查看特定行

# 查看某次提交引入的修改
git show commit_id

# 查看某文件的修改历史
git log --follow -- path/to/file

# 查找引入 bug 的提交
git bisect start
git bisect bad
git bisect good v1.0.0

8.2 恢复丢失的提交

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看 reflog
git reflog
git reflog show --all

# 恢复丢失的提交
git reset --hard HEAD@{1}
git checkout -b recovery-branch commit_id

# 查看 dangling 提交
git fsck --lost-found

# 恢复 dangling 提交
git cherry-pick commit_id

8.3 解决常见问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 问题:提交信息写错
git commit --amend -m "correct message"

# 问题:漏提交了文件
git add forgotten_file.txt
git commit --amend --no-edit

# 问题:提交到了错误的分支
git reset --hard HEAD~1 # 撤销提交
git checkout correct-branch
git cherry-pick original-branch

# 问题:合并冲突
git mergetool # 使用图形化工具
git diff --name-only --diff-filter=U # 查看冲突文件

# 问题:误删了分支
git reflog
git branch recovered-branch commit_id

# 问题:Git 仓库损坏
git fsck
git gc

8.4 性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 清理不必要的文件
git gc
git gc --aggressive

# 清理远程跟踪分支
git remote prune origin

# 查看仓库大小
git count-objects -v
git du -sh .git/objects/*

# 查找大文件
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '/^blob/ {print substr($0,6)}' | sort -k2 -nr | head -20

# 从历史中删除大文件
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch path/to/large-file' --prune-empty --tag-name-filter cat -- --all

九、实用脚本

9.1 日常开发脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 快速创建功能分支
function git-feat() {
git checkout -b "feat/$1"
}

# 快速创建修复分支
function git-fix() {
git checkout -b "fix/$1"
}

# 提交并推送
function git-commit-push() {
git add -A
git commit -m "$1"
git push -u origin $(git branch --show-current)
}

# 同步主分支
function git-sync-main() {
git fetch origin
git rebase origin/main
}

# 清理已合并分支
function git-cleanup() {
git branch --merged | grep -v "\*\|main\|develop" | xargs -n 1 git branch -d
}

# 查看今日提交
function git-today() {
git log --since="today" --author="$(git config user.name)"
}

9.2 Git 配置模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# .gitconfig 模板
[alias]
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
st = status -s
co = checkout
br = branch
ci = commit
last = log -1 HEAD
unstage = reset HEAD --
who = blame -L -20,-0

[user]
name = Your Name
email = your.email@example.com

[core]
editor = code --wait
autocrlf = input
filemode = false

[pull]
rebase = true

[push]
default = simple

[merge]
tool = vscode

[difftool "vscode"]
cmd = code --wait --diff $LOCAL $REMOTE

十、总结

Git 是开发者必备的工具,掌握常用命令可以大幅提升开发效率。关键要点:

  1. 基础操作:add、commit、push、pull
  2. 分支管理:branch、checkout、merge、rebase
  3. 问题排查:log、diff、blame、reflog
  4. 高级技巧:stash、cherry-pick、bisect

建议将常用命令配置为别名,并建立适合自己的工作流。


参考资料:

全面解析 MySQL 备份恢复方案,涵盖逻辑备份、物理备份、增量备份、PITR 恢复、主从复制、自动化脚本等核心内容,包含详细步骤和可运行代码

阅读全文 »

消息队列选型与应用:RabbitMQ、Kafka、RocketMQ 全面对比

本文深入分析主流消息队列的技术特点、适用场景和选型策略,结合实际案例讲解消息队列在解耦、异步、削峰等场景的最佳实践。

一、为什么需要消息队列?

1.1 核心应用场景

1
2
3
4
5
6
7
8
9
10
11
12
graph TB
A[消息队列核心价值] --> B[解耦]
A --> C[异步]
A --> D[削峰]
A --> E[顺序保证]
A --> F[事务消息]

B --> B1[系统间松耦合]
C --> C1[提升响应速度]
D --> D1[平滑流量峰值]
E --> E1[保证业务顺序]
F --> F1[分布式事务]

1.2 场景详解

场景 1:系统解耦

问题:订单系统直接调用库存、物流、通知系统,耦合严重。

1
2
3
4
5
6
7
8
9
10
11
12
13
graph TB
subgraph 改造前
A[订单系统] --> B[库存系统]
A --> C[物流系统]
A --> D[通知系统]
end

subgraph 改造后
E[订单系统] --> F[消息队列]
F --> G[库存系统]
F --> H[物流系统]
F --> I[通知系统]
end

改造前代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 订单服务 - 强耦合
@Service
public class OrderService {

@Autowired
private InventoryService inventoryService;

@Autowired
private LogisticsService logisticsService;

@Autowired
private NotificationService notificationService;

public Order createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(request.toOrder());

// 同步调用,任何一个失败都会导致订单创建失败
inventoryService.deduct(order.getItems());
logisticsService.createDelivery(order);
notificationService.sendOrderCreated(order);

return order;
}
}

改造后代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 订单服务 - 解耦
@Service
public class OrderService {

@Autowired
private RabbitTemplate rabbitTemplate;

public Order createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(request.toOrder());

// 发送消息,立即返回
OrderCreatedEvent event = new OrderCreatedEvent(order);
rabbitTemplate.convertAndSend("order.events", event);

return order;
}
}

// 库存服务 - 独立消费
@Component
public class InventoryListener {

@RabbitListener(queues = "inventory.queue")
public void handleOrderCreated(OrderCreatedEvent event) {
inventoryService.deduct(event.getItems());
}
}

// 物流服务 - 独立消费
@Component
public class LogisticsListener {

@RabbitListener(queues = "logistics.queue")
public void handleOrderCreated(OrderCreatedEvent event) {
logisticsService.createDelivery(event.getOrder());
}
}

收益:

  • 订单系统响应时间从 500ms 降至 50ms
  • 库存/物流系统故障不影响订单创建
  • 新增服务(如数据分析)无需修改订单系统

场景 2:异步处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 用户注册 - 同步处理(慢)
public User register(RegisterRequest request) {
User user = userRepository.save(request.toUser());

// 以下操作耗时且非核心
emailService.sendWelcomeEmail(user); // 200ms
smsService.sendVerifySms(user); // 300ms
analyticsService.trackUserRegister(user); // 100ms
couponService.grantWelcomeCoupon(user); // 150ms

return user; // 总耗时:750ms+
}

// 用户注册 - 异步处理(快)
public User register(RegisterRequest request) {
User user = userRepository.save(request.toUser());

// 发送消息,立即返回
eventPublisher.publish(new UserRegisteredEvent(user));

return user; // 总耗时:50ms
}

场景 3:削峰填谷

1
2
3
4
5
6
7
graph LR
A[流量峰值 10000 QPS] --> B[消息队列]
B --> C[后端服务 2000 QPS]

style A fill:#ff6b6b
style B fill:#4ecdc4
style C fill:#ffe66d

秒杀场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 秒杀请求 - 削峰处理
@Service
public class SeckillService {

@Autowired
private RabbitTemplate rabbitTemplate;

public SeckillResult seckill(SeckillRequest request) {
// 1. 快速验证
if (!seckillValidator.validate(request)) {
return SeckillResult.failed("验证失败");
}

// 2. 写入消息队列(快速返回)
rabbitTemplate.convertAndSend("seckill.queue", request);

return SeckillResult.processing("排队中");
}
}

// 后台消费 - 匀速处理
@Component
public class SeckillConsumer {

@RabbitListener(queues = "seckill.queue")
public void consume(SeckillRequest request) {
// 以稳定速率处理(2000 QPS)
seckillProcessor.process(request);
}
}

二、主流消息队列对比

2.1 产品概览

特性 RabbitMQ Kafka RocketMQ ActiveMQ
开发语言 Erlang Scala/Java Java Java
发布年份 2007 2011 2012 2004
所属组织 VMware Apache Apache Apache
协议支持 AMQP 自定义 自定义 JMS
消息模型 队列 Topic Topic/队列 队列
持久化 内存/磁盘 磁盘 磁盘 磁盘
吞吐量 万级 十万级 十万级 万级
延迟 微秒级 毫秒级 毫秒级 毫秒级
可靠性
社区活跃度 极高

2.2 架构对比

RabbitMQ 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
graph TB
A[Producer] --> B[Exchange]
B --> C[Queue1]
B --> D[Queue2]
B --> E[Queue3]
C --> F[Consumer1]
D --> G[Consumer2]
E --> H[Consumer3]

subgraph RabbitMQ Server
B
C
D
E
end

核心概念:

  • Producer:消息生产者
  • Exchange:消息交换机(Direct/Fanout/Topic/Headers)
  • Queue:消息队列
  • Consumer:消息消费者
  • Binding:Exchange 和 Queue 的绑定关系

Kafka 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
graph TB
A[Producer] --> B[Kafka Cluster]
B --> C[Topic: Partition 0]
B --> D[Topic: Partition 1]
B --> E[Topic: Partition 2]
C --> F[Consumer Group 1]
D --> F
E --> F
C --> G[Consumer Group 2]
D --> G
E --> G

subgraph Kafka Cluster
C
D
E
end

核心概念:

  • Topic:消息主题
  • Partition:主题分区(物理存储单元)
  • Broker:Kafka 服务器节点
  • Consumer Group:消费者组
  • Offset:消息偏移量
  • Zookeeper:元数据管理

RocketMQ 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
graph TB
A[Producer] --> B[NameServer]
A --> C[Broker A]
A --> D[Broker B]
C --> E[Consumer]
D --> E
B -.-> C
B -.-> D

subgraph RocketMQ Cluster
B
C
D
end

核心概念:

  • NameServer:无状态协调节点
  • Broker:消息存储和转发
  • Topic:消息主题
  • Queue:队列(分区)
  • Consumer Group:消费者组
  • Producer Group:生产者组

2.3 性能对比

吞吐量测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 测试环境
- 服务器:4 核 8G × 3
- 网络:千兆局域网
- 消息大小:1KB

# RabbitMQ
吞吐量:~20,000 msg/s
延迟:~1ms

# Kafka
吞吐量:~100,000 msg/s
延迟:~5ms

# RocketMQ
吞吐量:~80,000 msg/s
延迟:~3ms

可靠性对比

场景 RabbitMQ Kafka RocketMQ
消息持久化
事务消息
顺序消息 队列内有序 分区有序 分区有序
消息重试
死信队列
消息追踪

三、RabbitMQ 深度实践

3.1 安装与配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Docker Compose 部署
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3.11-management
container_name: rabbitmq
ports:
- "5672:5672" # AMQP 端口
- "15672:15672" # 管理界面
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: admin123
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 30s
timeout: 10s
retries: 5

volumes:
rabbitmq_data:

3.2 交换机类型详解

1
2
3
4
5
6
7
8
9
10
graph TB
A[Exchange 类型] --> B[Direct]
A --> C[Fanout]
A --> D[Topic]
A --> E[Headers]

B --> B1[精确匹配 RoutingKey]
C --> C1[广播到所有队列]
D --> D1[通配符匹配]
E --> E1[Header 匹配]

Direct Exchange(直连交换机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
public class DirectExchangeConfig {

@Bean
public DirectExchange directExchange() {
return new DirectExchange("order.direct");
}

@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.withArgument("x-message-ttl", 60000) // 消息 TTL 60 秒
.build();
}

@Bean
public Binding orderBinding(Queue orderQueue, DirectExchange directExchange) {
return BindingBuilder.bind(orderQueue)
.to(directExchange)
.with("order.created"); // Routing Key
}
}

// 生产者
rabbitTemplate.convertAndSend("order.direct", "order.created", message);

// 消费者 - 只接收 order.created 消息
@RabbitListener(queues = "order.queue")
public void handleOrderCreated(Message message) {
// 处理订单创建
}

Fanout Exchange(扇形交换机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Configuration
public class FanoutExchangeConfig {

@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("order.fanout");
}

@Bean
public Queue inventoryQueue() {
return QueueBuilder.durable("inventory.queue").build();
}

@Bean
public Queue logisticsQueue() {
return QueueBuilder.durable("logistics.queue").build();
}

@Bean
public Queue notificationQueue() {
return QueueBuilder.durable("notification.queue").build();
}

@Bean
public Binding inventoryBinding(Queue inventoryQueue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(inventoryQueue).to(fanoutExchange);
}

@Bean
public Binding logisticsBinding(Queue logisticsQueue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(logisticsQueue).to(fanoutExchange);
}

@Bean
public Binding notificationBinding(Queue notificationQueue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(notificationQueue).to(fanoutExchange);
}
}

// 生产者 - 消息广播到所有队列
rabbitTemplate.convertAndSend("order.fanout", "", message);

Topic Exchange(主题交换机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Configuration
public class TopicExchangeConfig {

@Bean
public TopicExchange topicExchange() {
return new TopicExchange("order.topic");
}

@Bean
public Queue allOrdersQueue() {
return QueueBuilder.durable("order.all").build();
}

@Bean
public Queue createdOrdersQueue() {
return QueueBuilder.durable("order.created").build();
}

@Bean
public Queue paidOrdersQueue() {
return QueueBuilder.durable("order.paid").build();
}

@Bean
public Binding allBinding(Queue allOrdersQueue, TopicExchange topicExchange) {
return BindingBuilder.bind(allOrdersQueue)
.to(topicExchange)
.with("order.*"); // 匹配 order.开头的所有消息
}

@Bean
public Binding createdBinding(Queue createdOrdersQueue, TopicExchange topicExchange) {
return BindingBuilder.bind(createdOrdersQueue)
.to(topicExchange)
.with("order.created"); // 精确匹配
}

@Bean
public Binding paidBinding(Queue paidOrdersQueue, TopicExchange topicExchange) {
return BindingBuilder.bind(paidOrdersQueue)
.to(topicExchange)
.with("order.paid.*"); // 匹配 order.paid.开头的消息
}
}

// 生产者
rabbitTemplate.convertAndSend("order.topic", "order.created.vip", message);
rabbitTemplate.convertAndSend("order.topic", "order.paid.normal", message);

3.3 消息可靠性保证

生产者确认(Publisher Confirm)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Configuration
public class RabbitConfig {

@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin123");

// 开启生产者确认
factory.setPublisherConfirms(true);
factory.setPublisherReturns(true);

return factory;
}

@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);

// 确认回调
rabbitTemplate.setConfirmCallback((correlationData, acknowledged, cause) -> {
if (acknowledged) {
log.info("消息发送成功:{}", correlationData);
} else {
log.error("消息发送失败:{}", cause);
// 记录失败,后续重试
}
});

// 返回回调
rabbitTemplate.setReturnsCallback(returnedMessage -> {
log.error("消息被退回:{}", returnedMessage.getMessage());
// 处理退回消息
});

return rabbitTemplate;
}
}

消费者手动 ACK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Configuration
public class ConsumerConfig {

@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
ConnectionFactory connectionFactory) {

SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);

// 手动 ACK
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);

// 并发消费者数量
factory.setConcurrentConsumers(5);
factory.setMaxConcurrentConsumers(10);

// 预取数量
factory.setPrefetchCount(10);

return factory;
}
}

// 消费者 - 手动 ACK
@Component
public class OrderConsumer {

@RabbitListener(queues = "order.queue")
public void handleMessage(Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();

try {
// 处理消息
OrderCreatedEvent event = parseEvent(message);
orderService.process(event);

// 手动确认
channel.basicAck(deliveryTag, false);

} catch (Exception e) {
log.error("处理消息失败", e);

// 判断是否重试
Integer retryCount = getRetryCount(message);
if (retryCount < 3) {
// 拒绝消息,重新入队
channel.basicNack(deliveryTag, false, true);
} else {
// 发送到死信队列
channel.basicNack(deliveryTag, false, false);
sendToDeadLetterQueue(message);
}
}
}
}

死信队列(DLQ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Configuration
public class DeadLetterConfig {

@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("dead.letter.queue").build();
}

@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange("dead.letter.exchange");
}

@Bean
public Binding deadLetterBinding(Queue deadLetterQueue, DirectExchange deadLetterExchange) {
return BindingBuilder.bind(deadLetterQueue)
.to(deadLetterExchange)
.with("dead.letter");
}

@Bean
public Queue orderQueueWithDLQ() {
return QueueBuilder.durable("order.queue")
.withArgument("x-dead-letter-exchange", "dead.letter.exchange")
.withArgument("x-dead-letter-routing-key", "dead.letter")
.withArgument("x-message-ttl", 60000)
.build();
}
}

3.4 延迟队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Configuration
public class DelayQueueConfig {

// 延迟消息 TTL 队列
@Bean
public Queue delayQueue() {
return QueueBuilder.durable("delay.queue")
.withArgument("x-message-ttl", 60000) // 60 秒
.withArgument("x-dead-letter-exchange", "order.exchange")
.withArgument("x-dead-letter-routing-key", "order.timeout")
.build();
}

@Bean
public DirectExchange delayExchange() {
return new DirectExchange("delay.exchange");
}

@Bean
public Binding delayBinding(Queue delayQueue, DirectExchange delayExchange) {
return BindingBuilder.bind(delayQueue)
.to(delayExchange)
.with("order.delay");
}

// 超时订单队列(死信队列)
@Bean
public Queue timeoutOrderQueue() {
return QueueBuilder.durable("order.timeout.queue").build();
}

@Bean
public Binding timeoutBinding(Queue timeoutOrderQueue, DirectExchange delayExchange) {
return BindingBuilder.bind(timeoutOrderQueue)
.to(delayExchange)
.with("order.timeout");
}
}

// 发送延迟消息
public void sendDelayOrder(Order order) {
rabbitTemplate.convertAndSend(
"delay.exchange",
"order.delay",
order,
message -> {
// 设置延迟时间(毫秒)
message.getMessageProperties().setDelay(30 * 60 * 1000); // 30 分钟
return message;
}
);
}

// 消费超时订单
@RabbitListener(queues = "order.timeout.queue")
public void handleTimeoutOrder(Order order) {
orderService.cancelOrder(order.getId());
}

四、Kafka 深度实践

4.1 安装与配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Docker Compose 部署
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.4.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000

kafka:
image: confluentinc/cp-kafka:7.4.0
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"

4.2 核心概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
graph TB
A[Topic] --> B[Partition 0]
A --> C[Partition 1]
A --> D[Partition 2]

B --> E[Offset 0]
B --> F[Offset 1]
B --> G[Offset 2]

H[Consumer Group] --> I[Consumer 1]
H --> J[Consumer 2]

I --> B
J --> C

4.3 生产者配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Configuration
public class KafkaProducerConfig {

@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> config = new HashMap<>();

// 基础配置
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

// 可靠性配置
config.put(ProducerConfig.ACKS_CONFIG, "all"); // 所有副本确认
config.put(ProducerConfig.RETRIES_CONFIG, 3); // 重试次数
config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // 幂等性

// 性能配置
config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); // 批次大小 16KB
config.put(ProducerConfig.LINGER_MS_CONFIG, 10); // 等待时间 10ms
config.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy"); // 压缩

return new DefaultKafkaProducerFactory<>(config);
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
KafkaTemplate<String, String> template = new KafkaTemplate<>(producerFactory());

// 发送回调
template.setProducerListener(new ProducerListener<String, String>() {
@Override
public void onSuccess(ProducerRecord<String, String> record,
RecordMetadata metadata) {
log.info("发送成功:topic={}, partition={}, offset={}",
metadata.topic(), metadata.partition(), metadata.offset());
}

@Override
public void onError(ProducerRecord<String, String> record,
RecordMetadata metadata, Exception exception) {
log.error("发送失败", exception);
}
});

return template;
}
}

4.4 消费者配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Configuration
public class KafkaConsumerConfig {

@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> config = new HashMap<>();

// 基础配置
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

// 消费者组
config.put(ConsumerConfig.GROUP_ID_CONFIG, "order-consumer-group");

// Offset 提交
config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); // 手动提交
config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); // 从最新开始

return new DefaultKafkaConsumerFactory<>(config);
}

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());

// 并发消费者
factory.setConcurrency(3);

return factory;
}
}

// 消费者 - 手动提交 Offset
@Component
public class OrderConsumer {

@KafkaListener(topics = "order-topic", groupId = "order-consumer-group")
public void consume(ConsumerRecord<String, String> record, Acknowledgment ack) {
try {
log.info("收到消息:key={}, value={}", record.key(), record.value());

// 处理消息
orderService.process(record.value());

// 手动提交 Offset
ack.acknowledge();

} catch (Exception e) {
log.error("处理消息失败", e);
// 不提交 Offset,下次重新消费
throw e;
}
}
}

4.5 顺序消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 生产者 - 保证分区有序
public void sendOrderMessage(Order order) {
// 使用订单 ID 作为 key,保证同一订单的消息发送到同一分区
String key = String.valueOf(order.getId());
kafkaTemplate.send("order-topic", key, order.toJson());
}

// 消费者 - 单线程消费保证顺序
@Component
public class OrderConsumer {

@KafkaListener(
topics = "order-topic",
groupId = "order-consumer-group",
containerFactory = "singleThreadKafkaListenerContainerFactory"
)
public void consume(ConsumerRecord<String, String> record) {
// 单线程处理,保证分区内顺序
orderService.process(record.value());
}
}

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> singleThreadKafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(1); // 单线程
return factory;
}

4.6 精确一次语义(Exactly-Once)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Configuration
public class KafkaExactlyOnceConfig {

@Bean
public ProducerFactory<String, String> exactlyOnceProducerFactory() {
Map<String, Object> config = new HashMap<>();

config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "tx-producer-1"); // 事务 ID
config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // 幂等性
config.put(ProducerConfig.ACKS_CONFIG, "all");

return new DefaultKafkaProducerFactory<>(config);
}

@Bean
public KafkaTransactionManager<String, String> kafkaTransactionManager() {
return new KafkaTransactionManager<>(exactlyOnceProducerFactory());
}
}

// 事务消息
@Service
public class OrderService {

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

@Transactional // 开启事务
public void createOrder(Order order) {
// 1. 保存订单到数据库
orderRepository.save(order);

// 2. 发送消息(同一事务)
kafkaTemplate.executeInTransaction(template -> {
template.send("order-topic", order.toJson());
template.send("inventory-topic", order.getItems().toJson());
return null;
});
}
}

五、RocketMQ 深度实践

5.1 安装与配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Docker Compose 部署
version: '3.8'
services:
namesrv:
image: apache/rocketmq:4.9.4
container_name: rmqnamesrv
ports:
- "9876:9876"
command: sh mqnamesrv
environment:
JAVA_OPT_EXT: "-server -Xms128m -Xmx128m"

broker:
image: apache/rocketmq:4.9.4
container_name: rmqbroker
depends_on:
- namesrv
ports:
- "10911:10911"
- "10909:10909"
command: sh mqbroker -n namesrv:9876
environment:
JAVA_OPT_EXT: "-server -Xms256m -Xmx256m"
BROKER_CONF: |
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
autoCreateTopicEnable=true

5.2 生产者配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Configuration
public class RocketMQConfig {

@Value("${rocketmq.name-server}")
private String nameServer;

@Bean
public DefaultMQProducer producer() throws MQClientException {
DefaultMQProducer producer = new DefaultMQProducer("order-producer-group");
producer.setNamesrvAddr(nameServer);
producer.setInstanceName(UUID.randomUUID().toString());

// 重试配置
producer.setRetryTimesWhenSendFailed(3);
producer.setRetryTimesWhenSendAsyncFailed(3);

// 超时配置
producer.setSendMsgTimeout(3000);

producer.start();
return producer;
}
}

// 发送消息
@Service
public class OrderMessageService {

@Autowired
private DefaultMQProducer producer;

// 同步发送
public void sendSync(Order order) throws Exception {
Message msg = new Message(
"order-topic",
"order-created",
order.toJson().getBytes(RemotingHelper.DEFAULT_CHARSET)
);

SendResult result = producer.send(msg);
log.info("发送成功:msgId={}", result.getMsgId());
}

// 异步发送
public void sendAsync(Order order) throws Exception {
Message msg = new Message(
"order-topic",
"order-created",
order.toJson().getBytes(RemotingHelper.DEFAULT_CHARSET)
);

producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("发送成功:msgId={}", sendResult.getMsgId());
}

@Override
public void onException(Throwable e) {
log.error("发送失败", e);
}
});
}

// 单向发送(不关心结果)
public void sendOneway(Order order) throws Exception {
Message msg = new Message(
"order-topic",
"order-created",
order.toJson().getBytes(RemotingHelper.DEFAULT_CHARSET)
);

producer.sendOneway(msg);
}
}

5.3 消费者配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Configuration
public class RocketMQConsumerConfig {

@Value("${rocketmq.name-server}")
private String nameServer;

@Bean
public DefaultMQPushConsumer consumer() throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order-consumer-group");
consumer.setNamesrvAddr(nameServer);
consumer.setInstanceName(UUID.randomUUID().toString());

// 消费点位
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

// 并发配置
consumer.setConsumeThreadMin(10);
consumer.setConsumeThreadMax(20);

// 批量消费
consumer.setConsumeMessageBatchMaxSize(1);

// 重试配置
consumer.setMaxReconsumeTimes(3);

consumer.subscribe("order-topic", "*");

// 消息监听
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
for (MessageExt msg : msgs) {
try {
String body = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
log.info("收到消息:msgId={}, body={}", msg.getMsgId(), body);

// 处理消息
orderService.process(body);

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

} catch (Exception e) {
log.error("处理消息失败", e);
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});

consumer.start();
return consumer;
}
}

5.4 事务消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 事务消息监听器
@Component
public class OrderTransactionListener implements TransactionListener {

@Autowired
private OrderService orderService;

@Autowired
private LocalTransactionTable transactionTable;

// 执行本地事务
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
Order order = parseOrder(msg);

// 1. 执行本地事务
orderService.createOrder(order);

// 2. 记录事务状态
transactionTable.record(msg.getMsgId(), TransactionStatus.COMMITTED);

return LocalTransactionState.COMMIT_MESSAGE;

} catch (Exception e) {
log.error("执行本地事务失败", e);
transactionTable.record(msg.getMsgId(), TransactionStatus.ROLLBACK);
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}

// 事务状态回查
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String msgId = msg.getMsgId();
TransactionStatus status = transactionTable.getStatus(msgId);

log.info("事务回查:msgId={}, status={}", msgId, status);

switch (status) {
case COMMITTED:
return LocalTransactionState.COMMIT_MESSAGE;
case ROLLBACK:
return LocalTransactionState.ROLLBACK_MESSAGE;
default:
return LocalTransactionState.UNKNOW;
}
}
}

// 发送事务消息
@Service
public class OrderTransactionService {

@Autowired
private TransactionMQProducer producer;

public void sendTransactionMessage(Order order) throws Exception {
Message msg = new Message(
"order-topic",
"order-created",
order.toJson().getBytes(RemotingHelper.DEFAULT_CHARSET)
);

// 发送事务消息
TransactionSendResult result = producer.sendMessageInTransaction(msg, order);

log.info("事务消息发送:msgId={}, state={}",
result.getMsgId(), result.getLocalTransactionState());
}
}

六、选型指南

6.1 选型决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
graph TD
A[开始选型] --> B{需要高吞吐?}
B -->|是 | C{需要顺序消息?}
B -->|否 | D{需要复杂路由?}

C -->|是 | E[Kafka]
C -->|否 | F{需要事务消息?}

D -->|是 | G[RabbitMQ]
D -->|否 | H{Java 技术栈?}

F -->|是 | I[RocketMQ]
F -->|否 | E

H -->|是 | I
H -->|否 | G

6.2 场景推荐

场景 推荐方案 理由
日志收集 Kafka 高吞吐、顺序保证
实时计算 Kafka 流处理生态完善
订单处理 RocketMQ 事务消息、可靠性高
通知系统 RabbitMQ 路由灵活、延迟低
金融交易 RocketMQ 事务消息、精确一次
物联网 Kafka 海量数据、高吞吐
微服务解耦 RabbitMQ 协议标准、易于集成

6.3 混合使用策略

1
2
3
4
5
6
7
8
9
10
11
graph TB
A[业务系统] --> B[RabbitMQ]
A --> C[Kafka]

B --> D[订单处理]
B --> E[通知系统]
B --> F[任务调度]

C --> G[日志收集]
C --> H[用户行为]
C --> I[实时监控]

七、总结

消息队列选型的关键要点:

  1. 明确需求:吞吐量、延迟、可靠性、顺序性
  2. 技术匹配:团队技术栈、运维能力
  3. 场景优先:不同场景选择不同方案
  4. 混合使用:多种 MQ 配合使用

没有最好的消息队列,只有最适合的。记住:合适的架构才是最好的架构


参考资料:

全面解析 RESTful API 设计的最佳实践,涵盖资源命名、HTTP 方法、状态码、版本控制、分页过滤、错误处理、安全认证等核心主题,包含 3 个架构图和 2 个实战案例

阅读全文 »