使用WebSocket向浏览器推送消息

一、技术选型 WebSocket VS Server-Sent Events(SSE)

WebSocket 跟SSE是目前主流的服务器主动向客户端推送消息的方案

  1. 连接方式

    • SSE:SSE使用HTTP协议,它建立在HTTP之上。这意味着它使用HTTP的请求-响应模型,即客户端向服务器发送请求,然后服务器响应请求。在SSE中,服务器主动向客户端推送事件(即数据)。
    • WebSocket:WebSocket则使用一个不同的协议(WebSocket协议),它是一个全双工通信协议,允许服务器和客户端之间的双向通信。在WebSocket中,服务器和客户端都可以主动发起通信。
  2. 性能

    • SSE:由于SSE建立在HTTP之上,因此它可以使用现有的HTTP基础设施。这使得SSE在某些情况下可能更高效,特别是在处理头部信息时。但是,SSE的连接是单线程的,每个连接只能处理一个请求-响应序列,这可能会限制其性能。
    • WebSocket:WebSocket的性能通常优于SSE,尤其是在处理大量并发连接时。WebSocket是专门为实时通信设计的协议,因此它支持全双工通信和高数据吞吐量。但是,WebSocket需要更多的开销来建立和维护连接。
  3. 用途

    • SSE:SSE通常用于从服务器向客户端推送实时更新的情况,例如博客文章、实时股市数据或天气预报等。由于SSE连接是单向的,因此很适合在需要由服务器推动数据的场景中使用。
    • WebSocket:WebSocket通常用于需要双向通信的应用程序,如实时聊天、游戏、实时交易等。由于WebSocket支持全双工通信,因此很适合在需要服务器和客户端之间频繁交换数据的场景中使用。
  4. 浏览器支持

    • SSE:所有现代浏览器都支持SSE,包括IE8及以上版本。
    • WebSocket:虽然大多数现代浏览器都支持WebSocket,但一些较旧的浏览器可能不支持。因此,在使用WebSocket时可能需要进行浏览器兼容性检查。
  5. 安全性

    • SSE:由于SSE使用HTTP协议,因此它可以使用HTTPS来提供安全的通信。但是,SSE连接只能由服务器关闭,这可能使其在处理恶意客户端时不太安全。
    • WebSocket:WebSocket可以使用WebSocket协议或HTTPS来提供安全的通信。与SSE不同,WebSocket连接可以由客户端或服务器关闭,这提供了更多的安全性选项。
  6. 扩展性

    • SSE:由于SSE建立在HTTP之上,因此可以使用现有的HTTP库和框架。这使得在后端使用SSE相对简单。
    • WebSocket:虽然WebSocket在某些方面可能更复杂,但它提供了一些SSE不具备的功能,例如消息队列和分帧传输等。此外,WebSocket库通常提供了更多的功能和选项,以满足不同的需求

二、业务场景&确认技术方案 (WebSocket)

WebSocket总体更符合我们的业务需求,SSE的优势是对浏览器的兼容比较好,但是我们不需要对IE进行适配,最重要的是SSE无法实现浏览器客户端向服务器主动推送消息,我们恰恰需要客户端向服务端推送消息(通知服务器删除消息)

实现过程问题

1、多台服务器部署问题

问题描述:如果是多台服务器部署项目,那么浏览器客户端跟哪个服务器建立连接是有很大的随机性,在不进行处理的情况下,发送消息可能会出现消息丢失的问题:

假如我们有两台服务器A跟B,此时客户端小虎上线,小虎通过nginx负载分配连接上了服务器A,跟服务器A保持连接,业务系统调用消息中心系统发送消息,通过注册中心把请求发送到了服务器B,此时服务器B并没有跟小虎建立连接,所有无法拿到Session会话向小虎发送消息,导致消息发送失败

解决办法:使用中间件发布订阅模式,如常见的MQ跟Redis都支持,只需要所有机器监听发布队列,当发送消息请求被服务器B接收到以后,不直接消费,而是将消息发布到广播队列中,通知所有监听该队列的服务器处理,服务区收到广播消息后,先判断小虎是否跟自己建立连接,如果建立则发送消息,没有建立则放弃发送。

在实现发布订阅模式时,MQ(消息队列)和Redis有以下的区别:

  1. 存储机制:MQ一般采用分布式存储,例如RabbitMQ、Kafka等,它们在多节点之间传递消息。而Redis通常作为内存数据库使用,它主要存储在单个节点或集群中。
  2. 性能:由于Redis是内存数据库,所以它的读写速度通常比MQ快。但是,如果系统需要处理大量的并发消息,并且需要保证消息的可靠传递,MQ可能会更合适。
  3. 持久性:Redis提供了持久化机制(如RDB和AOF),可以在系统崩溃后恢复数据。而MQ通常不具备这样的持久性,但是可以通过将消息写入磁盘或者使用复制策略来保证数据的可靠性。
  4. 扩展性:由于Redis是内存数据库,所以它的扩展性受到硬件的限制。而MQ由于是分布式系统,所以它们可以轻松地在多个节点之间分发消息,从而实现更好的扩展性。
  5. 使用场景:Redis通常用于缓存和会话管理,而MQ则更适用于处理异步任务和实现发布订阅模式。
  6. 发布订阅的特性:Redis的发布订阅模型支持单播和多播消息,但一般不支持持久化和重播。而MQ的发布订阅模型通常支持持久化、重播和消息确认等功能。
  7. 可靠性:Redis不提供像消息持久化、重试、确认和事务等可靠性保证。而MQ通常会提供这些功能以确保消息的可靠传递。
  8. 语言支持:Redis主要是用C语言编写的,而MQ通常支持多种语言。

总的来说,MQ功能强大,但并非功能越复杂越好,符合业务需求才是关键。如果业务需要持久化、消息确认、重试机制等功能,那么选择像RabbitMQ或Apache Kafka这样的MQ是合适的。但在某些情况下,如果业务更关注高性能、原子性操作和实时消息推送,那么Redis可能更为合适。对于我们而言,像消息确认和重播机制等额外功能并不需要,因为它们会增加不必要的维护成本。因此,最终我选择Redis来实现。

2、nginx长时间无数据传输会自动断开连接

问题描述:由于域名调用需要经过nginx网关,nginx对所有网络连接默认三分钟无数据传输时会自动断开连接,如果不处理,用户在3分钟内没有收到消息就会跟服务器断线,从而无法收到服务器消息通知

解决办法:客户端断线重连、心跳检测、nginx加长空闲连接时间

断线重连:客户端检测到被关闭连接后,立即发起重连请求,连接不一定会被nginx关闭,网络不好时也会被关闭,这个机制必须要

心跳检测:每隔一段时间向服务器发送一次心跳,同时服务器收到心跳后需要回复客户端,从而刷新nginx空闲时间

nginx加长空闲连接时间:可以通过proxy_connect_timeout跟proxy_send_timeout配置

如何使用消息中心给boss平台发送消息

一、2.0.1-SNAPSHOT版本以上版本支持

<dependency>
	<groupId>cn.victorplus.message</groupId>
	<artifactId>boss-message-api</artifactId>
	<version>2.0.1-SNAPSHOT</version>
</dependency>
注:原车回租项目需要在application.properties文件中添加两个属性,因为消息中心在boss注册中心中,原车回租不能通过注册中心调用
#是否指定url
boss-message-api.isAppointUrl=true
#消息服务url地址
boss-message-api.url=http://192.168.21.61:15061/

二、使用工具类cn.victorplus.boss.message.api.utils.MessageUtil

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个使用Netty实现WebSocket主动向浏览器推送消息的示例代码: ```java public class WebSocketServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap() .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new WebSocketServerHandler()); } }); ChannelFuture future = bootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private static class WebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> { private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class); @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { if (frame instanceof TextWebSocketFrame) { String request = ((TextWebSocketFrame) frame).text(); logger.info("Received message: {}", request); // 向浏览器发送消息 ctx.writeAndFlush(new TextWebSocketFrame("Hello, " + request + "!")); } else { String message = "Unsupported frame type: " + frame.getClass().getName(); throw new UnsupportedOperationException(message); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { logger.error("Exception caught: ", cause); ctx.close(); } } } ``` 在这个示例中,我们首先创建了一个`ServerBootstrap`,并且添加了一个`WebSocketServerHandler`来处理WebSocket请求。在`WebSocketServerHandler`中,我们通过`channelRead0`方法获取到来自浏览器消息,并且向浏览器发送一个回复。 需要注意的是,在这个示例中,我们使用了`HttpServerCodec`和`HttpObjectAggregator`来处理HTTP请求,使用了`WebSocketServerProtocolHandler`来处理WebSocket请求。同时,我们将WebSocket的路径设置为`/ws`,可以根据自己的需要进行修改。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值