zoukankan      html  css  js  c++  java
  • spring-flux

    最近在整理springcloud,在整理springcloud-gateway的时候出了一点问题.spring-cloud-gateway是基于spring-flux实现,所以不会spring-flux的话会很闹心啊。

    一、为什么

    springmvc他不香了吗?那自然不能的。官方给出的原因是:

    1. 部分原因是需要使用非阻塞web堆栈来处理少量线程的并发性,并使用更少的硬件资源进行伸缩
    2. 另一个原因就是函数式编程

    那么Reactive是什么,英译,反应的,其实我更喜欢响应式的。

    spring-flux是非阻塞的,非阻塞是被动的,因为我们现在处于操作完成或数据可用时对通知做出反应的模式,而不是被阻塞的模式。谈到这个,那么应该联想到网络编程(socket),网络编程中服务端是需要监听(accept)端口,也就是只有在客户端发出请求的时候服务端才能做出响应,但是我们往往需要的不是这样的结果,所以会通过nio将其变成非阻塞的。他们都是类似的。

    二、什么时候用?

    首先看看他和springmvc有什么区别:

     从上图不能看出,他们各有功能并且还有通用的,那么什么时候用呢?

    1. 如果您有一个工作良好的Spring MVC应用程序,则无需更改。命令式编程是编写、理解和调试代码的最简单的方法。您可以最大限度地选择库,因为从历史上看,大多数库都是被阻塞的
    2. 如果你已经购买一个非阻塞的网络堆栈,Spring WebFlux提供了和其他人同样的执行模型的好处在这个空间,也提供了一个选择的服务器(Netty,Undertow,Tomcat、Jetty Servlet 3.1 +容器),选择编程模型(带注释的控制器和功能性web端点),和选择反应库(RxJava反应堆,或其他)。
    3. 如果您对与Java 8 lambdas或Kotlin一起使用的轻量级、功能性web框架感兴趣,您可以使用Spring WebFlux functional web endpoints。对于需求不那么复杂的小型应用程序或微服务,这也是一个不错的选择,因为它们可以从更大的透明性和控制中获益
    4. 在微服务体系结构中,您可以使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端点的混合应用程序。在这两种框架中都支持相同的基于注释的编程模型,这使得在为正确的工作选择正确的工具时更容易重用知识。
    5. 评估应用程序的一种简单方法是检查其依赖关系。如果您有阻塞持久化api (JPA、JDBC)或网络api要使用,Spring MVC至少是通用架构的最佳选择。在单独的线程上使用Reactor和RxJava执行阻塞调用在技术上是可行的,但是您不能充分利用非阻塞web堆栈。
    6. 如果您有一个调用远程服务的Spring MVC应用程序,请尝试响应式WebClient。您可以直接从Spring MVC控制器方法返回反应类型(Reactor、RxJava或其他)。每次调用的延迟或调用之间的相互依赖性越大,好处就越明显。Spring MVC控制器也可以调用其他响应组件。
    7. 如果您有一个大型团队,请记住,在向非阻塞式、函数式和声明式编程的转变过程中,需要陡峭的学习曲线。一种不需要完全切换就可以开始的实用方法是使用响应式web客户机。除此之外,从小事开始,衡量好处。我们认为,对于广泛的应用程序,这种转变是不必要的。如果您不确定寻找哪些好处,那么首先了解一下非阻塞I/O是如何工作的(例如,单线程Node.js上的并发性)及其影响。

    三、怎么用

    现在市场上流行的框架是springboot,而springboot原生支持spring-flux,所以这里就用springboot演示了

    使用方法有两种,一种是基于Annotated Controllers,另一种就是Functional Endpoints

    • Annotated Controllers 

        这一种是基于注解实现,他和springmvc的注解是一样的,所以会springmvc的话这种方式是很容易上手的。想@Controller,@RestController等注解都是通用的

     可以看到,实现方式和springmvc没什么两样。不同的是springmvc是基于Servlet 阻塞式实现,而spring-flux是非阻塞的,所以request、response等实现是不同的(就是说不能再用httpservletrequest了)

      @GetMapping("test/{id}")
        public String test(ServerWebExchange exchange, @PathVariable long id, @MatrixVariable int age) {
            Object name = exchange.getAttribute("name");
            System.out.println("name = " + name);
            System.out.println("id = " + id);
            System.out.println("age = " + age);
            return "请求成功";
        }

    以下是spring-flux控制层方法所有支持的入参:

    Controller method argumentDescription

    ServerWebExchange

    访问完整的ServerWebExchange——HTTP请求和响应、请求和会话属性、checkNotModified方法等容器。

    ServerHttpRequestServerHttpResponse

    访问HTTP请求或响应

    WebSession

    访问会话。除非添加了属性,否则这不会强制开始新会话。支持反应类型

    java.security.Principal

    当前经过身份验证的用户——如果已知,可能是特定的主体实现类。支持反应类型。

    org.springframework.http.HttpMethod

    请求的HTTP方法。

    java.util.Locale

    当前请求区域设置,由可用的最特定的LocaleResolver确定—实际上,即配置的LocaleResolver/LocaleContextResolver。

    java.util.TimeZone + java.time.ZoneId

    与当前请求关联的时区,由LocaleContextResolver确定。

    @PathVariable

    用于访问URI模板变量. See URI Patterns.

    @MatrixVariable

    用于访问URI路径段中的名称-值对. See Matrix Variables.

    @RequestParam

    参数值被转换为声明的方法参数类型. See @RequestParam.

    @RequestHeader

    头值被转换为声明的方法参数类型. See @RequestHeader.

    @CookieValue

    Cookie值被转换为声明的方法参数类型. See @CookieValue.

    @RequestBody

    请求体内容通过使用HttpMessageReader实例转换为声明的方法参数类型。支持反应类型。See @RequestBody.

    HttpEntity<B>

    主体使用HttpMessageReader实例进行转换。支持反应类型. See HttpEntity.

    @RequestPart

    用于访问多部件/表单数据请求中的部件。支持反应类型。See Multipart Content and Multipart Data.

    java.util.Maporg.springframework.ui.Model, and org.springframework.ui.ModelMap.

    用于访问在HTML控制器中使用并作为视图呈现的一部分公开给模板的模型。

    @ModelAttribute

     用于访问应用了数据绑定和验证的模型中已存在的属性(如果不存在则实例化)。See @ModelAttribute as well as Model and DataBinder.

    ErrorsBindingResult

    错误或BindingResult参数必须在验证方法参数之后立即声明。

    SessionStatus + class-level @SessionAttributes

    用于标记表单处理完成,这将触发清除通过类级别的@SessionAttributes注释声明的会话属性。更多细节请参见@SessionAttributes。

    UriComponentsBuilder

    用于准备相对于当前请求的主机、端口、方案和路径的URL. See URI Links.

    @SessionAttribute

    用于访问任何会话属性——与作为类级别@SessionAttributes声明的结果存储在会话中的模型属性相反。 See @SessionAttribute for more details.

    @RequestAttribute

    用于访问请求属性。 See @RequestAttribute for more details.

    Any other argument

    如果一个方法参数没有与上面的任何一个匹配,默认情况下,如果它是简单类型,就解析为@RequestParam,如BeanUtils#isSimpleProperty所确定的那样,否则解析为@ModelAttribute。

    下表为返回支持的对象:

    Controller method return valueDescription

    @ResponseBody

    返回值通过HttpMessageWriter实例进行编码并写入响应。 See @ResponseBody.

    HttpEntity<B>ResponseEntity<B>

    返回值指定完整的响应,包括HTTP头,并且主体通过HttpMessageWriter实例进行编码并写入响应。 See ResponseEntity.

    HttpHeaders

    用于返回带有标题而没有正文的响应.

    String

    用ViewResolver实例解析并与隐式模型一起使用的视图名称——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明模型参数(前面已经描述过)以编程方式丰富模型。

    View

    一个视图实例,用于与隐式模型一起呈现——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明模型参数(前面已经描述过)以编程方式丰富模型。

    java.util.Maporg.springframework.ui.Model

    属性要添加到隐式模型中,视图名称根据请求路径隐式确定。

    @ModelAttribute

    要添加到模型中的属性,视图名根据请求路径隐式确定。注意@ModelAttribute是可选的。参见该表后面的“任何其他返回值”。

    Rendering

    用于模型和视图呈现场景的API。

    void

    如果一个方法有一个void,可能是异步的(例如mono&void&gt),返回类型(或者一个null返回值),如果它也有一个ServerHttpResponse,一个ServerWebExchange参数,或者一个@ResponseStatus注释,那么它被认为已经完全处理了响应。如果控制器对ETag或lastModified时间戳进行了正确的检查,也同样如此。// TODO: See Controllers for details.

    如果以上都不为真,则void返回类型还可以指示REST控制器“无响应体”或HTML控制器的默认视图名称选择。

    Flux<ServerSentEvent>Observable<ServerSentEvent>, or other reactive type

    释放服务器发送的事件。当只需要写入数据时,可以省略ServerSentEvent包装器(但是,必须通过produces属性在映射中请求或声明文本/事件流)。

    Any other return value

    如果返回值不匹配任何上述情况,,默认情况下,视为一个视图名称,如果是字符串或无效(默认视图名称选择适用),或作为一个模型属性被添加到模型中,除非它是一个简单类型,由BeanUtils # isSimpleProperty,在这种情况下,它仍未得到解决。

    • Functional Endpoints

     Spring WebFlux包括WebFlux.fn,一种轻量级函数编程模型,其中函数用于路由和处理请求,契约被设计为不变性。它是基于注释的编程模型的替代方案,但在其他方面运行在相同的反应性核心基础上。

    那么到底怎么用呢?

    这里需要两个,一个路由(route),一个处理器(handler)

    处理器:

    port org.springframework.web.reactive.function.BodyInserters;
    import org.springframework.web.reactive.function.server.ServerRequest;

    import static org.springframework.web.reactive.function.server.ServerResponse.*;

    import org.springframework.web.reactive.function.server.ServerResponse;
    import reactor.core.publisher.Mono;

    import javax.annotation.Resource;

    /**
    * <p></p>
    *
    * @author lhf
    * @since 2020/10/13 9:39
    */
    @Component
    public class SysUserHandler {


    @Resource
    private SysUserService sysUserService;

    public Mono<ServerResponse> selectOne(ServerRequest request) {
    String id = request.queryParam("id").get().trim();

    return ok().body(BodyInserters.fromValue(this.sysUserService.queryById(Long.valueOf(id))));
    }


    }

    然后是路由:

    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.ServerResponse;

    import javax.annotation.Resource;

    import static org.springframework.web.reactive.function.server.RouterFunctions.route;
    import static org.springframework.web.reactive.function.server.RequestPredicates.*;
    import static org.springframework.web.reactive.function.server.ServerResponse.ok;
    import static org.springframework.web.reactive.function.BodyInserters.fromValue;

    /**
    * <p></p>
    *
    * @author lhf
    * @since 2020/10/13 8:44
    */
    @Configuration
    public class SysUserRouter {

    @Resource
    private SysUserHandler handler;

    @Bean
    public RouterFunction<ServerResponse> test() {
    return route(GET("/test"), response -> ok().body(fromValue("成功了瓜皮")));
    }


    @Bean
    public RouterFunction<ServerResponse> selectOne() {
    return route().GET("/sysUser/selectOne", contentType(MediaType.ALL), handler::selectOne).build();
    }
    }

    第一个路由是一个测试路由,直接返回,第二个将会调用处理器,去处理逻辑

    初步使用就是这样了!!!!

    菜鸟一个,欢迎大佬吐槽

         

    /**
    * <p></p>
    *
    * @author lhf
    * @since 2020/10/13 9:39
    */
    public class SysUserHandler {
    private SysUserRouter router;

    public SysUserHandler(SysUserRouter router) {
    this.router = router;
    }

    @Resource
    private SysUserService sysUserService;

    public Mono<ServerResponse> selectOne(ServerRequest request) {
    String id = request.queryParam("id").get().trim();
    return ok().body(BodyInserters.fromValue(this.sysUserService.queryById(Long.valueOf(id))));
    }



    }
  • 相关阅读:
    用户与组
    初识linux
    权限管理
    认识vim 编辑器
    文件归档
    路由相关术语
    Access、Hybrid和Trunk
    #error作用
    交换芯片收发包的 DMA 实现原理
    linux网络学习
  • 原文地址:https://www.cnblogs.com/Tiandaochouqin1/p/13807819.html
Copyright © 2011-2022 走看看