Quarkus Reactive API
我们上一章创建了一个简单的HelloWorld项目,非常的简单并没有什么响应式编程的内容,既然Quarkus是支持响应式编程的,那我们今天先看一下Quaruks提供的响应式编程API。
2021-01-12 补充
最近一直忙于Coding,没有时间按计划写下面的文章。但是通过最近工作发现,除了熟悉Quarkus各种组件的工作方式外,比较重要的还是这里的Multi和Uni原理和API的使用,原理这个东西不太好说,但是使用经验可以在后期有时间的时候将最近一段时间API 的使用心得单独整理一遍文章吧。
本章目标:
- 学习使用Quarkus实现的响应式编程库
- io.smallrye.mutiny.Uni
- io.smallrye.mutiny.Multi
一 Quarkus 框架响应式处理原理
由上图可以看到Quarkus底层是由Vertx来调度应用的请求处理和响应,无论我们的应用是响应式的还是阻塞式的。当然为了更高的性能,Quarkus当然建议我们使用响应式的API来实现我们的应用。下面看一下响应式应用和同步应用的调度区别。
同步应用的路由调用是由应用工作线程来执行的。
可以看到响应式的应用是路由层直接调用IO线程来执行,这样会有更高的并发。
二 Multi和Uni
1 引入依赖
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-mutiny</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
因为我们基于一个Rest服务来学习,所以加入了一些其他依赖。其实今天的主角是quarkus-resteasy-mutiny
2 Quakus 提供的两个核心异步编程库
*io.smallrye.mutiny.Uni - 提供0或者一个结果的异步操作
*io.smallrye.mutiny.Multi -提供多个结果的异步操作
我们通过几个接口来尝试一下异步操作。
2.1 Uni<?>
我们写一个简单的返回字符串的例子:
@GET
@Path("uni_test1")
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> uni_test(){
return Uni.createFrom().item("uni");
}
访问接口: curl http://127.0.0.1:8080/rx/uni_test1
通过上面的简单例子可以看出构建一个Uni结果还是比较简单的, 同时Uni还提供了非常丰富的操作来构建Uni,接下来我们详细看一下Uni的常用API。
Uni 是一个接口,但是提供了两个静态的方法来快速创建一个Uni。
- combine() 是联合其他的Uni返回一个组合的Uni
- createFrom() 见名知意从from中来创建Uni 这里的From是什么呢? 我们来看UniCreate这个返回值。
- 从UniCreate类中看到,这个类是整合了所有Uni支持的创建方式
我们就几个比较常用的创建方式测试一下:
- item(Supplier) 如果大家对函数式编程不是很理解,可以着重去看下函数式编程。
@GET
@Path("uni_test2")
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> uni_test2(){
return Uni.createFrom().item(()->{return "hello uni";});
}
结果:
- completionStage
@GET
@Path("uni_test3")
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> uni_test3(){
return Uni.createFrom().completionStage(CompletableFuture
.supplyAsync(()->"hello ")
.thenApply(str->{return str + "completeStage";}));
}
结果:
- failure
@GET
@Path("uni_test4")
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> uni_test4(){
return Uni.createFrom().failure(RuntimeException::new);
}
结果:
当然我们开发中肯定不能这么干,我们需要基于事件做异常处理。
- multi multi后面会讲,这里说的是从Multi中创建Uni对象但只是从Multi中取第一个item
@GET
@Path("uni_test5")
@Produces(MediaType.TEXT_PLAIN)
public Uni<String> uni_test5(){
return Uni.createFrom().multi(Multi
.createFrom()
.items(Stream.of("1", "2")));
}
结果:
这里先讲这几个常用API,其实Api的使用方式非常简单,关键还是要熟悉Java中函数式和响应式编程的方式。
2.2 Multi
跟Uni一致,Multi也提供了两个快速创建Multi的静态方法。
- createFrom 类似UniCreate,封装了创建Multi的方式
- createBy 类似combine 是聚合或者连接两个Multi的
我们还是只看createFrom
- Item
@GET
@Path("multi_test1")
@Produces(MediaType.APPLICATION_JSON)
public Multi<String> multi_test1(){
return Multi.createFrom().items(Stream.of("1", "2", "3"));
}
结果:
这里需要注意的是Multi本身就代表了一个列表。如果我们item中放入的是一个list,那么返回值类型就是Multi<List> 结果就是 [[1,2,3,]]这种形式。
其他的API 跟 Uni类似,后面就不再一一测试了。
以上仅仅是介绍了创建Uni和Multi的方式,下面还有一些API 如 转换、事件处理等。
2.3 事件处理
响应式编程的实现中一般都是基于订阅模式,提供基于事件驱动的API,Quarkus同样也是基于此实现。那何为事件驱动,就是在流程中出现一些事件节点如数据准备好了、报错了或者超时了等等事件,在事件后面加上事件的处理逻辑。
这些方法都可以认为是事件方法,在一定条件下会触发,然后执行后面的逻辑。
2.4 转换
Uni和Multi或者其他类型的转换。
- Uni和Multi之间或自己转换 transformToXXX 类似于Java中的flatMap
- 转换成其他类型
Maybe<String> maybe = uni.convert().with(UniRxConverters.toMaybe());
Flux<String> flux = multi.convert().with(MultiReactorConverters.toFlux());
三 总结
- Quarkus提供的响应式开发库主要分为两类: Uni 和 Mult
- Api类型大致分为三类 创建数据、事件方法、转换方法
- 核心还是Java的函数式编程和异步编程