github: https://github.com/vert-x3
官网:http://vertx.io/
Vert.x诞生于2011年,当时叫node.x,不过后来因为某些原因改名为Vert.x,目前官网最新版本是4.4.0,官网上介绍Vert.x是一个用于在JVM上构建高效应用程序的工具包,是JVM上的Reative开发套件。说白了,Vert.x就是一堆的jar包,提供了一系列的编程API接口,通过这些API可以实现异步编程。
Vert.x目前是我见过功能最强大、对第三方库依赖最少的Java框架,是一个异步无阻塞的网络框架,其参照物是node.js,基本上node.js能干的事情,Vert.x都能干,它只依赖Netty4以及Jacskon,Vert.x利用Netty4的EventLoop来做单线程的事件循环,所以跑在Vert.x上的业务不能做CPU密集型的运算,这样会导致整个线程被阻塞,另外如果你需要建立分布式的Vert.x,则再依赖HazelCast这个分布式框架即可,注意Vert.x3必须基于Java8。由于基于JVM,所以Vert.x可以用其他语言来实现你的业务。
异步编程怎么理解呢?对于写过ajax的人来说,不难理解,$.ajax方法并不会阻塞,而是直接向下执行,等到远程服务器响应之后,才会回调success方法,那么这时候success方法才会执行。ajax下面的代码不会等到success方法执行完毕之后再执行,这就是所谓的异步。如下:
console.log("1");
$.ajax({
"url" : "/hello",
"type" : "post",
"dataType" : "json",
"success" : function(val) {
console.log("2");
}
});
console.log("3");
浏览器会先输出1,在hello方法没有相应之前不会输出2,但是会先输出3,等接口响应success之后会输出2,这就是异步,异步编程是Vert.x的一大特性,也是Vert.x的核心,Vert.x可以开发Web应用,但Vert.x不仅仅是一个Web开发框架,他更像Spring,是一个技术栈(Vert.x生态可以查看https://github.com/vert-x3/vertx-awesome),或者说是一个Vert.x生态体系。在这个体系中,Vert.x只是提供了Web开发的能力。下面对Vertx和Spring做一个对比:
项目 | Spring | Vertx |
---|---|---|
核心框架 | spring-core | vertx-core |
Web开发 | spring-webmvc | vertx-web |
jdbc框架 | spring-jdbc | vertx-jdbc-client |
redis | spring-data-redis | vertx-redis-client |
微服务 | spring-cloud | vertx-hazelcast |
可以说,很多spring能做的事情,Vertx也都能实现。那么既然如此,Spring如此强大,社区如此活跃,为何还会有Vertx呢?他们之前区别的核心点就只有一个:Spring的操作是同步的,Vertx的操作是异步的。异步带来了更高的性能,但同时也带来了编码和调试的复杂度,但不得不说异步可能是未来的一个趋势,至少在Java实现高性能服务器上的一个趋势。
在Java领域,做Web开发我们一般有很多的选择,比如使用原生的Servlet,比如使用SpringMVC,再比如使用Struts等等总之你有很多的选择。在国内,目前来讲,SpringMVC作为Spring体系下的Web层架构,是深受企业青睐的,绝大部分的企业可能都在使用SpringMVC,而对于我们今天要说的Vert.x这个Web层框架,却很少有人知道,但它却是仅次于SpringMVC,排名第二的一个Web层框架。
Vert.x是基于事件的,提供一个事件驱动编程模型,使用Vert.x作为服务器时,程序员只要编写事件处理器event handler即可,当TCP socket有数据时,event handler理解被创建调用,另外它还可以在以下几种情况激活: ‘当事件总线Event Bus接受到消息时,’ ‘当接收到HTTP消息时,’ 当一个连接断开时’,’ ‘当计时器超时时.’
作为服务层处理逻辑这一层基本上对应的传统Java里的领域模型处理层,各种Service调用以及对数据层的调用,差不多是一个承上启下的一层,传统的模型里,这一层基本上都是同步调用,即使有异步调用,也是与业务逻辑分离的异步,如果全异步会导致业务逻辑碎乱,代码很难描述清楚,到这里你会发现Vert.x其实不太好融合到业务性很强的服务层里,其主要原因如下
1、自身是异步体系,不适合描述顺序逻辑性强的业务。
2、由于异步的问题,访问数据层也必须是异步,导致业务模型进一步碎片化。
我们学习一个新东西,都要去了解理解它的基本概念,学习Vert.x也是如此。那么Vert.x又有哪些基本概念呢?
一句话总结:Java能做的,Vert.x都能做。咱这里主要列出来Vert.x擅长做什么?
不需要容器!不需要容器!不需要容器!
不需要处理底层细节(如拆包和粘包),注重业务代码编写。
微服务
应用。上面也提到了,Vert.x和Spring一样,也有着完善的生态,具体可以查看https://github.com/vert-x3/vertx-awesome 我们可以看到,每一块内容都提供了多种的实现,有官方支持的版本还有社区版本。下面我们具体介绍下技术体系中官方支持的版本。
// create a test table
execute(conn.result(), "create table test(id int primary key, name varchar(255))", create -> {
// start a transaction
startTx(conn.result(), beginTrans -> {
// insert some test data
execute(conn.result(), "insert into test values(1, 'Hello')", insert -> {
// commit data
rollbackTx(conn.result(), rollbackTrans -> {
// query some data
query(conn.result(), "select count(*) from test", rs -> {
for (JsonArray line : rs.getResults()) {
System.out.println(line.encode());
}
// and close the connection
conn.result().close(done -> {
if (done.failed()) {
throw new RuntimeException(done.cause());
}
});
});
});
});
});
});
再看一个使用Reactive2构建的多步操作的代码,paramCheckStep,insertPayDtlStep,requestStep等等都是异步方法,但这里就很好的处理了异步回调的问题,不再有那么多层的大括号,代码结构也geng
public void scanPay(JsonObject data, Handler<AsyncResult<JsonObject>> resultHandler) {
paramCheckStep(data) // 参数校验
.flatMap(this::insertPayDtlStep) // 插入流水
.flatMap(x -> requestStep(x, config)) // 请求上游
.flatMap(this::cleanStep) //参数清理
.subscribe(ok -> {
logger.info("成功结束");
resultHandler.handle(Future.succeededFuture(ok));
},
err -> {
logger.error("正在结束", err);
resultHandler.handle(Future.failedFuture(err));
}
);
}
首先新建一个Maven项目,然后在pom.xml中加入相关的依赖和插件,如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>vertxoneartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<vertx.version>4.0.0vertx.version>
<main.class>org.example.Mainmain.class>
properties>
<dependencies>
<dependency>
<groupId>io.vertxgroupId>
<artifactId>vertx-coreartifactId>
<version>${vertx.version}version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.4version>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${main.class}Main-Class>
manifestEntries>
transformer>
transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-prod.jaroutputFile>
configuration>
execution>
executions>
plugin>
plugins>
build>
project>
跟其它Maven项目一样,我们首先定义了项目的GroupId,ArtifactId以及版本号,随后我们定义了两个属性,分别是:vertx.version,也就是Vert.x的版本号,此处我们使用最新的Vert.x版本,也就是4.4.0;以及main.class,也就是我们要使用的包含有main函数的主类。之后我们引入了两个Maven插件,分别是maven-compiler-plugin和maven-shade-plugin,前者用来将.java的源文件编译成.class的字节码文件,后者可将编译后的.class字节码文件打包成可执行的jar文件,俗称fat-jar。
然后我们在src/main/java/org/example目录下新建两个java文件,分别是Main.java和MyFirstVerticle.java,代码如下:
Main.java
package org.example;
import io.vertx.core.Vertx;
/**
* @author dxm
* @description
* @ClassName
* @date 2023-03-14 15:19
*/
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(MyFirstVerticle.class.getName());
}
}
MyFirstVerticle.java
package org.example;
import io.vertx.core.AbstractVerticle;
/**
* @author dxm
* @description
* @ClassName
* @date 2023-03-14 15:25
*/
public class MyFirstVerticle extends AbstractVerticle {
public void start() {
vertx.createHttpServer().requestHandler(req -> {
req.response()
.putHeader("content-type", "text/plain")
.end("Hello World222!");
}).listen(8080);
}
}
然后用Maven的mvn package命令打包,随后在src的同级目录下会出现target目录,进入之后,会出现vertxone-1.0-SNAPSHOT.jar和vertxone-1.0-SNAPSHOT-prod.jar两个jar文件,后者是可执行文件,在有图形界面的操作系统中,您可双击执行或者用以下命令:java -jar vertxone-1.0-SNAPSHOT-prod.jar执行,然后打开浏览器,在浏览器的地址栏中输入:http://localhost:8080/ 便可看到熟悉的Hello World!啦。
我们也可以使用Launcher来替代Main类,这也是官方推荐的方式,在pom.xml中加入main.verticle属性,并将该属性值设置为maven-shade-plugin插件的manifestEntries的Main-Verticle对应的值,最后修改main.class为io.vertx.core.Launcher,修改后的pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>vertxoneartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<vertx.version>4.0.0vertx.version>
<main.class>io.vertx.core.Launchermain.class>
<main.verticle>org.example.MainVerticlemain.verticle>
properties>
<dependencies>
<dependency>
<groupId>io.vertxgroupId>
<artifactId>vertx-coreartifactId>
<version>${vertx.version}version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.4version>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${main.class}Main-Class>
<Main-Verticle>${main.verticle}Main-Verticle>
manifestEntries>
transformer>
transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-prod.jaroutputFile>
configuration>
execution>
executions>
plugin>
plugins>
build>
project>
然后在src/main/java/org/example目录下新增MainVerticle.java文件,代码如下:
package org.example;
import io.vertx.core.AbstractVerticle;
/**
* @author dxm
* @description
* @ClassName
* @date 2023-03-14 3:48 PM
*/
public class MainVerticle extends AbstractVerticle {
public void start() throws Exception {
vertx.deployVerticle(MyFirstVerticle.class.getName());
}
}
然后重新打包后执行,便可再次看到Hello World!。
下面我们将会介绍测试部分,首先引入两个新的测试依赖,修改后的pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>vertxoneartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<vertx.version>4.0.0vertx.version>
<main.class>io.vertx.core.Launchermain.class>
<main.verticle>org.example.MainVerticlemain.verticle>
properties>
<dependencies>
<dependency>
<groupId>io.vertxgroupId>
<artifactId>vertx-coreartifactId>
<version>${vertx.version}version>
dependency>
<dependency>
<groupId>io.vertxgroupId>
<artifactId>vertx-unitartifactId>
<version>${vertx.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-shade-pluginartifactId>
<version>3.2.4version>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>shadegoal>
goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${main.class}Main-Class>
<Main-Verticle>${main.verticle}Main-Verticle>
manifestEntries>
transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/io.vertx.core.spi.VerticleFactoryresource>
transformer>
transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-prod.jaroutputFile>
configuration>
execution>
executions>
plugin>
plugins>
build>
project>
随后在src/test/java/org/example目录下新增MyFirstVerticleTest.java文件:
package org.example;
import io.vertx.core.Vertx;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author dxm
* @description
* @ClassName
* @date 2023-03-14 3:56 PM
*/
@RunWith(VertxUnitRunner.class)
public class MyFirstVerticleTest {
private Vertx vertx;
@Before
public void setUp(TestContext context){
vertx = Vertx.vertx();
vertx.deployVerticle(MyFirstVerticle.class.getName(), context.asyncAssertSuccess());
}
@After
public void tearDown(TestContext context) {
vertx.close(context.asyncAssertSuccess());
}
@Test
public void testApplication(TestContext context) {
final Async async = context.async();
vertx.createHttpClient().getNow(8080, "localhost", "/", response -> {
response.handler(body -> {
context.assertTrue(body.toString().contains("Hello"));
async.complete();
});
});
}
}
执行该测试案例便可得到期望的结果,有兴趣的可以练习一下。
最后呢,我想说咱程序员不能只会一种语言,开发框架也不能只知道Spring,也得去了解学习接触一些其他的框架,拓展自己的知识面,在后面解决问题的时候,你就会发现,书到用时方恨少,所以还是要在日常进行不断的积累和总结,然后在适当的场景下选择合适的技术选型来解决问题,适合自己的才是最好的,最后给大家布置一个作业,就是结合自己现在的项目结构分层写一下增删改查的接口,感兴趣的你,赶紧去试试吧。