设计模式与容错处理:策略模式选择容错策略、状态模式管理状态、责任链处理异常

容错处理,说白了就是系统在出问题的时候还能「扛得住」。我做了十几年系统,见过太多因为容错没做好而翻车的案例。有一次线上支付服务挂了,就因为一个第三方接口超时,结果整个订单链路都堵死了。嗯,从那以后我特别重视容错设计。

今天咱们聊聊三个设计模式在容错场景下的配合:策略模式选容错策略、状态模式管服务状态、责任链模式处理异常。这三个模式组合起来,能搭出一套很灵活的容错框架。

核心思路:容错不是简单的 try-catch,而是一套可配置、可扩展的策略体系。策略模式负责「选」,状态模式负责「管」,责任链负责「链式处理」。

一、策略模式:动态选择容错策略

容错策略有很多种:重试、降级、熔断、限流、静默失败……每个场景适合的策略不一样。我个人习惯把策略定义成接口,然后让调用方按需选择。

举个例子,一个远程调用失败了,你可以选择:

  • 重试策略:最多重试3次,间隔递增
  • 降级策略:返回缓存数据或默认值
  • 熔断策略:直接拒绝请求,快速失败
  • 静默策略:吞掉异常,记录日志

我在项目中遇到过一种情况:同一个接口,白天流量大时用降级策略,晚上流量小时用重试策略。策略模式正好能解决这种动态切换的问题。

// 策略接口
public interface FaultToleranceStrategy {
    void handle(Throwable t, Context ctx);
}

// 重试策略
public class RetryStrategy implements FaultToleranceStrategy {
    private int maxRetries = 3;
    
    public void handle(Throwable t, Context ctx) {
        int retryCount = ctx.getRetryCount();
        if (retryCount < maxRetries) {
            ctx.incrementRetry();
            // 执行重试逻辑
        } else {
            throw new RuntimeException("重试耗尽", t);
        }
    }
}

// 降级策略
public class DegradeStrategy implements FaultToleranceStrategy {
    public void handle(Throwable t, Context ctx) {
        // 返回降级数据
        ctx.setResult(DegradeData.getDefault());
    }
}
我的建议:策略不要写死,最好做成可配置的。比如通过配置文件或配置中心动态切换,这样线上出问题时不用重启服务。

二、状态模式:管理容错状态

容错不是一成不变的。一个服务可能从「正常」状态,变成「熔断」状态,再变成「半开」状态,最后恢复。状态模式就是用来管理这种状态流转的。

你想想看,熔断器有三个状态:

  • CLOSED(关闭):正常状态,请求正常通过
  • OPEN(打开):熔断状态,直接拒绝请求
  • HALF_OPEN(半开):试探状态,放少量请求测试

状态模式的好处是:每个状态的行为是独立的,状态之间的转换逻辑清晰。我曾经在一个项目中用状态模式管理数据库连接池的健康状态,效果很好。

// 状态接口
public interface CircuitBreakerState {
    boolean allowRequest();
    void onSuccess();
    void onFailure();
}

// 关闭状态
public class ClosedState implements CircuitBreakerState {
    private int failureCount = 0;
    private int threshold = 5;
    
    public boolean allowRequest() { return true; }
    
    public void onFailure() {
        failureCount++;
        if (failureCount >= threshold) {
            // 切换到打开状态
        }
    }
}

// 打开状态
public class OpenState implements CircuitBreakerState {
    private long timeout;
    
    public boolean allowRequest() { 
        return System.currentTimeMillis() > timeout;
    }
    
    public void onSuccess() {
        // 切换到关闭状态
    }
}
注意:状态转换一定要考虑并发。多个线程同时修改状态,容易出问题。我建议用 CAS 或者加锁来保证状态切换的原子性。

三、责任链模式:链式处理异常

一个异常来了,可能不止一个处理器需要处理。比如:先记录日志,再发送告警,最后执行降级。责任链模式正好适合这种场景。

责任链的核心是:每个处理器只关心自己该做的事,处理完就传给下一个。这样新增一个处理器,不需要改现有代码。

// 处理器抽象
public abstract class ExceptionHandler {
    protected ExceptionHandler next;
    
    public void setNext(ExceptionHandler next) {
        this.next = next;
    }
    
    public void handle(Exception e, Context ctx) {
        doHandle(e, ctx);
        if (next != null) {
            next.handle(e, ctx);
        }
    }
    
    protected abstract void doHandle(Exception e, Context ctx);
}

// 日志处理器
public class LogHandler extends ExceptionHandler {
    protected void doHandle(Exception e, Context ctx) {
        Logger.log("异常发生: " + e.getMessage());
    }
}

// 告警处理器
public class AlertHandler extends ExceptionHandler {
    protected void doHandle(Exception e, Context ctx) {
        if (e instanceof TimeoutException) {
            AlertService.send("超时告警");
        }
    }
}

// 降级处理器
public class DegradeHandler extends ExceptionHandler {
    protected void doHandle(Exception e, Context ctx) {
        ctx.setResult(DegradeData.getDefault());
    }
}
避坑指南:我曾经在责任链里搞了个死循环——A处理器调了B,B又调了A。后来我加了个「已处理」标记,每个异常只处理一次。

四、三者如何配合

这三个模式不是孤立的。在实际项目中,它们经常一起出现:

  1. 策略模式决定「用什么策略」——比如根据错误类型选择重试还是降级
  2. 状态模式管理「当前状态」——比如熔断器是开还是关
  3. 责任链模式处理「异常流程」——比如先日志、再告警、最后降级

举个例子:一个 RPC 调用失败了,系统先查熔断器状态(状态模式),如果是关闭状态,就按策略执行重试(策略模式),重试失败后走责任链(记录日志、发送告警、返回降级数据)。

总结一下:策略模式解决「怎么做」,状态模式解决「当前是什么状态」,责任链解决「谁来做」。三个模式各司其职,组合起来就是一套完整的容错框架。

五、SVG 框架图:容错处理核心逻辑

容错处理核心逻辑 请求进入 状态模式:检查熔断器状态 CLOSED / OPEN / HALF_OPEN 状态正常? 策略模式:选择容错策略 重试 / 降级 / 静默 快速失败 责任链模式:异常处理 日志 → 告警 → 降级 返回结果 图例 状态模式 策略模式 责任链模式

六、实战中的注意事项

光有模式还不够,落地时有些坑得避开:

  • 策略不要太多:我见过有人搞了十几种策略,结果维护成本比收益还高。3-5种就够了。
  • 状态要持久化:熔断器状态如果只存在内存里,服务重启就丢了。建议用 Redis 或数据库持久化。
  • 责任链要可配置:每个异常走哪些处理器,最好能动态调整。我习惯用配置文件定义链的顺序。
  • 别忘了监控:容错策略执行了多少次?熔断器打开了几次?这些指标一定要上报,不然出了问题都不知道。
我的经验:容错框架刚上线时,先只开日志和监控,不要直接上熔断。观察一段时间,确认策略没问题了,再逐步放开。这样即使有 bug,也不会影响线上业务。

好了,今天的内容就到这里。这三个模式配合起来,能帮你搭出一套灵活又稳定的容错系统。下次遇到类似需求,不妨试试这个组合。

公众号:蓝海资料掘金营,微信deep3321