Skip to content

Latest commit

 

History

History
278 lines (205 loc) · 8.22 KB

File metadata and controls

278 lines (205 loc) · 8.22 KB

分布式系统学习笔记

项目:3台 AWS EC2 服务器 + Route 53 健康检查 + Python 负载均衡器


第一部分:systemd 服务配置

为什么需要 systemd?

之前用 python3 -c "..." 直接运行代码,进程是临时的,断了就没了。 用 systemd 注册成系统服务后:开机自动启动,崩溃自动重启。

1. 把代码存成文件

sudo tee /opt/server.py > /dev/null << 'PYEOF'
...代码...
PYEOF
  • sudo — 用管理员权限执行(普通用户没权限写 /opt/ 目录)
  • tee — 把内容写入文件
  • /opt/server.py/opt/ 是 Linux 约定放"额外安装软件"的地方
  • > /dev/null — 把屏幕输出扔掉
  • << 'PYEOF' — heredoc 语法,从这里到 PYEOF 之间的内容全部作为输入

2. 创建 systemd 服务文件

[Unit]
Description=Web Server
After=network.target

[Service]
ExecStart=/usr/bin/python3 /opt/server.py
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

[Unit] — 描述服务

  • Description — 服务名字,方便人看
  • After=network.target — 等网络启动完再启动,因为服务器需要网络

[Service] — 如何运行

  • ExecStart — 启动命令,必须写完整路径
  • Restart=always — 进程崩溃自动重启(守护进程的核心)
  • RestartSec=3 — 崩溃后等3秒再重启,防止反复崩溃

[Install] — 何时启动

  • WantedBy=multi-user.target — 正常开机时启动,Linux 固定写法

3. 激活服务的四条命令

sudo systemctl daemon-reload    # 重新读取所有 .service 文件
sudo systemctl enable webserver # 注册为开机自启
sudo systemctl restart webserver # 立刻启动
sudo systemctl is-active webserver # 检查是否在跑(返回 active 表示正常)

注意:每次修改 .service 文件后都要先跑 daemon-reload,否则 systemd 读的是旧版本。


第二部分:Python 服务器代码

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/health':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(json.dumps({'status': 'healthy', 'server': 'us-east'}).encode())
        else:
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(json.dumps({'message': 'Hello from US East (Virginia)!', 'server': 'us-east'}).encode())
    def log_message(self, f, *a): pass

HTTPServer(('0.0.0.0', 80), Handler).serve_forever()

导入库

  • http.server — Python 内置,提供写 HTTP 服务器需要的工具
  • HTTPServer — 负责监听端口、接受连接
  • BaseHTTPRequestHandler — 负责处理每一个请求,我们继承它写自己的逻辑
  • json — 把 Python 字典转成 JSON 字符串

继承(class Handler(BaseHTTPRequestHandler))

BaseHTTPRequestHandler 已经帮你处理了 HTTP 协议的底层细节(解析请求头、建立连接等)。 我们只需要告诉它"收到请求后做什么"。 就像继承了父母的基因,但有自己的性格——Handler 继承了底层网络能力,但自定义了响应逻辑。

do_GET 方法

HTTP 有多种请求类型:GET(获取)、POST(提交)、PUT(更新)、DELETE(删除)。 收到 GET 请求自动调用 do_GET,收到 POST 自动调用 do_POST,以此类推。

路径判断

if self.path == '/health':

self.path 是请求的路径:

  • 访问 http://3.236.124.247/healthself.path/health
  • 访问 http://3.236.124.247/self.path/

我们设计了两个接口:

  • /health — 给健康检查用,返回 {"status": "healthy"}
  • 其他所有路径 — 返回问候消息

返回响应(必须按顺序)

self.send_response(200)                          # 1. 状态码
self.send_header('Content-type', 'application/json')  # 2. 响应头
self.end_headers()                               # 3. 头部结束
self.wfile.write(json.dumps({...}).encode())     # 4. 响应正文

常见状态码:200(成功)、404(找不到)、500(服务器错误)

.encode() 的作用:网络传输需要字节,不能直接传字符串,所以要转换。

关掉日志

def log_message(self, f, *a): pass

覆盖父类的 log_message 方法,让它什么都不做(pass),关掉默认的请求日志打印。

启动服务器

HTTPServer(('0.0.0.0', 80), Handler).serve_forever()
  • '0.0.0.0' — 监听所有网卡,让外部可以访问('127.0.0.1' 只有本机能访问)
  • 80 — HTTP 默认端口,访问时不用写端口号
  • serve_forever() — 永远运行,不停接受新请求

第三部分:负载均衡器代码

import urllib.request
import json
import time
import itertools

SERVERS = [
    {"name": "US East (Virginia)", "ip": "3.236.124.247"},
    {"name": "US West (Oregon)",   "ip": "100.23.127.116"},
    {"name": "EU West (Ireland)",  "ip": "34.245.154.170"},
]

导入库

  • urllib.request — Python 内置,用来发 HTTP 请求
  • json — 解析 JSON 响应
  • time — 提供 time.sleep(),让程序暂停
  • itertools — 提供 itertools.cycle(),实现轮询的核心

健康检查函数

def check_health(server):
    try:
        url = f"http://{server['ip']}/health"
        with urllib.request.urlopen(url, timeout=3) as resp:
            data = json.loads(resp.read())
            return data.get("status") == "healthy"
    except Exception:
        return False
  • timeout=3 — 3秒没响应就放弃,网络操作不能无限等
  • with ... as resp — 用完自动关闭连接,防止资源泄漏
  • except Exception: return False — 任何错误(超时、连接拒绝)都返回 False,防御性编程

列表推导式

def get_healthy_servers():
    return [s for s in SERVERS if check_health(s)]

等价于:

result = []
for s in SERVERS:
    if check_health(s):
        result.append(s)
return result

轮询核心:itertools.cycle

普通列表有尽头:[A, B, C] → A, B, C, 然后结束

cycle 无限循环:cycle([A, B, C]) → A, B, C, A, B, C, A, B, C ... 永远

每次调用 next(server_cycle) 取下一台服务器,这就是 Round Robin(轮询) 算法。

主循环逻辑

if request_count % 10 == 0:
    healthy = get_healthy_servers()
    if healthy != last_healthy:
        server_cycle = itertools.cycle(healthy)
        last_healthy = healthy
  • % 10 == 0 — 取余运算,每10个请求做一次健康检查(不需要每次都查,太浪费)
  • if healthy != last_healthy — 只有列表变化时才重置轮询,否则每次检查都重置会造成不均匀

整体架构图

你的电脑(负载均衡器)
    ↓ 每10个请求做一次健康检查
    ├── US East  ✅/❌
    ├── US West  ✅/❌
    └── EU West  ✅/❌

    ↓ Round Robin 轮询(只发给健康的服务器)
    ├── 请求#1 → US East
    ├── 请求#2 → US West
    └── 请求#3 → EU West

故障转移全流程

正常:cycle([US-East, US-West, EU-West])
       → 每台各收 1/3 流量

US-East 挂了:
       → 健康检查发现
       → 重建 cycle([US-West, EU-West])
       → US-East 不再收到任何请求

US-East 恢复:
       → 健康检查发现
       → 重建 cycle([US-East, US-West, EU-West])
       → 自动重新加入轮询

项目总结

组件 技术
3 台服务器 AWS EC2,分布在 us-east-1、us-west-2、eu-west-1
Web 服务 Python HTTP Server,systemd 守护进程
健康监控 AWS Route 53 Health Checks,8个地区同时检测
负载均衡 Python Round Robin 算法
故障转移 自动检测 + 自动剔除 + 自动恢复

简历描述:

Built a highly available distributed system across 3 AWS regions (us-east-1, us-west-2, eu-west-1) with automatic health monitoring via Route 53, Round Robin load balancing, and zero-downtime failover.