spring-webflux-快速入门

版本说明2.2.6.RELEASE

快速入门

pom.xml

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

主类

@RestController
@SpringBootApplication
public class WebfluxBilibiliApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebfluxBilibiliApplication.class, args);
    }

    @RequestMapping("/greeting")
    public Mono<String> greeting(){
        return Mono.just("hello world");
    }

}

测试

$ curl localhost:8080/greeting
hello world

Server-Sent Event(SSE)

SSE vs WebSocket

在这里插入图片描述

  • SSE 是单向的。----股票交易市场
  • WebSocket是双向的。 双方可以互相发送数据,直到任意一方关闭链接。 —聊天工具

示例1

定义接口

    @RequestMapping("/sse")
    public Flux<String> sse(){
        //每一秒中推送一条数据
        return Flux.interval(Duration.ofMillis(1000)).map(val -> "->" + val);
    }

测试

$ curl localhost:8080/sse
->0->1->2->3->4->5->6->7->8->9->10->11->12->13  ###ctrl+c 终止命令

示例2

修改上述接口

指定mediaType= "text/event-stream"

 @RequestMapping(value = "/sse",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> sse(){
        //每一秒中推送一条数据
        return Flux.interval(Duration.ofMillis(1000)).map(val -> "->" + val);
    }

测试

$ curl localhost:8080/sse
data:->0

data:->1

data:->2

sse属性

https://docs.kilvn.com/JavaScript-Standards-Reference-Guide/htmlapi/eventsource.html

  • id: 字符串,默认为空
  • event: 事件,默认为空
  • data: 传递的数据,为字符串或任意对象
  • retry:

定义接口

   @RequestMapping(value = "/sse")
    public Flux<ServerSentEvent<String>> sse(){
        return   Flux.interval(Duration.ofMillis(1000))
                .map(val -> ServerSentEvent.<String>builder()
                        .id(UUID.randomUUID().toString())
                        .event("TEST_EVENT")
                        .data(val.toString())
                        .build()
                );
    }

测试

$ curl localhost:8080/sse
id:8e931afa-648d-4d07-8642-07364297cb37
event:TEST_EVENT
data:0

id:1de714ef-9937-467f-a1f9-462b8fd089cb
event:TEST_EVENT
data:1

EventSource

前端(浏览器)通过EventSource组件来接收服务端推送来的消息。

在这里插入图片描述

定义接口

只需在前文中的接口上添加:@CrossOrigin("*") 允许跨域即可。

html

<script type="text/javascript">
	if (!!window.EventSource) {
		// 建立连接
  		var source = new EventSource("http://localhost:8080/sse");

  		/**
  		0,相当于常量EventSource.CONNECTING,表示连接还未建立,或者连接断线。
		1,相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
		2,相当于常量EventSource.CLOSED,表示连接已断,且不会重连。
		**/
  		console.info(source.readyState);


  		source.onopen = function(event) {
		  console.info("open");
		};
		
        //监听指定的时间名称
		source.addEventListener("TEST_EVENT", function(event) {
		  var data = event.data;
		  var origin = event.origin; //服务器端URL的域名部分,即协议、域名和端口。
		  var lastEventId = event.lastEventId; //数据的编号,由服务器端发送。
		  
		  console.info(origin+"->"+lastEventId +"->"+data);
		}, false);

		source.onerror = function(event) {
		  console.info(error);
		};

		//source.close(); //close方法用于关闭连接。

	}
</script>

执行效果

在这里插入图片描述

WebFlux vs SpringMVC

响应式和非阻塞通常来讲也不会使应用运行的更快。相反,非阻塞方式要求做更多的事情,而且还会稍微增加一些必要的处理时间。也就是说,还可能稍稍变慢一点,what,那为啥还要用它呢?响应式和非阻塞的关键好处是,在使用很少固定数目的线程和较少的内存情况下的扩展能力

服务端搭建

  @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Employee {
        private int id;
        private String name;
    }

    private List<Employee> list;

    {
        list = new ArrayList<>();
        IntStream.rangeClosed(1, 100).forEach(index ->
                list.add(new Employee(index, "name_" + index))
        );
    }


    private void dosth() {
        try {
            //
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/flux")
    public Flux<Employee> findAll() {
        LocalDateTime start = LocalDateTime.now();
        Flux<Employee> flux = Flux.range(1, 5).map(val ->
                {
                    dosth();
                    return list.stream().filter(employee -> employee.getId() == val).findFirst().get();
                }
        );
        System.out.println("flux::" + Duration.between(start, LocalDateTime.now()).toMillis() + "ms");
        return flux;
    }

    @RequestMapping(value = "/rest")
    public List<Employee> findAll2() {
        LocalDateTime start = LocalDateTime.now();
        List<Employee> ret = IntStream.range(1, 5).mapToObj(val ->
                {
                    dosth();
                    return list.stream().filter(employee -> employee.getId() == val).findFirst().get();
                }
        ).collect(Collectors.toList());
        System.out.println("rest::" + Duration.between(start, LocalDateTime.now()).toMillis() + "ms");
        return ret;
    }

浏览器请求

浏览器结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zY6nZc2N-1587911439404)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1587909941784.png)]

后台日志

flux::4ms
rest::4040ms

使用webflux的前台响应时间反而比rest更耗时,
但是后台响应处理更为迅速。

WebClient

WebClient是基于Reactor实现的,非阻塞的。而RestTemplate是阻塞的。

快速使用

Flux<WebfluxBilibiliApplication.Employee> flux = WebClient.create("http://localhost:8080/flux")
    .get()
    .retrieve()
    .bodyToFlux(WebfluxBilibiliApplication.Employee.class);

Post传参

WebfluxBilibiliApplication.Employee employee = new WebfluxBilibiliApplication.Employee(999, "jhs");
WebClient.create("http://localhost:8080/save")
    .post()
    .body(Mono.just(employee), WebfluxBilibiliApplication.Employee.class)
    .retrieve()
    .bodyToFlux(WebfluxBilibiliApplication.Employee.class);

Form Data

MultiValueMap<String, String> formData = ... ;

Mono<Void> result = client.post()
        .uri("/path", id)
        .bodyValue(formData)
        .retrieve()
        .bodyToMono(Void.class);

///方法2
import static org.springframework.web.reactive.function.BodyInserters.*;

Mono<Void> result = client.post()
        .uri("/path", id)
        .body(fromFormData("k1", "v1").with("k2", "v2"))
        .retrieve()
        .bodyToMono(Void.class);

Multipart Data

MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("fieldPart", "fieldValue");
builder.part("filePart1", new FileSystemResource("...logo.png"));
builder.part("jsonPart", new Person("Jason"));
builder.part("myPart", part); // Part from a server request

MultiValueMap<String, HttpEntity<?>> parts = builder.build();

/ 
MultipartBodyBuilder builder = ...;

Mono<Void> result = client.post()
        .uri("/path", id)
        .body(builder.build())
        .retrieve()
        .bodyToMono(Void.class);

Client Filters

/1
WebClient client = WebClient.builder()
        .filter((request, next) -> {
            ClientRequest filtered = ClientRequest.from(request)
                    .header("foo", "bar")
                    .build();

            return next.exchange(filtered);
        })
        .build();
        
/2
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = WebClient.builder()
        .filter(basicAuthentication("user", "password"))
        .build();

///3
WebClient client = WebClient.builder()
        .filter((request, next) -> {
            Optional<Object> usr = request.attribute("myAttribute");
            // ...
        })
        .build();

client.get().uri("https://example.org/")
        .attribute("myAttribute", "...")
        .retrieve()
        .bodyToMono(Void.class);

    }

添加filter

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = webClient.mutate()
        .filters(filterList -> {
            filterList.add(0, basicAuthentication("user", "password"));
        })
        .build();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring WebFluxSpring Framework 5引入的新的响应式Web框架,它基于Reactor项目实现了响应式编程模型。相比于传统的Spring MVC框架,Spring WebFlux提供了更好的吞吐量和更低的延迟,适用于高并发场景。 下面是一个快速入门的示例: 1. 创建一个Spring Boot项目,选择Webflux和Reactive MongoDB依赖。 2. 创建一个数据模型类,比如User: ```java public class User { private String id; private String name; private int age; // 省略getter和setter } ``` 3. 创建一个数据访问层接口和实现,使用ReactiveMongoRepository来实现MongoDB的数据访问: ```java public interface UserRepository extends ReactiveMongoRepository<User, String> { } @Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public Mono<User> save(User user) { return userRepository.save(user); } public Mono<User> findById(String id) { return userRepository.findById(id); } public Flux<User> findAll() { return userRepository.findAll(); } public Mono<Void> deleteById(String id) { return userRepository.deleteById(id); } } ``` 4. 创建一个控制器类,定义接口: ```java @RestController @RequestMapping("/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping("") public Mono<User> save(@RequestBody User user) { return userService.save(user); } @GetMapping("/{id}") public Mono<User> findById(@PathVariable String id) { return userService.findById(id); } @GetMapping("") public Flux<User> findAll() { return userService.findAll(); } @DeleteMapping("/{id}") public Mono<Void> deleteById(@PathVariable String id) { return userService.deleteById(id); } } ``` 5. 启动应用程序,访问http://localhost:8080/users即可测试。 以上就是一个简单的Spring WebFlux入门示例,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值