二、系统架构设计:整体架构分层与高可用设计

好,咱们直接进入正题。系统架构设计这块,说白了就是搭骨架。骨架搭不好,后面加再多肌肉也白搭。我在做风控前置系统时,最深的体会就是:分层一定要清晰,职责一定要单一。你想想看,一个交易请求进来,如果所有逻辑都揉在一起,那排查问题就像在一锅粥里找米粒——累死人。

2.1 整体架构分层

我个人习惯把风控前置系统分成四层:接入层、路由层、风控层、决策层。每一层各司其职,互不干扰。我画了一张图,你先感受下整体脉络。

直连交易风控前置系统 - 四层架构 接入层 协议适配 | 报文解析 | 签名验签 | 限流熔断 路由层 交易类型识别 | 渠道分发 | 负载均衡 | 灰度路由 风控层 规则引擎 | 黑白名单 | 频次控制 | 设备指纹 决策层 评分模型 | 决策树 | 人工审核 | 最终放行/拒绝 数据流方向:交易请求 → 接入 → 路由 → 风控 → 决策 → 结果返回 交易数据流

2.2 各层模块职责划分

2.2.1 接入层

接入层是系统的门面。所有外部请求第一脚踩到的就是它。它的核心职责就四个字:收、解、验、限

  • 协议适配:HTTP、HTTPS、TCP、MQ,来啥接啥。我记得有一次对接一个老系统,对方只支持自定义二进制协议,那叫一个折腾。
  • 报文解析:JSON、XML、Protobuf,统一转成内部标准格式。
  • 签名验签:确保请求没被篡改。我曾经遇到过商户密钥泄露导致批量盗刷的案例,从那以后我对验签逻辑格外严格。
  • 限流熔断:防止突发流量把系统冲垮。说白了就是「别让一个人把饭全吃光了」。
我的经验:接入层一定要做「快速失败」。如果请求格式不对或签名错误,尽早返回错误,别让无效请求穿透到后面几层,白白浪费计算资源。

2.2.2 路由层

路由层干的事,就是「看人下菜碟」。根据交易类型、商户ID、渠道来源,把请求分发给对应的风控策略组。

  • 交易类型识别:支付、退款、充值、提现……每种交易的风险特征完全不同。
  • 渠道分发:不同渠道可能有不同的风控要求。比如微信支付和银行卡直连,风控逻辑就不一样。
  • 负载均衡:把请求均匀分配到后端的风控节点上。别让一台机器累死,其他机器闲死。
  • 灰度路由:新策略上线时,先切一小部分流量验证。没问题再全量放开。这个机制救过我很多次。

2.2.3 风控层

风控层是整个系统的核心。这里跑的是真正的「硬菜」——规则和模型。

  • 规则引擎:可配置的if-then逻辑。比如「单笔金额超过5万,触发人工审核」。
  • 黑白名单:IP黑名单、设备黑名单、银行卡黑名单。命中直接拒绝,效率最高。
  • 频次控制:同一用户1分钟内交易超过10笔?先拦下来看看。
  • 设备指纹:通过浏览器特征、设备ID等识别是不是同一台设备在搞鬼。
避坑指南:我曾经犯过一个错——把规则引擎的配置直接写在代码里。每次改规则都要发版,运维同学差点没把我拉黑。后来全部改成配置中心动态下发,这才消停。

2.2.4 决策层

决策层是最后一关。它综合风控层产出的所有信号,给出最终结论:放行、拒绝、还是转人工。

  • 评分模型:用机器学习模型给交易打分。分数越高风险越大。
  • 决策树:多层条件判断。比如「分数>80且命中黑名单 → 拒绝」。
  • 人工审核:拿不准的,推给风控运营人员人工判断。
  • 最终动作:返回结果给上游系统,同时记录日志用于事后分析。

2.3 高可用设计原则

做风控系统,最怕什么?最怕系统挂了,交易没人管。要么所有交易都放行(风险敞口大开),要么所有交易都拒绝(业务停摆)。这两种情况都够你喝一壶的。所以高可用设计是重中之重。

设计原则 说明 我的实践
无状态设计 每个节点不保存业务状态,请求可以打到任意节点 所有规则配置放Redis/配置中心,节点只做计算
冗余部署 至少2个机房,每个机房多节点 曾经单机房光纤被挖断,全靠异地节点撑着
熔断降级 下游服务挂了,自动切换到降级策略 比如模型服务超时,自动降级为纯规则判断
灰度发布 新版本先让1%流量试试水 有一次新规则误杀率高达30%,灰度救了我一命
全链路监控 每一层的耗时、错误率、吞吐量都要可观测 我习惯用Prometheus + Grafana搭看板

核心原则就一句话:别让任何一个单点成为系统的「阿喀琉斯之踵」。你想想看,如果风控层挂了,决策层拿不到评分结果怎么办?我的做法是:设置超时降级——风控层超过200ms没返回,决策层自动走「宽松策略」,只拦最明显的风险。

2.4 代码示例:接入层限流实现

光说不练假把式。我贴一段接入层限流的伪代码,用的是滑动窗口算法。这个算法比简单的计数器更平滑,能有效防止「突刺」流量。

// 滑动窗口限流器
public class SlidingWindowRateLimiter {
    private final int maxRequests;        // 窗口内最大请求数
    private final long windowSizeMs;      // 窗口大小(毫秒)
    private final ConcurrentLinkedQueue<Long> timestamps = new ConcurrentLinkedQueue<>();

    public SlidingWindowRateLimiter(int maxRequests, long windowSizeMs) {
        this.maxRequests = maxRequests;
        this.windowSizeMs = windowSizeMs;
    }

    public synchronized boolean tryAcquire() {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSizeMs;

        // 移除窗口外的旧时间戳
        while (!timestamps.isEmpty() && timestamps.peek() < windowStart) {
            timestamps.poll();
        }

        if (timestamps.size() < maxRequests) {
            timestamps.offer(now);
            return true;  // 放行
        } else {
            return false; // 限流
        }
    }
}
使用建议:接入层每个接口都应该配置独立的限流器。比如「查询余额」接口和「支付」接口的限流阈值肯定不一样。我在项目中习惯用注解 + AOP的方式,把限流逻辑和业务逻辑解耦,清爽得很。

2.5 高可用部署架构

最后聊聊部署。我推荐用「主备 + 多活」的模式。主备保证数据不丢,多活保证服务不挂。

  • 接入层:Nginx + Keepalived,做VIP漂移。一台挂了,另一台秒级接管。
  • 路由层:基于一致性哈希的负载均衡,保证相同商户的请求落到同一台机器,方便缓存命中。
  • 风控层:无状态节点,水平扩展。压力大了就加机器,简单粗暴。
  • 决策层:依赖Redis和数据库,必须做主从+哨兵模式。我见过一次Redis主库宕机导致全站交易瘫痪的惨案,印象深刻。

嗯,架构设计这块就聊到这儿。记住一句话:好的架构不是设计出来的,是踩坑踩出来的。你做得越多,越能体会每层存在的意义。


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