大家好,最近有知乎网友问我 Reqable 技术选型的问题,恰好 Reqable 也刚刚发布了非常重要的 1.3 版本更新,所以此次写一篇关于 Reqable 项目技术栈的全方面总结。
本篇文章的目的,是向大家分享我关于 Reqable 项目的一些技术思考、细节和填坑概要等。希望能够对大家有所帮助、避坑、少走一些弯路、更快交付。有必要说明的是,Reqable 项目完全由我个人独立完成,受限于本人经验和能力,肯定有非常多的不足。如果你对本篇总结中提到的技术选型、处理方式等有不同的见解,欢迎提交评论,一起讨论,共同学习。
如果有不了解 Reqable 的,可以看上面这张图,官网首页: https://reqable.com
下面,话不多说,正文开始。
Reqable 客户端是基于 Flutter 和 C++开发的,由于整体不涉及非常复杂的层次架构,所以我就不放 Arch 图了。其实简而言之,除了网络流量分析框架和 API 请求框架,这两个模块是使用 C++语言开发外,其他几乎都是使用 Dart 语言开发。下面,我们来依次进行介绍。
这个模块内部的代号是Netbare
,如果你查看 Reqable 解压的运行库,应该能看到这个。Netbare
从项目初始就确定了使用 C++语言进行开发,最核心的考量标准就是性能。Netbare
的职责包含下面几个:
MITM
)从上面的职责可以看到,Netbare
既承担了服务器的角色,又承担了客户端的角色。这对性能其实是有一定要求的,也就是说多线程是非常有必要的。又由于 Reqable 是跨平台的项目,C 和 C++成了首要选择语言,Java 、Javascript 、Dart 、C#、Python 等基本可以不考虑,Dart 又是单线程机制,Rust 也是一个可选项,但是这个语言我完全不熟悉。在我的技术栈里面,C++以绝对的优势胜出,恰好最近几年内我写了大量 C++的代码,手还热乎着。
确定 C++作为Netbare
开发语言后,网络框架库就非常自然地选择了asio
。但是我并不想引入Boost
这个庞大的库,所以选择了独立版本的asio,然后 SSL 选择了openssl,最后是格式化 io 库fmt。除此之外,虽然 HTTP2 协议规范是由Netbare
自己实现的,但还是引入了nghttp2作为辅助。这几个开源项目都是作为 git submodule 引入的,非常稳定,我没有修改过任何源码,在此向这些伟大的开源项目致敬。
Netbare
采用和我曾经开源的NetBare-Android项目类似的架构设计,支持拦截器机制,但是简化和优化了非常多。C++同样是面向对象的语言,我保留了 HTTP3 ( QUIC )的扩展接口,方便我后面可以很快地接入。Netbare
提供了一套非常简单的 API ,简单接入便可以单独编译出可执行文件直接运行,具备完整的抓包功能。事实上,大多数网络协议相关的调试分析我都是直接在Netbare
项目中进行的。
Netbare
使用 C++ 11 标准,Cmake
作为编译组织脚本,目前支持在 Windows 、Mac 和 Linux 平台下 ARM 和 x86 编译。目前还没有支持 Android 和 iOS 两个平台的编译,但后面很快会支持。
光有Netbare
库还无法直接在 Reqable 客户端中使用,还需要考虑接入的问题。尽管Netbare
提供了一套 C 的 API 接口,但由于客户端主体使用Flutter
框架( Dart 语言)开发,这里就涉及到跨语言调用的问题。为了给客户端业务逻辑提供更好的支持,我又额外提供了一套Dart
的接口:
换句话说,就是另外创建了一个Dart
模块项目,接入了Netbare
的 C++库,并提供了纯Dart
语言的接口供客户端调用,在客户端项目中只需要关注Dart
的 API 即可。从上面的图可以看出,无论是 C++接口还是 Dart 接口的调用方式都是基本一致的。
由于 Reqable 确定要支持 HTTP3 ( QUIC )协议,根据我之前的项目经验,当时可靠的方案有两个:curl
vs Cronet
,最终我选择了Cronet
,下面分别来说下两个方案的优劣。
curl
是一个非常老牌的网络库了,其已经内置到大部分系统中,例如 MacOS 、Linux 等。非常多常用的软件也都是基于 curl 发送网络请求,例如Git
、Unity
等等。另外,curl
很早就开始支持 HTTP3 的草案版本了,但是至今仍然处于实验功能阶段,详见这里。我曾经在 2021 年,编译并测试过 curl 的 HTTP3 协议版本,测试结果让我迅速放弃了它。
Cronet
是chromium
项目的一个子模块,源码详见这里。Cronet 本身并不是 HTTP 众多版本协议的实现者(真正的实现在这个net目录),只是对net
目录代码封装,而net
才是整个chromium
的网络核心。当然,Cronet 的好处是作为一个模块,可以编译出单独的模块制品,另外 API 设计得也更加地简单。据我之前了解,B 站、字节系等相关产品都采用了 Cronet 的方案,但大多数是移动端。也许是因为 Google 本身的大量移动端产品使用了此方案,另外,Cronet 还提供了 Java 等移动端语言的接口和成品库。
更多的实现细节,我都在这篇中详细地讲解了,有兴趣可以看看: https://juejin.cn/post/7254076875780620325
Reqable 客户端超过 80%的代码都是通过Dart
编写的,包含 UI 、UX 以及业务逻辑,另外还有一些 Flutter 插件,用于与 Native 平台进行相互调用。
我为什么选用Flutter
呢?
很多小伙伴会觉得Electron
更成熟、生态更完善、开发效率更高,而在 2022 年初,Flutter
桌面端正式版本都没有发布呢!当然,这些都毫无疑问是正确的、无懈可击的。但是,我还是有一些不同角度的看法。
首先,Reqable 的目标是支持桌面端+移动端,覆盖全部的终端设备平台。Electron
能够很好地解决桌面端的需求,但是解决不了移动端,这是一个致命的影响决策的因素。QT
也是一样还要收费,而像Tauri
等又小众了一些。
其次,性能考虑。Electron
的性能声名(狼藉)在外,大多数技术员可能都知道这个,不是所有基于Electron
的应用都能够像VS Code
那样去做特定优化,Postman
就是一个反面例子。另一个糟心的就是安装包大小和存储空间,以 Mac 为例,如果一个应用安装包体积超过了 100M ,十有八九是基于Electron
,安装后更大,像企 x 微 x 能够占用用到上 G 的空间,而Flutter
安装包只有 20M 左右、甚至更低。Reqable 是面向技术人员的产品,我相信没有多少技术人员喜欢在自己电脑里面安装一堆浏览器内核。猜一猜下图里面(本人开发机器)装有多少个浏览器?
当然,完全从性能考虑,QT
应该是最优项,但是 C++的开发效率又太低了,此外在没有设计伙伴支持的情况下 UI/UX 也很难做出赏心悦目的效果。
Flutter
是一个非常新的框架,至少在桌面端如是,不过基本功能都是相对齐全的,此外大多数不支持的特性都可以通过插件机制来解决。Flutter
的插件机制是一个非常好的设计,支持与 Native 语言交互从而可以调用系统的 API 接口,这个几乎可以解决所有的问题。
另外,作为一个技术人员,我相信新的技术是驱动这个社会进步的动力,相比于沉湎在旧技术的舒适区,拥抱一个全新的技术会带来更大的乐趣,挑战与机遇并存。
选择Flutter
就意味着我选择了Dart
,在语言层面,这并没有太大的障碍。在我当时已有的技术栈里面,Java 、C/C++、C#、Python 、JavaScript 、Kotlin 和 Groovy 等我都比较熟悉,也都有过项目实践。所以,在看了几天Dart
的官方文档后,便正式开始Flutter
之旅了。
熟悉Flutter
的同学,应该都能够理解状态管理
这个概念。Flutter
并不像 Android 、iOS 、Unity 或者前端等支持分离式布局,无论是布局、数据还是逻辑都是在Dart
源文件中直接编写,这也是目前非常多 UI 框架的采用的方式。众所周知,布局、数据还是逻辑肯定不应该写在同一个Dart
源文件中。MVC 也好,MVVM 也罢,在Flutter
中肯定也是需要一种类似的分离布局、数据和逻辑的框架,在Flutter
中这个叫做状态管理
框架,大概是因为Widget
分为有状态和无状态两种。
Reqable 的定位是中大型 Flutter 项目,选择合适的状态管理框架非常地重要。项目稳定后,推翻这个框架的成本是绝对无法承受的。当时可选的大概就是下面这些:
我的需求是找一个轻量级的状态管理框架,适合大型项目、利于维护、方便定制扩展。结论是 Reqable 选择了BLoC
。
首先我排除掉了GetX
,当时简单看了下,第一感觉这是一个非常重的框架,什么都有,像一个杂货店;无法想象在 Flutter 频繁更新的状态下这个框架的稳定性会怎么样,如果停止维护了又是一番什么样的景象。Provider
又太简单了些,无法应对复杂的场景。Riverpod
很不错,成熟度差一些。BLoC
的功能很完善,代码简单、稳定性和成熟度也很高,是一个非常契合 Reqable 项目的选择。
基于BLoC
,Reqable 主要业务逻辑主要分为了三个部分,实现了 MVC 的基本理念:
Reqable
├── bloc // 逻辑层
├── ui // UI 层
└── model // 数据层
当然BLoC
并不是完美的,Provider + Builder 的编写方式,需要写大量的模板代码,开发效率并不高。适当地基于BLoC
再包装一层代码非常有必要,这也是 Reqable 实际的处理方式。
除了状态
管理,另一个非常重要的机制就是组件通信。例如当底部栏左下角点击侧边栏图标后,侧边栏状态需要展开,当侧边栏手动关闭后,底部栏左下角图标需要变为闭合状态。这里就涉及到两个逻辑控制器( Bloc )之间的通信了。
Reqable 基于BLoC
框架设计了ReqableBus
,实现不过超过 30 行代码,这完全得益于BLoC
也是基于 Event 驱动的,可以直接复用此机制。下面是一个非常简单的ReqableBus
使用示例:
/// 事件接受方
class SidebarBloc extends ReqableBloc<SidebarState> {
SidebarBloc(super.state) {
on<SidebarOpenEvent>((event, emit) {
emit(newState);
});
}
}
/// 事件发送方
class BottombarBloc extends ReqableBloc<BottomState> {
void openSidebar() {
// 发送事件
ReqableBus.post(const SidebarOpenEvent());
}
}
熟悉BLoC
框架的应该能看出,上图的写法基本上是完全复用了其事件机制,达到了完美的统一。
Reqable 作为一款对标 Postman + Fiddler/Charles 的 API 开发和调试工具,如果没有一个顺手的代码和文本编辑器组件,那么口碑必然要大打折扣。
从功能上来讲,API 开发过程中常用的 JSON 、XML 等编辑的需求可以说是最基本的要求了,更何况还有 Python 脚本的编写,以及 HTML 、JS 、CSS 等数据格式的显示了。例如,下图 JSON 数据的编辑,可以说是 API 工具最基本的功能了。
基于 Electron 开发的应用,编码编辑器基本上都是选用的开源的Monaco Editor
,但是在 Flutter 下并没有可用的开源项目。所以我觉得自己从零开始实现一个轻量级的代码编辑器,用于处理文本、代码和常见类型数据的显示和编辑。
这部分是 Reqable 项目中耗费非常多时间,也是难度很大的一个模块,关于更多的细节和详情,请看我之前写的一篇专题文章: https://juejin.cn/post/7246672925666885689
由于没有加密的需求,再从性能和开发效率等方面考虑,Reqable 选用了ObjectBox
。我在多年前还在做 Android 的时候,就对这个数据库所有耳闻,现在终于正式用上了。作为新兴的数据存储框架,ObjectBox
的接入效率是极高的,文档齐全,另外对于数据库更新的处理也是非常傻瓜。可能唯一的缺点,就是添加或修改表格字段后,无法再安装回历史版本了。
最后一个需要注意的是,ObjectBox
的默认存储容量上限是1G
,超过这个容量,数据库操作就会报错。我们可以通过手动设置更高的上限来避免这个问题。
Flutter
的默认桌面端代码模板以及官方支持都存在较多的问题,作为一个追求用户体验完美的项目,Reqable 优化了很多方面。
首先是标题栏。MacOS 需要在xib
中移除默认的标题栏,这个非常简单; Windows 也需要移除默认的标题栏,因为默认的标题栏无法切换暗色和亮色,这是非常棘手的一个问题,有一个知名的库bitsdojo_window可以实现,但存在稳定性和一些其他问题,最后放弃没有使用,然后是自己通过调用 Windows API 代码实现了,但也不完美,比如窗口顶部无法 Resize ; Linux 的标题栏无法移除(也许有,但我没有找到方式),同时高度也存在问题,优化也并不复杂。
其次是多窗口。同样也有一个不错的开源项目desktop_multi_window,问题有点多,包括上面的标题栏等问题。Reqable 克隆了上面的仓库,进行了大刀阔斧的优化。不管怎样,仍然非常感谢这个库的开源,提供了非常大的帮助。多窗口下面一个非常重要的功能就是多窗口通信,比如用户在设置窗口中切换主题模式(暗色和亮色),需要应用到全部已打开的窗口包括主窗口。这是一个相对复杂的状态管理,Reqable 定义了一个名为ReqableCrossWindowBloc
的状态管理父类,自动订阅跨窗口的消息并自动更新状态。
生命周期问题。由于 Reqable 需要支持在后台工作,例如当用户关闭 Reqable 窗口或者进入了后台,在使用其他应用的时候,Reqable 要仍然能够进行调试。当点击桌面图标后,可以还原之前的 Reqable 窗口而不是新开一个程序进程。无论是 Windows 、Mac 还是 Linux ,都需要进行一些特定平台的功能开发。
托盘支持。既然支持后台模式,那就非常有必要提供一个托盘功能,可以通过托盘进行一些快捷操作而不需要打开主窗口,或是通过托盘恢复主窗口。Reqable 采用的是开源的tray_manager,一样有些小问题,需要定制优化。
应用关联支持。这个功能是非常重要的一个交互,例如我们可以直接通过双击视频文件,自动打开播放器进行播放。对于 Reqable ,用户能够通过HAR
文件格式关联到 Reqable ,并打开查看详情内容。这个同样需要进行一些特定平台的功能开发。
文件拖拽支持。Reqable 支持拖拽一个HAR
文件到主窗口释放来打开这个文件,同样的也支持拖拽任何文件并设置给一个 API 作为 Body 。Reqable 使用了开源项目desktop_drop,但是在多窗口模式下,支持并不是很好,需要做一些简单的修复。
应用菜单栏支持。在 Mac 系统上面,应用菜单栏是非常常见的功能,当然在 Windows 和 Linux 上面也有,但是系统原生的现在已经不多见了。我调研了 Flutter 官方出品的插件menubar,但效果并不好。在 MacOS 上,我彻底改造了前面所说的官方插件并利用了一些小 Trick 达到了现在的效果;在 Windows 和 Linux 上则彻底放弃了原生的应用菜单栏,使用 Flutter 实现了一套自己的应用菜单栏。顺便提一下,后来 Flutter 框架中正式加入了控制 Mac 系统菜单栏的 API ,但是效果依然不完美。
右键菜单栏支持。不同的系统都有各自的右键原生菜单栏样式,在开发 Reqable 的早期阶段,我就纠结过这个问题,是调用系统原生的菜单栏还是在基于Flutter
实现一套统一风格的。多次尝试后,我放弃了使用系统原生的菜单栏的方案,确定了现在的这种菜单栏样式,原因就是稳定可控,方便定制。
脚本功能是 Reqable 非常重要的特性之一,可以给予开发者极大的自由度,也将是后面规划中的插件功能
的基石之一。Postman
等使用JavaScript
作为脚本扩展,但我觉得Python
作为脚本而言可能更好一些。使用 Reqable 非常重要的一个群体就是测试人员,Python
往往测试人员的首选语言,所以我决定选用Python
作为 Reqable 的脚本语言。
因此,我需要提供一套丰富的Python API
作为支持,通过import reqable
引入调用。由于时间关系,尚未来得及公开脚本框架代码(主要是不想写文档的懒),后面还要抽空处理下。
Reqable 项目整体采用了组件化的模式,很多功能都拆分成了模块。由于 Flutter 生态不成熟的的问题,非常多的模块功能都需要 Reqable 自行实现,因此我借鉴了一些开源项目,在此对这些项目作者表示感谢!下面简单列了一些模块,供大家参考。
组件化带来的一个问题,就是如何管理这些组件,例如在编译前同步所有的组件版本等等。下图是 Reqable 这个项目的组件,其中绝大多数都是客户端的模块(稍微有点乱,需要进行整理,勿笑)。
有的组件是纯Dart
库,有的组件是包含 C++代码的,不同的组件类型带来了一些管理复杂度。Reqable 采用的方式是,C++模块单独预先编译(部分需要交叉编译,部分需要在特定平台编译)并发布到线上制品库,组件同步时从线上制品库进行更新。细节部分很多,由于涉及较多项目内部的逻辑,无法深入讲解。组件管理的控制流程,通过 Python 脚本来实现。
以上就是 Reqable 客户端的部分,在实际的实践过程中还有更多的问题,有的已经解决了,有的还没有处理。跨平台项目如果追求精益求精和完美体验,需要在非常多的细节上面花费时间和精力。非常抱歉这部分无法深入分享,也无法面面俱到,大家只能管中窥豹了,还敬请见谅。
Reqable 的前端包含两个域:reqable.com
官网主站和license.reqable.com
许可证管理中心,采用了完全不同的技术方案。
官网采用的是Docusaurus框架,开发语言是ReactJS
,支持中文和英文两种语言,支持亮色和暗色两种主题。下图,是暗色主题的中文官网主站。
官网由一些独立页面 + 文档 + 博客三部分组成,Docusaurus
对此提供了非常完善的支持。其中,独立页面(包含主页)都是通过ReactJS
编写,文档和博客则通过Markdown
和MDX
语言编写。Docusaurus
框架本身包含前端和后端两部分,支持编写时动态更新,可以非常方便地进行开发。开发完成后,也可以直接本地部署预览效果。
这里讲讲关于前端网站框架的选型,由于我没有多少网站的开发经验,这个领域涉足很浅。很久之前接触过showdoc
和Gitbook
,但或多或少都有点不满意。后来有个同事推荐了我Docusaurus
,第一个感觉就是:这就是我想要的。Docusaurus
是Facebook
的开源项目,算是后起之秀,文档完整,设计也好看,加上有非常多的Showcase可供参考(抄代码),对于非专业前端开发来讲是一个非常适合的框架。
当然,仅仅依靠Docusaurus
肯定做不出现在的效果。我还是使用ReactJS + css
实现了主要的交互功能,包括瀑布流、跑马灯效果等,不专业就不多讲了,以免贻笑大方。
前面提到文档和博客是通过Markdown
和MDX
语言编写。Markdown
大家应该都了解,不多讲,就简单说一说MDX
。MDX
其实就是允许在Markdown
中使用 JSX 代码,更多信息可以查看此网站: https://www.mdxjs.cn/ 。Reqable 编写了一些简单小组件并应用到了MDX
中,例如文档中的快捷键内容平台自适应。在 MacOS 上,一般使用 Command 键而不是 Control 键,Windows 和 Linux 都是使用 Control 键,所以非常有必要让 MacOS 的用户看到的快捷键内容是 Command ,例如Command + B
,而 Windows 和 Linux 用户看到的是Control + B
。
顺便说一句,Reqable 官网的文档部分是公开托管的,如果有想学习或者有兴趣想帮助完善的,可以看看: https://github.com/reqable/reqable-docs
Reqable 支持两种语言:中文和英文,无论是网站还是客户端程序,都已经支持了。Docusaurus
框架提供了基本的i18n
支持,但好像不包括资源文件(官网中文语言下图片还是英文版本的)。遇到的最大的问题还是部署问题,我希望根据浏览器的语言,自动切换到英文或者是中文。从技术方面讲,就是浏览器打开https://reqable.com
的时候根据语言自动重定向到https://reqable.com/zh-CN
或者https://reqable.com/en-US
,语言的检测可以通过请求头中的Accept-Language
的值来确定。
很多浏览器下Accept-Language
的值并不是单纯的zh-CN
或者en-US
,而是一个权重配置。一开始的时候,我尝试使用nginx
的反向代理解析Accept-Language
并通过rewrite
机制重定向,出现的问题是nginx
虽然能提取出 locale 但是无法比较权重。最后的一个解决方案是,起了一个专门解析Accept-Language
的 server 服务,从代码层面去判断语言并返回重定向的 Location 。
关于搜索引擎优化。Docusaurus
支持配置Metadata
,但貌似不支持国际化。我还是希望在不同的语言下使用本地语言的搜索关键字,Reqable 最后采用了在代码中配置的方案,因为在代码中可以很好地处理i18n
的问题。
许可证管理是通过Flutter
框架开发的。由于涉及到下单、支付、登录、许可证操作等强交互性功能,极少静态页面,所以我选择了 Flutter 作为开发框架,真正地将 Flutter 框架应用到 Reqable 项目的全平台上。
使用 Flutter 开发 Web 和桌面端移动端的方式并没有什么太大的区别,也许这就是 Flutter 独特的魅力之一。和桌面端一样,Web 端仍然有一些问题需要我们自己解决,下面我分享下我遇到的问题和解决思路。
这可能是非专业前端人员遇到的第一个难题,新手村 Boss 。最常见的就是发送了一个 POST 请求,然后失败了,不知道什么原因。当然,不出意外地我也遇到了,解决起来也很顺利。之前写了一篇文章,专门讲这个: https://juejin.cn/post/7247057861128486968
Flutter 开发的 Web 最致命的问题,就是加载慢,而加载慢主要就是因为有几个非常大的文件需要下载,字体文件是其中之一。出于安全原因WebAssembly
无法使用系统字体,Flutter 只能自己从 Goolge 的字体网站上下载字体文件,包含中文的思源字体大约有 7-8M ,下载下来可想而知有多慢。Reqable 的解决方案,是将字体放到本地 assets 并进行裁剪(即移除掉生僻字),裁剪后的字体大小大约是 900k 左右。
接下来,代码中再配置下使用本地字体不从 Google 字体网站下载。
MaterialApp(
title: 'Reqable',
theme: ThemeData(
fontFamilyFallback: const ['Noto Sans SC'],
),
);
CanvasKit 也是导致 Flutter Web 加载慢的元凶,wasm
好几 M 的文件大小下载就是慢,即使放到本地开了CDN
也感觉慢。没太好的解决方案,换回了 html 渲染--web-renderer=html
,副作用就是渲染效果差了,文字排版感觉有很大的问题,但也比加载半天才出来体验好些。
这个问题出现在选用 CanvasKit 作为渲染方式的时候。知乎上RustDesk
的作者建议我加个 Loading ,从效果上来说,确实比白屏好多了,感谢这个建议。在index.html
文件里,加了一些代码就可以实现,有需求可以简单参考下:
<head>
<style>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loading {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #FCB334;
border-radius: 50%;
animation: spin 1s infinite linear;
margin: 0 auto;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<body>
<div class="container">
<div id="loading" class="loading"></div>
</div>
<script>
window.flutterConfiguration = {
canvasKitBaseUrl: "/canvaskit/"
};
</script>
<script>
document.getElementById("loading").style.display = "block";
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
document.getElementById("loading").style.display = "none";
appRunner.runApp();
});
}
});
});
</script>
</body>
这个问题在开发模式下不会遇到,但是部署上线后会出现,表现就是在某个子页面刷新下浏览器,自动跳回首页了。这个问题通过修改服务器网页托管的代码完成修复。
在这里,我顺便说下服务端部署的问题。通过下面的命令,可以编译出 Web 端的制品:
flutter build web --release
然而 Flutter 并不负责服务端的网页部署,因此需要编写一个服务端程序。我选择了Python
,原因就是因为简单,下面是服务端程序的脚本示例代码:
from http.server import SimpleHTTPRequestHandler
import socketserver
from urllib.parse import urlparse
PORT = 3000
class MyRequestHandler(SimpleHTTPRequestHandler):
def do_GET(self):
print(self.path)
uri = urlparse(self.path)
# 解决路由跳转问题的关键逻辑
if '.' not in uri.path:
self.path = '/'
return SimpleHTTPRequestHandler.do_GET(self)
server = socketserver.TCPServer(('', PORT), MyRequestHandler)
print("Serving at PORT : " + str(PORT))
server.serve_forever()
前面的问题相比这个都是毛毛雨,耗费时间最多的就是接支付了。归根到底,还是 Flutter 的生态不够成熟导致,无论是国内的AliPay
还是国外的Paypal
,都未提供Dart
语言的 SDK ,意味着我只能通过REST API
的方式进行调用、然后自己排查错误。用一句话来形容这个体验,就是文档都快被翻烂了
。这本身不是什么问题,但是对于 Flutter Web 来讲,可能就是最大的问题了。
终于到了本篇总结的最后一个大章节——服务端,服务端内容包括网络服务、数据库、部署,简单运维等。对于 Reqable 这个项目而言,服务端是最不重要的部分,所以也没有花什么精力,就简单说一说了。
对于后端来讲,我的经验也不多,写过 Java 、Python 和 NodeJS 这三样,但也是多年前的事情了。可是,我最后的技术选型放弃这老三样,投入到了Dart
的怀抱。桌面端 + 移动端 + Web 端都已经拥抱 Flutter 了,为什么服务端不用Dart
呢,真正来个All in Dart
!
网络服务框架我选择了shelf,毕竟是Dart
官方出品的。可能是现在大部分网络服务框架设计模式都差不太多,shelf
几乎不需要花时间去上手,和NodeJS
和Python
下的一些框架的使用方式非常相像。shelf
提供了基本的拦截器功能,可以非常方便地处理CORS
和日志记录等中间件功能问题。
数据库采用Mysql
,原因就是有历史使用经验,节省时间。如果换个场景,Reqable 项目要做云服务的话,我基本上不会选择这个方案。主要是原因还是Dart
的生态问题,换句话说就是没有成熟稳定的的 Dart 语言库来支持Mysql
,Mongodb
可能还要稍微好一点。
还有一点就是关于ORM
框架,也没有什么成熟的方案。虽然 Reqable 的服务端逻辑很简单,但是数据库的开发体验也是相当糟糕。
关于数据库的 GUI 操作软件,Mac 上我强烈推荐Sequel-Ace,很好用。之前都是用Sequel Pro
,长久不更新各种闪退之后,换成了Sequel-Ace
,两者使用几乎一样。
最后一点,提一下Mysql
的 2038 年时间问题。发现这个问题起因是这样的,Reqable 有用户续费到 2038 年失败了,用户以为我做了限制反馈了给我,事实上我没有限制纯粹是Mysql
的 2038 年时间这个 bug 。这个 bug 是因为Mysql
使用 32 位整数来表示 Unix 时间戳,到 2038 年就溢出了。这个 bug 至今我还没有修复,有兴趣或者不相信的可以亲自去测试下。
Reqable 采用了阿里云
的邮件推送服务,到达率还不错。刚开始接入时,每天有 200 份邮件发送的免费额度,但是前几天通知我9 月 1 日
起免费额度改成总上限 2000 份了。好吧,还能怎么办呢,项目成本又要上升了。
Reqable 使用nginx
作为后端反向代理,pm2
作为服务进程管理工具。nginx
没啥好说的,基本上大家都是用这个。pm2
是因为我之前写NodeJS
服务时候用的,直接拿来了。
关于集群部署,目前完全不需要,单台服务器稳妥的。运维也就是定期自动备份下数据库,日志等,目前还缺一个服务稳定性监控,哪天抽个时间做了。
最后谈一谈部署服务的一些心得。
后端Dart
服务和NodeJS
或者Python
这一类脚本服务不同,Dart
是需要编译成二进制文件的,这个编译的过程挺耗性能的,我测试过阿里云最差的1 核 1G
的突发性能实例,代码稍微多一些就编译不了了,但是部署NodeJS
或者Python
服务一点问题都没有。
对于Docusaurus
,部署时(非运行时)同样挺耗性能,1 核心的云服务器也部署不了,最低 2 核心起步,有条件的话建议 4 核心,不然部署真的就太慢了。
从技术方面来讲,能分享的也就这么多了,但还是忍不住在最后谈一谈关于设计方面的心得。这里的设计不是指产品设计,而是 UI/UX 设计。一个好的产品,离不开优秀的 UI/UX 设计。尽管 Reqable 在这方面乏善可陈,但是还是要谈一谈,就当抛砖引玉了。
不同类型的产品,应当有不同风格的设计。Reqable 是开发工具,应当具备技术人员所偏爱类型的设计风格。因为我本人就是技术人员,相对来说了解这个群体,我认为优秀的开发工具简洁应当是第一要素,用英文来说就是 Clean 。技术人员是讲究效率的群体,应当摒弃一切杂乱又花里胡哨的堆叠。
VS Code
是这一类软件的行业标杆,无论是功能还是设计,Reqable 参考了VS Code
的很多的设计理念,包括布局、交互方式和配色方案等等。
同时,Reqable 基本遵循了Material Design 3
的设计规范,这也是Flutter
控件主推的设计方案。对此感兴趣的可以看看官方文档: https://m3.material.io/ ,前几天有幸参加的Google IO
中国开发者大会,Material Design 3
是其中的一项专题,可以预见在未来 Google 系列的产品,包括 Android 等都将进入Material Design 3
的风格时代。当然了,也有用户反馈表示不喜欢或者不习惯这个设计风格,这一点我也很难,也许审美方面有时就是众口难调吧。
谈到 UI 设计,少不了要使用图片或者图标。Reqable 没有图片相关的需求,图标倒是有非常之多。Reqable 的图标部分来自于开源网站,部分是我自己设计。一般是先生成SVG
文件,然后加入到IconFont
中,最后再在项目中使用。
推荐一些我常用的图标资源网站(注意非完全免费):
Font Awesome
: https://fontawesome.com/Flaticon
: https://www.flaticon.com/Icon8
: https://icons8.com/Flutter 生成IconFont
的网站,这里的 Icon 都是开源免费的,包括 Google 的Material Design Icons
: https://www.fluttericon.com/
如果需要自己设计和编辑图标,推荐使用Sketch
,处理矢量图SVG
的功能非常强大。
Reqable的理念是先进 API 生产力工具
,宗旨是做优秀的国产软件
。无论您是企业工程师还是个人开发者,我都希望 Reqable 的项目经验能够对您有所帮助。如果您对本篇文章满意的话,也可以通过订阅 Reqable
的方式来支持我。
Reqable 的官网: https://reqable.com
GitHub 建议&反馈: https://github.com/reqable/reqable-app
如果您对 Reqable 有任何问题都可以与我联系,或者在 GitHub 上提交 Issue !
感谢支持 Reqable ,谢谢!
1
weiwenhao 2023-09-08 15:37:05 +08:00
大佬牛逼。 我也还在寻找适合做官网和文档的开源组件,虽然目前用 Docusaurus 但是感觉 文档标题排版 比较差,首页也差点意思。
|
2
qq316107934 2023-09-08 15:48:31 +08:00
图标建议换一个,不然跟 HTTPCanary 完全一致,这是一款安卓端抓包工具
|
3
MegatronKing OP @weiwenhao 我认为文档的样式风格排版都无所谓了,意思差不多就行,主要还是首页难搞。用 Docusaurus ,首页就需要用 React 去深度定制开发才行,框架我觉得本身没问题,难的是深度开发,毕竟独立开发技术栈很难面面俱到。
|
4
MegatronKing OP @qq316107934 HTTPCanary 也是我写的,Reqable 的规划是完全替换掉 HTTPCanary 。
|
5
jsonzz 2023-09-08 15:56:06 +08:00
牛,谢谢分享,非常期待如何支持 Python 脚本的分享,大佬能给点关键词吗
|
6
qq316107934 2023-09-08 15:56:17 +08:00
@MegatronKing #4 大佬牛逼😱
|
7
u4zada 2023-09-08 15:57:55 +08:00
牛的,回头体验体验
|
8
SSQQ 2023-09-08 16:03:49 +08:00
|
9
lzgshsj 2023-09-08 16:53:30 +08:00 1
看到 Flutter ,不错,
看到 Flutter 桌面端,卧槽, 看到 Flutter 网页端,牛皮, 看到 Flutter 服务端,Orz |
10
hooych 2023-09-08 17:21:58 +08:00
感谢分享,学习了
|
11
jackdou 2023-09-08 19:46:26 +08:00
牛,收藏一个
|
12
lizhenda 2023-09-08 21:24:57 +08:00
都是干货啊,感谢分享
|
13
putaozhenhaochi 2023-09-09 08:42:39 +08:00 via iPhone
牛逼啊 敢用 dart 写多端
|