状态同步,究竟是推还是拉?

探讨了状态同步在即时通讯软件中的应用,包括好友及群友状态的实时性和一致性问题,以及采用推送与拉取的不同策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任何脱离业务的架构设计都是耍流氓。

 

状态同步,有好友状态的同步,有群友状态的同步,有的需要实时同步,有的能够容忍延时。结合具体场景来看下,状态同步,究竟是推还是拉。


用户的在线状态,分为客户端状态(端),服务端状态(云)两种形态。

 

什么是服务端状态?

服务端状态,主要分为在线online和离线offline,不同的状态,对于不同的业务处理流程可能不同。例如对于消息的处理:

  • 服务端状态在线,直接投递给用户

  • 服务端状态离线,直接存储离线消息,等用户下一次登录拉取

 

如何实时更新服务端状态?

用户uid-A登录时,会修改用户的服务端状态为在线


用户uid-A登出时,会修改用户的服务端状态为离线

经常的,服务端会将用户的服务端状态存储在高可用的缓存集群里。

 

什么是客户端状态?

不同的产品,会有不同的客户端状态,例如隐身、离线、忙碌、勿扰等,这些状态大多是产品功能需求。有的产品,例如微信,在设计之初,就摒弃了用户端状态这个概念。


后文为了方便描述,不妨设待讨论的是QQ这种拥有客户端状态的产品,并假设客户端状态也只有在线和离线两种状态,后文统一称为“用户状态”。

 

如何获取好友的状态?

uid-A登录时,先去数据库拉取自己的好友列表,再去缓存获取所有好友的状态。


用户uid-A的好友uid-B状态改变时(由登录、登出等动作触发),uid-A如何同步这一事件?

这里就有推拉的设计折衷了。

  • 如果对于状态变更实时性要求不高,可以采用拉取

uid-A向服务器轮询拉取uid-B(其实是自己的全部好友)的状态,例如每1分钟一次,其缺点是:

(1)如果uid-B的状态改变,uid-A获取不实时,可能有1分钟时延

(2)如果uid-B的状态不改变,uid-A会有大量无效的轮询请求,非常低效

 

  • 如果对于状态变更实时性要求较高,则必须推送

uid-B状态改变时(由登录、登出等动作触发),服务端不仅要在缓存中修改uid-B的状态,还要将这个状体改变的通知推送给uid-B的在线好友。

推送的优势是:实时

缺点是:当在线好友量很大时,任何一个用户状态的改变,会扩散成N个实时通知,这个N叫做“消息风暴扩散系数”。假设一个IM系统平均每个用户有200个好友,平均有20%的好友在线,那么消息风暴扩散系数N=40,这意味着,任何一个状态的变化会变成40个推送请求。

 

群友状态的一致性,和好友状态的一致性相比,复杂在哪里?可不可以采用实时推送?

群这个业务场景大伙也非常之熟悉,你能够加入若干群(例如20个),假设平均每个群有200人,即你会有4000个群友。

 

理论上群友状态也可以通过实时推送的方式实现,以保证实时性。进一步讨论之前,先一起估算下这个业务场景下的“消息风暴扩散系数”。

 

假设平均每个用户加了20个群,平均每个群有200个用户,依然假设20%的用户在线,那么为了保证群友状态的实时性,每个用户登录,就要将自己的状态改变通知发送给20*200*20%=800个群友,N=800,意味着,任何一个状态的变化会变成800个推送请求。如果说好友状态实时推送,消息风暴扩散系数N=40尚可以接受,那么群友状态实时推送,N=800则是灾难性的。此类业务往往采用轮询拉取的方式,获得群友的状态。

 

轮询拉取群友状态也会给服务器带来过大的压力,还有什么优化方式?

群友的数据量太大,虽然每个用户平均加入了20个群,但实际上并不会每次登录都进入每一个群。不采用轮询拉取,而采用按需拉取,延时拉取的方式,在真正进入一个群时才实时拉取群友的在线状态,是既能满足用户需求(用户感觉是状态是实时、一致的,但其实是进入群才拉取的),又能降低服务器压力。这是一种常见方法。

 

总结

状态的实时性与一致性是一个较难解决的技术问题,不同的业务实现方式不同,一般来说

  • 好友状态同步,是采用推送的方式同步

  • 群友状态同步,由于消息风暴扩散系数过大,一般采用拉取的方式同步

  • 群友状态同步,还能采用按需拉取的优化方式,进一步降低服务端压力

  • “消息风暴扩散系数”是指一个消息发出时,变成N个消息的扩散系数,这个系数一定程度上决定了技术采用推送还是拉


挖坑篇:《feed流,单聊群聊,系统通知,状态同步,到底是推还是拉?

填坑篇1:《系统通知,究竟是推还是拉?


若有收获,随手发。

<think>首先,用户的问题是:“我想在使用SPI接口时,加上电阻后应选择挽输出还是开漏输出 请问SPI通信中上电阻配合挽输出还是开漏输出更合适” 这是一个关于SPI通信配置的问题,具体是上电阻与输出模式(挽或开漏)的配合。 从系统级指令中,我需要: - 使用$...$格式的行内数学表达式,但这里可能不涉及数学表达式。 - 独立公式用$$...$$,但问题不涉及公式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,如[^1]。 用户提供的引用包括: - [^1]:关于I2C配置的讨论,提到硬件模式和软件模拟。 - [^2]:关于RS485和RS232的比较。 - [^3]:SPI配置的例子代码。 - [^4]:串口通信的知识点。 - [^5]:GPIO速度配置。 这些引用中,[^3]和[^5]最相关,因为它们涉及SPI和GPIO配置。 关键点: - SPI(Serial Peripheral Interface)是一种同步串行通信协议,常用于芯片间通信。 - GPIO(General Purpose Input/Output)引脚可以配置为挽输出或开漏输出。 - 上电阻用于确保信号在没有驱动时处于高电平状态。 在SPI通信中: - 挽输出(Push-Pull):可以驱动高电平和低电平,提供强驱动能力。 - 开漏输出(Open-Drain):只能驱动低电平;高电平需要外部上电阻来提供。 用户提到“加上电阻后”,所以上电阻是已经存在的。 在SPI通信中,通常使用挽输出,因为SPI是全双工的,需要快速切换高低电平。开漏输出常用于共享总线如I2C,以避免冲突。 从引用[^3]中,SPI配置的例子: - 在代码中,没有指定GPIO模式,但从上下文看,SPI通常配置为挽输出。 - 引用[^5]提到GPIO速度配置,其中高速信号如SPI使用50MHz速度,对应挽输出。 引用[^1]提到I2C的硬件模式是开漏输出,因为I2C需要多主机仲裁。而SPI没有仲裁机制,所以通常不需要开漏输出。 因此,对于SPI通信,挽输出更合适,即使有上电阻。上电阻在SPI中可能不必要,除非在特定情况下,比如长距离传输或噪声环境。 回答结构: 1. 解释SPI通信的基本要求。 2. 讨论挽输出和开漏输出的区别。 3. 分析在SPI中使用上电阻时的输出模式选择。 4. 给出建议。 5. 生成相关问题。 确保回答真实可靠:基于标准嵌入式实践和STM32文档。 最终建议:SPI通信中,挽输出更合适;上电阻在SPI中通常不需要,但如果使用,挽输出仍然首选。 生成相关问题:基于主题。</think>### SPI通信中上电阻与输出模式的选择 在SPI(Serial Peripheral Interface)通信中,输出模式的选择(挽输出或开漏输出)与上电阻的配合是关键配置问题。SPI是一种全双工、同步串行通信协议,常用于微控制器与外设(如传感器、存储器)的高速数据传输[^3]。输出模式影响信号的驱动能力、功耗和抗干扰性,而上电阻则用于确保信号在空闲状态或未驱动时保持稳定高电平。以下我将逐步分析您的疑问。 #### 1. **挽输出与开漏输出的基本区别** - **挽输出 (Push-Pull)**: 在这种模式下,GPIO引脚能主动驱动高电平和低电平。内部电路通过MOSFET管直接高或低信号,提供强驱动能力(例如,最大电流可达25mA)。优点包括高速切换(适用于SPI的50MHz高速模式)和低延迟,适合点对点通信[^5]。缺点是在多设备共享总线时可能引起冲突(如多个主机同时驱动信号)。 - 数学表达式示例:输出电平变化可建模为$V_{\text{out}} = V_{\text{DD}}$(高电平)或$V_{\text{out}} = 0$(低电平),其中$V_{\text{DD}}$是电源电压。 - **开漏输出 (Open-Drain)**: 此模式只能主动低信号(低电平),高电平需依赖外部上电阻提供。优点是多设备总线中避免冲突(如I2C的仲裁机制),但驱动能力较弱,切换速度较慢,可能增加功耗[^1]。 - 数学表达式示例:高电平状态由外部电阻决定,$V_{\text{out}} = V_{\text{DD}} - I \times R_{\text{pullup}}$,其中$I$是电流,$R_{\text{pullup}}$是上电阻值。 #### 2. **SPI通信中上电阻的作用** - 上电阻在SPI中通常**不是必需的**,因为SPI是全双工点对点通信,没有总线仲裁需求(不同于I2C)。但在以下场景可能添加: - **长距离传输**:减少噪声干扰,确保信号完整性(例如,RS485通信中常用上增强抗扰性[^2])。 - **多从设备共享MOSI/MISO线**:防止信号浮空(floating),但SPI通常通过片选(CS)引脚隔离设备。 - **特定硬件要求**:某些外设可能要求上以保证兼容性。 - 上电阻值通常在4.7kΩ到10kΩ之间,需根据总线电容和速度计算,避免信号延迟(例如,高速SPI中电阻值过大会导致上升时间变长)。 #### 3. **配合上电阻的输出模式选择** - **挽输出更合适**: - **理由**:SPI通信需要高速(可达50MHz)和低延迟切换,挽输出能主动驱动高/低电平,提供更好的信号完整性和时序精度。即使添加了上电阻,挽模式也能减少上升/下降时间,提升性能[^3][^5]。 - **典型配置**:在STM32中,SPI引脚(如SCK、MOSI)通常配置为挽输出(`GPIO_MODE_AF_PP`),速度设为50MHz(`GPIO_Speed_50MHz`)。上电阻仅在特殊需求下添加,例如在高噪声环境中增强高电平稳定性。 - **风险提示**:如果误用开漏输出,可能导致: - 速度下降:上电阻会增加上升时间,限制SPI的最大波特率。 - 功耗增加:上电阻在低电平状态产生持续电流。 - **开漏输出的适用场景**: 在SPI中**一般不荐**,除非: - 多主机共享总线(罕见,SPI本质是单主机)。 - 兼容5V外设(开漏模式可避免电平冲突)。 - 但即使添加上电阻,开漏模式在SPI中性能较差,易导致通信错误[^1]。 #### 4. **荐配置** - **最佳实践**: - 优先使用**挽输出**,无需额外上电阻(除非环境特殊)。 - 在STM32配置中,参考引用[^3]的SPI初始化代码: ```c // 示例:SPI GPIO配置(挽输出) GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用挽输出 GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上(默认) GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 50MHz速度 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 假设SPI引脚在GPIOA ``` - 如果必须添加上电阻(例如,抗干扰需求),保持挽输出模式,并在硬件设计中添加外部电阻(4.7kΩ)。避免在软件中启用内部上(`GPIO_PULLUP`),以免冲突[^5]。 - **验证步骤**: - 使用示波器检查信号波形:挽输出应有干净的方波,开漏输出可能显示缓慢上升沿。 - 测试通信速率:挽输出支持更高波特率(如SPI_BAUDRATEPRESCALER_2)。 #### 总结 在SPI通信中,**挽输出配合上电阻更合适**,但上电阻仅在特定场景需要。挽输出提供最优性能,而上电阻作为辅助措施增强可靠性。始终优先基于硬件需求配置GPIO模式,并参考芯片手册(如STM32参考手册)[^3][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值