【小项目】全栈开发培训手册 | 后端(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 工具包核心理念

  1. Verticle 实例
  2. Verticle 之间用来通信的Event Bus通信通道

宏观上来讲,vert.x core 在jvm上首先建立了一个 vert.x 运行容器,其中运行在容器里的每个程序模块叫做verticle ,很直观的,听名字就知道每个模块单元的实例都是垂直部署互不干扰。因此,每个verticle可以被部署多次,如果一个verticle中起了web server并且被部署了多次,那么请求将会以轮询调度法被分发到不同实例中。每个verticle都有自己的生命周期方法startstop,在verticle中,你也可以创建部署子verticle实例。

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

我们把视线聚焦verticle内部,来看看它到底是如何实现的异步io。

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

当消息被发送给verticle实例的时候,消息首先被放入Event Loop中,然后按消息到达的顺序被依次处理,可以看出,这是一个单线程阻塞式的实现,一旦处理一条消息时阻塞了当前线程,后续消息则无法被实时处理,那这可怎么办呢?不要急,如果处理消息的操作是个阻塞式的操作,那么可以调用vert.x的实例方法将操作分发到工作线程去处理。

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

verticle之间通过Event Bus通信,假设一个后端应用我们把应用层和持久层分为两个 verticle,两个实例通过Event Bus通信,当应用层想要查询一条数据时,应用层通过Event Bus发送一条消息给持久层要求持久层向数据库查询并返回响应。消息的格式技术上可以很随意,但是考虑到通用型工程上一般采用JSON

我们把消息分成3种形式:

  1. 点对点消息
  2. 请求-回复型消息
  3. 发布-订阅消息

神奇的是,有了Event Bus,我们能给其他vert.x容器中的实例互传消息:

  1. 当网络集群功能被开启时,分布式Event Bus被启动,因此消息可在集群内共享。
  2. 第三方组件可以简单的通过TCP协议加入Event Bus
  3. Event Bus可以通过一些特殊协议例如AMQP将消息作为统一消息服务导出。
  4. SockJS允许浏览器环境连接至Event Bus网络。

Hello World!

首先,我们需要新建一个工程,vert.x官网上虽然为大家准备了一个starter,但是不推荐使用,理由是生成的版本太陈旧,我在写文章的时候starter页面还处于521错误状态。官方的github主页其实是准备了starter工程的,习惯用maven的同学点这里,用gradle的同学点这里,我个人比较喜欢使用gradle。下载完成后自行修改GroupIDArtifactID

这里我使用IDEA 社区版导入项目,勾选 Use auto-import 和 Use default gradle wrapper 选项,注意,jdk版本一定要兼容JAVA 8

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

我们打开唯一的一个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

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

可以启动该服务器,命令行输出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);
  }

}

右键,重新编译文件。

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

正确的输出如下

【小项目】全栈开发培训手册 | 后端(1) vert.x框架理解

刷新浏览器,可以看到文本发生了变化。

需要注意的是,如果使用的是Windows系统,自动部署往往是不成功的,原因是在build.gradle中,文件发生改变的hook函数是这么写的

def doOnChange = './gradlew classes'

我们将其改成Windows格式

def doOnChange = 'gradlew classes'

大功告成。

未完待续。

相关推荐