【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解
开坑前言
我给这个专栏的名气取名叫做小项目,听名字就知道,这个专题最终的目的是带领大家完成一个项目。为什么要开这么大一个坑呢,一来,虽然网上讲IT知识点的书籍铺天盖地,但是国内带领大家实战的书籍少之又少,亦或是软件版本陈旧,导致很多人知识点说的头头是道,但是一旦遇到工程问题就凉凉了。因此以项目为例一步一步带领萌新通往工程师之路。二来,给大家新开坑的项目一个参考。
专题概览
本专题将以搭建一个简单博客系统为例,最终实现一个前后端分离的web网站和一个hybird APP。
使用的技术:
- vert.x 搭建博客后台API
- react 搭建博客web UI
- react native 搭建手机端
正文
我很喜欢vert.x的设计理念,它充分体现了面向未来编程的思想,无论是水平扩展还是垂直扩展都非常的方便,java 8 的语法糖吃起来也是非常的甜。只可惜中文资料甚少,且质量度不高,导致普及很成问题。其实vert.x是个非常先进的工具集,实用但不臃肿,你可以自由组合你需要的组件,不像spring那样强制要求你了解那么多配置选项。学习一个框架,如果英文水平过硬,最好的方式开始去看框架官网的文档,非常幸运的是,vert.x的官网写的非常详细,对于框架自身的功能点讲的非常清楚。那这个系列就是官网文档的翻译吗?不是的。一般官方文档只会讲解框架本身的东西,对于如何工程化,如何组织项目结构这一块是没有的。因此,本系列以主要以官方文档为基础,将尽可能多的特性融入本项目,并标注官网原文出处,有兴趣的小伙伴可点击深入了解。
什么是 vert.x
vert.x是一个运行于JVM平台的工具集,它的理念很像node,特点是事件驱动,非阻塞。这套工具集包含了许多优秀的工具包,例如core工具包用来提供异步编程、verticle实例部署等基础功能,web工具包提供路由功能和中间件支持,风格很像koa框架。当然最终最核心的肯定是下面要讲的core工具包了。
Core 工具包核心理念
- Verticle 实例
- Verticle 之间用来通信的Event Bus通信通道
宏观上来讲,vert.x core
在jvm上首先建立了一个 vert.x
运行容器,其中运行在容器里的每个程序模块叫做verticle
,很直观的,听名字就知道每个模块单元的实例都是垂直部署互不干扰。因此,每个verticle
可以被部署多次,如果一个verticle
中起了web server并且被部署了多次,那么请求将会以轮询调度法被分发到不同实例中。每个verticle
都有自己的生命周期方法start
和stop
,在verticle
中,你也可以创建部署子verticle
实例。
我们把视线聚焦verticle
内部,来看看它到底是如何实现的异步io。
当消息被发送给verticle
实例的时候,消息首先被放入Event Loop
中,然后按消息到达的顺序被依次处理,可以看出,这是一个单线程阻塞式的实现,一旦处理一条消息时阻塞了当前线程,后续消息则无法被实时处理,那这可怎么办呢?不要急,如果处理消息的操作是个阻塞式的操作,那么可以调用vert.x的实例方法将操作分发到工作线程去处理。
verticle
之间通过Event Bus
通信,假设一个后端应用我们把应用层和持久层分为两个 verticle
,两个实例通过Event Bus
通信,当应用层想要查询一条数据时,应用层通过Event Bus
发送一条消息给持久层要求持久层向数据库查询并返回响应。消息的格式技术上可以很随意,但是考虑到通用型工程上一般采用JSON
。
我们把消息分成3种形式:
- 点对点消息
- 请求-回复型消息
- 发布-订阅消息
神奇的是,有了Event Bus
,我们能给其他vert.x
容器中的实例互传消息:
- 当网络集群功能被开启时,分布式
Event Bus
被启动,因此消息可在集群内共享。 - 第三方组件可以简单的通过TCP协议加入
Event Bus
。 Event Bus
可以通过一些特殊协议例如AMQP将消息作为统一消息服务导出。SockJS
允许浏览器环境连接至Event Bus
网络。
Hello World!
首先,我们需要新建一个工程,vert.x
官网上虽然为大家准备了一个starter,但是不推荐使用,理由是生成的版本太陈旧,我在写文章的时候starter页面还处于521错误状态。官方的github主页其实是准备了starter工程的,习惯用maven
的同学点这里,用gradle
的同学点这里,我个人比较喜欢使用gradle
。下载完成后自行修改GroupID
和ArtifactID
。
这里我使用IDEA 社区版导入项目,勾选 Use auto-import 和 Use default gradle wrapper 选项,注意,jdk版本一定要兼容JAVA 8
。
我们打开唯一的一个java文件MainVerticle.java
。
package io.vertx.starter; import io.vertx.core.AbstractVerticle; public class MainVerticle extends AbstractVerticle { @Override public void start() { vertx.createHttpServer() .requestHandler(req -> req.response().end("Hello Vert.x!")) .listen(8080); } }
我们在新建自己的verticle
的时候,需要继承AbstractVerticle
类,这样我们就可以直接使用vertx
实例,创建服务器监听并传入回调函数。这样一个最简单的web server就搭好啦。
打开命令行,在命令行执行
gradlew run
可以启动该服务器,命令行输出Succeeded in deploying verticle表示启动成功,我们访问http://localhost:8080
, 成功显示"Hello Vert.x!"。
执行
gradlew shadowJar
则会调用gradle打包任务,最终在./build
目录下得到一个fat-jar
。
为什么呢,明明没有main函数啊,我们打开build.gradle
文件,有如下关键代码:
mainClassName = 'io.vertx.core.Launcher' def vertxVersion = '3.5.1' def mainVerticleName = 'io.vertx.starter.MainVerticle' def watchForChange = 'src/**/*' def doOnChange = './gradlew classes' shadowJar { classifier = 'fat' manifest { attributes "Main-Verticle": mainVerticleName } mergeServiceFiles { include 'META-INF/services/io.vertx.core.spi.VerticleFactory' } } run { args = ['run', mainVerticleName, "--redeploy=$watchForChange", "--launcher-class=$mainClassName", "--on-redeploy=$doOnChange"] }
可以看到,真正的launcher class
其实是io.vertx.core.Launcher
,启动类会自动部署我们的MainVerticle
。
你可能好奇启动参数中这个--redeploy
参数,是的,vert.x
具有自动重新部署功能,我们将MainVerticle.class
中的文本替换为"Hello World!",然后重新编译。
package io.vertx.starter; import io.vertx.core.AbstractVerticle; public class MainVerticle extends AbstractVerticle { @Override public void start() { vertx.createHttpServer() .requestHandler(req -> req.response().end("Hello World!")) .listen(8080); } }
右键,重新编译文件。
正确的输出如下
刷新浏览器,可以看到文本发生了变化。
需要注意的是,如果使用的是Windows系统,自动部署往往是不成功的,原因是在build.gradle
中,文件发生改变的hook函数是这么写的
def doOnChange = './gradlew classes'
我们将其改成Windows格式
def doOnChange = 'gradlew classes'
大功告成。
未完待续。