OK,还记得我开博客的第一篇文章就是想好好学习一下WebFlux,前段时间学习SpringBoot也就是想慢慢的学到这里来,然后和大家分享一下,在讲之前大家可以看看函数式编程基础了解一下。
在讲之前我想提一些问题,我们是否经常在网上看到这样一些关于reactive的讲法:
1.Reactive 是异步非阻塞编程
2.Reactive 能够提升程序性能
3.Reactive 解决传统编程模型遇到的困境
在讲解reactive之前,我们还是从传统的编程模型说起:
传统的编程模型往往是阻塞式的,那么有办法对传统编程模型进行改进吗?一般来说有两种方法
1. parallelize: use more threads and more hardware resources.
2. seek more efficiency in how current resources are used
一种是多线程,另一种就是资源有限(例如CPU)情况下,尽可能最大化CPU。
但是Reactor 认为阻塞可能是浪费的:
1.阻塞导致性能瓶颈和浪费资源
2.增加线程可能会引起资源竞争和并发问题
3.并行的方式不是银弹(不能解决所有问题)
我们看一个阻塞式的编程:
public class DataLoader {
public final void load() {
long startTime = System.currentTimeMillis(); // 开始时间
doLoad(); // 具体执行
long costTime = System.currentTimeMillis() - startTime; // 消耗时间
System.out.println("load() 总耗时:" + costTime + " 毫秒");
}
protected void doLoad() { // 串行计算
loadConfigurations(); // 耗时 1s
loadUsers(); // 耗时 2s
loadOrders(); // 耗时 3s
} // 总耗时 1s + 2s + 3s = 6s
protected final void loadConfigurations() {
loadMock("loadConfigurations()", 1);
}
protected final void loadUsers() {
loadMock("loadUsers()", 2);
}
protected final void loadOrders() {
loadMock("loadOrders()", 3);
}
private void loadMock(String source, int seconds) {
try {
long startTime = System.currentTimeMillis();
long milliseconds = TimeUnit.SECONDS.toMillis(seconds);
Thread.sleep(milliseconds);
long costTime = System.currentTimeMillis() - startTime;
System.out.printf("[线程 : %s] %s 耗时 : %d 毫秒\n",
Thread.currentThread().getName(), source, costTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new DataLoader().load();
}
}
由于串行执行的关系,导致消耗实现线性累加。Blocking 模式即串行执行 。
那我们变成并行的方式又会怎样呢?
public class ParallelDataLoader extends DataLoader {
protected void doLoad() { // 并行计算
ExecutorService executorService = Executors.newFixedThreadPool(3); // 创建线程池
CompletionService completionService = new ExecutorCompletionService(executorService);
completionService.submit(super::loadConfigurations, null); // 耗时 >= 1s
completionService.submit(super::loadUsers, null); // 耗时 >= 2s
completionService.submit(super::loadOrders, null); // 耗时 >= 3s
int count = 0;
while (count < 3) { // 等待三个任务完成
if (completionService.poll() != null) {
count++;
}
}
executorService.shutdown();
} // 总耗时 max(1s, 2s, 3s) >= 3s
public static void main(String[] args) {
new ParallelDataLoader().load();
}
}
明显地,程序改造为并行加载后,性能和资源利用率得到提升,消耗时间取最大者
延伸思考
1. 如果阻塞导致性能瓶颈和资源浪费的话,Reactive 也能解决这个问题?
2. 为什么不直接使用 Future#get() 方法强制所有任务执行完毕,然后再统计总耗时?
3. 由于以上三个方法之间没有数据依赖关系,所以执行