4_webflux服务端开发

一 初识Web Flux

  1. 概念
    Web Flux是Spring5.0提出的新的开发web的技术栈,它是一种非阻塞的开发模式,它运行在Netty或者Servlet 3.1的容器上边,支持非常高的并发量,也就是说我们现在开发Web应用又有了一种新的开发模式,可以使用之前的mvc开发模式也可以使用web Flux开发模式;
  1. Web Flux 与 MVC的区别
    2.1 开发方式的不同:Web Flux是异步非阻塞的开发模式,MVC同步的阻塞的I/O开发模式;
    2.2 运行环境的不同:MVC的运行环境是基于Servlet API的,所以它必须运行在Servlet容器上边,而Web Flux的开发模式是基于Reactive Stream流的方式,它可以运行在Netty或者Servlet 3.1及以后版本的容器上边;其实Spring默认的容器就是Netty;
    2.3 数据库类型的不同:关系型数据库(mysql等)暂时不支持响应式的开发模式;
  1. Web Flux的优势
    支持非常高的并发量;

二 异步Servlet

  1. 为什么要使用异步Servlet?
    因为使用异步Servlet不会阻塞Tomcat的Servlet线程,所以异步Servlet可以达到非常高的吞吐量,可以处理非常高的并发;
  1. 同步Servlet阻塞了什么?
    2.1 其实阻塞的是Tomcat容器的Servlet线程,当有网络请求发送到Tomcat以后,Tomcat会为每一个请求分配一个线程去处理,线程会调用对应的Servlet容器去处理,在这个处理的过程中,相关的业务代码所花费的时间就是Servlet线程等待的时间,这就是同步Servlet的阻塞;

代码演示:

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class SyncServlet
 */
@WebServlet("/SyncServlet")
public class SyncServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public SyncServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        long t1 = System.currentTimeMillis();

        // 执行业务代码
        doSomeThing(request, response);

        System.out.println("sync use:" + (System.currentTimeMillis() - t1));
    }

    private void doSomeThing(HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        // 模拟耗时操作
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }

        response.getWriter().append("done");
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}
  1. 异步Servlet是怎么工作的?
    因为使用异步Servlet不会阻塞Tomcat的Servlet线程,它会将请求放在独立的线程池中(特别是对于比较耗时的操作),结果会立马被返回,结果返回后就接着处理下一个请求,所以异步Servlet可以达到非常高的吞吐量;

代码演示:

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class AsyncServlet
 */
@WebServlet(asyncSupported = true, urlPatterns = { "/AsyncServlet" })
public class AsyncServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public AsyncServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        long t1 = System.currentTimeMillis();

        // 开启异步
        AsyncContext asyncContext = request.startAsync();

        // ִ执行业务代码
        CompletableFuture.runAsync(() -> doSomeThing(asyncContext,
                asyncContext.getRequest(), asyncContext.getResponse()));

        System.out.println("async use:" + (System.currentTimeMillis() - t1));
    }

    private void doSomeThing(AsyncContext asyncContext,
            ServletRequest servletRequest, ServletResponse servletResponse) {

        // 模拟耗时操作
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }

        //
        try {
            servletResponse.getWriter().append("done");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 业务代码处理完毕,通知结束
        asyncContext.complete();
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}

三 Web Fulx开发效率对比

package com.imooc;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@Slf4j
public class TestController {

    @GetMapping("/1")
    private String get1() {
        log.info("get1 start");
        String result = createStr();
        log.info("get1 end.");
        return result;
    }

    @GetMapping("/2")
    private Mono get2() {
        log.info("get2 start");
        Mono result = Mono.fromSupplier(() -> createStr());
        log.info("get2 end.");
        return result;
    }

    /**
     * Flux : 返回0-n个元素
     * 
     * @return
     */
    @GetMapping(value = "/3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    private Flux flux() {
        Flux result = Flux
                .fromStream(IntStream.range(1, 5).mapToObj(i -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                    }
                    return "flux data--" + I;
                }));
        return result;
    }

    private String createStr() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }
        return "some string";
    }
}
4_webflux服务端开发_第1张图片
普通方式调用
4_webflux服务端开发_第2张图片
Web Flux调用
执行效率结果展示
从上边可以看出,get1(正常模式)执行时间为5秒,get2(Web Flux模式)执行基本没有时间损耗,性能更高;关于原因已经在上边进行了阐述这里就不在进行说明了;
4_webflux服务端开发_第3张图片
Flux的使用展示

四 SSE(server-sent events)

  1. SSE的本质
    服务器向客户端声明,接下来要发送的是流信息(streaming),也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。
  1. SSE的服务端实现
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class SSE
 */
@WebServlet("/SSE")
public class SSE extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public SSE() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // 实现SSE必须要设置的内容
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");

        for (int i = 0; i < 5; i++) {
            // 指定事件标识
            response.getWriter().write("event:me\n");
            // 格式: data: + 数据 + 两个回车
            response.getWriter().write("data:" + i + "\n\n");
            response.getWriter().flush();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        }

    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}
  1. SSE的前端实现(基于H5)




Insert title here


    


  1. 效果展示
    4_webflux服务端开发_第4张图片
    SSE实现效果展示1
    4_webflux服务端开发_第5张图片
    SSE实现效果展示2

    我们可以自定义我们的消息返回,请看图示
    4_webflux服务端开发_第6张图片
    SSE实现效果展示3
    但是此时我们发现一个问题,就是如果不主动关闭的话,浏览器会一直的接受这个流,所以我们需要手动关闭,当执行到4的时候又从头开始接受了,所以我们限制一下让他接受到3的时候就关闭这个动作
    4_webflux服务端开发_第7张图片
    SSE实现效果展示4
  1. 更多关于SSE的问题请参考阮一峰的网络日志

你可能感兴趣的:(4_webflux服务端开发)