YMNNALFT:WebSockets

工程 | Josh Long | 2021年1月25日 | ...

欢迎来到《您可能不需要另一个库》(YMNNALFT)的另一期!自从2016年以来,我花了大量时间在我的 Spring Tips 视频中阐明(或者至少尝试阐明!)Spring 生态系统中一些更大的机会。我的 Spring Tips 视频。然而,今天我以不同的精神来到这里,希望关注那些功能强大的、有时隐藏的小巧宝石,它们可以为您节省一个额外的第三方依赖项及其带来的复杂性。

开放网络长期以来一直为那些希望建立一个商品平台以大规模构建和部署服务和应用程序的人们提供了希望。我们知道,一旦改进了一些方面,网络就可以变得引人注目。人们可以交付丰富的客户端,这些客户端可以通过刷新浏览器页面进行升级。他们可以交付以数据和多媒体为中心的沉浸式体验。我们知道,如果他们拥有构建网站和服务的正确范式,人们就可以做到这些。但他们说,没有酸就没有甜,所以社区开始寻找构建网站和服务的**最糟糕**的方法,而这就是我们如何获得 PHP 的故事。

结束…

好吧,事情并没有那么简单。一开始,后端和客户端都存在严重的限制。然而,客户端的问题比后端的问题持续了更长时间。到 21 世纪初,每个重要的编程语言社区都可以构建 HTTP 服务,但客户端的能力却停滞不前。(这几乎像是某种主要力量在恶意地阻止开放网络发展。但为什么?是谁?这是一个谜,我想我们永远无法解开…)

开放网络自行发展。它通过 HTTP PATCH 自我修复。在 90 年代后期,我们有了 PayPal 使安全商务成为可能。在 21 世纪初,我们得到了对 HTTP 的约束,称为 REST(代表 Really Easy Service Transactions,还是 REcent Software Trend?不,不对。是 Representational State Transfer!听起来像是…)。然后“Ajax”(不,不是清洁产品)出现了,它允许客户端就地向服务发出请求,而无需强制进行另一次 HTTP 往返服务以获取新页面。很好。然后我们花了大约五年痛苦的时间试图找到将数据从服务器推送到客户端的方法,而不是在响应客户端请求时将数据发送到客户端。

而且我们确实尝试了**一切**。有大量的权宜之计。(有趣的事实:在 Node.js 出现**之前**,就存在着笨拙的 JavaScript!)最终,在 2011 年,我们得到了一项所有 HTTP 浏览器供应商和 HTTP 服务器供应商都能一致支持的标准,这解决了我们 70% 的需求:WebSockets。WebSockets 太棒了!它们是在基于浏览器的应用程序中引入异步通信的最佳方法。它们速度快、轻量级且易于实现。

虽然您可以使用许多框架来实现 WebSocket 端点,但您无需再寻找其他框架,因为 Spring 本身就支持响应式和非响应式服务。让我们看一下使用 Spring Webflux(响应式 Web 运行时)的服务示例。

您需要以下依赖项。

  • Spring Initializr 上使用 Reactive Web - org.springframework.bootspring-boot-starter-webflux

这是我放入我的 application.properties 中的内容

spring.main.web-application-type=reactive

这是代码

package bootiful.websockets.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import reactor.core.publisher.Flux;

import java.util.Map;

@SpringBootApplication
public class BootifulApplication {

	public static void main(String[] args) {
		System.setProperty("spring.profiles.active", "wsserver");
		SpringApplication.run(BootifulApplication.class, args);
	}

	@Bean
	SimpleUrlHandlerMapping greetingsHm() {
		return new SimpleUrlHandlerMapping(Map.of("/ws/greetings", greetingsWsh()), 10);
	}

	@Bean
	WebSocketHandler greetingsWsh() {
		return session -> {

			Flux<WebSocketMessage> out = session.receive().map(WebSocketMessage::getPayloadAsText)
					.flatMap(name -> Flux.just("Hi, " + name).map(session::textMessage));

			return session.send(out);
		};
	}

}

SimpleUrlHandlerMappingWebSocketHandler 映射到 HTTP URI。WebSocketHandler 提供了响应式 WebSocket 端点逻辑,将传入的有效负载(名称)转换为问候语(Hi, NAME!)发送到客户端。

现在,我要做一些我通常不会做的事情。如果还有其他方法,朋友们,我当然会更喜欢其他方法,而不是这种不太体面的前进方式。我不会在有礼貌的场合这样做,但我感觉没有其他方法可以完成这件事,没有其他方法可以证明与 WebSocket 端点通信是多么简单。我不会轻易这样做。

我…要使用 JavaScript

    window.addEventListener('load', () => {
        const ws = new WebSocket('ws://127.0.0.1:8080/ws/greetings')
        ws.addEventListener('open', () => {
            ws.send('JavaScript Fans')
        })
        ws.addEventListener('message', (message) => {
            console.log(message.data)
        })
    })


在这个示例中,我们在 JavaScript 中设置了一个 WebSocket 对象,注册一个回调函数,以便在 WebSocket 最终准备就绪时通过它发送数据,并为到达的任何回复注册另一个回调函数。转到浏览器的“开发者工具”(每个浏览器的说明都不同,但如果您使用的是 Mac 并且使用的是 Chrome 或 Firefox,可以尝试使用 OPTION + CMD + I),然后选择“控制台”。您将在那里看到 WebSocket 端点的响应。

使用更多代码,我们还可以使用 Spring WebSocketClient 与该服务通信。

您需要以下依赖项。

  • Spring Initializr 上使用 Reactive Web - org.springframework.bootspring-boot-starter-webflux

这是代码

package bootiful.websockets.client;

import lombok.SneakyThrows;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import reactor.core.publisher.Mono;

import java.net.URI;

@SpringBootApplication
public class BootifulApplication {

	@SneakyThrows
	public static void main(String[] args) {
		System.setProperty("spring.profiles.active", "wsclient");
		SpringApplication.run(BootifulApplication.class, args);
		Thread.sleep(5_000);
	}

	@Bean
	WebSocketClient webSocketClient() {
		return new ReactorNettyWebSocketClient();
	}

	@Bean
	ApplicationListener<ApplicationReadyEvent> ready(WebSocketClient client) {
		return event -> client.execute(URI.create("ws://127.0.0.1:8080/ws/greetings"), webSocketSession -> {
			WebSocketMessage world = webSocketSession.textMessage("Spring Fans");
			return webSocketSession.send(Mono.just(world))
					.thenMany(webSocketSession.receive().map(WebSocketMessage::getPayloadAsText).log()).then();
		})//
				.subscribe();
	}

}

这是我放入我的 application.properties 中的内容

spring.main.web-application-type=none

WebSockets 使我们的基于浏览器的客户端更具活力。但是,我更喜欢使用 RSocket 进行服务到服务的通信。

您是否喜欢这种一目了然的方法?您学到什么了吗?与往常一样,我很乐意听到您的反馈,所以请在 Twitter 上发表您的意见(@starbuxman)!我将带着《YMNNALFT》的另一期回来,所以请不要错过。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部