<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>后端项目 on Tequila's 学习笔记</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/</link><description>Recent content in 后端项目 on Tequila's 学习笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 Tequila</copyright><atom:link href="https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/index.xml" rel="self" type="application/rss+xml"/><item><title>定时推送</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-b25f8d65bd2d44bb/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-b25f8d65bd2d44bb/</guid><description>核心思路：​
定时临时缓冲区：预设一个定时消息临时缓冲区，所有定时消息先在临时缓冲区暂存，做定时处理。等定时缓冲区的消息到点之后再捞出。​ 到点消息变为普通消息：捞出的【到点消息】将其变为一条【普通消息】，进行后续推送流程。此流程与直接推送普通消息一致。 字段 类型 约束/默认值 说明 id bigint(20) 主键，自增 ID msg_id varchar(256) 非空，唯一索引（idx_msgid） 消息ID req varchar(4096) 非空 请求内容 send_timestamp bigint(10) 定时发送时间 status int(10) 状态 create_time datetime 非空，默认 CURRENT_TIMESTAMP 创建时间 modify_time datetime 非空，默认 CURRENT_TIMESTAMP，更新时自动刷新 修改时间 Redis选型设计 # 存储介质选型上选择使用 Redis ZSet，以定时任务执行时间为 Score 进行有序结构的搭建，当定时任务数量达到一定量级时，ZSet 底层基于跳表作为有序表的实现. 一些更细致的实现流程如下：​
（1）以 Redis ZSet 作为存储介质；​ （2）每次添加定时任务时，执行 ZAdd 动作，以执行时间的【时间戳】为排序的键(Score) 进行有序结构的搭建；同时元素的值我们也用时间戳，这样保证同一时间点只会存储一份信息。</description></item><item><title>额度限制</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-40c960f34fa9298c/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-40c960f34fa9298c/</guid><description>全局配额表（t_global_quota）
字段 类型 说明 id bigint(20) 主键自增ID channel int(10) 消息类型（1: 邮件；2: 短信；3: 飞书） num int(10) 限频数（单位：秒） unit int(10) 限频单位（毫秒）1000=1秒，60000=1分钟 业务配额表（t_source_quota）
记录某个业务可发送消息的频率，比如某些平台短信一分钟只能触发一次
字段 类型 说明 id bigint(20) 主键自增ID source_id varchar(256) 业务ID channel int(10) 消息类型（1: 邮件；2: 短信；3: 飞书） num int(10) 限频数 unit int(10) 限频单位（毫秒）1000=1秒，60000=1分钟 用Redis实现限额器 # Redis 是一个高性能的分布式缓存和存储系统，非常适合实现分钟级/秒级额度限制。​
实现步骤​
定义 Redis 的计数器，键名格式：
rate_limit:{业务ID}:{channel}:{时间戳}
如 rate_limit:source123:2:202412071015 ​ 请求时：​
使用 Redis 的 INCR 操作递增计数器。​
检查计数器是否为1，是的话说明新创建，设置过期时间，比如60s，具体多少取决于传入的参数​
检查计数器值是否超过限制。​
如果超过限制，返回错误；否则允许请求。</description></item><item><title>数据库</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-6925429b0eac12ff/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-6925429b0eac12ff/</guid><description>关系型数据库普遍具备如下优点：​
1.可靠性强，数据是持久化到磁盘，没有丢失数据的风险。​
2.结构规范清晰，每一列数据都有格式和长度规范，一个表中每一行数据的属性都是相同的​
3.支持事务，很多时候多个操作希望要么成功、要么失败，比如我们扣减库存和记录库存扣减事件这两个操作，我们不希望只有一个成功，而是希望他们成对出现。</description></item><item><title>消息队列</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-ca5ebd88c91eca69/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-ca5ebd88c91eca69/</guid><description>为什么要使用消息队列 # 提高成功率，第三方推送平台不一定稳定，交互过程可能会使得，通过消息中转站可以很方便的实现异步重试，提高成功率 实现流量管控：削峰 能够更好的协调资源，让重要消息优先享受服务 消息队列的作用 # 异步 # 消息分发 # 削峰 # 为什么要用Kafka # 从性能上讲，RocketMQ、Kafka达到10万级，相比其它万级消息队列会更高一些，当然对于我们而言万级也够用，因为实际瓶颈在第三方推送；​ 作为消息推送中台，还是希望数据是可靠的，所以从可用性而言，RocketMQ、Kafka更符合我们的需求，因为它们都是分布式的；​ 从主题数目而言，RabbitMQ、RocketMQ在大量topic下具备一些优势，但Kafka也支持上百个topic，在消息推送场景都能满足需要。 主要还是看团队的技术栈，这里我们因为熟悉程度选用了Kafka，如果面试问到，可以说之前团队，普遍都是用Kafka，已有完整的实践和运维经验，故而使用Kafka。</description></item><item><title>消息模板</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-0a618ad08fb2cb61/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-0a618ad08fb2cb61/</guid><description>1.抽象内容信息，支持模板中插入动态数据（例如 {{username}}），即动态替换占位符生成完整的消息内容。​ 2.抽象渠道信息，比如支持短信、邮件等​ 3.提供模板管理能力，即提供模板的创建、修改、删除和查询功能。
text/template：用于生成纯文本内容（如配置文件、代码）
模板表(t_msg_template)
字段 类型 描述 id bigint 模板唯一标识 source_id string 业务ID name varchar(256) 模板名称 template_id varchar(256) 模板ID rel_template_id varchar(256) 关联模板ID（对接第三方平台模板时使用） content varchar(4096) 模板内容（支持占位符） subject varchar(256) 主题 sign_name varchar(256) 签名（短信渠道需要） channel int(10) 推送渠道（1: 邮件；2: 短信；3: 飞书） status tinyint(4) 状态（1: 等待审核；2: 正常） create_time datetime 创建时间 modify_time datetime 修改时间 RESTful 接口设计​ 创建模板：POST /api/templates​ 获取模板：GET /api/templates/{id}​ 更新模板：POST /api/templates/{id}​ 删除模板：DELETE /api/templates/{id}</description></item><item><title>优先级</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-6e5b6671d3525ce3/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-6e5b6671d3525ce3/</guid><description>为什么需要支持优先级 # 关键消息的优先处理：比如交易通知、系统告警等，要求在秒级时间内送达，又比如用户更关心紧急消息，优先处理能显著提升满意度。​ 避免低优消息堵住通道：平台资源有限，通过优先级调度，避免低优先级任务占用过多资源，在高峰期，保证高优先级消息能通畅，降低对整体服务的影响。​ 提升大客户体验感：比如一个业务90%的营收都由几个大客户支撑，那么为他们提供优先推送，保证他们的体验感，对业务而言就是必要的。 如何实现优先级 # 第一种，队列级别划分​
也就是为不同的消息类型分配优先级（例如高、中、低），比如重要消息，就投递到高优队列，注意重要消息一定是比较少的，不然都丢入高优队列，就等于都不重要了。​
消费消息时，低优队列和高优队列有专门的调度资源，就好像经济舱和头等舱，各自有不同的服务团队，各自提供独立机厢的服务。当然，也可以按时间调度，在一定时间内，按比例调度各级队列。例如：高优先级 = 70%，中优先级 = 20%，低优先级 = 10%。
第二种，放入有序的存储​
比如堆、有序链表这种有序数据结构，当然在分布式时代，一般我们存储都是组件式的，比如我们很多时候将数据存入MySQL，而MySQL的索引，底层是B+树结构，实际是有顺序的，可以设计按优先级排序，按优先级拉取即可。</description></item><item><title>中转站-数据库</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-eb29ee20679ea5ff/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-eb29ee20679ea5ff/</guid><description> 字段 类型 说明 id bigint(20) 主键自增ID msg_id varchar(256) 消息id，通过uuid生成 to varchar(256) 消息发给谁 subject string 消息主题 channel int(10) 消息类型：1邮件；2短信；3飞书 template_id varchar(256) 模板唯一ID，使用模板时有效 template_data varchar(256) 模板传入参数 status tinyint(3) 状态 create_time datetime 创建时间 modify_time datetime 修改时间 使用三张表，模拟高中低三个消息队列，即t_msg_queue_low、t_msg_queue_middle、t_msg_queue_high，对应优先级的消息进入对应的消息队列表，整体思路无非是普通消息进入低优队列，比较重要的消息进入中优队列，十分要紧的消息进入高优，本质就是进入高优的消息会远远少于低优，且高优拥有独立的资源，这个类比下高铁座位应该很好理解。
相比于消息队列，用MySQL做中转，有如下优缺点：​
优点1：更少的组件依赖，维护成本低​ 缺点1：吞吐没有消息队列高，消息创建速度超过5000/s时则不适用​ 缺点2: 消费者扩展性没有消息队列强，消息队列如Kafka天然分片，可以并发拉取消息，而MySQL则不行，如果做分片和管理对应调度关系，又是非常重的成本。好在大多数情况瓶颈都不在于拉取消息，一般在于第三方能力限制。</description></item><item><title>重试</title><link>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-019acef2a1e1bbc7/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://latnx.github.io/docs/notes/d-79f326be4409d51f/d-e34e602b75abb796/n-019acef2a1e1bbc7/</guid><description>快速重试：
异步重试 另一种思路是通过任务队列实现异步重试，避免长时间阻塞主业务线程。简单来说，我们用消息队列的话，就是直接扔到队尾（也就是重新投递进消息队列），这样可以自动重试。
重试是提高成功率的有效手段，在我们的系统中，我们同时启用快速重试+异步重试，快速重试是期待快速解决问题，异步重试是为了能过一段时间再重试。</description></item></channel></rss>