1-为什么选择响应式Spring

1-为什么选择响应式Spring

为什么Spring框架的开发团队决定将响应式方法作为Spirng5框架的核心部分?

可以设想一下,当用户访问系统, 系统中存在的服务 A 需要访问服务 B 时,在服务 A 发出请求之后,执行线程会等待服务 B 的返回,这段时间该线程就是阻塞的,整个过程的 CPU 利用效率低下,很多时间线程被浪费在了 I/O 阻塞上,见下图:

TcGgKS.png

我们应该为了针对I/O实现更高的资源利用率, 应该使用异步非阻塞式交互模式。

现实生活中, 这种通信就是(短信或者邮件)消息传递, 我们收到消息, 就会把所有的时间用于阅读与回复, 此外我们通常不会等待对方继续回复, 而会去处理其他事情, 这种工作方式其余时间可以被有效利用。 如下图:

TRWjlq.png

通常在分布式系统中, 为了服务之间的通信实现有效的资源利用, 我们必须采用消息驱动的通信原则。

实现消息驱动通信的方法之一是使用消息代理服务器。(消息中间件)

大型系统由多个小系统组成, 因此也依赖于这些组成部分的响应式特性, 也就是说响应式系统的设计原则适用于各个级别、规模的系统, 有助于他们很好的组合在一起。

在目前传递中我们最常使用也是最流行的传统技术是命令式编程 (如下图: )

TRvqqe.jpg

阻塞式通信直接违背了消息驱动原则, 后者明确的为我们提供了非阻塞式通信。

另一种非阻塞式的选择是使用JDK8提供的CompletionStage 以及他的直接实现类CompletableFuture

public interface ShoppingCardService {
    CompletionStage<Output> calculate(Input value);
}
// -------------------------------------------------
public class CompletionStageShoppingCardService implements ShoppingCardService {

    @Override
    public CompletionStage<Output> calculate(Input value) {
		
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return new Output();
        });
    }
}
public class OrdersService {
    private final ShoppingCardService shoppingCardService;

    public OrdersService(ShoppingCardService shoppingCardService) {
        this.shoppingCardService = shoppingCardService;
    }

    void process() {
        Input input = new Input();

        shoppingCardService.calculate(input) // 非阻塞式调用方法, 并在calculate里面中CompletableFuture.supplyAsync执行完毕消费thenAccept
                           .thenAccept(v -> System.out.println(shoppingCardService.getClass().getSimpleName() + " execution completed"));

        System.out.println(shoppingCardService.getClass().getSimpleName() + " calculate called");
    }

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        OrdersService ordersService1 = new OrdersService(new CompletionStageShoppingCardService());

        ordersService1.process();
        ordersService1.process();

        System.out.println("Total elapsed time in millis is : " + (System.currentTimeMillis() - start));

        Thread.sleep(1000);
    }
}

CompletionStage支持下, 我们可以编写函数式和声明式的代码, 并且能够异步的处理结果, 此外我们可以省略阻塞等待结果的过程, 实现执行完毕将结果自动回调处理。

这种模式多线程的成本会很高, 多线程是一门负责的技术, 当使用到多线程的时候, 需要考虑到线程安全, 线程变量共享、线程错误处理等。

并且在Spring 4 MVC 在很长时间内不支持 CompletionStage, 为了补上这一点, 它提供了自己的ListenanblFutre, 这么的做法是为了与旧版本的Java兼容

在Spring5框架与新的响应式WebClient的发布, 事情发生了改变, 在WebClient的支持下, 所有的跨服务通信都不在是阻塞式的,

此外Servlet3.0也引入了异步客户端-服务器通信, 并且很好的集成到SpringMVC中, 但是SpringMVC唯独没有提供一个开箱即用的异步非阻塞客户端。

但是我们可以返回CompletableFuture以构建异步服务。

下面是大概原理图:

T4PMHx.png

  • Spring MVC开始进行异步处理,并把该CompletableFuture对象提交给另一个独立线程的执行器默认的ForkJoinPool (并不推荐使用默认的)处理
  • DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回
  • Callable对象最终产生一个返回结果,此时Spring MVC会重新把请求分派回Servlet容器,恢复处理
  • DispatcherServlet再次被调用,恢复对Callable异步处理所返回结果的处理 上面就是Callable的一个执行流程

返回CompletableFuture的好处在于, 可以极大的提供系统的吞吐量, 但是并不能提升响应速度。

当然异步处理不只只能返回CompletableFuture 其他类型在下面官网查看

以tomcat(支持Servlet 3.0的)为例,你controller方法指定返回DeferredResult或Callable,spring会自动进行异步处理,将方法以及响应的任务交给spring默认或者你定义的taskExecutor线程池bean执行,原本tomcat负责处理请求的线程返回tomcat线程池,不影响servlet接受请求的能力。要阻塞也是taskExecutor线程池里的线程,而不是tomcat线程池的线程。

但是相比使用CompletableFuture更推荐使用webFlux, 因为支持更多功能, 包含背压功能。

与webFlux相比

扩展:

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async-deferredresult

https://kevinten10.github.io/2019/09/03/Async/Future/Async-Future-%E5%BC%82%E6%AD%A5%E8%BF%94%E5%9B%9E%E5%80%BC/

https://jishuin.proginn.com/p/763bfbd352e8

1

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵懵懂懂程序员

如果节省了你的时间, 请鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值