导读

FairPushy 是依据Flutter+Dart三端一体化打造的动态更新渠道首要由Web + Server + Native悉数运用Flutter+Dart编写,为Flutter动态化场景供给动态分发才干,全方位下降上手本钱,提升开发体会。完结了动态化资源产品主动化打包和动态分发的才干,让开发者摆脱了技术栈的壁垒,而且体系轻量化、简略易用,现在项目已在Github开源:

  • 动态化渠道 github:github.com/wuba/fairpu…

  • Fair github:github.com/wuba/fair

Flutter 动态化

Flutter信任大家必定不陌生了,它的规划初衷,就是允许在各种操作体系上复用同样的代码,例如 iOS 和 Android,用Flutter写的软件程序就能够在不同的渠道上具有原生体会的高功用运用。得益于他在每一个渠道上,都会包括一个特定的嵌入层,然后供给一个程序进口,程序由此能够与底层操作体系进行和谐。从2018年2月17日发布的beta1版别到现在3.0版别现已过了4个多年初。之所以这么火热无疑离不开他能够跨渠道的特性和较高的UI功用,几乎满意的悉数跨渠道开发者的幻想,可是包巨细和动态化问题也一向争议不断。

58也自研了Flutter动态化Fair,他是支持不发版(Android、iOS、Web)的情况下,经过事务bundle和JS下发完结更新,方法相似于React Native。Fair的UI渲染是无损的,能够做到像素等级的还原,如下图:

Flutter + Dart三端一体化动态化平台实践

动态化带来的优点毋庸置疑,会及时高效的满意事务需求,提升用户体会一起也会削减初始包的巨细,进步装机率。假如出现质量问题也能够在不发版的情况快速得到处理。可是Fair动态化的bundle和JS产品需求有一个Web渠道来管理分发,涉及Server、Web和Flutter插件,Flutter是依据Dart言语,Dart言语官方给的界说是”是面临方针的、单继承的语音他的语法与C言语有点相似,可在任何渠道上开发快速的运用程序“因而咱们计划悉数用Dart言语来开发动态化渠道。

FairPushy 选型

现在开发Server的言语现已很成熟了,比方常见的JAVA、Node.js、Go、PHP、Python等(排名不分先后)。从这几年的排行榜看Java仍稳坐铁王座第一名,是最受欢迎的言语,这必定离不开他的言语特性:是一种简略的,面向方针的,分布式的,强健安全的,可移植的,功用优异、多线程的动态言语。PHP(PHP: Hypertext Preprocessor)即“超文本预处理器”,是在服务器端履行的脚本言语,特别适用于Web开发并可嵌入HTML中。PHP语法学习了C言语,吸纳Java和Perl多个言语的特征开展出自己的特征语法;该言语当初创立的首要方针是让开发人员快速编写出优质的web网站比较适合用于个人网站、企业官网等轻量级的项目开发。Python语法和动态类型,以及解释型言语的本质,使它成为多数渠道上写脚本和快速开发运用的编程言语。Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有废物收回功用的编程言语。而Node.js和Dart的介绍如下:

Node.js:一个依据 Chrome 的 JavaScript 运转时构建的渠道,运用了一个事情驱动、非堵塞式I/O模型,让JavaScript 运转在服务端的开发渠道,假如你有前端开发经验无疑更适合用Node.js来开发服务端。

Dart:一种新的 Web 编程言语,包括库、虚拟机和工具。Dart 是一个内聚的、可扩展的渠道,用于构建在 Web(您能够运用 Polymer)或服务器(例如运用 Google Cloud Platform)上运转的运用程序。运用 Dart 言语、库和工具编写从简略脚本到功用齐全的运用程序的任何内容。

言语没有肯定的好坏,需求依据事务和本钱挑选适宜的。单领出来Node.js和Dart介绍首要考虑运用Flutter开发项目的大部分是前端和移动端同学,详细挑选那个需求从事务的场景中考虑,假如说我是移动端开发Flutter项目对Dart言语适当了解这样就更适合用Dart来开发Server,反之假如你是一名前端开发则用Node.js更适宜些。既然是开发Flutter项目假如能运用Dart开发后端服务最大的优势也就是不需求学习新的言语、最大程度确保渠道一致性、削减言语的学习本钱和重复作业本钱;因而咱们计划悉数用Dart言语来开发动态化渠道。

FairPushy 架构

Flutter + Dart三端一体化动态化平台实践

全体规划是以Dart言语为支撑,Dart言语是面向方针的,特色平和常用的言语有相似的语法、运转时环境变量,能够运转在浏览器、dart虚拟机和移动设备上。而且一起支持 JIT(Just In Time,即时编译)和 AOT(Ahead of Time,运转前编编译)的言语之一。上图的FairPushy大致能够分为四个方面如下:

1. Dart support:其间包括dart:core完结根底的内置类型、调集以及其它的一些核心功用和isolate完结并发编程

2. Dart Server:首要包括数据库和衔接池、ORM、RPC结构的建造和对Web和移动端供给上层事务的的HTTP接口的支撑

3. Flutter Web和Fair Sdk:Flutter Web供给Fair产品的打包上传、环境切换和动态化编译功用,Fair SDK首要负责Fair资源产品的下载、缓存和加载功用

4. 运维和研制支撑:因为全体的规划言语是相同的所以一些研制的根底组件也能够共用比方一些日志库、网络库、Crash监控和功用监控。

因为篇幅问题本分只剖析Dart Server开发的相关常识点,伟人曾经说过你要想知道梨子的滋味,就要亲口尝一尝,可是任重而道远。Dart Server完结的难点:

根底组件建造: 怎么写HTTP接口、怎么规划SQL表、怎么链接远端数据库等等
生态建造: 客户端同学初度接触到后端的常识,需求了解后端怎么开发、布置和排查处理过错
监控体系: 代码出现异常过错怎么监控和告警?
并发问题: Dart言语编写的后端服务是否能够满意高并发下事务恳求?

Dart Server实践

Flutter + Dart三端一体化动态化平台实践
全体从初度接触后端服务开发者角度剖析首要分为两个流程,开发阶段和布置阶段:

开发阶段:需求进行Dart环境的装备和Studio装置。咱们封装了依据Dart Server根底库建造,其间最首要的是日志库、路由结构和Widget封装。其次是生态环境的建造这也是Dart Server最难的地方。像Java有Spring全家桶能够很便利的进行事务需求的开发,而Dart Server生态环境建造大致可分为异常的监控、RPC结构、ORM结构和MySql结构。具备了这些功用就能够进行事务需求开发了。

布置阶段:凭借的58云渠道完结服务的主动化布置,需求构建依据Dart环境的根底镜像来运转服务。之后经过Docker编写脚本经过Git分支拉取Dart Server的事务代码,然后履行run/server.dart脚原本服务的发动。发动后能够经过监控服务监听事务是否正常运转,假如出现阈值之外的异常会经过告警组通知开发人员。

5.1 根底才干建造

让Dart言语开发后端服务需求一些根底库,比方怎么链接远端数据库?怎么接纳HTTP接口恳求?怎么处理异常的过错?等等。具备了这些根底库就能够完结一些简略的接口开发了。在Flutter中悉数皆可Widget,咱们为此坚持了与Flutter统一的编码风格,在写后端接口也能够和写前端页面一样悉数皆能够Widget。在服务发动的时候需求注册ServerPages,悉数的HTTP接口需求在此注册。原理和Flutter中的路由装备相似。其间name表明接口恳求的路径,page表明完结这个接口逻辑详细完结类,method表明HTTP恳求的方法Get或Post等,needAuth是一个bool值,表明这个接口是否需求登录鉴权,true则会校验Token假如不满意会回来鉴权过错的Response。

mixin ServerPages {
  static final routes = [
    GetPage(
      name: Routes.GET_APP_PATCH,
      page: () => GetBundlePage(),
      method: Method.get,
      needAuth: false,
    ),
    GetPage(
      name: Routes.GET_PROJECT,
      page: () => GetProjectPage(),
      method: Method.post,
    ),
  ];
}

咱们封装了FairServiceWidget能够经过继承完结server()来很便利的获取HTTP接口恳求的参数;PatchDao是ORM结构的模型的完结类后面会详细介绍,能够经过他来完结数据库的增修改查,searchBundleId是查询数据的详细SQL句子,最后经过toBundleJson来完结数据的序列化。以动态化渠道实际的功用获取Bundle资源接口为例,一个完整的HTTP接口恳求大致如下1、继承FairServiceWidget完结Server()处理事务逻辑2、经过request_params获取恳求的参数和参数的校验3、参数正确会经过PatchDao查询远端数据库信息4、获取bundle数据后会进行Response封装回来:

class GetBundlePage extends FairServiceWidget {
  @override
  Future<ResponseBaseModel> service(Map? request_params) async {
    var bundleId = request_params?['bundleId'];
    if (bundleId == null || bundleId == "") {
      return ParamsError(msg: "bundleId==null");
    }
    var bundleList = [];
    await withTransaction<void>(() async {
      final dao = PatchDao();
      var rows = await dao.searchBundleId(bundleId);
      for (int i = 0; i < rows.length; i++) {
        bundleList.add(rows[i].toBundleJson());
      }
    }).catchError(((error, stack) {
      ResponseError(msg: error.toString());
    }));
    return ResponseSuccess(data: bundleList);
  }
}

5.2 Dart并发问题

Dart是单线程履行,也就是说一旦Dart函数开端履行,就会一向持续直到完毕,Dart函数不能被其他Dart代码中止。不过Dart能够经过async-await、isolate支持并发代码编程。Dart 代码并不在多个线程上运转,取而代之的是它们会在 isolate 内运转。每一个 isolate 会有自己的堆内存,其各自的GC不会影响到其他isolate的,然后确保 isolate 之间互相阻隔,无法互相拜访状况。因为这样的完结并不会同享内存,所以不需求忧虑 互斥锁和其他锁。所以咱们能够经过把用内存空间较大且生命周期较短的接口放到isolate中,这样即使另外一个isolate GC了并不会对咱们正常的事务流程形成影响。

Flutter + Dart三端一体化动态化平台实践

在运用 isolate 时, Dart 代码能够在同一时刻进行多个独立的使命,而且运用可用的处理器。 Isolate 与线程和进程近似,可是每个 isolate 都具有独立的内存,以及运转事情循环的独立线程。Event queue能够确保一起处理多个使命。event loop的作业就是从event queue内拿一个event然后处理它,一向重复这个操作直到queue里悉数处理完毕。event queue内的event有可能是文件I/O、timers等等

Flutter + Dart三端一体化动态化平台实践

如图所示,Dart运用程序在其main isolate履行运用程序的main()函数时开端履行。 main()退出后,main isolate开端逐一处理events queues的内容。一个Dart运用程序只要一个event loop,可是有两个Queue,event queue包括悉数的外部事情,I/O、timers、两个isolates之间的消息等,microtask queue则表明一个短时间内就会完结的异步使命。它的优先级最高,高于event queue,只要队列中还有使命,就能够一向霸占着事情循环。microtask queue添加的使命首要是由 Dart内部发生。

Flutter + Dart三端一体化动态化平台实践

5.3 生态完善

Dart开发后端服务是空白的,需求一些组件才干让开发更便利,比方远端SQL链接、衔接池、日志监控和ORM等。当然业界也有一些开源的结构能够直接运用。因为篇幅受限,所以咱们找其间一个首要的点来剖析:ORM结构在Dart Server中的实践。

ORM 的英文是 Object Relation Mapping,方针联系映射,是 RDBMS 和事务实体方针之间的一个映射,把底层的 RDBMS 封装成事务实体方针,供给给事务逻辑层运用。ORM结构供给了一种持久化形式,在Java中比较常见结构是MyBatis,他能够高效地对数据库进行拜访。

Flutter + Dart三端一体化动态化平台实践

在Dart Server中完结ORM需求封装对数据库的衔接、数据方针映射和增修改查等,能够按照前面介绍的获取Bundle资源接口为比如,需求先创立PatchDao类,内部需求完结了对数据信息的封装,只需求填写要操作的表明、映射的方针类和要害查询句子即可。比方要更新补丁表信息则能够直接传入最新的映射的方针类完结更新即可。

class PatchDao extends Dao<Patch> {
  PatchDao() : super(tablename);
  PatchDao.withDb(Db db) : super.withDb(db, tablename);
  static String get tablename => 'patch_info';
  @override
  Patch fromRow(Row row) => Patch.fromRow(row);
  Future<void> updateByPatch(Entity<Patch> entity) async {
    final fields = entity.fields;
    final values = convertToDb(entity.values);
    final sql = 'update patch_info '
        'set `${fields.join("`=?, `")}`=? '
        'where bundle_id=?';
    await db.query(sql, [...values, entity.id]);
  }
}

能够看到上面在做update的sql句子是经过拼接处理,咱们供给一套公共sql句子模板,然后在详细实体方针操作的时候将实体方针的特点名称和特点值当作参数拼接进去,组装成完整的sql句子。假如查询操作则会依据回来的数据映射成Dart方针类,所以数据驱动回来的数据通常都是以数据为核心的数据调集,咱们经过ORM结构将类方针和数据库回来的列数据进行逐个匹配获取,然后赋值到方针上:

List<E> fromResults(Results results) {
    final rows = <E>[];
    for (final results in results) {
      rows.add(fromRow(Row(results.fields)));
    }
    return rows;
  }

ORM还为能咱们做了什么?ORM结构做的最多的就是“缓存”。因为数据库操作是要和硬盘打交道的,而程序是在内存中运转的,操作内存的速度要比操作硬盘快数十倍以上,可见一个拜访量较高的大型体系很简单因为数据库操作过于频繁而拖慢全体速度,然后影响体系的运用。因而,ORM结构还需求帮助咱们削减数据库的拜访,加快体系速度。

5.4 动态化在线编译

Flutter Fair动态化产品有json和js文件需求上传到CDN服务动态分发到APP上来完结动态化功用,其间产品需求在Flutter Web渠道进行打包上传,现在支持两种方法:

手动上传:需求开发者在本地编译器编译生成Fair产品,到Flutter Web渠道手动上传打包好的产品包\

主动上传:开发者无需关心Fair产品,只需求在Flutter Web渠道装备项目Git地址、Flutter版别等,点击在线编译即可触发主动化构建和Fair产品上传

Flutter + Dart三端一体化动态化平台实践

如图所示,主动化上传Fair产品由开发者在Flutter Web渠道装备构建信息,转到Dart Server 进行编译处理,依据Web渠道的构建装备信息会进行入库操作,把Git地址、Git分支和Flutter版别等与本次创立的Fair资源做相关记载。之后会进行环境清理、拉取填入的GIt项目地址到服务器,履行flutter pub get和flutter pub run build_runner build指令生成Fair产品,要害脚本如下:

var shell = Shell();
    shell = shell.cd("/opt/");
    await shell.run('''
    rm -rf $git_dir_path
    mkdir -p $git_dir_path
    ''');
    shell = shell.cd(git_dir_path);
    await shell.run('''    
    git clone $patchGitUrl ./
    git checkout $patchGitBranch
    flutter pub get
    flutter pub run build_runner build
    ''');

拿到Fair编译产品后会上传到cdn,然后取得cdn上传后的url,把url更新patch_online_build数据库表中。至此完结了整个动态化编译流程。其间需求凭借Docker来完结一部分的脚本履行,docker是一个开源的运用容器引擎,依据go言语开发并遵从了apache2.0协议开源。 docker能够打包运用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何的linux服务器,也能够完结虚拟化。 容器是彻底运用沙箱机制,而且容器开销极其低,感兴趣的能够查阅材料了解下。

总结与展望

本文分享首要以Flutter Fair动态化为布景的热更新渠道完结方案,经过对Dart Server研究和实践确认Dart开发后端服务的可行性,关于开发Flutter的客户端和前端同学能够扩大视界和进步全体化思想,而且极大的削减沟通本钱。“任总而道远”需求完结的功用仍是有很多,而且需求开发者经过不停的迭代与优化才干越做越好,这个进程将会是一个绵长且繁琐的进程。现在项目现已在Github上开源,也欢迎对Flutter Dart和动态化感兴趣的同学给咱们点个star给予鼓励:

  • 动态化渠道 github:github.com/wuba/fairpu…

  • Fair github:github.com/wuba/fair

参考文献和其他Fair文章:

  • Fair逻辑动态化通信完结:mp.weixin.qq.com/s/8G5rEXc0Z…
  • Fair逻辑动态化架构规划与完结:mp.weixin.qq.com/s/Xq5BAa6G8…