YMNNALFT:Websockets

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

歡迎來到另一期的 您可能不需要另一個函式庫 (YMNNALFT)!自 2016 年以來,我花了很多時間在 我的 Spring Tips 影片中,闡明(或試圖闡明!)Spring 生態系統中一些更巨大的機會。 然而,今天,我以不同的精神來到您身邊,希望專注於那些小而隱藏的寶石,它們能完成出色的事情,並且可以避免您額外依賴第三方函式庫及其隱含的複雜性。

開放網路長期以來一直為那些希望建立一個商品平台,以大規模建構和部署服務及應用程式的人們帶來希望。 我們知道,一旦改進了一些東西,網路就可以變得引人注目。 人們可以提供豐富的客戶端,這些客戶端可以透過重新整理瀏覽器頁面來升級。 他們可以提供以資料和多媒體為中心、身歷其境的體驗。 我們知道,如果他們有正確的範例來建構網站和服務,人們就可以做到這些事情。 但他們說,沒有酸就無法欣賞甜,因此社群開始尋找建構網站和服務的絕對最差的方法,而這就是我們如何獲得 PHP 的故事。

結束。...

好吧,所以還有一點點。 一開始,後端和客戶端都存在嚴重的限制。 然而,客戶端的問題持續的時間比後端的問題長得多。 到 2000 年代初期,每個重要的程式語言社群都可以建構 HTTP 服務,但客戶端的能力停滯不前。 (幾乎就像某種主要力量出於惡意阻止開放網路發展一樣。 但是為什麼? 誰? 這是一個謎,我想我們永遠無法解決......)

開放網路自行演變。 它使用 HTTP PATCH 修補了自己。 在 90 年代末,我們讓 PayPal 成為可能進行安全的商業交易。 在 2000 年代初期,我們獲得了對 HTTP 的約束,稱為 REST(代表 Really Easy Service Transactions,還是 REcent Software Trend? 不。 這不對。 Representational State Transfer! 聽起來是對的......)。 然後 "Ajax"(不,不是清潔產品)出現了,它允許客戶端就地向服務發出請求,而無需強制另一個 HTTP 往返服務以獲取新頁面。 很好。 然後我們花了令人痛苦的五年左右的時間,試圖尋找將資料從伺服器推送到客戶端的方法,而不是回應用戶端的請求將資料發送到客戶端。

我們確實嘗試了所有方法。 有很多拙劣的解決方案。 (這是一個有趣的事實:janky JavaScript 在 Node.js 到來之前就存在了!)最終,在 2011 年,我們獲得了一個所有 HTTP 瀏覽器供應商和 HTTP 伺服器供應商都可以始終如一地支援的標準,並且解決了我們 70% 的需求:WebSockets。 Websockets 太棒了! 它們是將非同步通訊引入基於瀏覽器的應用程式的最佳方式。 它們快速、輕量且易於實作。

雖然有很多框架可以用於實作 WebSocket 端點,但您不必再尋找 Spring,因為它開箱即用地支援反應式和非反應式服務。 讓我們看看使用 Spring Webflux(反應式 Web 運行時)的服務範例。

您將需要以下依賴項。

  • Spring Initializr 上的反應式 Web - org.springframework.boot : spring-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),然後選擇 Console。 您將在那裡看到來自 WebSocket 端點的回應。

使用更多程式碼,我們也可以使用 Spring WebSocketClient 與該服務通訊。

您將需要以下依賴項。

  • Spring Initializr 上的反應式 Web - org.springframework.boot : spring-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 社群中所有即將舉辦的活動。

檢視全部