1. 程式人生 > >springboot2.x簡單詳細教程--高階篇幅之響應式程式設計(第十五章)

springboot2.x簡單詳細教程--高階篇幅之響應式程式設計(第十五章)

 

 

 

一、SprinBoot2.x響應式程式設計簡介


    簡介:講解什麼是reactive響應式程式設計和使用的好處

    1、基礎理解:
        依賴於事件,事件驅動(Event-driven)
        一系列事件稱為“流”
        非同步
      
 非阻塞

1)買奶茶的案例(一個一個等待處理)

2)響應式程式設計

        觀察者模式:服務員一直觀察後臺的情況,然後通知客戶

    網上的一個例子:
        int b= 2;
        int c=3
        int a = b+c  //指令式程式設計後續b和c變化
,都不影響a
        b=5;

        int b= 2;
        int c= 3
        int a = b+c  //響應式程式設計中,a的變化,會和b、c的變化而變化(事件驅動)
        b=5;

    2、官網:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-webflux


        SpingBoot2底層是用spring5,開始支援響應式程式設計,Spring又是基於Reactor試下響應式。

    

    學習資料
        1、reactive-streams學習資料:http://www.reactive-streams.org/
        2、web-flux相關資料:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux

 總結響應式程式設計,必須每一個階段都不阻塞,才能提升效能

 

二、SpringBoot2.x響應式程式設計webflux介紹


    簡介:講解SpringBoot2.x響應式程式設計介紹 Mono、Flux物件和優缺點
    
    1、Spring WebFlux是Spring Framework 5.0中引入的新的反應式Web框架
    與Spring MVC不同
,它不需要Servlet API,完全非同步和非阻塞,並 通過Reactor專案實現Reactive Streams規範
    RxJava

 

    2、Flux和Mono  User List<User>
        1)簡單業務而言:和其他普通物件差別不大,複雜請求業務,就可以提升效能
        2)通俗理解:
            Mono 表示的是包含 0 或者 1 個元素的非同步序列 (序列理解成流)
                mono->單一物件 User     redis->使用者ID-》唯一的使用者Mono<User>  
            
            Flux 表示的是包含 0 到 N 個元素的非同步序列
                flux->陣列列表物件 List<User>   redis->男性使用者->Flux<User>
            Flux 和 Mono 之間可以進行轉換


    3、Spring WebFlux有兩種風格基於功能和基於註解的基於註解非常接近Spring MVC模型,如以下示例所示:
        第一種:
          
        第二種:
         “WebFlux.fn”是功能變體,它將路由配置與請求的實際處理分開,如以下示例所示:

   @Configuration
public class RoutingConfiguration {

	@Bean
	public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
		return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
				.andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
				.andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
	}

}

@Component
public class UserHandler {

	public Mono<ServerResponse> getUser(ServerRequest request) {
		// ...
	}

	public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
		// ...
	}

	public Mono<ServerResponse> deleteUser(ServerRequest request) {
		// ...
	}
}

    4、Spring WebFlux應用程式不嚴格依賴於Servlet API,因此它們不能作為war檔案部署也不能使用src/main/webapp目錄
    
    5、可以整合多個模板引擎
        除了REST Web服務外,您還可以使用Spring WebFlux提供動態HTML內容。Spring WebFlux支援各種模板技術,包括Thymeleaf,FreeMarker

 

 

 
三、SpringBoot2.x webflux實戰


    簡介:webflux響應式程式設計實戰
    
    1、WebFlux中,
請求和響應不再是WebMVC中的ServletRequest和ServletResponse,而是ServerRequest和ServerResponse

    2、加入依賴,如果同時存在sprin g-boot-starter-web,則會優先用spring-boot-starter-web
       

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.itcast</groupId>
	<artifactId>Springboot08_A</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
	</parent>
	<dependencies>
		<!-- <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>	 -->
		<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-webflux</artifactId>
			</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
    
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

      官網說明

 測試

1)

2)


    

    3、啟動方式預設是Netty,8080埠

    Netty:高效能網路框架,非同步非阻塞

    4、參考:https://spring.io/blog/2016/04/19/understanding-reactive-types

   5.模擬資料庫實現CRUD

   1)service

package com.itcast.demo.service;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Service;

import com.itcast.demo.domain.User;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class UserService {

	 //模擬資料庫
	 private static final Map<String,User> map =new HashMap<>();
	  static{//一建立物件就初始化map
		  map.put("1", new User("1", "小王1"));
		  map.put("2", new User("2", "小王2"));
		  map.put("3", new User("3", "小王3"));
		  map.put("4", new User("4", "小王4"));
		  map.put("5", new User("5", "小王5"));
		  map.put("6", new User("6", "小王6"));
		  map.put("7", new User("7", "小王7"));
		  map.put("8", new User("8", "小王8"));		  
	  }
	  //返回所有資料
	  public Flux<User> list(){
		  Collection<User> list = UserService.map.values();
		  return Flux.fromIterable(list);
	  }
	  //通過id查出
	  public Mono<User> getById(final String id){
		return   Mono.justOrEmpty(UserService.map.get(id));
	  }
	  
	  //通過id刪除使用者
	  public Mono<User> del(final String id){
		return  Mono.justOrEmpty(UserService.map.remove(id));
	  }
}

2)controller

package com.itcast.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.itcast.demo.domain.JsonData;
import com.itcast.demo.domain.User;
import com.itcast.demo.service.UserService;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @作者po
 */
@RestController
@RequestMapping("/api/v1")
public class UserController {
    //@Autowired
	//private UserService userService;
	
	//通過構造的方式
	private final UserService userService;
	
    public UserController(final UserService userService ) {
		this.userService=userService;
	}
	@GetMapping("test")
	public Mono<String> Test01() {
		return Mono.just("hello Mono");
	}
	
    @GetMapping("getById")
	public Mono<User> getById(final String id){
		return userService.getById(id);
	}
    @GetMapping("del")
   	public Mono<User> del(final String id){
   		return userService.del(id);
   	}
    
    @GetMapping("list")
   	public Flux<User> list(){
   		return userService.list();
   	}
     
}

3)訪問

4)list

5)del

6)呼叫list的時候,希望拿到一個值就返回,如何體現這種效果

     //import org.springframework.http.MediaType;
     //拿到一個值就返回,即2s就返回,produces不配就沒有這樣的效果
    @GetMapping(value="list",produces=MediaType.APPLICATION_STREAM_JSON_VALUE)
   	public Flux<User> list(){//延遲兩秒
   		return userService.list().delayElements(Duration.ofSeconds(2));
   	}

應該:服務端的推送(像流水一樣

 

四、WebFlux客戶端WebClient講解


    簡介:講解SpringBoot2.x WebFlux客戶端WebClient的介紹和使用
        1、反應式客戶端

        官網地址:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-webclient

實戰

1)在測試

package com.domain;

import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

public class WebClientTest {

	@Test
	  public void testBase(){
	    Mono<String> resp = WebClient.create()
	        .get()
	        //多個引數也可以直接放到map中,引數名與placeholder對應上即可
	        .uri("http://localhost:8080/api/v1/getById?id=1") //使用佔位符
	        .accept(MediaType.APPLICATION_JSON)
	        .retrieve()
	        .bodyToMono(String.class);
	    System.out.println(resp.block());
	 
	  }
	
	@Test
	  public void testPlaceHolder(){
	    Mono<String> resp = WebClient.create()
	        .get()	       
	        .uri("http://localhost:8080/api/v1/getById?id={id}",1) //使用佔位符
	        .accept(MediaType.APPLICATION_JSON)
	        .retrieve()
	        .bodyToMono(String.class);
	    System.out.println(resp.block());
	 
	  }
	
}

2)先啟動主程式,再在方法上點JUnit Test