Nohost 实现原理 —— 远程办公下的开发测试协同提效方案

大厂技术  坚持周更  精选好文

在《Whistle 实现原理 —— 从 0 开始实现一个抓包工具》一文中给大家详细介绍了如何实现一个抓包调试工具 Whsitle。事实上 Whistle 不仅可以供开发人员本地开发调试,也可以部署到公共服务器给产品运营、测试等非开发人员用来访问测试环境、远程抓包调试。但 Whistle 是单进程服务,无法满足多人多项目同时使用,所以基于 Whistle 开发了多进程多用户的远程代理服务 -- Nohost,其主要功能:

Github 仓库:https://github.com/Tencent/nohost 

  1. 团队每个成员或业务模块分配一个 Whistle 实例。

  2. 每个 Whistle 实例可以设置多个独立的环境。

  3. 每个环境可以配置任意 Whistle 规则。

  4. 用户可以通过页面选择需要访问的环境。

Nohost 基本架构

5fb2b2410b9a11a0ebe1091753979056.png

有点类似 Node 的 Cluster 模块,其核心思想是:将多进程多问题转成单进程 Whistle 的问题,即给每个团队成员或业务模块分配一个 Whistle 进程,并通过一个 Master 进程来管理各个 Whistle 进程以及分发请求。

交互流程图及实现原理

14bedf6677e72829197e32279da0abad.png

  1. 访问接入 Nohost 的页面,Nohost 会在页面左下角注入一个小圆点(环境切换按钮)(具体原理后面讲)。

    如何接入 Nohost 参见:https://nohost.pro/docs/quickstart

  2. 点击小圆点弹出环境选择框,点击选择环境后,页面会先将选择的 账号/环境 发送到 Nohost 的 Master 进程a7b8bf3dae842cd6e9f9c1e0f1b2b8ed.png

  3. Master 进程会先获取 clientId(如何获取参见后面的 Master 进程实现原理),将获取的 clientId 与选择的 账号/环境 存到 Master 进程的内存 LRU Cache 里面。

  4. 页面等待设置环境的接口响应或超时后自动刷新页面,这时页面所有请求也会经过 Master 进程,Master 进程通过 3 的方式获取每个请求的 clientId,并根据 clientId 获取之前选择的 账号/环境 ,再将请求转到账号对应的 Whistle 进程及执行对应环境的规则(具体实现参见后面的 Master 进程实现原理)。

这样每个人或业务只需关注自己的 Whistle 进程,功能上也就跟本地的 Whistle 差不多,也可以直接使用所有 Whistle 插件。

Master 进程实现原理

最后看下 Master 进程的实现,Master 进程可以主要有三个功能:

  1. 注入小圆点(环境切换按钮)。

  2. 记录用户环境选择状态。

  3. Whistle 进程管理与请求分发。

    启动(关闭)指定 Whistle 进程并将请求转发到该进程及执行对应环境规则

注入小圆点(环境切换按钮)

小圆点的注入不是简单的往页面追加脚本,还涉及到:

  1. 解析 HTTPS 请求(不然 HTTPS 请求无法注入任何内容)。

  2. 只对 HTML 页面注入小圆点(还需要排除一些返回 json 数据,但类型写成 HTML 的接口)。Nohost 采用在 Master 里面内置一个 Whistle 进程处理上述问题:

    const startWhistle = require('whistle')

97344dc95ef13a92f96e2d79838324f8.png

其中:

  1. 接收请求、解析 HTTPS 请求原理参见:Whistle 实现原理

  2. 注入小圆点,主要是由内置插件 whistle.nohost 实现的,分三部分内容:

  • 注入小圆点白名单:在 Nohost 的管理后台上传证书域名,及入口规则配置的域名、路径、正则、通配符等。02848623f92b3f71ae5e9332bb0280b8.png

  • 插件的 rules.txt 动态获取白名单,并配置规则将上述白名单请求转发到插件。44f404ce9e37d3a84914d545bfed1927.png

  • 插件的 _rules.txt 配置的私有规则,对请求到插件的 html 页面注入小圆点。e815c93048db7abde52f49825484ec5d.png

这里引入了一个插件 whistle.nohost,该插件除了上述注入小圆点功能以外,还有如下的功能。

382848f201f4800f5bad0657d1d7db80.png

记录用户环境选择状态

点击选择环境,页面会发送请求 /.whistle-path.5b6af7b9884e1165/whistle.nohost/cgi-bin/select?name=imweb&envId=test&time=1637026271911 ,其中:

  1. /.whistle-path.5b6af7b9884e1165 是一个特殊路径,Whistle 自动拦截该路径并作为内部请求。

  2. name:账号(分组)名称。

  3. envId:环境名称的编码,encodeURIComponent(envName)

该请求会转到 Nohost 的内置的 whistle.nohost 插件进程的 uiServer,该插件会通过以下方式获取 clientId

  1. 先尝试从请求头 x-whistle-client-id 获取 clientId

  2. 如果没有请求头 x-whistle-client-id,则分别通过请求头 x-forwarded-for 或 req.socket.remoteAddress 获取 clientIp 作为 clientId

将获取的 clientId 和 name/env 存到插件进程的 LRU Cache:lru.set(clientId, name/env)

Whistle 进程管理与请求分发

对 Worker 里面的 Whistle 进程的管理也是通过 whistle.nohost 插件实现的:

  1. 插件通过实现 rulesServer 和 tunnelRulesServer 设置转发规则。bbd7122d77b7f845b9f100bc5ec65878.png

  2. 插件用上面的方式获取 clientId 并根据 clientId 获取用户选择的账号和环境。

  3. 如果没有选择任何环境,则返回空字符串,请求自动转到现网。

  4. 如果有选择环境,则通过 pfork 启动一个 Whistle 进程,并自动分配一个随机端口。

  5. 启动 Whistle 成功后获取端口,设置规则 * internal-proxy://127.0.0.1:xxxxx 让 Master 的 Whistle 进程将请求转到该端口。

internal-proxy 是 Whistle 代理直接转发协议,它可以通过用 http 的方式转发 https 请求

Nohost 插件会自动根据请求需求拉起对应进程,且如果该进程有超过 6 分钟没有请求,则会自动关闭。可以通过 Whistle 提供的方法检测进程是否有请求详见:https://github.com/Tencent/nohost/blob/master/lib/plugins/whistle.nohost/lib/whistle.js#L126。

插件的完整实现代码参见:https://github.com/Tencent/nohost/blob/master/lib/plugins/whistle.nohost/lib/rulesServer.js

参考资料

  1. Github 仓库:https://github.com/Tencent/nohost

  2. 详细文档:https://nohost.pro/

更多文章

浏览器缓存库设计总结(localStorage/indexedDB)

程序员必备的几种常见排序算法和搜索算法总结

使用Intersection Observer API实现视频队列自动播放

从零开发可视化大屏制作平台

electron桌面端Dooring搭建实战

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值