长连接(socket)可靠消息架构与海量消息架构浅析

2024-03-08 08:08:27 浏览数 (3)

引言

本文站在可靠连接的场景下分析,不再保证可靠性连接做过多论述。

研究背景与动机

  • 探究和总结在使用长连接技术进行实时通信时,确保消息可靠性的策略和方法。
  • 研究如何高效处理巨量消息,确保长连接服务的稳定性和扩展性。

研究目标与问题描述

  • 如何在长连接中实现可靠消息传输机制?
  • 如何设计一个能够处理巨量消息的长连接架构?
  • 如何在保证消息实时性的同时,优化系统资源利用,提高系统稳定性?

研究范围与结构

  1. 研究范围
    • 研究将聚焦于长连接在实现可靠消息传输和处理巨量消息方面的应用与最佳实践。
    • 涵盖消息确认机制、持久化策略、负载均衡、水平扩展等关键技术点。
  2. 研究结构
    • 引言:介绍研究背景、动机和目标。
    • 理论基础:深入分析长连接的工作原理及其在实时通信中的应用。
    • 可靠消息传输方案:探讨和评估确保长连接消息可靠性的不同策略和技术。
    • 巨量消息处理架构:研究如何构建高效的长连接消息处理架构,应对大规模并发和数据流。
    • 实践案例分析:分析业界典型应用案例,提取成功经验和教训。
    • 结论与展望:总结研究成果,提出未来研究方向。

长连接概述

长连接技术主要用于维持客户端和服务器之间的持续通信,减少因频繁建立和断开连接带来的开销。

实现方式

  1. HTTP长连接(HTTP Persistent Connection)
    • 在HTTP 1.1协议中,默认开启了长连接,即Connection: keep-alive,使得多个HTTP请求/响应可以在一个TCP连接上连续传送,而不需要每次传输都建立新连接。
    • HTTP长连接可以减少连接和关闭的次数,降低延迟,提高效率,但它不是实时的,仍依赖请求-响应模式。
  2. WebSocket
    • WebSocket协议提供了在单个TCP连接上进行全双工通信的能力。
    • 它允许服务器主动向客户端发送信息,适用于需要服务器实时推送数据到客户端的场景,如在线聊天、游戏、实时交易等。
    • WebSocket在建立连接时需要进行一次握手,建立后可以持续不断地传输数据,直到任一方主动关闭连接。
  3. Server-Sent Events (SSE)
    • SSE是一种允许服务器主动向浏览器发送事件的技术,只支持服务器到客户端的单向通信。
    • 相比于WebSocket,SSE更简单易用,只需要使用标准的HTTP协议。
    • SSE适用于不需要客户端到服务器的双向通信,只需服务器推送数据到客户端的场景。
  4. 长轮询(Long Polling)
    • 长轮询是对传统轮询的改进,客户端发起请求后,如果服务器没有数据,它不会立即返回,而是等到有数据时才响应。
    • 长轮询可以减少请求的次数,提高数据实时性,但它仍然需要频繁地建立HTTP连接。
  5. TCP长连接
    • 在更底层的网络通信中,可以直接使用TCP协议维持长连接。
    • TCP长连接可以减少TCP握手和挥手的次数,适用于需要长时间保持连接的场景,如数据库连接、文件传输等。
    • 需要注意的是,在使用TCP长连接时要考虑心跳机制,以便检测和维持连接的活性。

    不同的方式有不同的场景,例如ChatGPT就是采用SSE来进行消息流推送的,又比如各种游戏都是使用UDP建立数据连接,而很多RPC框架底层是TCP连接,现代浏览器提供了WebSocket支持。

基本原理和特性

基本原理:
  1. 连接建立
    • 长连接的建立通常开始于客户端向服务器发送连接请求,两者通过一定的握手协议(如TCP握手或WebSocket握手)建立连接。
    • 在HTTP长连接中,客户端通过在请求头加入Connection: keep-alive来告知服务器希望保持连接。
  2. 数据交换
    • 一旦连接建立,客户端和服务器就可以在这个持续的连接上进行数据交换。与短连接不同,长连接不需要每次交换数据时重新建立连接。
    • 在WebSocket或TCP长连接中,数据可以实时双向传输,而在HTTP长连接中,通信仍遵循请求-响应模式,但多个请求可以复用同一个连接。
  3. 连接维护
    • 为了维持长连接,双方需要有机制来检测连接是否仍然活跃,这通常通过发送心跳包实现。心跳包是指定期发送的轻量级数据包,用于确认对方仍然在线。
    • 如果长时间未收到响应,连接可能会被视为已断开,需要重新建立。
  4. 连接终止
    • 长连接会持续存在,直到客户端或服务器决定关闭连接。在TCP协议中,任一方都可以发起连接终止请求。
    • 在应用层如HTTP、WebSocket等协议中,也有明确的机制来关闭长连接。
特性:
  1. 减少连接建立的开销:由于不需要频繁建立和断开连接,长连接可以减少因为TCP握手或TLS握手带来的时间和资源开销。
  2. 提高数据传输效率:长连接减少了每次数据交互都要建立连接的需要,从而降低了延迟,提高了数据传输的效率。
  3. 保持会话状态:在一些需要保持会话状态的应用中,如数据库连接和文件传输,长连接可以简化会话管理。
  4. 心跳机制:长连接通常需要通过心跳机制来检测和保持连接的活性,防止连接由于超时而被关闭。
  5. 资源占用:与短连接相比,长连接会在其生命周期内持续占用系统资源,如端口和内存,这可能在一定程度上影响服务器的扩展能力。

对于TCPSocket.IO这类直接长久占据线程资源的长连接,无疑在连接数量过多的情况下会导致连接被打满,这就是为什么很多公司不愿意在很多业务场景去使用它,比如扫码登陆和扫码支付多半采用HTTP短轮询请求,因为长连接在业务量大的情况下,架构维护的成本是直线上升的,需要进行扩散机制、集群扩容、数据一致性机制等等。

可靠消息方案架构

消息可靠性的重要性

为什么需要消息可靠?这主要取决于实际的业务场景,对于数据准确性要求不高的场景,或许并不需要在这方面多花功夫,但是如果是类似即时通讯这类场景,那一致性是非常重要的,否则上下文丢失,不能衔接语意,最主要的是,通信最重要的就是要保证100%收到消息。

IM场景下,需要确保信息准确无误地从发送者传递到接收者。

设计与实现

双端确认机制

在生活中,我们经常有快递需要寄取,那么收件方怎么知道快递到了?发件方怎么知道快递已经取了? 很明显,双方都会收到通知,那么在开发中也可以这样做。

  • 客户端确认逻辑
    • 当客户端发送消息后,它应该等待服务器的确认响应。如果客户端没有在预定时间内收到确认,它可能会选择重发消息或记录失败事件。
    • 客户端应实现机制以区分新消息和重发消息,避免在服务器端造成重复处理。
  • 服务器端确认逻辑
    • 服务器接收到客户端的消息后,应当进行处理,并发送一个确认响应回客户端。这个响应应包含足够的信息,使客户端能够确认哪条消息被成功处理。
    • 服务器还应当能够识别和处理重复的消息,确保每条消息只被处理一次。
  • 状态中间件机制: 上面开头的举例可以看出,其实收件方与发件方并不是直接联系的,而是会收到快递公司发送的通知短信,那么我们是不是也可以形成这样的中间人? 做过并发业务的朋友可能知道锁机制,尤其是分布式锁,实际上就是有一个中间件作为中间人,然后从业务中谁都可以从中间人获取的一个信息,这个信息是双方共享的。 这种实际上就是一个状态机机制,使用中间人(中间件)来管理唯一的状态信息,这种好处是减少了双方的通信,同时保证了数据的绝对一致性。
超时与重试
  • 超时策略:设定一个合理的超时时间,如果在该时间段内没有收到期望的响应,认为操作超时,超时时间的设置需要根据网络状况、系统负载等因素考虑。
  • 重试策略
    • 在发生超时后,客户端可以采取重试策略,重试次数和重试间隔应该是可配置的,以适应不同的网络环境和应用场景。
    • 需要实现指数退避算法或其他智能算法来动态调整重试间隔,避免过于频繁的重试对系统造成额外压力。

    如果某个请求没有收到对方的回执或者消息,那么我们不能一直去等待,计算机是很笨的,所以我们需要实现一个机制来兜底,保证最终的请求消费(成功)。 我们可以定义,如果一个消息,在n秒内没有收到消息,我们将它默认为对方没有收到消息,我们需要对他进行重新请求,即重试。 但是如果第二次也失败了怎么办??? 这取决于业务对请求保证的容忍性,如果允许丢掉部分请求,我们可以不再重试,因为对方很可能不在线,没必要发起重试了。 但是某些请求是必须送达的,我们需要保证数据最终一致性,那么此时可以设置多次重试机制,比如最多重试n次,x秒一次。 可是我们这样想,如果它只是暂时这段时间都不在线呢?那就没法保证短时间内重试成功,此时我们可以采用叠加延时重试机制,即1s、2s、4s、8s、16s....24h,设置最大的时间值,超出后不再重试,进行进一步兜底方案触发。

  • 重试幂等问题: 对于重试,如果网络不好的情况下,会产生请求已经收到,但是响应不及时,所以客户端超时,于是客户端会重试请求,而实际上服务端已经处理过了,这样就会产生幂等问题。 幂等性是指无论一个操作执行多少次,结果都保持一致的特性。在分布式系统和网络通信中,确保幂等性对于保持系统的一致性和稳定性至关重要。 我们需要保证同一个请求不被重复处理,可以采取以下方法:
    1. 唯一事务ID
      • 为每个操作分配一个唯一的事务ID。无论这个操作尝试执行多少次,系统都识别这是同一个操作请求,并确保只执行一次。
      • 这种方法常用于支付系统中,防止同一笔支付被处理多次。
    2. 利用数据库的唯一索引
      • 利用数据库的唯一约束条件,防止插入重复数据。
      • 例如,在订单表中为订单编号设置唯一索引,即使收到多个创建相同订单编号的请求,数据库也只会接受第一个请求。
    3. 乐观锁
      • 通过在数据库记录中加入版本号或时间戳字段,每次操作前检查版本号或时间戳是否发生变化,只有在未变化的情况下才进行操作,并更新版本号或时间戳。
      • 这种方法常用于防止并发更新导致的数据不一致问题。
    4. 状态机
      • 将操作建模为状态机,每个操作使状态机从一个状态转换到另一个状态。
      • 如果操作试图将状态机从一种已经达到的状态转换到相同的状态,那么这个操作可以被视为是重复的,从而不执行任何操作。
    5. 幂等框架支持
      • 使用支持幂等性的框架或中间件,这些工具通常提供了检测和处理重复请求的机制。
      • 例如,一些消息队列中间件支持消息去重,确保消息消费者不会处理重复的消息。
    6. 令牌机制
      • 在进行操作前,客户端需要从服务端获取一个唯一令牌,然后在执行操作时将这个令牌提交给服务端。
      • 服务端接收到操作请求后,首先检查令牌是否有效,并确保每个令牌只被使用一次,从而防止重复操作。

      对于HTTP请求,我们通常可以收集该请求的很多信息然后做哈希值运算,比如IP、参数、请求类型、请求头....等等,利用这些数据计算出该请求的唯一哈希值并缓存,然后下一次请求我们进行一次哈希值对比。 为什么需要多个参数来哈希计算呢?因为哈希计算是有小概率重复问题的,如果我们的计算因子(数据)较多更能保证计算出了的哈希值的唯一性。 如果担心请求量过大缓存哈希值而带来的内存问题,我们可以进一步使用布隆过滤器来优化,这样空间使用效率会更高。

持久化
  • 消息持久化是确保消息不会因为系统故障而丢失的重要机制,在服务器端收到消息后,应该先将其持久化存储,然后再进行处理。
  • 持久化可以使用数据库、文件系统或专门的消息队列系统实现,持久化策略应确保数据的一致性和完整性。

上面说的是服务端持久化,这个一般不用多说,是必然会做的。 如果是存在大量实时数据的传输,我们去采用客户端消息持久化,这样如果断开连接后,我们不需要消耗服务度的CPU和带宽资源来推送之前的数据包,这样重连机制也更加顺畅。 可以尝试这样实现:前端每隔一段时间就将当前数据压缩携带上当前时间节点并缓存到浏览器的持久化空间中,当某一时刻出现了网络抖动,需要重新连接并拉数据,此时不需要全部拉过来,而是拉缓存时间之后的数据,这样在客户端特别多的情况下可以缓解连接握手和首次数据推送的压力,不过具体还得考虑是否存在这样的需求。

事务

事务消息通常指那些需要在事务上下文中处理的消息,这意味着它们需要完全成功处理,或在失败时进行回滚。 例如,在电子商务平台中,用户的支付和订单更新需要在同一事务中处理,以确保数据的一致性。 事务分为强一直性与最终一直性方案

  1. 事务消息的发送
    • 在长连接环境中,客户端和服务器维护一个持久的连接,客户端发送事务性请求到服务器,需要确保这些请求能在服务器端按预期处理。
    • 事务消息发送后,客户端通常需要等待服务器的响应,以确认事务是否成功处理。
  2. 事务状态管理
    • 服务器需要维护每个事务的状态,包括开始、处理中、成功或失败。
    • 服务器端可能需要实现逻辑来处理事务的各种状态,包括对失败事务的回滚。
  3. 错误处理和回滚
    • 在处理事务消息时,如果遇到错误或异常,需要有明确的回滚机制来撤销已经执行的操作,防止数据不一致。
    • 例如,如果订单创建成功但支付失败,需要回滚订单创建操作,确保系统状态的一致性。
  4. 幂等性保证
    • 在长连接下处理事务消息时,需要保证消息处理的幂等性,即同一消息被多次处理的结果应该相同,避免因网络重试等原因导致的重复处理问题。
  5. 超时和重试机制
    • 在长连接的环境中,还需要考虑事务处理的超时机制和可能的重试策略,确保即使在网络波动或服务繁忙时,事务消息也能得到可靠处理。
    • 重试机制就是去保证最终一致性,比如我们MQ也存在消费重试机制,这都是去保证一个操作的最终一致性,而不是去回滚操作,这样对性能优化更加友好
  6. 事务日志记录
    • 记录事务处理的日志,包括每个事务的请求、响应和最终状态,这对于事后分析、故障排查和系统审计都非常重要。
消息顺序
  • 顺序问题的影响:
  1. 数据不一致:如果消息不是按照发送的顺序被接收和处理,可能导致数据状态出现不一致,比如最终的数据状态反映的是较早的操作结果而不是最新的。
  2. 逻辑错误:许多逻辑处理都依赖于事件的顺序,错误的顺序可能导致逻辑判断错误,进而引发错误的业务处理结果。
  3. 用户体验下降:在即时通信和在线游戏等应用中,消息的顺序错误可能直接影响到用户的体验,比如消息乱序、游戏状态同步错误等。
  • 顺序控制的策略:
  1. 消息编号
    • 在发送端为每个消息分配一个唯一的、递增的编号,接收端按照这个编号来排序消息,确保消息按序处理。
    • 这种方法简单直观,但需要确保编号的唯一性和递增性。
  2. 消息队列
    • 在接收端使用消息队列来管理消息,确保消息可以按照接收的顺序被处理。
    • 在分布式系统中,可以利用像Kafka这样的消息队列服务保证跨多个生产者和消费者的消息顺序。
  3. 确认机制
    • 设计确认机制,确保接收端处理完一个消息后再处理下一个,例如,接收端只有在处理完当前消息并发送确认后,发送端才发送下一个消息。
    • 这种方法可能会降低系统的吞吐量,因为它引入了额外的等待时间。
  4. 分段和重组
    • 对于大消息,可以在发送端将其分段,并在每个段上标记序号,接收端根据这些序号来重组消息,确保消息的完整性和顺序性。
    • 这种方法适用于大数据传输,需要注意的是分段和重组逻辑需要保证高效和可靠
  5. TCP保证
    • 利用TCP协议的特性来保证消息的顺序,TCP协议通过序列号保证了传输的数据包按序到达和重组。
    • 对于依赖TCP的长连接来说,这是一种内建的保证顺序的方法,不过它仅限于点对点通信,并且不能解决应用层的重排序问题。

巨量消息方案架构

大规模实时通信的挑战

  • 可扩展性
    • 随着用户数量和交互强度的增加,系统必须能够水平扩展以支持更多的并发连接和消息吞吐量。
    • 设计良好的微服务架构、使用高效的消息队列和负载均衡策略是解决可扩展性问题的关键。
  • 性能优化
    • 实时通信要求低延迟和高吞吐量,这需要优化网络传输、减少数据包大小、使用高效的编解码算法等。
    • 在后端,需要优化消息路由、处理逻辑和数据库访问等,以减少每个消息的处理时间。
  • 消息可靠性和顺序性
    • 在网络不稳定或系统负载高时,保证消息的不丢失、不重复和按序到达是一个挑战。
    • 可以通过消息持久化、使用可靠的消息传输协议(如TCP)、实现消息确认和重试机制等方法来提高消息的可靠性和顺序性。
  • 容错性和高可用性
    • 系统需要能够处理节点故障、网络问题和服务中断等异常情况,快速恢复并继续提供服务。
    • 通过构建多副本、故障转移、熔断机制和自动恢复策略等可以提高系统的容错性和高可用性。
  • 数据一致性和同步
    • 在分布式环境中保持数据的一致性和同步是非常困难的,特别是在存在多个数据中心或服务节点时。
    • 使用分布式数据库、实现事务性消息处理和数据同步策略可以帮助保持数据的一致性。
  • 安全性
    • 实时通信系统需要保护数据不被未授权访问或篡改,尤其是涉及敏感信息的应用。
    • 通过实现数据加密、安全的认证授权机制、网络隔离和防火墙策略等可以提高系统的安全性。
  • 成本控制
    • 随着系统规模的扩大,资源消耗和运维成本也会增加。
    • 合理的资源规划、使用云服务自动扩展资源、优化系统架构和算法可以帮助控制成本。

消息队列

异步、解耦、削峰、蓄洪。

  1. 解耦生产者和消费者
    • 利用消息队列作为中间层可以解耦消息的生产者和消费者,使得生产者无需等待消费者处理完毕即可继续发送消息,从而提高生产者的响应性和吞吐率
    • 消费者可以根据处理能力从消息队列中拉取消息,避免因无法及时处理导致的长连接阻塞。
  2. 负载均衡
    • 在消息队列前设置负载均衡器,根据各消费者节点的处理能力和当前负载动态分配消息,可避免某个节点过载而其他节点空闲的情况。
    • 对于长连接服务器也应实现负载均衡,确保连接请求均匀分配到不同的服务器节点,防止单点过载
  3. 水平扩展
    • 无论是消息队列还是长连接服务器,都应设计为支持水平扩展,随着负载的增加可以动态增加节点处理更多的连接和消息。
    • 水平扩展可以有效提高系统的处理能力和可靠性,同时也支持更高的并发需求。
  4. 压缩数据包: 对大数据包进行压缩后传输,不过压缩需要牺牲一定计算成本。
  5. 合并数据包: 将多个小数据包合并后传输,节省传输线程资源。
  6. 切分数据包: 将大数据包进行分切后传输,这样可以更快的传输而不会阻塞。
  7. 流式传输: 让数据向自来水在自来水管中一样传输,不再是一个包一个包或者多个包,直接就是源源不断的直到消息被完全接收。
  8. 传输协议:
    • 选择合适的传输协议对优化大数据包的传输也非常关键,例如,TCP提供可靠的传输服务,但在传输大数据包时可能因其拥塞控制和流量控制机制导致效率不高,而UDP虽然传输效率更高,但不提供可靠性保证(现在其实新出炉的许多UDP封装协议已经解决了这个问题)。
    • 根据应用需求选择合适的协议或者设计应用层协议来平衡传输效率和数据可靠性。
  9. 利用多路传输: 如果网络环境允许,可以使用多路传输技术,如多路径TCP(MPTCP),来利用多条网络路径并行传输数据,从而提高传输速率和可靠性。
  10. 调整缓冲区大小:
    • 理调整发送端和接收端的缓冲区大小,可以减少等待时间和提高数据处理速率。
    • 缓冲区过小可能导致频繁的等待和确认,而缓冲区过大则可能增加内存的消耗和数据处理的延迟。
  11. 设置消息优先级: 拆分长连接的数据传输管道,重要的消息优先传输和使用更好的资源消耗。
  12. 持久化与可靠性保证
    • 消息队列应保证消息的持久化存储,即使在系统故障时也能保证消息不丢失,确保消息的可靠传递。
    • 通过实现消息确认机制,确保每条消息都被正确处理,未被确认的消息可以重新入队或通知生产者。
  13. 异步处理与批处理
    • 消费者处理消息时应采用异步处理机制,避免因单条消息处理阻塞影响整体吞吐率。
    • 在可能的情况下,可以采用批处理机制,即一次从队列中获取多条消息进行批量处理,提高处理效率。
  14. 监控与调优
    • 实施有效的监控机制,实时监控消息队列和长连接服务器的性能指标,如队列长度、处理延迟、错误率等。
    • 根据监控数据定期进行系统调优,优化资源分配、调整消息处理策略等,以应对不断变化的业务需求和数据量。

    MQ可以应对实时消息传出量突增问题,一般是在IM中使用的较多。

负载均衡

如果需要对长连接进行负载均衡的话,那么这将是一件非常有挑战的问题,尤其是消息的分布式同步问题。

  • 负载均衡技术的选择
    • 硬件负载均衡器
      • 硬件负载均衡器通常提供高性能和高可靠性,但成本较高。它适用于流量极大、对延迟和性能要求极高的场景。
    • 软件负载均衡器
      • 软件负载均衡器如NginxHAProxy提供灵活配置和良好的性能,对于大多数应用已经足够。
      • 它们支持多种负载均衡算法,可以根据实际需求进行配置。
    • DNS负载均衡
      • 通过DNS轮询分配不同的IP地址,实现请求的负载均衡。这种方式简单易部署,但无法实时响应后端服务器的健康状况。
    • 应用层负载均衡
      • 在应用层实现负载均衡,可以更智能地分发请求,例如根据用户的地理位置、会话信息等进行路由。

      很多时候,负载均衡不会只在一层,而是多层次链路负载,比如:DNS选择最优地区的服务器—>最优的服务器Nginx—>最优的服务器网关—>最优的服务端。

  • 状态共享与同步问题
    • 会话保持(Sticky Sessions):
      • 对于需要保持用户状态的长连接,可以使用会话保持技术,确保来自同一用户的请求路由到同一服务器。
      • 这可以通过在负载均衡器设置会话亲和性(Session Affinity)来实现。
    • 分布式缓存
      • 使用分布式缓存如Redis来共享状态信息,可以让任何服务器访问共享的会话数据,从而解决状态同步问题。
      • 这样即使用户的连接被路由到不同服务器,应用状态也可以保持一致。
    • 数据同步机制
      • 在后端服务之间实现数据同步机制,确保各个服务器上的数据保持最新和一致。
      • 这通常需要引入额外的数据同步逻辑,可能会影响系统的复杂度和性能。
    • 无状态设计
      • 尽可能设计无状态的应用,这样每个请求都是独立的,不需要依赖之前的状态。
      • 这样可以简化负载均衡的实现,提高系统的伸缩性和可靠性。

      对于分布式状态与数据同步共享问题,可以参考我之前的一篇:浅谈分布式环境下WebSocket消息共享问题

水平扩展

无状态扩展
  1. 特点
    • 无状态服务意味着服务的每个实例都可以独立处理请求,不依赖于本地会话或上下文信息。
    • 所有需要的状态信息都存储在外部系统(如数据库、缓存服务)中,任何实例都可以访问这些信息以完成请求处理。
  2. 扩展策略
    • 可以简单通过增加服务实例的数量来实现扩展,新实例可以立即接入流量,处理请求。
    • 负载均衡器可以均匀地分配连接到所有实例,不需要考虑实例间的状态同步。
  3. 跨机房部署
    • 确保所有机房可以访问状态存储,并保持状态存储的一致性和高可用性
    • 考虑使用地理位置感知的负载均衡策略,将用户请求路由到最近的机房以减少延迟。
有状态扩展
  1. 特点
    • 有状态服务意味着服务实例需要维护连接状态信息,如用户会话、临时数据等。
    • 扩展有状态服务需要考虑如何在服务实例之间共享和同步状态信息
  2. 扩展策略
    • 可以使用会话亲和性(Sticky Sessions)来确保来自同一客户端的请求总是被路由到同一服务实例。
    • 状态信息可以在实例间通过消息传递或共享存储进行同步。
  3. 跨机房部署
    • 考虑状态同步跨机房的延迟和带宽消耗,可能需要在机房间进行数据复制或使用中心化的状态存储
    • 为了提高容错性,需要确保每个机房都能独立提供服务,同时处理跨机房故障转移。
跨机房问题
  1. 数据一致性和同步:确保所有机房中的数据保持一致性,特别是在有状态模式下,可能需要实现复杂的数据同步和冲突解决策略。
  2. 灾难恢复:跨机房部署可以提高系统的灾难恢复能力,但需要设计有效的数据备份和故障转移机制。
  3. 网络延迟和成本
    • 跨机房同步状态或访问远程状态存储会增加网络延迟,可能影响服务质量。
    • 跨机房数据传输还可能增加网络成本,需要在设计时加以考虑。

监控与告警

  • 需要存在对关键指标的监控。
  • 需要能够捕获到性能瓶颈然后诊断与调优。

总结

本文探讨了在长连接环境下确保消息可靠性和处理海量消息的策略和架构,包括消息确认机制、超时和重试策略、消息持久化以及顺序控制等。

同时,讨论了在大规模实时通信中面临的挑战,以及通过消息队列、负载均衡和水平扩展等手段来优化系统的方法。

对于优化措施,需要权衡硬件与网络的实际情况,没有最优的方案,只有最适合的方案,空间换时间、资源换时间,对于需要尽可能节省服务端资源的场景,通常就是在客户端做手脚了,例如QQ与微信,都是使用了SQL Lite来做本地缓存,这样断网重连也只需要拉某一时间之后的数据,然后进行数据合并处理,极大的减少了传输消耗,但是带来的就是客户端的存储成本,可以查看一下QQ与微信这些软件的空间占用。

如果需要保证消息的绝对可靠,那就还是需要进行大量网络数据同步,QQ与微信如果清空本地空间,就会丢失许多聊天数据,当然,并不是说腾讯服务端不存储消息,而是不愿提供这种带宽成本。

综上所属,优化点如下:

  1. 大数据包:压缩、切片、流传输、协议优化
  2. 可靠性:重试、确认机制、幂等
  3. 一致性:事务、顺序保障
  4. 频繁传输:队列、负载、水平扩容
  5. 最终保障:监控与告警

同时,均存在对立面(格式为,方法->牺牲的资源):压缩->算力,切片->传输资源,流传输->时间,重试->业务复杂性,确认机制->请求负担,幂等->算力,一致性、顺序->速度,扩容->复杂性与资源,队列->复杂性与内存,负载->链路追踪与同步复杂性

参考文献

segmentfault.com/a/119000004…

juejin.cn/post/734286…

www.52im.net/

0 人点赞