古茗的供应链项目贯穿多个业务部门,在开发阶段我们需要完成大多数业务场景的开发和自测,这样才能保证联调阶段上下游业务的足够通顺。但在日常项目开发中,我们发现和接口有太多的对接和联调,如若要满足上述要求,我们就需要有一个接口工具来满足以下需求:
目前古茗内部使用了 yapi 作为接口管理平台,yapi 在提供接口文档的同时还提供了基于接口元数据的 Mock 能力。我们期望围绕着现有的 yapi 平台打造一份既能实现接口抓包,也能提供接口 Mock 的前端工具。
此前古茗内部团队已经陆续尝试过不同形式的接口抓包和 Mock 工具了,如下所示:
+ webview | + 配置简单 + 对业务代码无任何侵入性 | + 真机调试体验不好,经常卡 | ||
+ webview + H5 | + 需要侵入性安装三方 npm + 需要对全局接口进行拦截 | |||
+ webview + H5 | + 对业务代码无任何侵入性 |
+ Mock 成本高(所有字段需要一一定义) + Mock 后忘记撤回导致线上问题 | |||
+ 需要安装插件 | |||
+ 配置麻烦 | |||
+ 对业务代码无侵入性 | + 只能基于 yapi project mock,无法单接口简单 mock + 接口需要二次调整后才能使用 | ||
+ Mock 成本高(所有字段需要一一定义) + 对业务代码有侵入性 |
基于上述背景和分析,我们细化了接口工具的需求点:
正是在这种背景下,供应链内部的接口抓包 & Mock 解决方案 plug
应运而生,它极大程度的贴合了我们古茗的接口开发习惯,接下来会为大家核心介绍下
plug 在英文中代表 插头 🔌,生动形象的表达了这个工具的定位(数据衔接)
plug 提供了网页端和 APP 端的管理后台,主要包含以下功能
和大多数抓包工具类似,plug
提供了 http``https``ws
协议的抓包能力,主要用于移动端调试和问题定位:
Mock 能力是 plug 的核心功能,plug 打通了 yapi,可以基于项目批量 Mock 和单接口 Mock。
同时我们提供了对于 Mock 数据的二次编辑能力
plug 默认会使用 yapi 的 mock 能力,二次编辑后便会使用编辑后的数据进行 mock
plug
的核心就是接口代理,无论是 mock
还是抓包,本质上都需要经过代理后再进行二次处理
http 接口代理如下所示,plug 会拦截所有请求,并有选择性的转发到源服务器,或是 mock 服务上。整个过程都会受到 plug 的网络监控,并实时同步到管理后台上
在 http 下,代理都显得那么自然并且简单,直到有了 https,一切都变了,我们不能再用 http 代理的方式去做 https 接口的代理
But Why ?为什么 https 这么特殊呢?我们先通过下图简单介绍下 https 下的数据通信过程:
由于 https 里增加了 「证书验证」和「数据加密」环节,导致普通的中间人根本无法拿到 https 传输过程中的数据,所以我们要换个思路来解决这个问题。通过中间人伪造证书,同时和 「浏览器」、「服务器」进行双向的 「证书验证」和「数据加密」,调整后的整个流程如下图所示:
这里只介绍了 plug 实现的核心流程,隐藏了部分 https 通信的细节,如果对 https 、证书、加密感兴趣的可以查阅网上相关资料
CA 是 Certificate Authority 的缩写,也叫“证书授权中心”
在 https 安全层建立环节中,CA 证书起到了至关重要的环节。如果要建立一个可以同时与客户端和服务端进行通信的网络服务,plug
必须要「伪造」一个根证书以及具备生成不同子证书的能力。
讲到这里,大家一定会好奇,为什么 plug
需要「伪造」根证书,不能像普通的网站一样去申请证书吗?
很遗憾,答案是基本不可能。But why ?
我们需要先通过一张图来简单介绍下 CA 证书的递进关系:
证书信任链:如果你的系统信任了根证书,那么会自动信任根证书颁发的所有子证书
这里回答一下 ”为什么 plug
需要「伪造」根证书?“
最后我们看下 plug
内部是如何管理证书的:
在没有使用 plug
之前,大家的电脑基本都使用了各种 ”科学上网“ 的工具。在使用 plug
工具之后,请求都代理到了plug
上并进行转发,那 ”科学上网“ 不就没用了吗?在这种背景下,plug
适合兼顾 “科学上网” 呢?
其实有挺多三方插件都能实现这个诉求,我们通过下图来简单描述下其背后的实现原理:
Node Agent 对 tcp 的连接进行了池化管理,并做了连接复用,正式利用了这个特点,我们在 tcp 阶段和代理服务进行连接
plug
的 Mock 功能在使用过程中发现了几个问题:
data.length
完全对不上以下内容是 plug 内部调用 yapi 的 Mock 服务所生成的
针对上述问题,我们发现问题 3 比较好解决,因为古茗内部的接口已经遵循一定的格式规范,我们可以在 Mock 服务端代码里来处理分页逻辑的问题。但是对于问题 1、2,以现有的 yapi 或是三方插件都不能妥善的解决 (它需要能理解我们的字段 key 的含义,同时需要借鉴字段的 description 内容)。
随后我们考虑是否可以让大模型来辅助我们进行 Mock,我们尝试将接口的 json schema 定义通过 prompt 发送给大模型,让大模型来 Mock 上述接口
以下内容是通过调用 deepseek 官网提供的 API 接口所生成的
相比之后我们发现大模型 Mock 的字段足够语义化、且能解决枚举字段的问题。再配合 Mock 服务的分页逻辑处理,便能很好的解决上述 3 个问题。
既然大模型的 Mock 结果如此理想,我们就考虑如何把 plug
和大模型结合起来,这时就产生了两个方案:「使用大模型的开放 API 服务」、「使用本地部署的大模型」
使用大模型提供的 API 服务,能带来以下优势:
但与此同时也会带来一些问题 (以 deepseek 官网提供的 API 能力为例):
针对上述问题,我们尝试了以下方案来 “缓解” 问题带来的影响:
火上引擎上 deepseek V3 的收费标准
deepseek 官网提供的收费标准
我们尝试在本地通过 ollama
部署了蒸馏版的大模型,并通过 ollama
提供的 API 服务调用本地部署好的大模型。
我们可以通过其他方式在本地部署大模型,笔者采用 ollama 部署的主要原因是因为 ollama 比较方便而且有 API 可以调用,另外由于机器受限,部署的大模型基本都是蒸馏版本
部署之后,我们用同样的参数和 prompt 调用本地的模型,发现接口的 RT 基本都能控制在 10s 以内(这对你的 prompt 有一定的要求),但是大模型返回的 Mock 内容有以下问题:
format: json
时,返回的内容乱七不糟,有时甚至直接返回输入的 prompt (罢工了)我们翻阅了文档,发现以下参数或配置会影响大模型的生成文本的随机性:
2、3 基本可以排除了,看来主要是 1 的问题,1 里面涉及的关键词较多,这边只列出几个关键的参数说明:
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 内容就比较稳定了,基本都能返回以下内容:
额外说明:
关于调用模型的优化还包含了其他方面(比如说基于不同接口的 prompt 优化),文章中没有一一阐述 调用本地的模型时,首次 RT 会稍久些,后面调用的 RT 能控制在 10s 内(主要是因为 ollama 提供的 keep_alive 能力,让模型缓存在内存中)
在 plug
最新的 beta 版里,我们实验性的开启了基于大模型的智能 Mock 功能。同时我们采取了优先使用「本地部署的大模型」,可选择性启用 「火山引擎的大模型的开放 API 服务」
本文通过从功能到原理的形式,介绍了古茗供应链团队接口 Mock 解决方案 - plug
。并结合现在流行的大模型解决了 Mock 数据不真实的问题,虽然这个功能还在试验中,且会出现不稳定的问题,但我们会持续优化plug
的功能和稳定性,让 Mock 能力更加便捷、有效。