Reqable项目技术栈全方面总结

news2024/12/24 21:21:27

大家好,最近有知乎网友问我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发送网络请求,例如GitUnity等等。另外,curl很早就开始支持HTTP3的草案版本了,但是至今仍然处于实验功能阶段,详见这里。我曾经在2021年,编译并测试过curl的HTTP3协议版本,测试结果让我迅速放弃了它。

Cronetchromium项目的一个子模块,源码详见这里。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编写,文档和博客则通过MarkdownMDX语言编写。Docusaurus框架本身包含前端和后端两部分,支持编写时动态更新,可以非常方便地进行开发。开发完成后,也可以直接本地部署预览效果。

2.1.1 关于Docusaurus

这里讲讲关于前端网站框架的选型,由于我没有多少网站的开发经验,这个领域涉足很浅。很久之前接触过showdocGitbook,但或多或少都有点不满意。后来有个同事推荐了我Docusaurus,第一个感觉就是:这就是我想要的。DocusaurusFacebook的开源项目,算是后起之秀,文档完整,设计也好看,加上有非常多的Showcase可供参考(抄代码),对于非专业前端开发来讲是一个非常适合的框架。

当然,仅仅依靠Docusaurus肯定做不出现在的效果。我还是使用ReactJS + css实现了主要的交互功能,包括瀑布流、跑马灯效果等,不专业就不多讲了,以免贻笑大方。

2.1.2 文档

前面提到文档和博客是通过MarkdownMDX语言编写。Markdown大家应该都了解,不多讲,就简单说一说MDXMDX其实就是允许在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几乎不需要花时间去上手,和NodeJSPython下的一些框架的使用方式非常相像。shelf提供了基本的拦截器功能,可以非常方便地处理CORS和日志记录等中间件功能问题。

3.1.2 数据库

数据库采用Mysql,原因就是有历史使用经验,节省时间。如果换个场景,Reqable项目要做云服务的话,我基本上不会选择这个方案。主要是原因还是Dart的生态问题,换句话说就是没有成熟稳定的的Dart语言库来支持MysqlMongodb可能还要稍微好一点。

还有一点就是关于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,谢谢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/989639.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙应用程序入口UIAbility详解

一、UIAbility概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。每一个UIAbility实例&#xff0c;都对应于一个最近任务列表中的任务。一个应用可以有一个UIAbility&am…

Kafka生产与消费示例

Kafka生产与消费流程 Kafka是一款消息中间件&#xff0c;消息中间件本质就是收消息与发消息&#xff0c;所以这节课我们会从一条消息开始生产出发&#xff0c;去了解生产端的运行流程&#xff0c;然后简单的了解一下broker的存储流程&#xff0c;最后这条消息是如何被消费者消…

GNU Linux 的退出码规定

参考&#xff1a;https://tldp.org/LDP/abs/html/exitcodes.html 从这张表里来看&#xff0c;小于 128 的是被程序员捕捉的错误&#xff0c;大于等于 128 的是真正的 bugs &#xff1f;

MySQL卸载干净再重新安装【Windows】

家人们&#xff0c;谁懂啊&#xff1f; 上学期学的数据库&#xff0c;由于上学期不知道为什么抽风&#xff0c;过得十分的迷&#xff0c;上课跟老师步骤安装好了Mysql&#xff0c;但后面在使用的过程中出现了问题&#xff0c;而且还出现了忘记密码这么蠢的操作&#xff0c;后半…

linux设置登录超时自动退出

问题背景 最近登录某台linux服务器&#xff0c;经常遇到超时自动退出现象&#xff0c;如下图&#xff1a; 是因为服务器设置了超时时间&#xff0c;如果某个超时时间段内服务器没有任何操作&#xff0c;则会自动注销 解决方法 查看服务器设置的超时时间(TMOUT 变量的值)&am…

非科班菜鸡算法学习记录 | 代码随想录算法训练营第59天||503.下一个更大元素II 42. 接雨水

503. 下一个更大元素 II 知识点&#xff1a;单调栈 状态&#xff1a;不会&#xff0c;全靠卡哥 思路&#xff1a; 用i%nums.size()来代替i&#xff0c; 用for遍历时遍历两次 // 版本二 class Solution { public:vector<int> nextGreaterElements(vector<int>&am…

TLA+学习记录1——hello world

0x01 TLA是个好工具 编程人员一个好习惯是凡事都想偷懒&#xff0c;当然是指要科学地偷懒&#xff0c;而不是真的偷懒。一直想找到一种能检验写出的代码&#xff0c;做出的设计是否真的完全正确&#xff0c;而不是靠经验检视、代码Review、反复测试去检验。因为上述方法不管怎…

MATLAB中M文件编写

简介 所谓M文件就是将处理问题的各种命令融合到一个文件中&#xff0c;该文件以.m为扩展名。然后&#xff0c;由MATLAB系统编译M文件&#xff0c;得出相应的运行结果。M文件具有相当大的可开发性和扩展性。M文件有脚本文件和函数文件两种。脚本文件不需要输入参数&#xff0c;…

【美团3.18校招真题1】

大厂笔试真题网址&#xff1a;https://codefun2000.com/ 塔子哥刷题网站博客&#xff1a;https://blog.codefun2000.com/ 小美剪彩带 提交网址&#xff1a;https://codefun2000.com/p/P1088 题意&#xff1a;找出区间内不超过k种数字子数组的最大长度 使用双指针的方式&…

js使用crypto-js做加密解密

首先安装 crypto-js npm install crypto-js下面是完整代码&#xff0c;首先引入 crypto-js 里的 AES 和 enc&#xff0c;声明加密方法和解密方法进行测试 let { AES, enc } require("crypto-js");// 加密方法 function encryptString(str, key) {// 使用 AES 加密…

Unity Shader 溶解效果

一、效果图 二、原理分析 实现原理就是在片元着色器中&#xff0c;对像素点进行丢弃不显示。借助美术做的噪点图(利用噪点图中rgb中r值来做计算)。比如噪点图r值从0-1。我们从小到大让r值逐渐丢弃&#xff0c;比如刚开始r < 0.1丢弃&#xff0c;然后t < 0.2丢弃...知道t…

TinTin Web3 动态精选:以太坊基金会推出 EELS、Arbitrum Stylus 上线

TinTin 快讯由 TinTinLand 开发者技术社区打造&#xff0c;旨在为开发者提供最新的 Web3 新闻、市场时讯和技术更新。TinTin 快讯将以周为单位&#xff0c; 汇集当周内的行业热点并以快讯的形式排列成文。掌握一手的技术资讯和市场动态&#xff0c;将有助于 TinTinLand 社区的开…

基于SSM的汽车客运站管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

无涯教程-JavaScript - TODAY函数

描述 TODAY函数返回当前日期的序列号。序列号是Excel用于日期和时间计算的日期时间代码。如果在输入函数之前单元格格式为"常规",Excel会将单元格格式更改为"日期"。如果要查看序列号,必须将单元格格式更改为"常规"或"数字"。 语法 …

应用人脸活体检测技术,保障刷脸支付安全畅通

如今&#xff0c;人脸识别已经走进了我们生活中的方方面面&#xff0c;拿起手机扫脸付账&#xff0c;扫描人脸完成考勤&#xff0c;刷脸入住酒店纷纷便利了我们的生活。而人脸识别里一项必不可少的技术就是人脸活体检测&#xff0c;即AI不但要确定这是“你”&#xff0c;还需要…

揭秘iPhone 15 Pro Max:苹果如何战胜三星

三星Galaxy S23 Ultra在我们的最佳拍照手机排行榜上名列前茅有几个原因&#xff0c;但iPhone 15 Pro Max正在努力夺回榜首——假设它有一个特定的功能。别误会我的意思&#xff0c;苹果一直在追赶三星&#xff0c;因为它的iPhone 14 Pro和14 Pro Max都表现强劲。尽管如此&#…

百度地图3D棱柱鼠标事件

百度地图2D API JavaScript API | 百度地图API SDK 百度地图3D API jspopularGL | 百度地图API SDK 3D棱柱效果如下 一. 渲染地图 var map new BMapGL.Map(container, {style: {styleJson: styleJson2} }) map.centerAndZoom(new BMapGL.Point(116.404, 39.925), 9); map…

《消息队列》专栏介绍

《消息队列》专栏介绍 目录 《消息队列》专栏介绍专栏导言什么是消息队列呢&#xff1f;应用场景&#xff08;作用&#xff09; 为什么要用消息队列呢&#xff1f;异步处理削峰填谷 举个例子 分布式消息队列的优势 应用解耦优点发布订阅优点 分布式消息队列应用场景不同消息队列…

[SSM]MyBatisPlus拓展

五、拓展篇 5.1逻辑删除 在电商网站中&#xff0c;我们会上架很多商品&#xff0c;这些商品下架以后&#xff0c;我们如果将这些商品从数据库中删除&#xff0c;那么在年底统计商品的时候&#xff0c;这个商品要统计的&#xff0c;所以这个商品信息我们是不能删除的。 如果商城…

球谐函数实现环境光照漫反射实践

该文章以及代码主要来自 图形学论文解析与复现&#xff1a;【论文复现】An Efficient Representation for Irradiance Environment Maps 作者&#xff1a;Monica的小甜甜 与原文的不同&#xff1a; 对一些有问题的地方进行了修改添加了注释对有疑问的地方添加了疑问点引入了其…