一、Vert.X是什么
Vert.x框架基于事件和异步,依托于全异步Java服务器Netty,并扩展了很多其他特性,以其轻量、高性能、支持多语言开发而备受开发者青睐。Vert.x是事件驱动的,其处理请求的高性能也是基于其事件机制。
- Event Loop:即事件循环,是由Vert.x启动的事件处理线程,也是Vert.x项目对外开放的入口,Vert.x由此接收请求事件。一个Vert.x有一个或多个事件循环线程组成,线程最大数量为主机有效的CPU核数。
- Event Loop Vertical:事件的业务处理线程,存在于Event Loop中,用于处理非阻塞短任务。
- Worker Vertical : 事件的业务处理线程,用于处理长任务阻塞任务。
- Event Bus:即事件总线,是Vert.x事件模型中最核心的部分,所有的事件都经由事件总线进行分发,包括Vertical之间的通信事件。
- Vert.x Module : Vert.x项目模块,一个应用通常由多个模块组成,每个模块一般包含多个Vertical。
二、依赖引入
<!-- vertx -->
<vertx.version>4.5.7</vertx.version>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-codegen</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-common</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-rx-java</artifactId>
<version>${vertx.version}</version>
</dependency>
三、使用示例
3.1 创建HttpServer及路由Router
@Slf4j
public class SimpleRouter extends AbstractVerticle {
@Override
public void start() {
// 创建HttpServer
HttpServer server = vertx.createHttpServer();
// 创建路由对象
Router router = Router.router(vertx);
// 监听/index地址,不限制HTTP请求方式
router.route("/index").handler(request -> request.response().end("INDEX SUCCESS"));
// route方式,限制HTTP请求方式为GET
router.route(HttpMethod.GET, "/get-route-index").handler(request -> request.response().end("get-route-index SUCCESS"));
// 限制HTTP请求方法为POST
router.post("/post-demo").handler(request -> request.response().end("POST SUCCESS"));
// 限制HTTP请求方法为GET
router.get("/get-demo").handler(request -> request.response().end("GET SUCCESS"));
// Mount the handler for all incoming requests at every path and HTTP method
router.route("/json-demo").handler(requestHandler -> {
// Get the address of the request
String address = requestHandler.request().connection().remoteAddress().toString();
// Get the query parameter "name"
MultiMap queryParams = requestHandler.queryParams();
String name = queryParams.contains("name") ? queryParams.get("name") : "unknown";
// Write a json response
requestHandler.json(
new JsonObject()
.put("name", name)
.put("address", address)
.put("message", "Hello " + name + " connected from " + address)
);
});
// Capturing path parameters, more than HTTP method
router.route("/catalogue/products/:productType/:productId").method(HttpMethod.POST).method(HttpMethod.GET)
.handler(requestHandler -> {
String productType = requestHandler.pathParam("productType");
String productId = requestHandler.pathParam("productId");
requestHandler.json(
new JsonObject()
.put("productType", productType)
.put("productId", productId)
);
});
// 把请求交给路由处理
server.requestHandler(router);
server.listen(8080).onSuccess(handle ->
log.info("HTTP server started on port {}", handle.actualPort())
);
}
}
然后通过HTTP请求调用即可,例如:localhost:8080/catalogue/products/123/abc
3.2 响应结果设置格式
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
router.route().handler(requestHandler -> {
HttpServerResponse response = requestHandler.response();
response.putHeader("content-type", "text/plain");
response.end("Hello World from Vert.x-Web!");
});
server.requestHandler(router).listen(8080);
3.3 使用阻塞处理程序
router.route().blockingHandler(ctx -> {
service.doSomethingThatBlocks();
ctx.next();
});
如果你需要从阻塞处理程序中处理多部分表单数据,你必须首先使用非阻塞处理程序来调用setExpectMultipart(true)。
router.post("/some/endpoint").handler(ctx -> {
ctx.request().setExpectMultipart(true);
ctx.next();
}).blockingHandler(ctx -> {
// ... 处理你的业务
});
3.4 通过以某物开头的路径进行路由
Route route = router.route().path("/some/path/*");
route.handler(ctx -> {
// `/some/path/`,
// `/some/path/`
// `/some/path/a`
// `/some/path/a/b.html`
});
3.5 路径参数
3.5.1 占位符方式
// Capturing path parameters, more than HTTP method
router.route("/catalogue/products/:productType/:productId").method(HttpMethod.POST).method(HttpMethod.GET)
.handler(requestHandler -> {
String productType = requestHandler.pathParam("productType");
String productId = requestHandler.pathParam("productId");
requestHandler.json(
new JsonObject()
.put("productType", productType)
.put("productId", productId)
);
});
3.5.2 使用正则表达式捕获路径参数
Route route = router.routeWithRegex(".*foo");
// This regular expression matches paths that start with something like:
// "/foo/bar" - where the "foo" is captured into param0 and the "bar" is
// captured into param1
route.pathRegex("\\/([^\\/]+)\\/([^\\/]+)").handler(ctx -> {
String productType = ctx.pathParam("param0");
String productID = ctx.pathParam("param1");
// Do something with them...
});
3.6 正则表达式路由
3.6.1 路径或路由指定
Route route = router.route().pathRegex(".*foo");
route.handler(ctx -> {
// This handler will be called for:
// /some/path/foo
// /foo
// /foo/bar/wibble/foo
// /bar/foo
// But not:
// /bar/wibble
});
也可以在创建路由时指定正则表达式:
Route route = router.routeWithRegex(".*foo");
route.handler(ctx -> {
// This handler will be called same as previous example
});
3.6.2 使用命名捕获组
命名捕获组被映射到与组同名的路径参数
router
.routeWithRegex("\\/(?<productType>[^\\/]+)\\/(?<productID>[^\\/]+)")
.handler(ctx -> {
String productType = ctx.pathParam("productType");
String productID = ctx.pathParam("productID");
// Do something with them...
});
3.7 HTTP方法路由
3.7.1 匹配特定方式路由
Route route = router.route().method(HttpMethod.POST);
route.handler(ctx -> {
// This handler will be called for any POST request
});
或者
Route route = router.route(HttpMethod.POST, "/some/path/");
route.handler(ctx -> {
// This handler will be called for any POST request
// to a URI path starting with /some/path/
});
或者
router.get().handler(ctx -> {
// Will be called for any GET request
});
router.get("/some/path/").handler(ctx -> {
// Will be called for any GET request to a path
// starting with /some/path
});
router.getWithRegex(".*foo").handler(ctx -> {
// Will be called for any GET request to a path
// ending with `foo`
});
3.7.2 多个HTTP方式
// Capturing path parameters, more than HTTP method
router.route("/catalogue/products/:productType/:productId").method(HttpMethod.POST).method(HttpMethod.GET)
.handler(requestHandler -> {
String productType = requestHandler.pathParam("productType");
String productId = requestHandler.pathParam("productId");
requestHandler.json(
new JsonObject()
.put("productType", productType)
.put("productId", productId)
);
});
3.8 路由排序
3.8.1 默认排序
默认情况下,按照添加顺序排序
router
.route("/some/path/")
.handler(ctx -> {
HttpServerResponse response = ctx.response();
// enable chunked responses because we will be adding data as
// we execute over other handlers. This is only required once and
// only if several handlers do output.
response.setChunked(true);
response.write("route1\n");
// Now call the next matching route
ctx.next();
});
router
.route("/some/path/")
.handler(ctx -> {
HttpServerResponse response = ctx.response();
response.write("route2\n");
// Now call the next matching route
ctx.next();
});
router
.route("/some/path/")
.handler(ctx -> {
HttpServerResponse response = ctx.response();
response.write("route3");
// Now end the response
ctx.response().end();
});
输出
route1 route2 route3
3.8.2 指定排序
如果两个匹配的路由具有相同的order值,那么它们将按照添加的顺序被调用。
router
.route("/some/path/")
.order(1)
.handler(ctx -> {
HttpServerResponse response = ctx.response();
response.write("route1\n");
// Now call the next matching route
ctx.next();
});
router
.route("/some/path/")
.order(0)
.handler(ctx -> {
HttpServerResponse response = ctx.response();
// enable chunked responses because we will be adding data as
// we execute over other handlers. This is only required once and
// only if several handlers do output.
response.setChunked(true);
response.write("route2\n");
// Now call the next matching route
ctx.next();
});
router
.route("/some/path/")
.order(2)
.handler(ctx -> {
HttpServerResponse response = ctx.response();
response.write("route3");
// Now end the response
ctx.response().end();
});
输出
route2 route1 route3
3.8.3 指定顺序为last
可以直接指定最后 last
router
.route("/some/path/")
.last()
.handler(ctx -> {
HttpServerResponse response = ctx.response();
response.write("route last\n");
// Now call the next matching route
ctx.next();
});