架构设计

难度等级:⭐⭐⭐⭐ 前置知识:Web 后端开发、数据存储 后续衔接:微服务架构

学习路径


一、设计模式

设计模式是软件工程中反复出现的问题的通用解决方案。它们不是可以直接编译的代码,而是经过验证的设计经验的总结。掌握设计模式能够帮助开发者写出更易扩展、更易维护的代码。

1.1 创建型模式

创建型模式关注对象的创建过程,将对象的创建与使用分离,降低系统耦合度。

工厂方法模式(Factory Method)

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。

核心思想:定义一个创建对象的接口,让实现类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

典型应用场景:日志框架中,根据配置创建不同格式的日志输出器;支付系统中,根据支付方式创建对应的支付处理器。

// 产品接口
public interface Logger {
    void log(String message);
}

// 具体产品
public class JsonLogger implements Logger {
    public void log(String message) {
        System.out.println("{\"level\":\"INFO\",\"msg\":\"" + message + "\"}");
    }
}

public class TextLogger implements Logger {
    public void log(String message) {
        System.out.println("[INFO] " + message);
    }
}

// 工厂接口
public interface LoggerFactory {
    Logger createLogger();
}

// 具体工厂
public class JsonLoggerFactory implements LoggerFactory {
    public Logger createLogger() {
        return new JsonLogger();
    }
}

public class TextLoggerFactory implements LoggerFactory {
    public Logger createLogger() {
        return new TextLogger();
    }
}

使用工厂方法模式的好处是,当需要新增一种日志格式时,只需新增对应的产品和工厂类,无需修改现有代码,符合开闭原则。

抽象工厂模式(Abstract Factory)

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。与工厂方法的区别在于,抽象工厂创建的是产品族,而非单一产品。

典型场景:UI 组件库中,需要同时创建按钮、文本框、下拉框等多种组件,且不同主题(Windows、Mac、Linux)下的组件风格不同。

public interface Button { void render(); }
public interface TextBox { void display(); }

// Windows 产品族
public class WindowsButton implements Button {
    public void render() { System.out.println("Windows style button"); }
}
public class WindowsTextBox implements TextBox {
    public void display() { System.out.println("Windows style textbox"); }
}

// Mac 产品族
public class MacButton implements Button {
    public void render() { System.out.println("Mac style button"); }
}
public class MacTextBox implements TextBox {
    public void display() { System.out.println("Mac style textbox"); }
}

// 抽象工厂
public interface UIFactory {
    Button createButton();
    TextBox createTextBox();
}

public class WindowsFactory implements UIFactory {
    public Button createButton() { return new WindowsButton(); }
    public TextBox createTextBox() { return new WindowsTextBox(); }
}

public class MacFactory implements UIFactory {
    public Button createButton() { return new MacButton(); }
    public TextBox createTextBox() { return new MacTextBox(); }
}

单例模式(Singleton)

单例模式确保一个类只有一个实例,并提供一个全局访问点。

实现要点:

// 双重检查锁定(推荐实现)
public class ConfigManager {
    private static volatile ConfigManager instance;
    private Properties config;

    private ConfigManager() {
        // 加载配置
    }

    public static ConfigManager getInstance() {
        if (instance == null) {
            synchronized (ConfigManager.class) {
                if (instance == null) {
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }
}

// 枚举实现(最简洁、最安全)
public enum ConfigManagerEnum {
    INSTANCE;
    private Properties config;
    // 初始化逻辑
}

单例模式的适用场景:配置管理器、连接池、缓存管理器、日志管理器。

建造者模式(Builder)

建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。适用于参数众多、且部分参数可选的复杂对象创建。

public class ServerConfig {
    private final String host;
    private final int port;
    private final int timeout;
    private final int maxConnections;
    private final boolean sslEnabled;

    private ServerConfig(Builder builder) {
        this.host = builder.host;
        this.port = builder.port;
        this.timeout = builder.timeout;
        this.maxConnections = builder.maxConnections;
        this.sslEnabled = builder.sslEnabled;
    }

    public static class Builder {
        private String host;
        private int port;
        private int timeout = 3000;
        private int maxConnections = 100;
        private boolean sslEnabled = false;

        public Builder(String host, int port) {
            this.host = host;
            this.port = port;
        }

        public Builder timeout(int timeout) {
            this.timeout = timeout;
            return this;
        }

        public Builder maxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
            return this;
        }

        public Builder sslEnabled(boolean sslEnabled) {
            this.sslEnabled = sslEnabled;
            return this;
        }

        public ServerConfig build() {
            return new ServerConfig(this);
        }
    }
}

// 使用
ServerConfig config = new ServerConfig.Builder("localhost", 8080)
    .timeout(5000)
    .maxConnections(200)
    .sslEnabled(true)
    .build();

1.2 结构型模式

结构型模式关注类和对象的组合,通过继承和组合来构建更大的结构。

适配器模式(Adapter)

适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

典型场景:第三方支付接口适配、旧系统接口兼容、多数据源适配。

// 目标接口
public interface PaymentProcessor {
    void processPayment(double amount);
}

// 已有类(不兼容目标接口)
public class AlipayGateway {
    public void pay(String amount) {
        System.out.println("Alipay: " + amount);
    }
}

public class WechatPayGateway {
    public void sendPay(double amount) {
        System.out.println("Wechat: " + amount);
    }
}

// 适配器
public class AlipayAdapter implements PaymentProcessor {
    private AlipayGateway alipay;
    public AlipayAdapter() { this.alipay = new AlipayGateway(); }
    public void processPayment(double amount) {
        alipay.pay(String.valueOf(amount));
    }
}

public class WechatPayAdapter implements PaymentProcessor {
    private WechatPayGateway wechat;
    public WechatPayAdapter() { this.wechat = new WechatPayGateway(); }
    public void processPayment(double amount) {
        wechat.sendPay(amount);
    }
}

装饰器模式(Decorator)

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个包装对象(装饰器)来包裹原有的对象。

典型场景:Java I/O 流(BufferedInputStream、DataInputStream)、HTTP 请求增强(添加认证、日志、缓存)。

public interface Coffee {
    double getCost();
    String getDescription();
}

public class SimpleCoffee implements Coffee {
    public double getCost() { return 10; }
    public String getDescription() { return "Simple coffee"; }
}

// 装饰器基类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; }
    public double getCost() { return decoratedCoffee.getCost(); }
    public String getDescription() { return decoratedCoffee.getDescription(); }
}

// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    public double getCost() { return super.getCost() + 3; }
    public String getDescription() { return super.getDescription() + ", milk"; }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) { super(coffee); }
    public double getCost() { return super.getCost() + 1; }
    public String getDescription() { return super.getDescription() + ", sugar"; }
}

// 使用:可以任意组合装饰
Coffee coffee = new MilkDecorator(new SugarDecorator(new SimpleCoffee()));

代理模式(Proxy)

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。

代理的几种类型:

public interface Image {
    void display();
}

public class RealImage implements Image {
    private String filename;
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }
    private void loadFromDisk() {
        System.out.println("Loading " + filename);
    }
    public void display() {
        System.out.println("Displaying " + filename);
    }
}

// 虚拟代理(延迟加载)
public class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;
    public ProxyImage(String filename) { this.filename = filename; }
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

外观模式(Facade)

外观模式为子系统中的一组接口提供一个一致的界面,定义了一个高层接口,使得子系统更加容易使用。

// 子系统
public class CPU { public void freeze() {} public void execute() {} }
public class Memory { public void load(long pos, byte[] data) {} }
public class HardDrive { public byte[] read(long lba, int size) { return new byte[0]; } }

// 外观
public class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;

    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }

    public void start() {
        cpu.freeze();
        memory.load(0, hardDrive.read(0, 1024));
        cpu.execute();
    }
}

1.3 行为型模式

行为型模式关注对象之间的通信和职责分配。

策略模式(Strategy)

策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户而变化。

典型场景:排序算法选择、压缩算法切换、支付方式选择。

public interface SortStrategy {
    void sort(int[] data);
}

public class QuickSortStrategy implements SortStrategy {
    public void sort(int[] data) { /* 快速排序实现 */ }
}

public class MergeSortStrategy implements SortStrategy {
    public void sort(int[] data) { /* 归并排序实现 */ }
}

public class SortContext {
    private SortStrategy strategy;
    public void setStrategy(SortStrategy strategy) { this.strategy = strategy; }
    public void sort(int[] data) { strategy.sort(data); }
}

观察者模式(Observer)

观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

典型场景:事件驱动系统、消息订阅、GUI 事件监听。

import java.util.ArrayList;
import java.util.List;

public interface Observer {
    void update(String event);
}

public class Subject {
    private List<Observer> observers = new ArrayList<>();
    private String state;

    public void attach(Observer o) { observers.add(o); }
    public void detach(Observer o) { observers.remove(o); }

    public void setState(String state) {
        this.state = state;
        notifyAll(state);
    }

    private void notifyAll(String event) {
        for (Observer o : observers) {
            o.update(event);
        }
    }
}

public class EmailNotifier implements Observer {
    public void update(String event) {
        System.out.println("Sending email notification: " + event);
    }
}

public class SmsNotifier implements Observer {
    public void update(String event) {
        System.out.println("Sending SMS notification: " + event);
    }
}

责任链模式(Chain of Responsibility)

责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

典型场景:Servlet Filter、Spring Interceptor、审批流程。

public abstract class Handler {
    protected Handler next;
    public void setNext(Handler next) { this.next = next; }
    public abstract void handleRequest(String request);
}

public class AuthHandler extends Handler {
    public void handleRequest(String request) {
        if (request.contains("auth")) {
            System.out.println("AuthHandler: processing authentication");
        } else if (next != null) {
            next.handleRequest(request);
        }
    }
}

public class LogHandler extends Handler {
    public void handleRequest(String request) {
        System.out.println("LogHandler: logging request - " + request);
        if (next != null) {
            next.handleRequest(request);
        }
    }
}

// 构建链
Handler auth = new AuthHandler();
Handler log = new LogHandler();
auth.setNext(log);
auth.handleRequest("user login");

模板方法模式(Template Method)

模板方法模式定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现,使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。

public abstract class DataProcessor {
    // 模板方法
    public final void process() {
        readData();
        processData();
        writeData();
    }

    protected abstract void readData();
    protected abstract void processData();
    protected abstract void writeData();
}

public class CsvProcessor extends DataProcessor {
    protected void readData() { System.out.println("Reading CSV file"); }
    protected void processData() { System.out.println("Processing CSV data"); }
    protected void writeData() { System.out.println("Writing CSV output"); }
}

public class JsonProcessor extends DataProcessor {
    protected void readData() { System.out.println("Reading JSON file"); }
    protected void processData() { System.out.println("Processing JSON data"); }
    protected void writeData() { System.out.println("Writing JSON output"); }
}

命令模式(Command)

命令模式将请求封装成对象,从而允许使用不同的请求、队列或者日志来参数化其他对象。

public interface Command {
    void execute();
}

public class Light {
    public void on() { System.out.println("Light is ON"); }
    public void off() { System.out.println("Light is OFF"); }
}

public class LightOnCommand implements Command {
    private Light light;
    public LightOnCommand(Light light) { this.light = light; }
    public void execute() { light.on(); }
}

public class RemoteControl {
    private Command command;
    public void setCommand(Command command) { this.command = command; }
    public void pressButton() { command.execute(); }
}

二、架构风格

架构风格定义了系统的组织结构,决定了系统如何被分解为组件以及组件之间如何交互。

2.1 分层架构

分层架构是最常见的架构风格之一,将系统按照职责划分为不同的层次,每一层只与相邻层通信。

典型分层:

┌─────────────────────────────────┐
│       表现层 (Presentation)      │  ← 处理用户交互、HTTP 请求
├─────────────────────────────────┤
│       业务层 (Business/Service)  │  ← 核心业务逻辑、规则验证
├─────────────────────────────────┤
│    数据访问层 (Data Access)      │  ← 数据库操作、外部服务调用
├─────────────────────────────────┤
│       持久层 (Persistence)       │  ← 数据库、文件系统
└─────────────────────────────────┘

分层架构的核心原则是依赖倒置:上层依赖下层接口,但不依赖具体实现。每一层只能调用相邻下层的服务,不能跨层调用。

分层架构的优缺点:

2.2 六边形架构(Hexagonal Architecture)

六边形架构(又称端口与适配器架构)由 Alistair Cockburn 提出,核心思想是将业务逻辑(核心)与外部依赖(适配器)分离。

                    ┌──────────────────┐
                    │   用户界面适配器   │
                    └────────┬─────────┘
                             │
┌────────────────┐    ┌──────▼──────┐    ┌────────────────┐
│   数据库适配器   │◄──►│   核心领域   │◄──►│  消息队列适配器  │
└────────────────┘    └──────▲──────┘    └────────────────┘
                             │
                    ┌────────┴─────────┐
                    │   REST API 适配器 │
                    └──────────────────┘

核心概念:

六边形架构的优势:

// 入站端口(核心对外暴露)
public interface OrderService {
    Order createOrder(CreateOrderCommand cmd);
    Order getOrder(String orderId);
}

// 出站端口(核心需要的外部服务)
public interface OrderRepository {
    void save(Order order);
    Order findById(String id);
}

public interface PaymentGateway {
    boolean charge(Order order);
}

// 核心实现(不依赖任何框架)
public class OrderServiceImpl implements OrderService {
    private final OrderRepository repository;
    private final PaymentGateway paymentGateway;

    public OrderServiceImpl(OrderRepository repository, PaymentGateway paymentGateway) {
        this.repository = repository;
        this.paymentGateway = paymentGateway;
    }

    public Order createOrder(CreateOrderCommand cmd) {
        Order order = new Order(cmd);
        if (paymentGateway.charge(order)) {
            repository.save(order);
        }
        return order;
    }
}

// 适配器层(依赖框架)
@Repository
public class JpaOrderRepository implements OrderRepository {
    @Autowired private OrderJpaRepository jpaRepo;
    public void save(Order order) { jpaRepo.save(toEntity(order)); }
    public Order findById(String id) { return toDomain(jpaRepo.findById(id)); }
}

2.3 整洁架构(Clean Architecture)

整洁架构由 Robert C. Martin(Uncle Bob)提出,是对六边形架构的扩展,强调依赖规则:源代码依赖必须指向更内层。

┌─────────────────────────────────────────────────────┐
│                   Frameworks & Drivers              │  ← 最外层:数据库、UI、外部 API
│  ┌─────────────────────────────────────────────┐    │
│  │              Interface Adapters             │  ← 控制器、网关、展示器
│  │  ┌─────────────────────────────────────┐    │    │
│  │  │          Use Cases                  │  ← 应用业务规则
│  │  │  ┌─────────────────────────────┐    │    │    │
│  │  │  │        Entities             │  ←  ← 企业业务规则
│  │  │  └─────────────────────────────┘    │    │    │
│  │  └─────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────┘

四层结构:

  1. Entities(实体):企业级业务规则,最不具体的、最高层次的业务逻辑
  2. Use Cases(用例):应用特定的业务规则,协调实体完成具体任务
  3. Interface Adapters(接口适配器):将外部数据格式转换为内部使用的格式
  4. Frameworks & Drivers(框架与驱动):所有具体实现细节

依赖规则(Dependency Rule):

2.4 CQRS(Command Query Responsibility Segregation)

CQRS 模式将系统的写操作(Command)和读操作(Query)分离为两个独立的模型。

┌─────────────┐         ┌──────────────┐
│   Command   │         │    Query     │
│   Model     │         │    Model     │
│             │         │              │
│  写操作      │         │  读操作       │
│  创建/更新   │         │  查询/展示    │
└──────┬──────┘         └──────┬───────┘
       │                       │
       ▼                       ▼
┌─────────────┐         ┌──────────────┐
│ Write DB    │         │  Read DB     │
│ (规范化)     │         │  (反规范化)   │
└──────┬──────┘         └──────┬───────┘
       │                       │
       └───────────┬───────────┘
                   │
            ┌──────▼──────┐
            │   Event Bus  │
            │  (同步数据)   │
            └─────────────┘

CQRS 的适用场景:

CQRS 的优势:

注意事项:

2.5 Event Sourcing(事件溯源)

Event Sourcing 不直接存储当前状态,而是存储导致状态变化的一系列事件。当前状态通过重放事件重建。

┌─────────────────────────────────────────────────────┐
│                   Event Store                       │
│                                                     │
│  Event 1: OrderCreated {id: 1, amount: 100}        │
│  Event 2: ItemAdded {orderId: 1, item: "A"}        │
│  Event 3: OrderPaid {orderId: 1, method: "CC"}     │
│  Event 4: OrderShipped {orderId: 1, carrier: "SF"} │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│                  State Reconstruction               │
│                                                     │
│  replay(Event 1) → Order {id: 1, status: CREATED}  │
│  replay(Event 2) → Order {items: ["A"]}            │
│  replay(Event 3) → Order {status: PAID}            │
│  replay(Event 4) → Order {status: SHIPPED}         │
└─────────────────────────────────────────────────────┘

Event Sourcing 的核心概念:

Event Sourcing 的优势:

Event Sourcing 的挑战:


三、分布式系统设计

分布式系统是由多个独立计算机组成的系统,这些计算机通过网络通信和协调,对外表现为一个统一的整体。分布式系统设计的核心挑战是处理网络延迟、节点故障和数据一致性。

3.1 CAP 定理

CAP 定理由 Eric Brewer 提出,指出在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)三者不可兼得,最多只能同时满足两个。

                    Consistency (C)
                         ▲
                        / \
                       /   \
                      /     \
                     /       \
                    /         \
             CA ◄──┤           ├──► CP
                  / \         / \
                 /   \       /   \
                /     \     /     \
               /       \   /       \
              /         \ /         \
             └───────────┴───────────►
        Partition (P)           Availability (A)

三个特性的含义:

CAP 定理的实践指导:

现代分布式系统通常选择 AP + 最终一致性CP + 强一致性,根据业务场景权衡。

3.2 BASE 理论

BASE 理论是对 CAP 定理中 AP 方案的补充,由 eBay 提出,是一种通过牺牲强一致性来保证可用性的设计哲学。

BASE 的三个要素:

最终一致性的实现方式:

BASE 理论的典型应用:DNS 系统、电商库存系统、社交网络 feed 流。

3.3 分布式锁

分布式锁用于在分布式系统中保证多个节点对共享资源的互斥访问。

Redis Redlock

Redlock 是 Redis 官方提出的分布式锁算法,通过在多个独立的 Redis 实例上获取锁来保证可靠性。

算法步骤:

  1. 记录开始时间
  2. 依次向 N 个 Redis 实例发送 SET 命令(带 NX 和 EX 选项)
  3. 如果在超过半数实例上成功获取锁,且总耗时小于锁有效期,则获取锁成功
  4. 否则,向所有实例发送 DEL 命令释放锁
// 使用 Redisson 实现 Redlock
RLock lock1 = redisson1.getLock("myLock");
RLock lock2 = redisson2.getLock("myLock");
RLock lock3 = redisson3.getLock("myLock");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
try {
    boolean success = redLock.tryLock(10, 30, TimeUnit.SECONDS);
    if (success) {
        // 执行业务逻辑
    }
} finally {
    redLock.unlock();
}

Redlock 的争议:分布式系统专家 Martin Kleppmann 指出 Redlock 依赖系统时钟,在时钟跳变时可能出现问题。

ZooKeeper 分布式锁

ZooKeeper 通过顺序节点和 Watch 机制实现分布式锁。

实现原理:

  1. 在指定路径下创建临时顺序节点
  2. 获取该路径下所有子节点并排序
  3. 如果自己创建的节点序号最小,则获取锁
  4. 否则,监听前一个节点的删除事件
  5. 前一个节点被删除后,重新检查自己是否序号最小
// 使用 Curator 实现 ZooKeeper 分布式锁
InterProcessMutex lock = new InterProcessMutex(client, "/locks/myLock");
try {
    lock.acquire();
    // 执行业务逻辑
} finally {
    lock.release();
}

ZooKeeper 锁的优势:不依赖系统时钟,可靠性高;劣势:性能相对较低,依赖 ZooKeeper 集群。

数据库锁

数据库锁是最直接的分布式锁实现方式。

-- 悲观锁(SELECT ... FOR UPDATE)
BEGIN;
SELECT * FROM locks WHERE resource_id = 1 FOR UPDATE;
-- 执行业务逻辑
COMMIT;

-- 乐观锁(版本号机制)
UPDATE resources SET data = 'new_data', version = version + 1
WHERE id = 1 AND version = 5;

-- 唯一约束(INSERT 方式)
INSERT INTO distributed_locks (lock_name, expire_at)
VALUES ('myLock', NOW() + INTERVAL 30 SECOND);

3.4 分布式 ID 生成

在分布式系统中,需要生成全局唯一的 ID 来标识实体。

雪花算法(Snowflake)

雪花算法由 Twitter 提出,生成 64 位 Long 型 ID。

┌─────────────────┬──────────────┬──────────────┬─────────────┐
│  1 bit (符号位)  │  41 bit 时间戳 │  10 bit 机器ID │  12 bit 序列号 │
│      0          │   毫秒级      │   数据中心+机器 │   每毫秒计数   │
└─────────────────┴──────────────┴──────────────┴─────────────┘

雪花算法的优势:

public class SnowflakeIdGenerator {
    private final long workerId;
    private final long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    // 时间起点(2020-01-01)
    private static final long EPOCH = 1577836800000L;
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);

    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("Worker ID 超出范围");
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID 超出范围");
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨,拒绝生成 ID");
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = waitNextMillis();
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }

    private long waitNextMillis() {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

UUID

UUID(Universally Unique Identifier)是 128 位的标识符,标准格式为 8-4-4-4-12 的十六进制字符串。

UUID 的优势:全局唯一、无需中心服务、生成简单。 UUID 的劣势:无序(不适合作为数据库主键)、长度较长、可读性差。

Leaf / TinyID

美团 Leaf 和滴滴 TinyID 是工业级的分布式 ID 生成方案。

Leaf 的两种模式:

TinyID 的核心:基于数据库号段模式,支持多 db 负载均衡,提供 HTTP 和 Java SDK 两种调用方式。

3.5 缓存架构

缓存是提升系统性能的重要手段,但使用不当会导致数据不一致、系统崩溃等问题。

缓存穿透

缓存穿透指查询一个不存在的数据,缓存不命中,请求直接打到数据库。

解决方案:

// 缓存空值示例
public User getUser(String id) {
    User user = cache.get(id);
    if (user != null) {
        return user == NULL_USER ? null : user;
    }
    user = db.findById(id);
    if (user == null) {
        cache.set(id, NULL_USER, 60); // 缓存空值 1 分钟
    } else {
        cache.set(id, user, 3600);
    }
    return user;
}

缓存击穿

缓存击穿指某个热点 key 在过期瞬间,大量请求同时到达数据库。

解决方案:

// 互斥锁方案
public User getUserWithMutex(String id) {
    User user = cache.get(id);
    if (user != null) {
        return user;
    }
    // 获取分布式锁
    String lockKey = "lock:user:" + id;
    boolean locked = redis.setnx(lockKey, "1", 10);
    if (locked) {
        try {
            user = db.findById(id);
            if (user != null) {
                cache.set(id, user, 3600);
            }
        } finally {
            redis.del(lockKey);
        }
    } else {
        // 等待后重试
        Thread.sleep(50);
        return getUserWithMutex(id);
    }
    return user;
}

缓存雪崩

缓存雪崩指大量缓存在同一时间过期,导致数据库压力骤增。

解决方案:

// 随机过期时间
int expireTime = 3600 + new Random().nextInt(300); // 3600~3900 秒
cache.set(key, value, expireTime);

缓存一致性

缓存与数据库数据不一致是常见问题,常见的保证一致性的方案:

// Cache Aside Pattern
public void updateUser(User user) {
    db.update(user);
    cache.delete("user:" + user.getId()); // 删除缓存,而非更新
}

3.6 负载均衡

负载均衡是将请求分发到多个后端服务器,以提高系统的处理能力和可用性。

客户端负载均衡

客户端负载均衡由服务调用方决定请求发送到哪个服务实例。

典型实现:Spring Cloud Ribbon + Feign

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

// 使用
restTemplate.getForObject("http://user-service/users/1", User.class);

服务端负载均衡

服务端负载均衡由独立的负载均衡器(如 Nginx、HAProxy)分发请求。

upstream backend {
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=2;
    server 192.168.1.12:8080 backup;
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

负载均衡算法


四、架构决策与权衡

架构设计的核心不是选择最好的技术,而是在约束条件下做出最合适的权衡。

4.1 技术选型原则

技术选型应考虑以下因素:

  1. 业务需求:技术必须服务于业务目标,而非相反
  2. 团队能力:选择团队熟悉的技术,降低学习成本
  3. 生态成熟度:选择有活跃社区、丰富文档和工具的技术
  4. 长期维护:评估技术的生命周期,避免选择即将淘汰的技术
  5. 成本:包括开发成本、运维成本、授权费用
  6. 可扩展性:技术是否能支撑未来 1-3 年的业务增长
  7. 安全性:技术是否有成熟的安全实践

技术选型的常见陷阱:

4.2 架构演进路径

系统的架构通常经历以下阶段:

单体应用 → 垂直拆分 → 服务化 → 微服务 → Service Mesh
  1. 单体应用:所有功能部署在一个进程中,适合初创团队和简单业务
  2. 垂直拆分:按业务模块拆分为多个独立应用,解决单体部署慢的问题
  3. 服务化(SOA):抽取共享服务,通过 ESB(企业服务总线)集成
  4. 微服务:细粒度服务拆分,去中心化治理,独立部署
  5. Service Mesh:将服务间通信、负载均衡、熔断等基础设施下沉到 Sidecar

架构演进的关键原则:

4.3 架构文档

良好的架构文档是团队协作和知识传承的基础。

C4 模型

C4 模型由 Simon Brown 提出,从四个层次描述软件架构:

  1. Context(上下文):系统在整个业务环境中的位置
  2. Container(容器):系统的顶层组件(应用、数据库、文件系统)
  3. Component(组件):容器内的主要组件及其职责
  4. Code(代码):组件的代码实现(类图、时序图)
┌─────────────────────────────────────────────┐
│              System Context                 │  ← 系统与外部系统的关系
│  ┌─────────────────────────────────────┐    │
│  │            Containers               │  ← 系统的可部署单元
│  │  ┌─────────────────────────────┐    │    │
│  │  │         Components          │  ←  ← 容器内的模块
│  │  │  ┌─────────────────────┐    │    │    │
│  │  │  │        Code         │  ←  ←  ← 代码实现
│  │  │  └─────────────────────┘    │    │    │
│  │  └─────────────────────────────┘    │    │
│  └─────────────────────────────────────┘    │
└─────────────────────────────────────────────┘

ADR(Architecture Decision Record)

ADR 是一种轻量级的架构决策记录方式,记录架构决策的背景、选项和结果。

模板:

# ADR-001: 选择 PostgreSQL 作为主数据库

## 状态
已接受

## 背景
系统需要支持复杂查询和事务,且团队有 PostgreSQL 经验。

## 决策
使用 PostgreSQL 14 作为主数据库。

## 后果
- 优点:支持 JSONB、全文搜索、成熟的事务支持
- 缺点:水平扩展能力不如 NoSQL
- 风险:单点故障,需要配置主从复制

五、学习资源推荐

书籍

在线资源

实践项目


总结

架构设计是一项需要持续学习和实践的技能。掌握设计模式可以帮助你写出更优雅的代码,理解架构风格可以帮助你更好地组织系统,而分布式系统设计的知识则是构建大规模系统的基础。

关键要点:

  1. 没有银弹:每种架构风格都有其适用场景和局限性
  2. 权衡取舍:架构决策是在约束条件下做出的最优选择
  3. 持续演进:架构应该随着业务的发展而不断调整
  4. 简单优先:不要过度设计,简单的方案往往是最好的
  5. 数据驱动:用监控和指标来验证架构决策的正确性