首页 > 其他分享 >如何实现一个通知系统?

如何实现一个通知系统?

时间:2024-11-01 14:33:07浏览次数:3  
标签:服务 渠道 通知 系统 用户 发送 如何 处理器

在实际工作中,我们经常会用到通知系统,比如,用户完成在线购买后,需要发送订单确认邮件、支付处理成功的短信以及包裹发货的推送通知。那么,什么是通知系统?如何设计一个通知系统?

需求收集

在设计之前,我们先来详细了解下通知系统的需求,本文从功能需求和非功能需求两个方面来介绍。

功能需求

  • 通知类型:例如消息通知、警告通知、活动通知等。
  • 用户群体:需要通知的用户群体是谁,是否有分组。
  • 通知渠道:例如邮件、短信、推送通知、应用内通知等。
  • 通知频率:通知的发送频率和限流策略。
  • 优先级:不同通知的优先级管理。
  • 用户偏好:用户是否可以自定义接收通知的偏好。
  • 重试机制:处理通知发送失败的情况,必要时重试(如短信或电子邮件发送失败)。

非功能需求

  • 可扩展性:系统应能够每分钟处理数百万条通知,支持数百万并发用户。
  • 高可用性:确保最小的停机时间,即使在故障情况下也能发送通知。
  • 可靠性:保证至少一次的通知传递,对于某些使用场景可能需要保证只有一次传递。
  • 低延迟:通知应尽快发送,以确保及时交付。

容量预估

在深入设计之前,让我们先估算下系统规模以更好地做出设计决策。假设系统服务于 1000万日活用户,每个用户平均每天接收 5条通知。

峰值负载

假设在峰值时间内(如秒杀期间)1分钟内发送 100万条通知,这意味着系统应能够处理:

  • 每天的通知数量:10,000,000 x 5 = 50,000,000条通知
  • 峰值每秒通知数量:1,000,000 / 60 = ~17,000条通知/秒

存储需求

假设每条通知的数据量大小是 1KB,则存储容量评估为:

  • 用户数据存储需求:10,000,000 * 1 KB = 10GB
  • 每日通知存储需求:10,000,000 * 5 * 1 KB = 50GB

High-level 设计

从 High-level 层面来看,通知系统将包括以下组件:

 

1. 通知服务(Notification Service)

  • 通知服务是所有通知请求的入口,无论是来自外部应用程序还是内部系统。它暴露的API可以供各种客户端调用以触发通知。
  • 这些请求可以是发送事务性通知(如密码重置邮件)、促销通知(如折扣优惠)或系统警报(如停机警告)。
  • 每个请求都被验证以确保其包含所有必要的信息,如接收者ID、通知类型、消息内容以及应通过哪些渠道发送通知(电子邮件、短信等)。
  • 对于需要在未来日期或时间发送的通知,通知服务与调度服务(Scheduler Service)集成。
  • 处理请求后,通知服务将通知推送到通知队列(如 Kafka或 RabbitMQ)。

2.用户偏好服务

  • 用户偏好服务允许用户控制如何接收通知。
  • 它存储和检索用户接收不同渠道通知的个人偏好。
  • 服务跟踪用户明确选择加入或退出的通知类型。
  • 例如:用户可以选择退出营销或促销内容。
  • 为防止用户被通知淹没,用户偏好服务对某些类型的通知(尤其是促销消息)实施频率限制。
  • 例如:用户每天只能接收2条促销通知。

3.调度服务

  • 调度服务负责存储和跟踪定时通知——那些需要在特定未来时间发送的通知。
  • 这些可以包括提醒、促销活动或其他不立即发送但必须基于预定时间触发的时间敏感通知。
  • 例如:促销消息可能计划在下周发送。
  • 一旦到达预定时间,调度服务将从其存储中提取通知并将其发送到通知队列。

4. 通知队列

  • 通知队列在通知服务和渠道处理器之间充当缓冲区。
  • 通过将通知请求提交与通知发送解耦,队列使系统能够更有效地扩展,尤其是在高流量期间。
  • 队列系统提供消息传递的保证。
  • 根据使用场景,可以配置为:
    • 至少一次传递:确保每条通知至少发送一次,即使这在罕见情况下会导致重复消息。
    • 只有一次传递:确保每条通知只发送一次,防止重复,同时保持可靠性。

5. 渠道处理器

  • 渠道处理器负责从通知队列中提取通知并通过特定渠道(如电子邮件、短信、推送通知和应用内通知)发送给用户。
  • 通过将通知服务与实际发送解耦,渠道处理器实现了独立扩展和异步处理通知。
  • 这种设置允许每个处理器专注于其指定的渠道,确保可靠的发送,并内置重试机制和高效处理故障。

6. 数据库/存储

  • 数据库/存储层管理大量数据,包括通知内容、用户偏好、定时通知、发送日志和元数据。
  • 系统需要混合存储解决方案来支持不同需求:
    • 事务性数据:使用关系数据库(如 PostgreSQL或 MySQL)存储结构化数据,如通知日志和发送状态。
    • 用户偏好:使用NoSQL数据库(如 MongoDB)存储大量用户特定数据,如偏好和限速。
    • Blob存储:对于包含大附件的通知(如带图片或 PDF的电子邮件),使用 OSS,Amazon S3或类似服务存储这些附件。

Low-level设计

设计完 High-level,我们将进入更详细的 Low-level 设计层面,主要包含以下步骤:

步骤1:通知请求创建

首先,通知系统的调用方(如电商平台、或营销系统等)需要生成通知请求。

请求的消息结构如示例请求:

{
  "requestId": "xxx1",
  "timestamp": "2024-09-18T22:00:00Z",
  "notificationType": "transactional",
  "channels": ["email", "sms", "push"],
  "recipient": {
    "userId": "user1",
    "email": "[email protected]"
  },
  "message": {
    // 消息体
  }
}

步骤2:通知服务接收

当调用方发出请求后,通知服务(通过API网关/负载均衡器)会接收到通知请求。请求经过身份验证和验证,确保其来自授权来源,并包含所有必要信息(接收者、消息、渠道等)。

步骤3:获取用户偏好

通知服务会查询用户的一些偏好服务,这部分带有一些定制化的功能,可以根据实际情况决定是否需要此部分:

  • 偏好的通知渠道(如某些用户可能偏好通过电子邮件接收促销消息,但通过短信接收关键警报)。
  • 选择加入/退出偏好:确保符合用户偏好,如用户选择退出营销邮件。
  • 限速:确保用户没有超过其配置的通知限制(如每天最多3条促销短信)。

步骤4:定时发送

如果通知计划需要在未来的某个时刻(例如:每分钟或基于更细粒度的间隔))发送,通知服务将通知发送到调度服务,后者将通知及其预定发送时间存储在基于时间的数据库或允许基于时间高效查询的 NoSQL数据库中。

调度服务需要定时功能,当到达预定时间时,调度服务将通知发送到通知队列。

步骤5:将通知放入队列

一旦通知服务创建并格式化了所需渠道的消息,它将每个消息放入通知队列系统中的相应主题(如Kafka、RocketMQ等)。

每个渠道(电子邮件、短信、推送等)都有自己的专用主题,确保消息由相关的渠道处理器独立处理。

例如:如果通知需要通过电子邮件、短信和推送发送,通知服务将生成三条消息,每条消息都针对相应的渠道进行定制。

  • 电子邮件消息放入电子邮件主题。
  • 短信消息放入短信主题。
  • 推送通知消息放入推送主题。

这些主题允许每个渠道处理器专注于消费其相关的消息,减少复杂性并提高处理效率。

每条消息包含通知负载、渠道特定信息和元数据(如优先级和重试计数)。

步骤6:渠道特定的消息处理

通知队列存储消息,直到相关的渠道处理器拉取它们进行处理。

每个渠道处理器作为队列的消费者,负责消费自己的消息:

  • 电子邮件处理器从电子邮件主题拉取消息。
  • 短信处理器从短信主题拉取消息。
  • 推送处理器从推送主题拉取消息。
  • 应用内处理器从应用内主题拉取消息。

步骤7:发送通知

每个渠道处理器负责通过指定的渠道发送通知:电子邮件处理器

  • 连接到电子邮件提供商(如SendGrid、Mailgun、Amazon SES)。
  • 发送电子邮件,确保其符合用户偏好(如HTML或纯文本)。
  • 处理错误如退信或无效的电子邮件地址。

短信处理器

  • 连接到短信提供商(如Twilio、Nexmo)。
  • 发送短信,并进行任何格式调整以满足字符限制或区域要求。
  • 处理问题如无效的电话号码或网络错误。

推送通知处理器

  • 使用服务如Firebase Cloud Messaging(FCM)用于Android或Apple Push Notification Service(APNs)用于iOS。
  • 发送推送通知,包括任何元数据(如应用程序特定的操作或图标)。
  • 处理失败如过期的设备令牌或离线设备。

应用内通知处理器

  • 通过WebSockets或长轮询将应用内通知发送到用户的活动会话。
  • 格式化消息以在应用程序的UI中显示,遵循任何应用程序特定的显示规则。

步骤8:监控和发送确认

每个渠道处理器等待来自外部提供商的确认:

  • 成功:消息已发送。
  • 失败:消息发送失败(如网络问题、无效地址)。

渠道处理器将每条通知的状态记录在通知日志表中,以供将来参考、审核和报告。

关键问题和瓶颈

故障和重试

如果通知发送由于临时问题(如第三方提供商停机)而失败,渠道处理器将尝试重发通知。

通常使用指数退避策略,每次重试的延迟时间逐渐增加。

如果通知在设定次数的重试后仍未发送成功,则将其移动到死信队列(DLQ)以进一步处理。

管理员可以手动审核和重新处理死信队列中的消息。

可扩展性

水平扩展

系统应设计为水平扩展,意味着组件可以通过增加实例来应对负载增加。

  • 通知服务:随着请求量的增加,可以部署更多实例来管理增加的通知请求量。
  • 通知队列:分布式队列系统(如Kafka或RabbitMQ)天然具有可扩展性,可以通过将队列分布在多个节点上来处理更大的工作量。
  • 渠道处理器:每个处理器(电子邮件、短信等)应水平扩展以处理大量通知。

分片和分区

为了高效处理大量数据,特别是用户数据和通知日志,分片和分区将负载分布在多个数据库或地理区域:

  • 基于用户的分片:根据地理位置或用户ID将用户分布在不同的数据库或区域,以平衡负载。
  • 基于时间的分区:将通知日志组织成基于时间的分区(如每日或每月),以提高查询性能并管理大量历史数据。

缓存

使用Redis或Memcached等解决方案实现缓存,以存储频繁访问的数据,如用户偏好。

缓存减少数据库负载,并通过避免重复的数据库查询来提高实时通知的响应时间。

可靠性

为了高可用性,数据(如用户偏好、日志)应在多个数据中心或区域之间复制。这确保即使一个区域故障,数据在其他地方仍然可用。

  • 多AZ复制:在多个可用区存储数据,以提供冗余。

使用负载均衡器将传入流量均匀分布在通知服务的各个实例之间,确保没有单个实例成为瓶颈。

监控和日志记录

为了确保系统在大规模下的平稳运行,系统应具备:

  • 集中式日志记录:使用ELK Stack或Prometheus/Grafana等工具收集各种组件的日志并监控系统健康。
  • 警报:设置警报以监控故障(如通知发送失败率超过阈值)。
  • 指标:跟踪每个渠道的成功率、失败率、发送延迟和吞吐量等指标。

安全性

对所有传入通知服务的请求实施强认证(如OAuth 2.0)。使用基于角色的访问控制(RBAC)限制对关键服务的访问。

通过在API网关上实施速率限制保护服务免受滥用,防止DoS攻击。

归档旧数据

由于通知系统随着时间的推移会处理大量数据,实施归档旧数据的策略非常重要。

归档涉及将过时或不常访问的数据(如旧的发送日志、通知内容和用户历史记录)从主存储移动到成本较低、长期存储解决方案。

这样可以减少主存储的负载并提高系统的整体性能。

标签:服务,渠道,通知,系统,用户,发送,如何,处理器
From: https://www.cnblogs.com/azwz/p/18520098

相关文章

  • ubuntu系统升级
    sudoaptupdatesudoaptupgrade-ysudoaptdist-upgrade-y安装更新管理器核心包sudoaptinstallupdate-manager-core-y编辑/etc/update-manager/release-upgrades文件,将Prompt设置为ltssudonano/etc/update-manager/release-upgrades找到以下行并修改:Pr......
  • AP5165B 系统上电后通过 VIN 脚对芯片供电,当 VIN 电压高于 2.5V 后,芯片电路开始工作。
    产品描述AP5165B是一款外围电路简单的连续电流模式的降压型LED恒流驱动芯片。在输入电压高于LED电压时,可以有效地用于驱动一颗或者多颗串联LED。输出电流可调,大可达1A。适用于3-36V电压范围的非隔离式恒流LED驱动领域。AP5165B内置功率开关和一个高端电流检测电路,可以......
  • AP5165B 系统上电后通过 VIN 脚对芯片供电,当 VIN 电压高于 2.5V 后,芯片电路开始工作。
    产品描述AP5165B是一款外围电路简单的连续电流模式的降压型LED恒流驱动芯片。在输入电压高于LED电压时,可以有效地用于驱动一颗或者多颗串联LED。输出电流可调,最大可达1A。适用于3-36V电压范围的非隔离式恒流LED驱动领域。AP5165B内置功率开关和一个高端电流检测电路,可......
  • 安卓APP开发中,如何使用加密芯片?
    加密芯片是一种专门设计用于保护信息安全的硬件设备,它通过内置的加密算法对数据进行加密和解密,以防止敏感数据被窃取或篡改。如下图HD-RK3568-IOT工控板,搭载ATSHA204A加密芯片,常用于有安全防护要求的工商业场景,下文将为大家介绍安卓APP开发中,如何使用此类加密芯片。1. Android......
  • 看CG-FS-M2 金属风速传感器 如何在项目中运用
    产品概述采用电路模块技术开发变送器,操作简单,使用方便,用于实现对环境风速的测量,根据需求可输出脉冲信号,0.4-2V电压或4-20mA电流信号,RS485信号。功能特点◆体积小,携带方便、安装简捷◆测量精度高,量程宽,稳定性好◆结构设计合理,外观质量佳◆数据信息线性度好,信号传输距离长......
  • 微信支付商户系统Native支付
    简易demo演示demo演示点击体验###Native支付介绍目前微信支付有以下几种场景*JSAPi支付,适合微信公众号及微信小程序*APP支付*H5支付*Native支付,适合PC网站页面支付[微信支付商户平台](https://pay.weixin.qq.com/)[微信支付Native接口文档](https://pay.weixin.q......
  • 如何使用WebSockets在网页应用中实现实时通信
    摘要:实现网页应用中的实时通信,1、选择合适的WebSockets库以简化实施过程;2、在服务器端与客户端建立WebSocket连接;3、设计有效的消息协议;4、确保通信安全性;5、处理网络问题和重连机制。其中选择合适的WebSockets库是基础。它能够帮助开发者快速构建实时通信功能,如Socket.IO、Web......
  • 在K8S中,集群服务暴露失败 如何解决?
    在Kubernetes(K8S)中,集群服务暴露失败可能由多种原因引起。为了解决这个问题,可以按照以下步骤进行详细的排查和解决:1.检查服务是否存在首先,需要确认要暴露的服务是否已经存在。使用kubectlgetservices命令查看当前命名空间下的所有服务,确认目标服务是否在其中。如果服务不存在,......
  • 在K8S中,集群服务访问失败 如何解决?
    在Kubernetes(K8S)中,集群服务访问失败是一个常见的问题,可能由多种原因引起。下面是一些排查和解决问题的步骤,可以帮助你定位并解决服务访问失败的问题。1.检查服务定义首先,确保你的服务定义是正确的。检查服务的YAML文件,确认selector标签与后端Pod的标签匹配。如果服务定义有......
  • 如何开始漏洞赏金计划并成为漏洞挖掘猎手
    OzgurAlp:10年经验丰富的进攻性网络安全专家、讲师和讲师,持有OSCP、OSWE和CEH证书。获得Synack颁发的2019|2020|2021SRT总冠军、2021年最受信任黑客、2021年度SRT和2022年度导师等奖项和冠军。正文包括漏洞挖掘领域在内的任何领域,都不存在唯一真正的成功方......