关于springboot:Spring-Boot系列之Async异步调用

63次阅读

共计 4418 个字符,预计需要花费 12 分钟才能阅读完成。

写在后面的话

哈喽,好久不见,你们还好吗?

明天给大家带来的是我在理论我的项目上遇到的一个问题。

流程大抵是,调用接口,而后将接口返回的数据更新一份到本地数据库,而后返回给前端。更新到本地数据库这个操作本来是用的异步。

国庆回老家,公司打电话来,前端转几秒的圈圈,而后无数据。经查,是 Redis 出了问题,用不了。

什么意思?

从接口申请到的数据,更新到本地数据库,这里有一个策略,先将数据放到 Redis 中,而后进行比照,如果不统一,再更新。Redis 不可用,那么都查询数据库,就会很慢,前端申请接口个别是 5s 超时。

如果是异步,也就不会呈现这个问题了。

所以,咱们就先看看过后,我的代码明明是异步的,为什么没有失效呢?

@Async 有效

先看一个例子。

Controller 代码如下:

@GetMapping(“/invalid”)
public String invalidAsyncExample() {
iTestAsyncService.invalidAsyncExample();
return “ 测试实现 ” + LocalDateTime.now().toString();
}

Service 代码如下:

@Override
public void invalidAsyncExample() {
log.info(“ 流程 -1-{}”, Thread.currentThread().getId());
invalidAsyncTask();
log.info(“ 流程 -3-{}”, Thread.currentThread().getId());
}

Async 代码如下:

@Async
public void invalidAsyncTask() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(“ 流程 -2-{}”, Thread.currentThread().getId());
}

执行后果:

2020-11-11 21:14:06.784 INFO 13592 — [nio-8080-exec-1] c.f.s.a.s.impl.TestAsyncServiceImpl     : 流程 -1-125
2020-11-11 21:14:08.785 INFO 13592 — [nio-8080-exec-1] c.f.s.a.s.impl.TestAsyncServiceImpl     : 流程 -2-125
2020-11-11 21:14:08.785 INFO 13592 — [nio-8080-exec-1] c.f.s.a.s.impl.TestAsyncServiceImpl     : 流程 -3-125

后果剖析:的确是同步执行,没什么明明加了 @Async 的,异步没失效呢?

带这个这样一个疑难,在百度上寻找答案。同时,也决定浏览这一块的源码。想看一下,这个异步到底是怎么实现的。

通过浏览源码,会发现,Spring 默认是用代理实现异步的。

什么意思?

你能够这样了解,你调用的类须要 Spring 帮你代理,而后能力异步去执行。

下面的示例代码,invalidAsyncTask(); 调用的办法很明确,不须要代理,这时候 Spring 也就不能帮你异步去执行了。

对于源码剖析,前面在写源码博文的时候,再来。

无返回值的异步工作

首先呢,须要 @EnableAsync

Controller:

@GetMapping(“/no-value”)
public String noValueAsyncExample() {
iTestAsyncService.noValueAsyncExample();
return “ 测试实现 ” + LocalDateTime.now().toString();
}

Service:

@Override
public void noValueAsyncExample() {
log.info(“ 流程 -1-{}”, Thread.currentThread().getId());
iAsyncService.exampleTask();
log.info(“ 流程 -3-{}”, Thread.currentThread().getId());
}

耗时工作:

@Override
public void exampleTask() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(“ 耗时工作 -2-{}”, Thread.currentThread().getId());
}

Async:

@Override
@Async
public void exampleTask() {
iTestAsyncService.exampleTask();
}

这里要留神,因为咱们把耗时的工作放在同一个 service 外面,所以就会产生循环依赖的问题,须要用到 @Lazy

测试后果:

2020-11-11 22:32:50.019 INFO 18888 — [nio-8080-exec-7] c.f.s.a.s.impl.TestAsyncServiceImpl     : 流程 -1-131
2020-11-11 22:32:50.020 INFO 18888 — [nio-8080-exec-7] c.f.s.a.s.impl.TestAsyncServiceImpl     : 流程 -3-131
2020-11-11 22:32:52.021 INFO 18888 — [task-9] c.f.s.a.s.impl.TestAsyncServiceImpl     : 耗时工作 -2-152

有返回值的异步工作

也是须要 @EnableAsync

Controller:

@GetMapping(“/value”)
public int valueAsyncExample() {
return iTestAsyncService.valueAsyncExample();
}

Service:

@Override
public int valueAsyncExample() {
int result = 0;

long startTime = System.currentTimeMillis();

List<Future<Integer>> futureList = new ArrayList<>();

for (int i = 0; i < 10; i++) {
Future<Integer> future = iAsyncService.addTask(i);
futureList.add(future);
}

for (Future<Integer> f : futureList) {
Integer value = null;
try {
value = f.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (value != null)
result += value;
}

long endTime = System.currentTimeMillis();

log.info(“ 耗时 {} s”, (endTime – startTime) / 1000D);

return result;
}

工作:

@Override
public Future<Integer> addTask(int n) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(“ 计算工作 -{}”, Thread.currentThread().getId());
return AsyncResult.forValue(n + 2);
}

Async:

@Override
@Async
public Future<Integer> addTask(int i) {
return iTestAsyncService.addTask(i);
}

同样,这里要留神,因为咱们把耗时的工作放在同一个 service 外面,所以就会产生循环依赖的问题,须要用到 @Lazy

测试后果:

2020-11-11 22:27:05.152 INFO 18888 — [task-3] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -146
2020-11-11 22:27:05.152 INFO 18888 — [task-5] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -148
2020-11-11 22:27:05.152 INFO 18888 — [task-4] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -147
2020-11-11 22:27:05.152 INFO 18888 — [task-6] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -149
2020-11-11 22:27:05.153 INFO 18888 — [task-7] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -150
2020-11-11 22:27:05.152 INFO 18888 — [task-2] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -145
2020-11-11 22:27:05.153 INFO 18888 — [task-8] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -151
2020-11-11 22:27:05.152 INFO 18888 — [task-1] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -144
2020-11-11 22:27:07.154 INFO 18888 — [task-6] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -149
2020-11-11 22:27:07.154 INFO 18888 — [task-3] c.f.s.a.s.impl.TestAsyncServiceImpl     : 计算工作 -146
2020-11-11 22:27:07.154 INFO 18888 — [nio-8080-exec-1] c.f.s.a.s.impl.TestAsyncServiceImpl     : 耗时 4.006 s

页面后果

65

测试代码

https://github.com/fengwenyi/study-spring-boot/tree/master/spring-boot-async

正文完
 0