从抓包到 Mock,Plug 工具如何让开发更智能

avatar
@古茗科技

前言

古茗的供应链项目贯穿多个业务部门,在开发阶段我们需要完成大多数业务场景的开发和自测,这样才能保证联调阶段上下游业务的足够通顺。但在日常项目开发中,我们发现和接口有太多的对接和联调,如若要满足上述要求,我们就需要有一个接口工具来满足以下需求:

  • 接口抓包:移动端调试的时候需要查看接口的入参和返回内容
  • 接口 Mock: 联调前,前端需要通过接口 Mock 的形式来完成页面开发

目前古茗内部使用了 yapi 作为接口管理平台,yapi 在提供接口文档的同时还提供了基于接口元数据的 Mock 能力。我们期望围绕着现有的 yapi 平台打造一份既能实现接口抓包,也能提供接口 Mock 的前端工具。

现状

此前古茗内部团队已经陆续尝试过不同形式的接口抓包和 Mock 工具了,如下所示:

接口抓包

方式适用范围使用成本优点缺点
小程序开发者工具 - 真机调试+ 小程序
+ webview
简单+ 无需三方工具
+ 配置简单
+ 对业务代码无任何侵入性
+ 需要本地编译,不能直接监控正式版小程序
+ 真机调试体验不好,经常卡
vconsole+ 小程序
+ webview
+ H5
简单+ 配置简单+ 移动端控制台查看效果不佳
+ 需要侵入性安装三方 npm
+ 需要对全局接口进行拦截
charles+ 小程序
+ webview
+ H5
较难+ 适配广、功能强大
+ 对业务代码无任何侵入性
+ 配置麻烦

接口 Mock

方式使用成本优点缺点
使用静态数据简单+ 方便+ 对业务代码有侵入性
+ Mock 成本高(所有字段需要一一定义)
+ Mock 后忘记撤回导致线上问题
使用 NPM 包 Mock简单+ 直接 mock,不需要一一定义+ 对业务代码有侵入性
+ 需要安装插件
charles较难+ mock 功能强大且灵活+ Mock 成本高(所有字段需要一一定义)
+ 配置麻烦
维斯 mock 工具一般+ 打通 yapi 一键 mock
+ 对业务代码无侵入性
+ 影响翻墙工具
+ 只能基于 yapi project mock,无法单接口简单 mock
+ 接口需要二次调整后才能使用
Apifox 等三方工具简单+ 功能完善、支持本地和云端+ 不能与私有化的 yapi 打通
+ Mock 成本高(所有字段需要一一定义)
+ 对业务代码有侵入性

目标

基于上述背景和分析,我们细化了接口工具的需求点:

  • 对代码无侵入性
  • 集接口监控和 Mock 于一身,能够适配小程序和 pc 端
  • 简单配置
  • 不影响开发者本地的翻墙
  • 打通 yapi,提供一站式的接口 Mock 和二次编辑能力
  • 能对 Mock 数据进行一些通用的业务处理(例如将返回的 code 置为 0)

正是在这种背景下,供应链内部的接口抓包 & Mock 解决方案 plug应运而生,它极大程度的贴合了我们古茗的接口开发习惯,接下来会为大家核心介绍下

plug 功能概述

plug 在英文中代表 插头 🔌,生动形象的表达了这个工具的定位(数据衔接)

plug 提供了网页端和 APP 端的管理后台,主要包含以下功能

接口抓包

和大多数抓包工具类似,plug 提供了 http``https``ws协议的抓包能力,主要用于移动端调试和问题定位:

接口 Mock

Mock 能力是 plug 的核心功能,plug 打通了 yapi,可以基于项目批量 Mock 和单接口 Mock。

同时我们提供了对于 Mock 数据的二次编辑能力

plug 默认会使用 yapi 的 mock 能力,二次编辑后便会使用编辑后的数据进行 mock

plug 实现原理

plug 的核心就是接口代理,无论是 mock还是抓包,本质上都需要经过代理后再进行二次处理

http 接口代理

http 接口代理如下所示,plug 会拦截所有请求,并有选择性的转发到源服务器,或是 mock 服务上。整个过程都会受到 plug 的网络监控,并实时同步到管理后台上

画板

https 接口代理

在 http 下,代理都显得那么自然并且简单,直到有了 https,一切都变了,我们不能再用 http 代理的方式去做 https 接口的代理

But Why ?为什么 https 这么特殊呢?我们先通过下图简单介绍下 https 下的数据通信过程:

画板

由于 https 里增加了 「证书验证」和「数据加密」环节,导致普通的中间人根本无法拿到 https 传输过程中的数据,所以我们要换个思路来解决这个问题。通过中间人伪造证书,同时和 「浏览器」、「服务器」进行双向的 「证书验证」和「数据加密」,调整后的整个流程如下图所示:

画板

这里只介绍了 plug 实现的核心流程,隐藏了部分 https 通信的细节,如果对 https 、证书、加密感兴趣的可以查阅网上相关资料

CA 证书

CA 是 Certificate Authority 的缩写,也叫“证书授权中心”

在 https 安全层建立环节中,CA 证书起到了至关重要的环节。如果要建立一个可以同时与客户端和服务端进行通信的网络服务,plug必须要「伪造」一个根证书以及具备生成不同子证书的能力。

讲到这里,大家一定会好奇,为什么 plug需要「伪造」根证书,不能像普通的网站一样去申请证书吗?

很遗憾,答案是基本不可能。But why ?

我们需要先通过一张图来简单介绍下 CA 证书的递进关系:

画板

证书信任链:如果你的系统信任了根证书,那么会自动信任根证书颁发的所有子证书

这里回答一下 ”为什么 plug需要「伪造」根证书?“

  • 1、理论上 plug 要申请所有被代理的域名的证书
  • 2、正牌的证书申请会校验你对域名是否拥有实际支配权

最后我们看下 plug内部是如何管理证书的:

画板

兼顾系统代理

在没有使用 plug之前,大家的电脑基本都使用了各种 ”科学上网“ 的工具。在使用 plug工具之后,请求都代理到了plug上并进行转发,那 ”科学上网“ 不就没用了吗?在这种背景下,plug适合兼顾 “科学上网” 呢?

其实有挺多三方插件都能实现这个诉求,我们通过下图来简单描述下其背后的实现原理:

画板

Node Agent 对 tcp 的连接进行了池化管理,并做了连接复用,正式利用了这个特点,我们在 tcp 阶段和代理服务进行连接

plug & 大模型

plug的 Mock 功能在使用过程中发现了几个问题:

  1. Mock 数据太不真实了,基本都需要二次编辑调整,这样带来的使用成本又增高了
  2. 部分枚举字段的枚举值记录在字段的 description 里,Mock 的时候完全不会采取
  3. Mock 的分页数据逻辑有问题,分页数量和 data.length 完全对不上

以下内容是 plug 内部调用 yapi 的 Mock 服务所生成的

探索大模型协助 Mock

针对上述问题,我们发现问题 3 比较好解决,因为古茗内部的接口已经遵循一定的格式规范,我们可以在 Mock 服务端代码里来处理分页逻辑的问题。但是对于问题 1、2,以现有的 yapi 或是三方插件都不能妥善的解决 (它需要能理解我们的字段 key 的含义,同时需要借鉴字段的 description 内容)。

随后我们考虑是否可以让大模型来辅助我们进行 Mock,我们尝试将接口的 json schema 定义通过 prompt 发送给大模型,让大模型来 Mock 上述接口

以下内容是通过调用 deepseek 官网提供的 API 接口所生成的

相比之后我们发现大模型 Mock 的字段足够语义化、且能解决枚举字段的问题。再配合 Mock 服务的分页逻辑处理,便能很好的解决上述 3 个问题。

既然大模型的 Mock 结果如此理想,我们就考虑如何把 plug 和大模型结合起来,这时就产生了两个方案:「使用大模型的开放 API 服务」、「使用本地部署的大模型」

使用大模型的开放 API 服务

使用大模型提供的 API 服务,能带来以下优势:

  • 享受全尺寸大模型能力
  • 能够自动配置一些模型参数:比如温度参数、随机种子等,同时对 prompt 也会有更好的优化
  • 返回的 Mock 内容比较稳定(从内容随机性的角度来看)

但与此同时也会带来一些问题 (以 deepseek 官网提供的 API 能力为例):

  1. 调用大模型的 API 接口的 RT 时间基本都在 15s 以上 (有时甚至更久)
  2. 模型的 API 接口不稳定,可能会返回空的字符串

针对上述问题,我们尝试了以下方案来 “缓解” 问题带来的影响:

  1. 问题 1 我们在 plug 内部采用了「预热」的形式做提前 Mock,然后将 Mock 的内容缓存到本地,只有当用户点击界面上的同步按钮之后,我们才会再次调用大模型进行 Mock
  2. 问题 2 有两种解决方式:
    1. 降级模式:如果大模型返回的数据异常,我们就降级使用 yapi 的 Mock 服务,当然我们也在不断的调整 prompt 内容,让大模型能够更好的理解需求
    2. 火上引擎:不调用 deepseek 官网提供的 API 服务,而是用火山引擎提供的 deepseek API 服务。相比于 deepseek 官网,火山引擎提供的 API 服务更贵、更稳定(RT 基本也稳定在 10 - 20 s 之间)

火上引擎上 deepseek V3 的收费标准

deepseek 官网提供的收费标准

使用本地部署的大模型

我们尝试在本地通过 ollama部署了蒸馏版的大模型,并通过 ollama提供的 API 服务调用本地部署好的大模型。

我们可以通过其他方式在本地部署大模型,笔者采用 ollama 部署的主要原因是因为 ollama 比较方便而且有 API 可以调用,另外由于机器受限,部署的大模型基本都是蒸馏版本

部署之后,我们用同样的参数和 prompt 调用本地的模型,发现接口的 RT 基本都能控制在 10s 以内(这对你的 prompt 有一定的要求),但是大模型返回的 Mock 内容有以下问题:

  1. 内容随机性太高:对同样的接口,大模型返回的 Mock 内容不固定,而且经常会遗漏对某个字段的 Mock
  2. 对 json 的支持度不高:当配置了 format: json时,返回的内容乱七不糟,有时甚至直接返回输入的 prompt (罢工了)

解决内容随机性问题

我们翻阅了文档,发现以下参数或配置会影响大模型的生成文本的随机性:

  1. 温度参数、随机种子、top-k、top-p 等模型参数
  2. 上下文依赖、模型版本
  3. 硬件差异

2、3 基本可以排除了,看来主要是 1 的问题,1 里面涉及的关键词较多,这边只列出几个关键的参数说明:

  • 温度参数:温度参数作用于模型的输出概率分布。模型会为每个可能的词生成一个概率分布,温度参数通过调整这些概率值,影响最终生成的文本。
    • 温度 < 1:高概率的词权重放大,生成的文本更保守,适合需要稳定输出的服务
    • 温度 > 1:低概率的词权重放大,生成的文本多样化,适合需要创建多样性的服务
    • 温度 = 1:不改变原始概率分布,模型按原始概率生成文本
    • 温度 -> 0:输出完全确定性,但可能缺乏多样性
  • 随机种子:随机种子是控制随机过程的关键参数,通过固定随机种子可以确保实验的可复现性。在文本生成中,随机种子影响采样方法的结果,固定种子可以确保相同的输入和参数下生成相同的输出

通过固定这两个参数,我们发现模型返回的 Mock 内容较为稳定了,基本都是返回以下的内容:

解决对 json 的支持度不高问题

Mock 的结果基本都是 json 格式的,但是如果我们直接传递 format: json给大模型,其返回内容就不太理想了:

返回的内容并不是预期的 Mock 结果,由于本地部署的大模型中间包了一层 ollama,不知道是哪一层支持有问题,随后便考虑使用更完善的 json schema 定义,把 formt: json改成了如下的 json shcema 配置:

"format": {
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "messageBizType": {
            "type": "string",
          },
          // 其他字段的 schema 配置
        },
      }
    }
  }
},

调整后再次调用大模型,返回的 json 内容就比较稳定了,基本都能返回以下内容:

至此为止,本地模型的 Mock 已经达到了较为稳定的情况,返回的 Mock 内容也返回预期情况,同时 RT 基本能控制在 10s 以内

额外说明:

  • 关于调用模型的优化还包含了其他方面(比如说基于不同接口的 prompt 优化),文章中没有一一阐述
  • 调用本地的模型时,首次 RT 会稍久些,后面调用的 RT 能控制在 10s 内(主要是因为 ollama 提供的 keep_alive 能力,让模型缓存在内存中)

plug 的大模型选择

plug最新的 beta 版里,我们实验性的开启了基于大模型的智能 Mock 功能。同时我们采取了优先使用「本地部署的大模型」,可选择性启用 「火山引擎的大模型的开放 API 服务」

总结

本文通过从功能到原理的形式,介绍了古茗供应链团队接口 Mock 解决方案 - plug。并结合现在流行的大模型解决了 Mock 数据不真实的问题,虽然这个功能还在试验中,且会出现不稳定的问题,但我们会持续优化plug的功能和稳定性,让 Mock 能力更加便捷、有效。