大家好,最近有知乎网友问我Reqable技术选型的问题,恰好Reqable也刚刚发布了非常重要的1.3版本更新,所以此次写一篇关于Reqable项目技术栈的全方面总结。
本篇文章的目的,是向大家分享我关于Reqable项目的一些技术思考、细节和填坑概要等。希望能够对大家有所帮助、避坑、少走一些弯路、更快交付。有必要说明的是,Reqable项目完全由我个人独立完成,受限于本人经验和能力,肯定有非常多的不足。如果你对本篇总结中提到的技术选型、处理方式等有不同的见解,欢迎提交评论,一起讨论,共同学习。
如果有不了解Reqable的,可以看上面这张图,官网首页:https://reqable.com
下面,话不多说,正文开始。
1. 客户端
Reqable客户端是基于Flutter和C++开发的,由于整体不涉及非常复杂的层次架构,所以我就不放Arch图了。其实简而言之,除了网络流量分析框架和API请求框架,这两个模块是使用C++语言开发外,其他几乎都是使用Dart语言开发。下面,我们来依次进行介绍。
1.1 网络流量分析模块
这个模块内部的代号是Netbare
,如果你查看Reqable解压的运行库,应该能看到这个。Netbare
从项目初始就确定了使用C++语言进行开发,最核心的考量标准就是性能。Netbare
的职责包含下面几个:
- 建立代理服务器
- 分析入流量,解析代理协议
- SSL证书重签和握手(
MITM
) - 中间人客户端
- 中间人服务器
- HTTP协议解析和封包
- 插件和拦截器处理
从上面的职责可以看到,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接口的调用方式都是基本一致的。
1.2 Rest API请求模块
由于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
1.3 Dart & Flutter
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
之旅了。
1.3.1 Flutter状态管理
熟悉Flutter
的同学,应该都能够理解状态管理
这个概念。Flutter
并不像Android、iOS、Unity或者前端等支持分离式布局,无论是布局、数据还是逻辑都是在Dart
源文件中直接编写,这也是目前非常多UI框架的采用的方式。众所周知,布局、数据还是逻辑肯定不应该写在同一个Dart
源文件中。MVC也好,MVVM也罢,在Flutter
中肯定也是需要一种类似的分离布局、数据和逻辑的框架,在Flutter
中这个叫做状态管理
框架,大概是因为Widget
分为有状态和无状态两种。
Reqable的定位是中大型Flutter项目,选择合适的状态管理框架非常地重要。项目稳定后,推翻这个框架的成本是绝对无法承受的。当时可选的大概就是下面这些:
- GetX
- Provider
- BLoC
- Riverpod
我的需求是找一个轻量级的状态管理框架,适合大型项目、利于维护、方便定制扩展。结论是Reqable选择了BLoC
。
首先我排除掉了GetX
,当时简单看了下,第一感觉这是一个非常重的框架,什么都有,像一个杂货店;无法想象在Flutter频繁更新的状态下这个框架的稳定性会怎么样,如果停止维护了又是一番什么样的景象。Provider
又太简单了些,无法应对复杂的场景。Riverpod
很不错,成熟度差一些。BLoC
的功能很完善,代码简单、稳定性和成熟度也很高,是一个非常契合Reqable项目的选择。
基于BLoC
,Reqable主要业务逻辑主要分为了三个部分,实现了MVC的基本理念:
Reqable
├── bloc // 逻辑层
├── ui // UI层
└── model // 数据层
当然BLoC
并不是完美的,Provider + Builder的编写方式,需要写大量的模板代码,开发效率并不高。适当地基于BLoC
再包装一层代码非常有必要,这也是Reqable实际的处理方式。
1.3.2 组件通信机制
除了状态
管理,另一个非常重要的机制就是组件通信。例如当底部栏左下角点击侧边栏图标后,侧边栏状态需要展开,当侧边栏手动关闭后,底部栏左下角图标需要变为闭合状态。这里就涉及到两个逻辑控制器(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
框架的应该能看出,上图的写法基本上是完全复用了其事件机制,达到了完美的统一。
1.3.3 代码编辑器
Reqable作为一款对标Postman + Fiddler/Charles的API开发和调试工具,如果没有一个顺手的代码和文本编辑器组件,那么口碑必然要大打折扣。
从功能上来讲,API开发过程中常用的JSON、XML等编辑的需求可以说是最基本的要求了,更何况还有Python脚本的编写,以及HTML、JS、CSS等数据格式的显示了。例如,下图JSON数据的编辑,可以说是API工具最基本的功能了。
基于Electron开发的应用,编码编辑器基本上都是选用的开源的Monaco Editor
,但是在Flutter下并没有可用的开源项目。所以我觉得自己从零开始实现一个轻量级的代码编辑器,用于处理文本、代码和常见类型数据的显示和编辑。
这部分是Reqable项目中耗费非常多时间,也是难度很大的一个模块,关于更多的细节和详情,请看我之前写的一篇专题文章:https://juejin.cn/post/7246672925666885689
1.3.4 数据库
由于没有加密的需求,再从性能和开发效率等方面考虑,Reqable选用了ObjectBox
。我在多年前还在做Android的时候,就对这个数据库所有耳闻,现在终于正式用上了。作为新兴的数据存储框架,ObjectBox
的接入效率是极高的,文档齐全,另外对于数据库更新的处理也是非常傻瓜。可能唯一的缺点,就是添加或修改表格字段后,无法再安装回历史版本了。
最后一个需要注意的是,ObjectBox
的默认存储容量上限是1G
,超过这个容量,数据库操作就会报错。我们可以通过手动设置更高的上限来避免这个问题。
1.3.5 桌面端支持
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
实现一套统一风格的。多次尝试后,我放弃了使用系统原生的菜单栏的方案,确定了现在的这种菜单栏样式,原因就是稳定可控,方便定制。
1.3.6 脚本支持
脚本功能是Reqable非常重要的特性之一,可以给予开发者极大的自由度,也将是后面规划中的插件功能
的基石之一。Postman
等使用JavaScript
作为脚本扩展,但我觉得Python
作为脚本而言可能更好一些。使用Reqable非常重要的一个群体就是测试人员,Python
往往测试人员的首选语言,所以我决定选用Python
作为Reqable的脚本语言。
因此,我需要提供一套丰富的Python API
作为支持,通过import reqable
引入调用。由于时间关系,尚未来得及公开脚本框架代码(主要是不想写文档的懒),后面还要抽空处理下。
1.3.7 更多组件
Reqable项目整体采用了组件化的模式,很多功能都拆分成了模块。由于Flutter生态不成熟的的问题,非常多的模块功能都需要Reqable自行实现,因此我借鉴了一些开源项目,在此对这些项目作者表示感谢!下面简单列了一些模块,供大家参考。
- HexViewer,这个是在前面所讲的编辑器的基础上修改实现的,作为了一个单独模块,并没有合并到编辑器模块中。
- HAR支持,参考了HAR的文件规范,自行实现了HAR文件的解析和编码。
- 文件类型检测,基于file-type实现了一套Dart语言的版本。
- curl支持,参考curl官方文档,自行实现了cURL命令的解析和生成。
- 代码生成,基于httpsnippet实现了一套Dart语言的版本。
1.3.8 组件化管理
组件化带来的一个问题,就是如何管理这些组件,例如在编译前同步所有的组件版本等等。下图是Reqable这个项目的组件,其中绝大多数都是客户端的模块(稍微有点乱,需要进行整理,勿笑)。
有的组件是纯Dart
库,有的组件是包含C++代码的,不同的组件类型带来了一些管理复杂度。Reqable采用的方式是,C++模块单独预先编译(部分需要交叉编译,部分需要在特定平台编译)并发布到线上制品库,组件同步时从线上制品库进行更新。细节部分很多,由于涉及较多项目内部的逻辑,无法深入讲解。组件管理的控制流程,通过Python脚本来实现。
以上就是Reqable客户端的部分,在实际的实践过程中还有更多的问题,有的已经解决了,有的还没有处理。跨平台项目如果追求精益求精和完美体验,需要在非常多的细节上面花费时间和精力。非常抱歉这部分无法深入分享,也无法面面俱到,大家只能管中窥豹了,还敬请见谅。
2. 前端
Reqable的前端包含两个域:reqable.com
官网主站和license.reqable.com
许可证管理中心,采用了完全不同的技术方案。
2.1 官网
官网采用的是Docusaurus框架,开发语言是ReactJS
,支持中文和英文两种语言,支持亮色和暗色两种主题。下图,是暗色主题的中文官网主站。
官网由一些独立页面 + 文档 + 博客三部分组成,Docusaurus
对此提供了非常完善的支持。其中,独立页面(包含主页)都是通过ReactJS
编写,文档和博客则通过Markdown
和MDX
语言编写。Docusaurus
框架本身包含前端和后端两部分,支持编写时动态更新,可以非常方便地进行开发。开发完成后,也可以直接本地部署预览效果。
2.1.1 关于Docusaurus
这里讲讲关于前端网站框架的选型,由于我没有多少网站的开发经验,这个领域涉足很浅。很久之前接触过showdoc
和Gitbook
,但或多或少都有点不满意。后来有个同事推荐了我Docusaurus
,第一个感觉就是:这就是我想要的。Docusaurus
是Facebook
的开源项目,算是后起之秀,文档完整,设计也好看,加上有非常多的Showcase可供参考(抄代码),对于非专业前端开发来讲是一个非常适合的框架。
当然,仅仅依靠Docusaurus
肯定做不出现在的效果。我还是使用ReactJS + css
实现了主要的交互功能,包括瀑布流、跑马灯效果等,不专业就不多讲了,以免贻笑大方。
2.1.2 文档
前面提到文档和博客是通过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
2.1.3 多语言支持
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。
2.1.4 SEO
关于搜索引擎优化。Docusaurus
支持配置Metadata
,但貌似不支持国际化。我还是希望在不同的语言下使用本地语言的搜索关键字,Reqable最后采用了在代码中配置的方案,因为在代码中可以很好地处理i18n
的问题。
2.2 许可证管理中心
许可证管理是通过Flutter
框架开发的。由于涉及到下单、支付、登录、许可证操作等强交互性功能,极少静态页面,所以我选择了Flutter作为开发框架,真正地将Flutter框架应用到Reqable项目的全平台上。
使用Flutter开发Web和桌面端移动端的方式并没有什么太大的区别,也许这就是Flutter独特的魅力之一。和桌面端一样,Web端仍然有一些问题需要我们自己解决,下面我分享下我遇到的问题和解决思路。
2.2.1 跨域问题
这可能是非专业前端人员遇到的第一个难题,新手村Boss。最常见的就是发送了一个POST请求,然后失败了,不知道什么原因。当然,不出意外地我也遇到了,解决起来也很顺利。之前写了一篇文章,专门讲这个:https://juejin.cn/post/7247057861128486968
2.2.2 字体问题
Flutter开发的Web最致命的问题,就是加载慢,而加载慢主要就是因为有几个非常大的文件需要下载,字体文件是其中之一。出于安全原因WebAssembly
无法使用系统字体,Flutter只能自己从Goolge的字体网站上下载字体文件,包含中文的思源字体大约有7-8M,下载下来可想而知有多慢。Reqable的解决方案,是将字体放到本地assets并进行裁剪(即移除掉生僻字),裁剪后的字体大小大约是900k左右。
接下来,代码中再配置下使用本地字体不从Google字体网站下载。
MaterialApp(
title: 'Reqable',
theme: ThemeData(
fontFamilyFallback: const ['Noto Sans SC'],
),
);
2.2.3 CanvasKit问题
CanvasKit也是导致Flutter Web加载慢的元凶,wasm
好几M的文件大小下载就是慢,即使放到本地开了CDN
也感觉慢。没太好的解决方案,换回了html渲染--web-renderer=html
,副作用就是渲染效果差了,文字排版感觉有很大的问题,但也比加载半天才出来体验好些。
2.2.4 加载进度条
这个问题出现在选用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>
2.2.5 部署&路由问题
这个问题在开发模式下不会遇到,但是部署上线后会出现,表现就是在某个子页面刷新下浏览器,自动跳回首页了。这个问题通过修改服务器网页托管的代码完成修复。
在这里,我顺便说下服务端部署的问题。通过下面的命令,可以编译出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()
2.2.6 支付业务逻辑
前面的问题相比这个都是毛毛雨,耗费时间最多的就是接支付了。归根到底,还是Flutter的生态不够成熟导致,无论是国内的AliPay
还是国外的Paypal
,都未提供Dart
语言的SDK,意味着我只能通过REST API
的方式进行调用、然后自己排查错误。用一句话来形容这个体验,就是文档都快被翻烂了
。这本身不是什么问题,但是对于Flutter Web来讲,可能就是最大的问题了。
3. 服务端
终于到了本篇总结的最后一个大章节——服务端,服务端内容包括网络服务、数据库、部署,简单运维等。对于Reqable这个项目而言,服务端是最不重要的部分,所以也没有花什么精力,就简单说一说了。
3.1 后端服务
对于后端来讲,我的经验也不多,写过Java、Python和NodeJS这三样,但也是多年前的事情了。可是,我最后的技术选型放弃这老三样,投入到了Dart
的怀抱。桌面端 + 移动端 + Web端都已经拥抱Flutter了,为什么服务端不用Dart
呢,真正来个All in Dart
!
3.1.1 网络服务框架
网络服务框架我选择了shelf,毕竟是Dart
官方出品的。可能是现在大部分网络服务框架设计模式都差不太多,shelf
几乎不需要花时间去上手,和NodeJS
和Python
下的一些框架的使用方式非常相像。shelf
提供了基本的拦截器功能,可以非常方便地处理CORS
和日志记录等中间件功能问题。
3.1.2 数据库
数据库采用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至今我还没有修复,有兴趣或者不相信的可以亲自去测试下。
3.1.3 邮件推送
Reqable采用了阿里云
的邮件推送服务,到达率还不错。刚开始接入时,每天有200份邮件发送的免费额度,但是前几天通知我9月1日
起免费额度改成总上限2000份了。好吧,还能怎么办呢,项目成本又要上升了。
3.2 部署 & 运维
Reqable使用nginx
作为后端反向代理,pm2
作为服务进程管理工具。nginx
没啥好说的,基本上大家都是用这个。pm2
是因为我之前写NodeJS
服务时候用的,直接拿来了。
关于集群部署,目前完全不需要,单台服务器稳妥的。运维也就是定期自动备份下数据库,日志等,目前还缺一个服务稳定性监控,哪天抽个时间做了。
最后谈一谈部署服务的一些心得。
后端Dart
服务和NodeJS
或者Python
这一类脚本服务不同,Dart
是需要编译成二进制文件的,这个编译的过程挺耗性能的,我测试过阿里云最差的1核1G
的突发性能实例,代码稍微多一些就编译不了了,但是部署NodeJS
或者Python
服务一点问题都没有。
对于Docusaurus
,部署时(非运行时)同样挺耗性能,1核心的云服务器也部署不了,最低2核心起步,有条件的话建议4核心,不然部署真的就太慢了。
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
的功能非常强大。
5 结语
Reqable的理念是先进API生产力工具
,宗旨是做优秀的国产软件
。无论您是企业工程师还是个人开发者,我都希望Reqable的项目经验能够对您有所帮助。如果您对本篇文章满意的话,也可以通过订阅Reqable
的方式来支持我。
Reqable的官网:https://reqable.com
GitHub建议&反馈:https://github.com/reqable/reqable-app
如果您对Reqable有任何问题都可以与我联系,或者在GitHub上提交Issue!
感谢支持Reqable,谢谢