写这篇文章是为了分享我对使用WebSocket进行前后端实时通信的探索。近年来,微服务是许多开发者采用的一种架构方式,而微服务架构的关键原则之一就是“单一职责原则”。
在本次分享中,将研究设计和实现一个 WebSocket 服务器,该服务器负责与前端(Web 应用程序)建立 WebSocket 连接,并充当前端和后端之间实时通信的中间件(或代理)。
注意:本文不会详细介绍 WebSocket 或发布-订阅消息模式的工作原理。
在许多情况下,Web 应用程序(前端)需要客户端(浏览器)和服务器(后端)之间的实时通信。此类用例的一些示例包括实时提要、实时协作编辑、实时数据可视化、实时聊天、通知、事件更新等。
假设每个用例都有一个微服务;上图将说明如何在客户端(前端)和每个单独的微服务(后端)之间建立 WebSocket 连接。
如上图,这不是一个优雅的设计,因为当微服务的数量增加时,会创建更多的 WebSocket 连接。因此,让我们看看 WebSocket 服务器如何帮助解决这个问题。
微服务架构中 WebSocket 服务器的高级图
在上面的设计中,WebSocket 服务器是唯一与 Web 应用程序(前端)建立 WebSocket 连接的微服务。对于其他微服务,与 Web 应用程序(前端)进行实时通信的主要方式有两种:
——所有微服务(后端)都可以通过 API 将消息发送到 WebSocket 服务器,然后消息将通过 WebSocket 转发到 Web 应用程序(前端)。
——Web 应用程序(前端)可以通过 WebSocket 向 WebSocket 服务器发送消息,然后消息将通过 Pub/Sub 转发到微服务(后端)。
- 微服务(后端)可以通过 Pub/Sub 将消息发送到 WebSocket 服务器,然后消息将通过 WebSocket 转发到 Web 应用程序(前端)。
基于上述设计使用 Spring Boot、Stomp 和 Redis Pub/Sub 构建 WebSocket 服务器。
初始化一个 Spring Boot 项目。您至少需要 Spring Web、Redis 和 Websocket 依赖项。
创建一个配置文件,WebsocketConfig.kt然后添加下面的配置。该配置为 Spring Boot 应用程序启用 WebSocket 功能。
请注意,演示环境下,stomp 端点允许所有来源,生产环境不可以这样配置。
启用 Spring Boot Websocket 的配置 (WebsocketConfig.kt)
API 端点为微服务(后端)向 Web 应用程序(前端)发送消息提供了一种方式。由于消息只需要单向流(后端 → WebSocket 服务器 → 前端),使用 API 将是微服务(后端 → Websocket 服务器)之间良好的通信方式。
用于向 WebSocket 服务器发送消息的 API 端点
上面的代码创建了一个带有 POST 请求端点的 REST 控制器,该端点接收请求主体“ NewMessageRequest”,其中topic是客户端(前端)订阅的 STOMP 目标,并且message是字符串格式的实际消息。有了这个,您现在可以通过 API 向 WebSocket 服务器发送消息,然后通过 WebSocket 将消息转发到 Web 应用程序(前端)。
注意:根据您的场景,如果您不需要 Web 应用程序(前端)和微服务(后端)之间的双向实时通信,则可以省略此步骤。
与使用发布-订阅消息模式相比,微服务(后端和 Websocket 服务器)之间通过 API 进行的通信对于实时通信来说并不是最佳的。因此,对于双向通信,可以使用发布-订阅消息模式。
有很多方法可以实现发布-订阅消息传递模式,但为了演示和简单起见,我们将使用 Redis Pub/Sub。
首先,使用 docker ( docker run — name redis-server -p 6379:6379 -d redis) 在本地运行 Redis 服务器,并将以下配置添加到 application.yml 文件中,以便 WebSocket 服务器连接到 Redis 服务器。
# application.yml
spring.redis:
主机: localhost
端口: 6379
接下来,创建一个配置文件 ,RedisConfig.kt并在下面添加配置。本质上,我们正在配置一个ReactiveRedisTemplate与 Redis 服务器通信并配置为将消息序列化和反序列化为字符串。
@Configuration
class RedisConfig {
@Bean
fun reactiveRedisTemplate(factory: LettuceConnectionFactory): ReactiveRedisTemplate {
val serializer = Jackson2JsonRedisSerializer(String::class.java)
val builder = RedisSerializationContext.newSerializationContext(StringRedisSerializer())
val context = builder.value(serializer).build()
return ReactiveRedisTemplate(factory, context)
}
}
在此之后,创建一个RedisService包含订阅和发布到 Redis 服务器的逻辑。在下面的示例中,我们订阅了一个入站通道主题GREETING_CHANNEL_INBOUND,该主题侦听来自其他微服务(后端)的传入消息并将收到的所有消息转发到 STOMP 目的地/topic/greetings。
@Service
class RedisService(
private val reactiveRedisTemplate: ReactiveRedisTemplate,
private val websocketTemplate: SimpMessagingTemplate
) {
fun publish(topic: String, message: String) {
reactiveRedisTemplate.convertAndSend(topic, message).subscribe()
}
fun subscribe(channelTopic: String, destination: String) {
reactiveRedisTemplate.listenTo(ChannelTopic.of(channelTopic))
.map(ReactiveSubscription.Message::getMessage)
.subscribe { message ->
websocketTemplate.convertAndSend(destination, message)
}
}
@PostConstruct
fun subscribe() {
subscribe("GREETING_CHANNEL_INBOUND", "/topic/greetings")
}
}
最后,创建一个Controller处理来自 Web 应用程序(前端)的消息,这些消息使用前缀 发送到 WebSocket 服务器/app。在下面的示例中,发送到的消息/app/greet将被转发(发布)到出站通道主题GREETING_CHANNEL_OUTBOUND,然后由正在侦听该通道的任何微服务(后端)处理。
@Controller
class WebsocketController(private val redisService: RedisService) {
@MessageMapping("/greet")
fun greetMessage(@Payload message: String) {
redisService.publish("GREETING_CHANNEL_OUTBOUND", message)
}
}
这样,我们将 WebSocket 服务器设置为充当中间件(或代理),通过 WebSocket 与 Web 应用程序(前端)进行通信,并通过 Redis Pub/Sub 与微服务(后端)进行通信。
启动 WebSocket 服务器,并ws://localhost:8080/stomp使用 WebSocket 调试器工具通过 STOMP 协议连接到 WebSocket 服务器。连接后,配置 WebSocket 调试器工具以订阅主题/topic/toast。
接下来,使用以下命令向 WebSocket 服务器发送 HTTP POST 请求:
curl -X POST -d '{"topic": "/topic/toast", "message": "测试 API 端点" }' -H 'Content-Type: application/json' localhost:8080/api/notification
WebSocket 调试器工具应具有如下所示的输出:
这表明 WebSocket 服务器已经通过 API 成功接收到消息,并通过 WebSocket 将消息转发到 Web 应用程序(前端)。
启动 WebSocket 服务器,并ws://localhost:8080/stomp使用 WebSocket 调试器工具通过 STOMP 协议连接到 WebSocket 服务器。连接后,配置 WebSocket 调试器工具以订阅主题/topic/greetings。
使用 Redis CLI,使用命令将消息发布到通道主题GREETING_CHANNEL_INBOUND PUBLISH GREETING_CHANNEL_INBOUND “\"Test Message from Backend PubSub\"”。
请注意,\”由于 WebSocket 服务器配置为接收字符串消息,因此需要额外的内容。WebSocket 调试器工具应该会收到如下所示的消息
这说明 WebSocket 服务器已经通过 Redis Pub/Sub 成功接收到消息,并通过 WebSocket 将消息转发到 Web 应用程序(前端)。
启动 WebSocket 服务器,并ws://localhost:8080/stomp使用 WebSocket 调试器工具通过 STOMP 协议连接到 WebSocket 服务器。连接后,使用 Redis CLI,使用命令订阅频道主题GREETING_CHANNEL_OUTBOUND SUBSCRIBE GREETING_CHANNEL_OUTBOUND。使用 WebSocket 调试器工具向 STOMP 目的地发送消息/app/greet,您应该观察以下内容:
这说明 WebSocket 服务器已经通过 WebSocket 成功接收到消息,并通过 Redis Pub/Sub 将消息转发到微服务(后端)。
总之,完成了微服务架构中 WebSocket 服务器的可能设计。拥有 WebSocket 服务器与微服务的“单一职责原则”非常一致,它管理与 Web 应用程序(前端)的所有 WebSocket 连接,并处理 Web 应用程序(前端)和其他微服务(后端)之间的实时通信.
请帮忙点赞关注转发,将后续分享研究扩展 WebSocket 服务器。
留言与评论(共有 0 条评论) “” |