Origin X Documentation

天行有常,不为尧存,不为桀亡。——荀子《天论》

零之起源

2017年年底,Zero Up诞生了,其实不是什么大作,仅仅只是为了给社区的人一种选择,之所以喜欢使用vert.x更多的时候仅仅是因为兴趣,后来开了《逐陆记》写原生的vert.x教程,工作一忙起来更新的频率就成了最大的问题(不过我一直在弱弱努力更新中)。一直不写Zero的文章是因为毕竟这种新框架没有经历过生产环境的验证,而如今已经有三个系统用了它,而最近开了新项目,所以才静下心来思考写点中文的教程,分享一下项目过程中的心得——这个过程写出来的东西比起官方当时自己写的那种纯技术的东西可能会带给读者更大的收获。

序:Zero?

这个框架为什么叫Zero?最初在写这个框架时它有另外一个名字,叫做Vert.x Up,有一天玩游戏时听到了一句话:零式、行きます。觉得日语发音中的“零”听起来比英文更有意思(我是个漫迷),所以就更名为了Zero Up,只是大家习惯了就直接叫Zero了。其实这个框架也是源起于spring boot,由于它很实用,而在vert.x的生态中刚好没有这种工具,于是就写了这样一个工具,当时确实有一种想法就是让这个东西去填补vert.x中的空缺。——不过正如我在《逐陆记》中写过的一样:vert.x并不是为了微服务架构下的企业服务系统打造的一个框架,所以它对微架构的支持是需要我们自己去细细思考和打磨的。

vert.x带来了什么

其实接触vert.x已经是2014年的事了,那还是3.x出来之前的版本,那个版本很不成熟——相信使用过的人都明白,之后横空出世的3.x的确是一个革命性的版本,进行了大量模块化的重新设计,应该说它并没有让我们这些“老船长”失望。从我个人的角度上看来,分享下边几点心得:

穿别人的鞋,走自己的路,让别人找去吧!

我的朋友里有很多springer(传说中的春天的信仰者),每次都会问我:既然spring已经提供了像spring boot这种东西,为什么一定要用vert.x呢?其实我一直在强调,软件工程是百家争鸣取其优,不是计较谁好谁坏的——我拒绝盲目的信仰,况且不论是springvert.x归根到底只是工具,代表不了生产成品,而我们的甲方往往只关注最终的成品。

1.启动

1.1.Maven配置

言归正传,开始第一课的内容就是搭环境,在0.4.6的版本之前,Maven中使用的是依赖模式,后来(0.4.8)改成了继承模式,如果需要使用Zero,那么可以在您的Maven项目的pom.xml文件中加入下边片段:

<parent>
    <artifactId>vertx-import</artifactId>
    <groupId>cn.vertxup</groupId>
    <version>0.4.10</version>
</parent>

由于作者本人更熟悉Maven,对Gradle的环境功底有限,所以Zero中仅仅提供了Maven的基本环境,有兴趣的读者可以自行研究。

1.2.零式、行きます

设置了上述配置后,就可以直接写一个Java中的程序入口(main程序)来启动Zero,入口代码如下:

import io.vertx.up.VertxApplication;
import io.vertx.up.annotations.Up;

@Up
public class Driver {

    public static void main(final String[] args) {
        VertxApplication.run(Driver.class);
    }
}

直接运行(使用Java Application模式)上述程序,当你看到控制台输出了下边内容就意味着你的程序启动成功了:

[ ZERO ] ZeroHttpAgent Http Server has been started successfully. \
    Endpoint: http://0.0.0.0:6083/

2.结构

上一个章节已经知道了Zero是如何启动的,这个章节主要讲解Zero中的基本项目结构(这个只是为了帮助开发人员更好去解读Zero,了解如何使用)。

2.1.项目结构

Zero源起于vert.x,根项目为:vertx-zero,主要包含以下几个子项目(第三级项目粒度比较细,这里不阐述):

关于 vertx-pin 和 vertx-ifx vertx-ifx是底层插件项目,不依赖 Zero 容器,属于非业务功能插件,技术性偏重,而vertx-pin是 Zero 中新开的扩展模块,这些模块多数带有自己的数据表设计、业务逻辑,属于业务功能插件,这些插件本身依赖 Zero 容器( vertx-up项目),属于上层的扩展插件。

整体的项目结构如下:

vertx-zero
  |- vertx-gaia
    |- vertx-co      # Zero核心库
    |- vertx-up      # Zero容器
    |- vertx-tp      # Optional/Plug-in(第三方插件)
    |- vertx-ifx     # Optional/Plug-in(内置常用插件和Vert.x中的本地化插件)
    |- vertx-rx      # 和up同一级别的项目,Rx化
  |- vertx-import    # Zero环境初始化入口项目
  |- vertx-zeus      # 新版已经移除
    |- ……            # 示例代码
  |- vertx-pin
    |- zero-rbac     # RBAC认证授权模块(带数据表)
    |- zero-crud     # 动态CRUD业务模块
    |- zero-ambient  # 多应用配置管理模块(带数据表)
    |- ……

2.2.支持的功能

王婆卖瓜,自卖自夸,Zero官方的教程当时写的时候只是把每种功能的细节全部列出来了,实际上少了一份概述,并且对使用过程中的一些教程细节并没有那么完善,那么这里简单介绍一下究竟使用Zero可以做什么——本系列的教程也是为了将更多Zero使用过程中的心得展示给开发人员。

Zero的目的就是少代码高效率地处理企业服务应用、微服务应用。

Zero中包含的功能如下:

虽然Zero中提供的功能并不多,但实际上从目前开发过的三个已经运行在生产环境的项目看来,配合Zero UI的前端框架,已经能够满足大部分用户的需求了,至于实际的使用效果,我个人倒是觉得给它60分并不过分。

2.3.项目名?起源?

既然大家喜欢听故事,那么我也乐意分享Zero相关的故事:

有人问过我,为什么Zero的项目名称使用了类似:gaia、zeus以及希腊神话中神的名字,其实我只想说,最初的Zero就是源起于神话。当时我和Dean在一个山中别墅中写一个企业系统的后端流程(那是Zero的第一个版本,基于纯数据驱动的微服务容器——也是我论文的主题,那个系统杯盘狼藉。)当时他问我,是不是可以开发一个和spring-boot近似的框架,而且是在vert.x中开发,其实当时就萌生了Zero的想法,取名为Zero Up。

然后,就像泄洪的水闸一样,本来只是一个玩具,慢慢地把这个项目改写得不断成熟起来,直到最后自己用在了生产环境中——最初的那篇论文里,其实已经把很多开发过程中的痛点,应该说开发人员最厌倦的东西踩得差不多了,毕竟我是一个懒人,对于懒是有一定信仰的,所以在Zero中引入了很多帮助开发人员快速开发的东西,在这个仅仅只有十万行左右代码的背后,其实还碾死过一个差不多接近三十万行代码的容器。

神话一直是我比较喜欢的东西,就像本人喜欢日漫和日本幻想系游戏一样,所以当时在想,如果开发容器,不能太随意,容器代表着完整应用系统的基础,在基础之上,必有应用——就像我们写一个话剧剧本,剧本中最核心的就是有角色、有行为、有交互,也有让观众真正可以看的成品。当时发了一篇说说,说说的内容正好是圣斗士中海魔女苏兰特和朱利安索罗的一张图片,于是想到了波士顿,于是想到了雅典娜,于是想到了……好吧,最后就是用了希腊神话中众神的名字来搭建Zero的案例,也就是vertx-zeus的起源。

对开发人员而言,也许vertx-zeus更有参考价值,因为它毕竟是从开发人员视角去看Zero究竟怎么用,而不是单纯的技术教程,让它知道了这个世界的足迹,那么这个世界才会完整,所以在最初写Zero过程中,官方教程被翻新过三次,直到最后搭建了域名,申请了Maven中心仓库的权限,以及最终让Zero问世。

3.殇

3.1.CRUD

嗯,写个接口,搭建个全栈的前端,然后联调,然后上线,然后就等着收钱?企业系统哪里有这么简单——我只能简单说互联网系统的思维不是不能应用于企业服务,而是不能不经消化直接驱动,否则等到你踩到坑你才明白为什么甲方系统迟迟不上线。

很多人说CRUD的系统简单,没有技术含量,我不说这种想法对与错,我只能说我对这种评价有所保留。

其实CRUD的系统的复杂程度和业务紧密相关,不是一句话说得清楚的,技术还是那些技术,只是真正在生产环境,当你遇到时,如果你太小看它也难免会遭受它对你的暴击,其实大部分系统都是CRUD的。我一直都告诉周围的人,一个架构师,如果仅仅懂技术,那不叫架构师,那只是一个工艺大师,我们输出给甲方的价值是一个商品,不是艺术品,更多的时候架构师更要懂业务,更要懂交互。你不需要告诉你的客户你的东西多牛逼,你只需要让他看到过后对你的作品露出喜悦和惊叹就足够了。

很多企业总是喜欢在不计成本的研发投入中把一个RoadMap画得如此高大上,当甲方穿透到系统内部,所有的系统都成为了POC,都成为了样子货,正如罗马不是一天建成,更多的时候,任何系统,我们需要存在谦卑和敬畏之心,保证自己的每一份输出都是有价值的,那么这样的系统才会及格。

所以我一直都觉得,CRUD的系统不是难,是检测你的逻辑是否及格的基本标准,也是每个工程师的必修课。

3.2.组件化

最初的Zero容器名字叫做vie(法文翻译中的“生活”),那个项目包含了45个子项目,一个纯粹的数据驱动型的系统,连业务逻辑的代码都是直接通过脚本引擎来实现的,也算是把Java中的各种坑踩到了,其实那一刻我意识到以层为核心的系统需要付出的代价。

在很长一段时间中,我苦苦思索,究竟容器和业务系统的区别点在哪儿,直到有一天为了给别人做培训,重新去翻了Tomcat的源代码,似乎明白了,其实系统最核心的一点不一定是分层,而是“组件化”,就像我们人体的器官一样。如果将所有的内容全部组件化,并且给每个不同的组件给予不同的职能,让它们协同起来,告别曾经的:Service/Data/Model的N层设计,是不是可以给自己带来更多的设计灵感。

在这样的思维中,有了Zero让很多人都看不懂的设计、代码,以及各种奇怪的命名,实际上Zero中的命名挺有讲究。比如:

	/* 1.Find Agent for deploy **/
	Runner.run(() -> {
		final Scatter<Vertx> scatter = Ut.singleton(AgentScatter.class);
		scatter.connect(vertx);
	}, "agent-runner");

	/* 2.Find Worker for deploy **/
	Runner.run(() -> {
		final Scatter<Vertx> scatter = Ut.singleton(WorkerScatter.class);
		scatter.connect(vertx);
	}, "worker-runner");

Scatter的词义是“分散”,Zero在启动过程中,把所有的流程都分散在不同的步骤中,所以当时使用了这样一个词语,它表示一旦系统启动,每个“组件”都分散在不同的线程中,去完成不同的事情,如果需要添加新的启动流程,那么仅仅创建一个新的Scatter就可以搞定了,这样就不需要再修改其他代码。而且细心的读者会发现,一旦Scatter初始化完成后,它会去连接Vertx实例,对的,对于任何一个组件的初始化,让它自己做连接,即使有一天你不想要了,那么这个Scatter就可以关闭掉连接(这个是可以扩展配置的内容),这样的系统会相对比较灵活。

归根到底,Zero之中没有原来的N层架构,全是组件,不同组件不同职责,不同组件不同名称,这些组件相互协作、编连,直到最后让整个系统驱动起来。

3.3.效率

软件项目的另一个痛点,就是开发人员的开发效率,同样是一种:殇。我们每天都在说不要重复造轮子,实际上,我们每天都在重复造轮子,为什么,因为我们的轮子是和车子黏在一起的。软件复用技术一直是我们讨论得最多的一个技术,但真正在项目工期、实施的过程中,很多软件开发人员放弃了自己心中追求的东西,开始堆烂代码,然后把这个锅甩给下一个人,往下一个雄心壮志的地方去迈步。

所以我们催生了无数的烂代码、肥沃的土壤变成了沼泽……

从最早(大概是小学五年级)接触Forbase、Turbo C开始,我一直都觉得,技术需要我们给一个态度,如今我能给这个态度的定义就是:敬畏和谦卑。软件最终的成品是落在你的价值输出上,并不是说句话一个承诺那么简单,山盟海誓抵不过风行草偃,沧海桑田终究抵不过光阴细流,用软件磨练自己的性子也是一种修炼。

回到Zero,其实当初设计它的目的就是提高效率,也就是我们说的“单位时间的生产力”,而很多企业把定语省掉了,直接演变成恶性循环,996、通宵上线、疲劳战术,到最后出来的东西几乎是用生命烧出来的,没有了持续,最后其实不是DevOps,而是DeadOps。开发人员也对系统失去了兴趣,累觉不爱了。所以我还是坚持:生产效率 > 生产力,对的,我自己也一直在尝试和摸索,如何提高开发的效率,甚至有时候也期待Zero本身给我一个答案。

4.总结

因为是开篇,所以聊些无关痛痒的东西,后边可能就再也没有这样的机会了,但是我依然希望有人对技术充满了激情和热忱,一直以来都用生生不息的兴趣去驱动自己在软件领域坚持的决心。我不是一个善于推销的人,所以我很少给别人推荐Zero,而我自己却乐在其中,不过你愿意,我也会尝试改掉懒惰的毛病,写完《逐陆记》,同样写完这一本教程,如果要给这套教程取个名字,我觉得就是《零》。

为什么要写一套中文教程?其实还是希望像《逐陆记》一样,用更加有味道的方式去呈现这个系统,既不失严谨,也不丢趣味。