Vert.x 初体验
前言
Vert.x 核心思想:异步处理一、Vert.x 是什么?
1.Vert.x 简介
Vert.x 采用类似 Node.js 的 eventloop callback 机制,优势是 Eventloop 是单线程场景下几乎是最快的并发解决方案,但也需要周边生态的支持,比如 DbClient/HttpClient 这些跟 IO 打交道的 API 需要支持异步回调的风格,社区干脆就整合或者自己实现了。整体启动时间大概是同规模 spring boot 工程的四分之一左右,基准内存占用少一半。
Vert.x 底层通信框架依赖于Netty,并封装了对Http协议的支持,因此可以非常方便的进行Web开发,且不依赖于任何中间件,不依赖中间件,编程会变得非常灵活,定制性非常强,安全性也会得到一定层度的提高。
2.Vert.x 优势
- Vert.x 支持多种编程语言。
- Vert.x 是一个异步非阻塞框架。
- Vert.x 不依赖中间件,底层依赖Netty,因此在使用 Vert.x 构建 Web 项目时,不依赖中间件。
- Vert.x 具有完善的生态,Vert.x 提供Web客户端操作、数据库操作、NoSQL数据库等操作,清新简洁,但足够使用。
- Vert.x 为微服务而生,提供了各种组件来构建基于微服务的应用程序,通过EventBus可以非常容易的进行服务之间的交互,并且提供了HAZELCAST来实现分布式。
3.Web 服务选型
对于实现一个简单的web服务,有很多种选择,简单划为三种:
- 使用 Java 中间件来实现,只要下载一个 Tomcat,再编写个 web 项目就可以对外提供服务。这种方式我们完全不需要考虑线程的交互,不需要考虑网络协议,只需要关注业务逻辑,可以说是一种全包的形式。
- 使用 Java 原生的 Socket 或者 NIO 来实现,但这种方式比较复杂,对编程功底要求最高,而且很难保证性能和稳定性。这种方式完全需要手动处理,很少会使用这种方式来实现HTTPServer,可以说这是最为原始形式。
- 介于全包和原始形式之间,就是用第三方的框架来实现,比如 Vert.x 或者偏底层的 Netty,可以利用框架封装的 API 简单的创建一个 HttpServer,并不需要关注底层的通信协议。这种方式更为灵活,一般来讲性能也更高,并且不依赖中间件。
二、Vert.X Hello World
使用 IDEA 创建普通的 spring boot 项目,然后引入 vertx 依赖,可以选择 maven 或 gradle。
1.引入依赖
依赖如下:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<vertx.version>3.9.4</vertx.version>
</dependency>
2.代码实现
- 创建 MainVerticle 类继承 AbstractVerticle 类,重写 start() 方法。
- 事件监听:对于 Spring 容器的一些事件,可以监听并且触发相应的方法。通常的方法有两种:ApplicationListener 接口和 @EventListener 注解。
代码如下:
public class MainVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
vertx.createHttpServer().requestHandler(req -> {
req.response().putHeader("content-type", "text/plain")
.end("Hello from Vert.x!");
}).listen(8888, http -> {
if (http.succeeded()) {
System.out.println("HTTP server started on port 8888");
} else {
}
});
}
}
@Slf4j
@SpringBootApplication
public class VertxApplication {
public static void main(String[] args) {
SpringApplication.run(VertxApplication.class, args);
}
@EventListener
public void deployVerticles(ApplicationReadyEvent event) {
VertxOptions options = new VertxOptions();
options.setWorkerPoolSize(200);
Vertx vertx = Vertx.vertx(options);
// 部署服务
vertx.deployVerticle(MainVerticle.class.getName(), new DeploymentOptions(),
result -> {
if (result.succeeded()) {
log.info("HTTP服务器部署成功");
} else {
log.error("HTTP服务器部署失败", result.cause());
}
});
}
}
三、Vert.X Web
Vert.x 提供了 Web 开发组件 vertx-web,提供了许多 Web 开发中常用的功能,比如参数封装、路由、国际化、认证和授权、session 和 cookie 以及模板等,可以非常方便的进行 Web 开发。
1.引入依赖
依赖如下:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<vertx.version>3.9.4</vertx.version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
2.代码实现
- 服务部署代码同 Hello World
代码如下:
public class RouterVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
router.route(HttpMethod.GET, "/hello").handler(req -> {
req.response().putHeader("content-type", "text/plain")
.end("Hello from Vert.x!");
});
vertx.createHttpServer().requestHandler(router).listen(8888, http -> {
if (http.succeeded()) {
System.out.println("HTTP server started on port 8888");
} else {
System.err.println("HTTP server started error");
}
});
}
}
四、Vert.x TCP
想要实现一个 http 服务非常简单, 使用 tomcat 或其他中间件就能实现。但是如果要实现 tcp 服务就没那么简单了,因为 tcp 是传输层协议,并不像 http 那样,有服务中间件封装底层的网络协议和线程的交互。要实现一个 tcp 服务,只能自己处理网络和线程问题。
1.引入依赖
依赖如下:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<vertx.version>3.9.4</vertx.version>
</dependency>
2.代码实现
- 创建tcp服务:NetServer
- 监听指定端口的请求
- 处理请求并响应
代码如下:
server 端
@Slf4j
public class TcpServerVerticle extends AbstractVerticle {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(TcpServerVerticle.class.getName(), new DeploymentOptions(),
result -> {
if (result.succeeded()) {
log.info("TCP服务器部署成功");
} else {
log.error("TCP服务器部署失败", result.cause());
}
});
}
@Override
public void start() {
vertx.createNetServer().connectHandler(socket -> {
socket.handler(buffer -> {
// 在这里应该解析报文,封装为协议对象,并找到响应的处理类,得到处理结果,并响应
System.out.println("接收到的数据为:" + buffer.toString());
// 按照协议响应给客户端
socket.write(Buffer.buffer("Server Received"));
}).closeHandler(close -> {
System.out.println("客户端退出连接");
});
}).listen(9999, res -> {
if (res.succeeded()) {
System.out.println("TCP server started on port 9999");
} else {
System.err.println("TCP server started error");
}
});
}
}
client 端
public class TcpClientVerticle extends AbstractVerticle {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(TcpClientVerticle.class.getName());
}
@Override
public void start() throws Exception {
// 创建一个TCP客户端
vertx.createNetClient().connect(9999, "localhost", conn -> {
if (conn.succeeded()) {
NetSocket socket = conn.result();
// 向服务器写数据
socket.write(Buffer.buffer("hello"));
// 读取服务器的响应数据
socket.handler(buffer -> System.out.println(buffer.toString()));
} else {
System.out.println("连接服务器异常");
}
});
}
}