Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 484, comments - 13, trackbacks - 0, articles - 1

2025年1月10日

本文由美团安全研究员乔丹分享,作者硕士毕业于复旦大学,目前在美团致力于云原生安全建设。原题“浅谈硬编码密码及其扫描工具”,下文进行了排版和内容优化。

1、引言

密码是对服务、系统和数据的访问权限进行授权的数字身份凭证,常见的密码有API密钥、非对称私钥、访问Token等。硬编码密码(Hardcoded Secret),或称嵌入式密码(Embedded Secret),是指将密码以明文方式直接写入代码中。这种处理方式极大地提高了攻击者命中密码的概率,使服务或系统暴露在风险中,容易造成严重损失。

针对此问题,本文详细讨论了硬编码密码的成因、危害及治理方法,同时本文从安全人员的角度出发,对现有的硬编码密码检测工具的算法进行了深入调研,并提出了我们的自动化检测工具。

 
 

2、系列文章

本文是IM通讯安全知识系列文章中的第15篇,此系列总目录如下:

3、为什么会用硬编码密码

随着互联网组织转向云架构、SaaS 平台和微服务,密码等数字身份验证凭证的数量和多样性正在快速增长。与此同时,企业也不断推动更短的发布周期,开发人员面临巨大时间压力的同时,需要处理的密码量比以往任何时候都多。许多开发人员采取捷径,选择使用硬编码的方式处理密码。

在企业的代码仓库中普遍存在大量的硬编码密码问题。据GitGuardian统计,在公共Git存储库上每天会泄露数以千计的密码,其中仅2020年就有超过200万个密码被上传至Git存储库中,而2021年该组织发现的密码数量超过600万,同比增长近2倍,而私人存储库的密码泄露事件存在可能性比公共库高4倍。

根据统计,硬编码密码包括API密钥、访问Token、非对称私钥、认证ID、安全证书、口令、特权用户账户等类型。

硬编码密码所涉及的平台十分广泛,包括如下领域:

  • 1)开发工具,如Django、Rapid API;
  • 2)数据存储,如MySQL、Mongo;
  • 3)金融服务,如PayPal、Amazon MWS;
  • 4)消息通讯系统,如Gmail、Telegram;
  • 5)云提供商,如AWS、Azure、Google;私钥;
  • 6)社交媒体,如Twitter、Facebook;
  • 7)版本控制平台,如Github、Gitlab 等等。

除了程序代码中,这些硬编码还容易出现在基础设施配置文件、监控日志、运行日志、堆栈调试track记录、git历史中。所有类别的硬编码密码都使企业暴露在攻击之下。

4、硬编码密码的典型危害

硬编码密码主要对安全和研发两方面具有危害。

1) 削弱系统安全性:

攻击者常通过公共代码库或反编译分析获得硬编码密码字符串,利用密码访问敏感数据或获取敏感操作权限。攻击者还可以进一步扩大攻击范围,进行数据勒索、帐户操纵、帐户创建、通过用户数据进行利用等,使得企业和用户都遭受严重损失。在以下案例中,攻击均是从密码的泄露开始的:2014年,Uber数据库被未经授权访问,导致数千名Uber司机私人信息的数据被泄露;2016年,Uber又因外部的未授权访问导致5700万用户的个人信息被泄露;2018年,Github和Twitter在内部日志系统中以明文方式存储密码,分别涉及2700万和3.3亿用户数据泄露;2020年,用户在Github仓库中发现了星巴克的API密钥,涉及重大信息泄露;2021年,黑客组织 Sakura Samurai 在一次重大数据泄露事件中获得了访问联合国 (UN) 员工私人数据和系统的权限……由硬编码密码导致的安全事故层出不穷,也不断有相关CVE和CWE被披露。

硬编码密码对特定设备、固件、服务、应用程序本身,对其连接的IT生态系统其他部分,甚至使用服务的第三方都存在风险,使其同样暴露在风险中。

2)不易于程序维护:

硬编码密码的修复较为困难,密码一旦被利用无法轻易被修正。对于正在线上运行的服务或系统,修复硬编码密码问题需要停服重新发布。大型企业的服务流量较大,服务间还存在依赖,则需要灰度发布,修复流程更长,其间可能持续受到攻击者威胁。

密码的蔓延也使维护变得困难。与传统凭证不同,密码旨在分发给开发人员、应用程序和基础设施系统,这将不可避免地使开发中使用的密码数量增加,一个密码可能出现在代码中多处位置,这进一步增加了修复的难度。

此外,开源的代码造成密码泄露,即使在源码中删除硬编码密码,也会残留在git历史里。

5、怎样避免硬编码密码

企业代码中的硬编码密码问题日益严重,只有通过安全人员和研发人员的共同协作才能解决。源代码中的密码泄露很难彻底避免,但与其他漏洞一样,它完全由内生因素决定:开发人员需要访问更多的资源,以更快的速度构建和部署。这意味着只要有足够的纪律和教育,再加上正确的工具,就有可能大幅改善这种情况。

从开发人员角度:需要注意尽量避免将密码以明文形式写入代码中。代码中需要对密码进行校验时,对入站身份验证可使用强单向散列函数进行密码模糊化,并将这些散列结果存储在具有适当访问控制的配置文件或数据库中;对出站身份验证,可将密码存储在代码之外的一个经过严格保护的、加密的配置文件或数据库中,该配置文件或数据库不会被所有外部人员访问,包括同一系统上的其他本地用户;大型企业可以使用KMS服务进行一站式密码管理。

从安全人员角度:应尽量做到风险左移,尽早发现密码泄露,帮助开发人员降低修复成本。可通过代码检测扫描,将硬编码密码检测集成到开发工作流程中,提前发现硬编码密码问题。

6、硬编码密码的典型检测方法

由于硬编码密码有如此的危险性,学术界和工业界都有许多组织针对此问题研发了代码扫描工具。我们对开源工具和学术文章进行了一系列调研,总结了目前的硬编码密码扫描工具常用的检测算法,并对其优缺点进行了讨论。

6.1 正则表达式匹配

正则表达式通常被用来检索符合某种模式的字符串。对于检测具有固定结构或特征的密码,正则表达式可能很有效。

常用于密码检测的正则表达式可分为:

  • 1)针对各种特定平台密码的表达式;
  • 2)不针对任何平台的通用表达式。

1)针对各种特定平台密码的表达式:

许多平台的API密钥、访问Token、认证ID等具有平台独有的特征,例如亚马逊AWS密钥均以“AKIA”字符串开头;常用于非对称加密的私钥如RSA、EC、PGP及通用私钥等,常由ssh-keygen、openssl等工具生成,多数情况下私钥以单独的PEM等文件格式存储,其内容也具有一定特征,例如RSA私钥文件由"-----BEGIN RSA PRIVATE KEY-----"字符串作为开头。对于这类密码,可以通过匹配具有其特征的正则表达式进行检测。

下表列举了部分常用平台密码的类型以及正则表达式。本文仅以此表举例,实际上特定平台的密码种类十分丰富,此处不便一一列举。

2)不针对特定平台的通用的表达式:

由于特定平台表达式和平台的一一对应性质,其覆盖范围有限,此时需要用覆盖范围较广的通用表达式来补充。许多平台的密码具有一些通用的特征,例如密码字符串以api_key、access_token等关键字为开头。

此外,根据开发人员的编程命名习惯规范,也可以根据变量名中的关键字进行匹配,例如变量名中含有Secret、Token等关键字的字符串很可能是密码。

优点:

  • 1)配置简单;
  • 2)自定义扩展方便。

缺点:

  • 1)正则表达式覆盖范围不够广则容易漏报;
  • 2)使用一组不准确的正则表达式容易出现大量误报;
  • 3)即使是正确的正则表达式也有一定程度的误报,例如“AKIAXXXEXAMPLEKEYXXX”虽然符合亚马逊AWS的正则表达式,但并不是有效的密码;
  • 4)通用表达式中使用变量名关键字匹配的检测方法容易被对抗。

6.2 熵字符串编码检测

在信息论概念中,熵是对不确定性的量度,越随机的数据的熵越高。大多数API密钥、访问Token等密码字符串具有高度熵的特性,因此可以通过搜索高熵字符串来检测密码。这种算法被以TruffleHog为代表的工具所采用。现有的工具一般采用香农熵算法来计算字符串熵值,对字符串A的香农熵值计算公式如下图所示,其中pi表示第i个字符出现的频率。

1)优点:

  • 1)能够检测出无明显特征的密码,对于正则表达式未覆盖到的范围有补充效果;
  • 2)可用于对正则表达式检测结果的验证,如上文提到的正则表达式误报字符串“AKIAXXXEXAMPLEKEYXXX”,其熵值较低,可通过验证筛除。

2)缺点:

  • 1)字符串被判定为密码的熵阈值难以确定,阈值过高容易漏报,阈值过低又容易误报。即使是学术论文中的阈值也全凭实验经验确定,缺乏坚实的理论支撑;
  • 2)一些高熵值的SHA、MD5等字符串容易被误报为密码。对于这一问题,可通过过滤SHA、MD5值出现较密集的文件扩展名来降低误报,例如.lock, .inc文件;
  • 3)容易将具有明显升降序的字符串误报为密码。香农熵只对出现频率进行计算,不考虑字符顺序,具有明显升降序的字符串同样会表现出较高熵值,例如Hex编码的“123456789abcdef”和“d9b41a72f683ce5”两字符串香农熵相等,但前者一般不会是一个有效的密码。对于这一问题,需要通过一些启发式处理方式降低升降序字符串的熵值,或通过后期过滤筛除。

7、美团的硬编码密码检测工具研发实践

为了保障美团整体研发环境安全,同时节约安全人员的审计成本,我们研发了针对硬编码密码的代码扫描工具。我们认为在众多字符串中寻找密码如同在沙里淘金,因此将工具命名为Gold-digger。

7.1 工具设计

为服务于全公司研发环境,Gold-digger工具有如下需求:

  • 1)编程语言无关:公司各业务使用的编程语言不同,Gold-digger需要无障碍地应用于所有编程语言代码中;
  • 2)模块化,方便扩展迭代:为了根据测试反馈结果不断提高效果,Gold-digger需要长期不断迭代;
  • 3)能够集成到软件开发生命周期中:Gold-digger侧重预防,需要工具集成在CI/CD管道中,从源头遏制密码泄露风险;
  • 4)高精确率召回率:Gold-digger的设计初衷之一是节约人力成本,为降低审计、维护和运营压力,必需尽可能准确、全面地检测密码。

基于上述需求,Gold-digger的架构主要分为四个模块:核心引擎、转换器、检测器、过滤器。

Gold-digger工作流程如下图,箭头表示数据流向。核心引擎依次读取代码仓库中文件,经过预验证和输入处理后将代码以行为单位传输给检测器,其中部分特殊格式由转换器处理后再传输给检测器;检测器在代码中检测密码候选值;过滤器对密码候选值进行后过滤,将过滤后的密码传回核心引擎;最后核心引擎将代码仓库中所有密码进行收集后,将密码相关信息输出为可读性较强的JSON文件报告。

1)核心引擎:

该模块为Gold-digger赋能,负责调度其他模块,也是负责输入输出处理、数据收集存储等。输入处理部分负责读取代码文件,先调用过滤器对文件后缀进行全局预验证,再通过引号标识匹配或调用转换器识别代码中的字符串及其赋值变量;然后核心引擎调用调用检测器和过滤器进行密码收集,将检测到的密码数据以文件为单位存入不同集合,能够方便地对集合进行合并、删减等操作;输出处理部分将仓库中所有密码关键信息,如密码值、文件位置、检测算法等,输出为JSON格式。

2)转换器:

该模块是专为部分特殊格式文件进行格式转换的处理器。尽管核心引擎能处理大部分代码格式,但无法处理.yaml、.ini、.properties等不使用引号作为字符串特征标识的格式。为保证语言无关性,我们使用转换器处理上述特殊格式,将其转换为用引号标识字符串的代码。这种解析方式无需为不同语言分析抽象语法树,能够有效节省算力。

3)检测器:

该模块是Gold-digger的密码检测算法模块。我们在综合调研同类型检测工具后,汲取了各方优点,采用了正则表达式匹配和熵字符串检测两类算法。Gold-digger的检测器包含数十种特定平台正则表达式、通用正则表达式以及Hex编码、Base64编码的熵字符串检测算法。检测器中每种算法以插件方式各自独立,方便扩展、启用或禁用,同时Gold-digger也允许用户自定义检测算法插件。代码将遍历所有检测算法,任一算法命中便记录为密码候选值。

4)过滤器:

该模块为Gold-digger的验证、过滤模块。预验证部分在检测器运行前对文件格式进行验证,筛除压缩文件、多媒体文件等非文本类型;后过滤部分在检测器运行后对所有密码候选值进行启发式过滤,并将过滤后的密码传回核心引擎。后过滤过程中每项密码候选值将遍历所有过滤规则,所有规则都未能筛除的候选值会被记录在密码集合中(检测器和过滤器的主要处理流程如下图所示)。过滤规则主要为开发测试人员凭经验总结的启发式规则,例如:过滤升降序字符串、过滤高度重复字符串、过滤uuid、过滤间接引用赋值等。

5)CI/CD集成:

Gold-digger的最终目标是消除代码中的密码泄露问题,因此检测到密码并不是最后一步,修复才是最后一步。我们把Gold-digger集成到公司研发环境CI/CD管道中,方便开发人员根据密码报告及时修复漏洞,从源头遏制风险。当开发人员向公共存储库提交代码后,Gold-digger会进行on-push扫描,对包含密码的提交进行拦截和告警。此外,我们还允许开发人员在本地使用Gold-digger,进行pre-commit或pre-push检查,整体风险左移。

7.2 数据对比

我们使用Gold-digger与最先进的开源工具进行了对比。

我们对内部服务代码进行了分析,将人工安全审计发现的密码作为测试基准,使用工具对代码进行测试并统计结果。下表结果显示,我们的工具准确率和召回率高于其他所有开源工具,误报率和漏报率低于其他所有开源工具。

分析显示,Gold-digger检测的密码中大部分是通过通用正则表达式和熵字符串检测获得的。这是由于内部代码中包含的密码大多无明显前缀后缀特征,特定平台表达式检测不到。正因如此,大部分工具尽管定义了大量的特定平台的正则表达式但漏报率仍很高,例如trufflehog定义了700多种特定平台正则表达式,但通用正则表达式种类较少,故对特定平台表达式未覆盖到部分的检测能力较弱。Gold-digger可以利用通用正则表达式和熵字符串检测进行弥补,有效降低漏报。

密码检测的一大难点是避免来自非密码字符串的误报。Gold-digger通过多种启发式规则的过滤得到了较低的误报率。其他工具大量误报的主要原因则是正则表达式的匹配范围太宽泛又缺乏有效过滤手段,例如Gitleaks通过通用正则表达式识别到大量密码候选值,但其中既有真正的密码,又有appkey name、间接引用等,但未进行筛除。

8、本文小结

随着互联网组织架构的高速发展和软件发布周期的不断缩短,硬编码密码问题在企业代码仓库中日益严重,其危害已通过多起严重安全事故显示出来。硬编码密码的大规模治理必需由安全人员和研发人员共同合作。

美团为保障研发安全,设计了具有编程语言无关、模块化架构、集成在CI/CD管道等特点的硬编码密码的扫描工具Gold-digger。该工具的效果优于目前所有开源工具,能够有效帮助开发人员尽早发现并修复密码泄露,从源头保障研发安全。

9、相关资料

[1] Over two million corporate secrets detected on public GitHub in 2020

[2] 传输层安全协议SSL/TLS的Java平台实现简介和Demo演示

[3] 理论联系实际:一套典型的IM通信协议设计详解(含安全层设计)

[4] 微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解

[5] 来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享

[6] 简述实时音视频聊天中端到端加密(E2EE)的工作原理

[7] 移动端安全通信的利器——端到端加密(E2EE)技术详解

[8] Web端即时通讯安全:跨站点WebSocket劫持漏洞详解(含示例代码)

[9] 通俗易懂:一篇掌握即时通讯的消息传输安全原理

[10] IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

[11] 快速读懂量子通信、量子加密技术

[12] 一分钟理解 HTTPS 到底解决了什么问题

[13] 一篇读懂HTTPS:加密原理、安全逻辑、数字证书等

[14] 基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等

[15] 手把手教你为基于Netty的IM生成自签名SSL/TLS证书

[16] 微信技术分享:揭秘微信后台安全特征数据仓库的架构设计


(本文已同步发布于:http://www.52im.net/thread-4781-1-1.html)

posted @ 2025-02-20 11:31 Jack Jiang 阅读(21) | 评论 (0)编辑 收藏

本文由转转 梁会彬、杜云杰分享,原题“转转IM的实践与思考”,下文进行了排版和内容优化。

1、引言

接上篇《整体架构设计》,笔者将以转转IM架构为起点,介绍IM相关组件以及组件间的关系;以IM登陆和发消息的数据流转为跑道,介绍IM静态数据结构、登陆和发消息时的动态数据变化;以IM常见问题为风景,介绍保证IM实时性、可靠性、一致性的一般方案;以高可用、高并发为终点,介绍保证IM系统稳定及性能的小技巧

 技术交流:

2、系列文章

本文是系列文章中的第2篇,本系列文章的大纲如下:

3、本文作者

梁会彬:转转架构部资深Java工程师,主要负责服务治理平台、Docker云平台、IM、分布式ID生成器、短域名服务等,有丰富的线上实战经验。

4、 IM架构回顾

应用层:使用IM服务的上游业务方,包括app(ios和android)、小程序/PC/m页、push、业务方等。

接入层:

  • 1)tcp entry:使用TCP协议,主要用于长连接保持、会话管理、协议解析;
  • 2)http entry:使用http协议,采用long pull技术,主要用于长连接保持、会话管理、协议解析;
  • 3)mq:接收电商推广等系统消息。推送量具有脉冲特点,使用mq削峰填谷;
  • 4)rpc-server:业务查询用户聊天数据、发送实时系统消息等。

逻辑层:

  • 1)logic:核心逻辑服务,负责登陆信息管理、在线消息管理、离线消息管理、在线推送管理等;
  • 2)ext-logic:扩展逻辑服务,负责子母账号推送、登陆信息统计、系统消息管理等。

数据层:

  • 1)MySQL:联系人数据、消息数据、系统消息数据等;
  • 2)Redis:登陆信息等。

5、IM消息收发

5.1场景说明

数据流中以用户A和用户B的对话为例,其中用户A的uid为1,用户B的uid为2。

下图为用户聊天场景图:

下图为用户聊天IM系统的数据流转图:

5.2数据结构

登陆信息存储在Redis中,联系人和消息数据放在TiDB中。

1)登陆信息:

key:uid

value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}

2)联系人:

说明:

  • 1)recent_msg_content:最近一条对话消息的内容,用于联系人列表中展示最近的消息内容;
  • 2)recent_read_time:最近一次读取该会话消息的时间,用于控制已读状态,小于该时间的所有消息,都为已读状态。

3)消息:

说明:

  • 1)client_msg_id:客户端生成的id,客户端幂等设计,防重复;
  • 2)direction:消息方向(0代表较大uid向较小uid发送消息,1则反之)。

数据流=数据+流。上面部分讲数据,即联系人和消息表,从静态的角度介绍了IM的数据结构;下面部分讲流(IM中最重要的两个流程),即登陆和发消息,从动态的角度来阐述IM系统中数据的流转。

5.3主要流程

5.3.1 )登陆:

1)问题:entry地址发现:app直接访问vip,由vip转发到entry。

2)流程(下面的数字为图中数字的说明):

  • 1)建连:app通过vip发起与entry连接;
  • 2)转发:entry转发登陆信息到logic,获取用户uid并管理该用户的连接;
  • 3)入库:logic记录用户登陆信息到redis。

3)数据:

Redis中数据如下:

key:1

value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}

5.3.2 )发消息(下面的数字为图二中数字的说明):

1)流程处理:

  • 1)发送:通过用户与entry的长连接发送文字"hello world";
  • 2)转发:entry转发文字信息"hello world"到logic;
  • 3)入库:logic存入数据库,即更新联系人表和消息表,其中联系人表更新recent_msg_content字段,消息表增加一条新消息记录;
  • 4)推送:从Redis中获取用户B登陆entry,如果未登录,走离线逻辑(发送push、推送微信、短信唤起);
  • 5)送达:用户B收到消息;
  • 6)确认:发送ack到entry;
  • 7)完成:logic收到ack,取消定时器;如果没有收到ack,logic会定时重发(用户在线时)。

2)数据:

联系人数据如下:

消息表数据如下:

5.3.3)关于数据的几个问题:

1)消息和联系人是如何分库分表的?使用TiDB,无需分库分表(现在的表设计支持根据uid_a分表,也就是无缝支持以MySQL为存储)。

2)联系人表一条消息为什么记录了两条数据?业务逻辑上,考量支持已读、删除联系人;索引性能上,考虑用户查询联系人时,sql条件为where uid_a=?,联系人表索引为uid_a,如果存单条数据,无法有效利用索引。

3)消息表一条消息记录一条数据,用户B与用户A的消息怎么查询?该表索引为<big_uid, small_uid>联合索引,无论是用户A查询与用户B的聊天信息,还是用户B查询用户A的聊天信息,其sql统统为where big_uid =max(uid_a,uid_b) and small_uid =min(uid_a,uid_b),然后根据direction字段展示聊天方向,这样就可以用一条消息,无需和联系人表一样存储两份数据,满足两种查询,节省一半的消息存储。

6、IM常见问题

6.1消息的实时性

1)是什么:

用户A给用户B发送消息"hello world",用户B怎么第一时间感知到?这里说的实时性,就是指用户如何实时获取发送的消息。

2)io模型带来的启示:

  • 1)poll、select、epoll;
  • 2)poll/select相比epoll最大的劣势在于轮询,轮询就需要轮询间隔,间隔小会浪费cpu,间隔大会不实时。epoll具有don't call me i will call you的特点,保证实时性;
  • 3)IM也面临着轮询还是通知的问题,也就是pull和push的问题。

3)怎么办:

  • 1)向epoll致敬:epoll_create、epoll_ctl、epoll_wait(此三者是epoll系统调用api);
  • 2)整个IM系统和epoll模型类似,app和entry保持长连接(epoll_create);entry session管理(即长连接管理epoll_ctl);logic等待用户A发送给用户B消息,获取用户B所登陆entry,触发推送消息(epoll_wait);综述,entry扮演着(epoll_create,epoll_ctl),logic扮演着(epoll_wait)这样IM系统就解决了消息实时性问题。

6.2消息的可靠性

1)是什么:

  • 1)用户A给用户B发送消息"hello world",用户B在线,怎么保证用户B确实收到了消息。这里说的可靠性,就是指用户如何可靠发送的消息。

2)tcp模型带来的启示:

  • 1)失败重传、ack确认。

3)怎么办:

  • 1)失败重传:图二中(1、发送2、转发3、入库)失败,告知客户端失败,由客户端重传;
  • 2)ack确认:图二中(4、推送5、送达6、确认7、完成)失败,即ack处理失败,启动重新通知逻辑。

6.3消息的一致性

1)是什么:

  • 1)现象:本来用户A给用户B发送了一个"hello world",而用户B确收到了两个"hello world";
  • 2)原因:由于可靠性逻辑中的重传逻辑,可能造成客户端认为失败了,但是服务端却成功了;推送ack返回错误,造成重推。

2)身份证带来的启示。

3)怎么办:

  • 1)client_msg_id:客户端发送消息时生成客户端id,对于单个客户端,该id具有唯一性,像身份证一样;
  • 2)客户端去重:如果客户端发现相同client_msg_id的消息,则仅仅展示一条数据。

7、IM高可用、高并发

1)扩缩容:

依托公司rpc服务注册发现能力,借助docker快速扩容,核心处理逻辑logic服务实现秒级扩容。扩容依据为各种监控指标,包括机器性能指标、 entry/logic qps指标、jvm指标、sql监控等综合考量。

2)熔断:

当大流量进入时,如果核心服务依赖的服务(比如母子账号服务)出现不可用的情况。这时,我们是直接使IM服务不可用吗?是不是有更好的选择?答案是肯定的,我们可以牺牲母子账号功能,也就是熔断不重要的依赖服务,做到柔性可用。

3)限流:

如果遇到瞬时高流量,仅仅扩容有可能适得其反。如果db处理能力达到极限,扩容就不是明智的选择,扩容反而会导致db连接增多,增加db的压力,导致服务崩溃。这时退一步采用限流,应用“fast fail”策略,让部分流量快速失败,减小服务压力,达到部分可用的效果。

4)总结:

IM作为电商应用中的一个重要节点,其重要性不言而喻,对其怎么重视都不为过。我们使用监控工具定义IM的核心metrics,根据指标进行扩缩容,这样做到了高可用;

高可用是万能的吗?IM依赖了很多服务,比如用户,母子账号,风控等服务,如果这些服务出现不可用的情况呢?这个时候就要学习一下古人的智慧,壮士断腕,牺牲小我,换取大我了,也就是柔性可用;

仅仅这样还是不够的,如果遇到突发流量,db(不可瞬时扩大处理能力)等处理能力达到极限时这个时候就要牺牲部分请求了,也就要做到部分可用。从“高可用”到“柔性可用”再到“部分可用”,面对不同case,IM要做到游刃有余。

其实,这种思想又何止IM呢,任何重要的服务都要面对这些问题吧,推而广之,面对自己负责的服务,怎么精细小心都不为过。

8、本文小结

诚然,这篇文章给大家对IM系统简单的认识,阐述了IM的一般架构、主要业务逻辑、常见问题和解决方案以及服务治理相关应用,IM还有很多业务逻辑和技术挑战。

在业务上,如未读数、群聊、多端登陆、母子账号等;在技术上,entry长连接100k问题优化、时间轮计时器实现、海量数据拆分与存储选型等。

路漫漫其修远兮,吾将上下而求索。

9、参考资料

[1] 零基础IM开发入门(二):什么是IM系统的实时性?

[2] 零基础IM开发入门(三):什么是IM系统的可靠性?

[3] 零基础IM开发入门(四):什么是IM系统的消息时序一致性?

[4] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递

[5] IM消息送达保证机制实现(二):保证离线消息的可靠投递

[6] 如何保证IM实时消息的“时序性”与“一致性”?

[7] 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践

[8] 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践

[9] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[10] 融云技术分享:全面揭秘亿级IM消息的可靠投递机制

[11] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[12] 一套原创分布式即时通讯(IM)系统理论架构方案

[13] 从零到卓越:京东客服即时通讯系统的技术架构演进历程

[14] 蘑菇街即时通讯/IM服务器开发之架构选择

[15] 现代IM系统中聊天消息的同步和存储方案探讨

[16] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

[17] 马蜂窝旅游网的IM系统架构演进之路

[18] 一套分布式IM即时通讯系统的技术选型和架构设计

[19] 微信团队分享:来看看微信十年前的IM消息收发架构,你做到了吗

[20] 携程技术分享:亿级流量的办公IM及开放平台技术实践


(本文已同步发布于:http://www.52im.net/thread-4773-1-1.html)

posted @ 2025-02-13 12:33 Jack Jiang 阅读(42) | 评论 (0)编辑 收藏

     摘要: 本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,下文有修订和改动。1、引言Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡方案,从某种意义上来讲,Nginx几乎是低成本、高负载Web服务端代名词。如此深入人心的Nginx,很多人也想当然的认为,在IM或消息推送等场景下是否也能使用Ng...  阅读全文

posted @ 2025-02-06 13:54 Jack Jiang 阅读(53) | 评论 (0)编辑 收藏

1、前言

一个孤独的小人,面对巨大的地球站在那里,这就是微信的启动画面,许多人对此非常熟悉。而知道画面里是非洲,就不是那么容易了。这是因为图片里的地球为云团笼罩,只露出一部分地貌特征,需要对世界地图比较熟悉的有心人才能发现这一点。

 
 技术交流:

2、图片解读

不过,因为有了我,一切都变得很简单了:

非洲大陆东北角上,那狭长的红海,和东南角上的马达加斯加岛,我都用红圈标注了出来。通过对比,这是一望而知的事情。两张图的区别在于,地图是在赤道上空看地球,而照片要偏南半球很多,以至于欧洲被挤到了图片之外,相反是南极洲在图片下方占据了大量面积。

问题在于,各个国家习惯性地把自己国家置于地图的中心。例如下面英国出版的世界地图,和你平常看到的世界地图绝对不一样:

中国不在地图的中心,是不是这样?那么,为什么微信的启动画面上的地球是正对非洲,而不是中国呢?

因为它不是地图,而是一张照片,一张真实的照片,用哈苏照相机在4万5千公里外拍摄的地球照片。这张照片的官方编号是:AS17-148-22727,民间给它的名字叫“蓝色弹珠”(The Blue Marble)。

3、“蓝色弹珠”(The Blue Marble)

要谈一下这张照片,必须说到美国的“阿波罗登月计划”。从1961年5月到1972年12月的十一年时间里,美国持续进行了一系列载人登月太空飞行。其中,有6次成功登上月球。而大家最为熟悉的失败案例是阿波罗13号登月计划,它被拍成了一部非常精彩的电影。

蓝色弹珠这张照片拍摄于1972年12月7日,当时宇航员在阿波罗17号飞船上,三名宇航员中的某一位用一台80毫米镜头的哈苏照相机,拍下了完整的地球照片。这张照片非常难得,因为阿波罗17号飞船执行的是最后一次阿波罗登月计划,从此以后,人类已经很久没有飞船抵达这个距离对地球进行拍照。我们看到的大量地球的照片,都是合成照,而不是太空实拍。

站在宇航员的角度,于4万5千公里之外看过去,地球就像是一颗很小的蓝色弹珠,这就是这张照片名字的由来。

4、最文艺的解读

微信的启动画面就是用了这张“蓝色弹珠”,人类最近一次在太空中远眺母星的景象。作为一种人际沟通工具,没有第二张图能更好地表达出人类内心的孤独,以及地球家园的美好。所以,整个画面有一种孤清中的淡淡暖意,给人的感觉是文艺到死。等你知道了背后的故事,又会觉得闷骚到死。

5、另外一种解读

我这里有另外一个不同的答案:

— 为什么不是正对着中国?

— 因为他在等待着那人转过来。

— 转过来之后呢?

— I WANT YOU!

6、更多QQ、微信的技术故事

技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail

QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年

闲话即时通讯:腾讯的成长史本质就是一部QQ成长史

2017微信数据报告:日活跃用户达9亿、日发消息380亿条

腾讯开发微信花了多少钱?技术难度真这么大?难在哪?

技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码

技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史

技术往事:“QQ群”和“微信红包”是怎么来的?

开发往事:深度讲述2010到2015,微信一路风雨的背后

开发往事:微信千年不变的那张闪屏图片的由来

开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)

一个微信实习生自述:我眼中的微信开发团队

首次揭秘:QQ实时视频聊天背后的神秘组织

为什么说即时通讯社交APP创业就是一个坑?

微信七年回顾:历经多少质疑和差评,才配拥有今天的强大

前创始团队成员分享:盘点微信的前世今生——微信成功的必然和偶然

即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等

QQ的成功,远没有你想象的那么顺利和轻松

QQ现状深度剖析:你还认为QQ已经被微信打败了吗?

[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?

QQ和微信止步不前,意味着即时通讯社交应用创业的第2春已来? 

那些年微信开发过的鸡肋功能,及其带给我们的思考

读懂微信:从1.0到7.0版本,一个主流IM社交工具的进化史

同为IM社交产品中的王者,QQ与微信到底有什么区别

还原真实的腾讯:从最不被看好,到即时通讯巨头的草根创业史

QQ设计团队分享:新版 QQ 8.0 语音消息改版背后的功能设计思路

社交应用教父级人物的张小龙和马化腾的同与不同

专访马化腾:首次开谈个人经历、管理心得、技术创新、微信的诞生等

一文读懂微信之父张小龙:失败天才、颠覆者、独裁者、人性操控师

微信纯血鸿蒙版正式发布,295天走完微信14年技术之路!

>> 更多同类文章 ……

(本文已同步发布于:http://www.52im.net/thread-204-1-1.html

posted @ 2025-02-05 11:47 Jack Jiang 阅读(115) | 评论 (0)编辑 收藏

本文为CSDN的《程序员》杂志原创文章,下文有修订和改动”。

1、引言

南方企业一直有过年找老板“逗利是”的习俗,每年春节后开工的第一天,腾讯大厦都会排上长长的队伍,集体上楼找老板们领红包。按照广东习俗,已经结婚的同事也要给未婚同事发红包,这一天腾讯员工就在春茗和寻找红包中度过。

由此孵化了一个内部项目,通过微信来收发红包,把这个公司全员娱乐活动与最活跃的IM平台微信结合起来。最初这个项目并没有预期对外,但是入口不小心开放后,成为了现象级产品。2014年开始爆发性增长,每年的发放量都是上一年的若干倍。根据腾讯公布的数据,到2016年春节,已经是每秒十万次支付,每天近十亿订单的系统。

微信红包本质是小额资金在用户帐户流转,有发、抢、拆三大步骤。在这个过程中对事务有高要求,所以订单最终要基于传统的RDBMS,这方面是它的强项,最终订单的存储使用互联网行业最通用的MySQL数据库。支持事务、成熟稳定,我们的团队在MySQL上有长期技术积累。但是传统数据库的扩展性有局限,需要通过架构解决。

补充说明:本文对应的演讲PPT详见《微信红包数据架构演变(PPT) [附件下载]》。

技术交流:

二、分享者

莫晓东:微信支付高级DBA,拥有丰富的数据架构和运维实战经验,擅长大规模MySQL数据库集群的架构、优化和高可用。2010年起在腾讯从事DBA工作,目前专注于微信社交支付的存储层运维和架构优化。

三、系列文章

❶ 系列文章目录:

社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进

社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节

社交软件红包技术解密(四):微信红包系统是如何应对高并发的

社交软件红包技术解密(五):微信红包系统是如何实现高可用性的

社交软件红包技术解密(六):微信红包系统的存储层架构演进实践》(* 本文)

社交软件红包技术解密(七):支付宝红包的海量高并发技术实践

社交软件红包技术解密(八):全面解密微博红包技术方案

社交软件红包技术解密(九):谈谈手Q春节红包的设计、容灾、运维、架构等

社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术实践

社交软件红包技术解密(十一):最全解密微信红包随机算法(含演示代码)

社交软件红包技术解密(十二):解密抖音春节红包背后的技术设计与实践

社交软件红包技术解密(十三):微信团队首次揭秘微信红包算法,为何你抢到的是0.01元

❷ 其它相关文章:

技术往事:“QQ群”和“微信红包”是怎么来的?

QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

月活8.89亿的超级IM微信是如何进行Android端兼容测试的

开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]

微信技术总监谈架构:微信之道——大道至简(演讲全文)

微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]

如何解读《微信技术总监谈架构:微信之道——大道至简》

微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]

微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案

微信朋友圈海量技术之道PPT [附件下载]

架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

快速裂变:见证微信强大后台架构从0到1的演进历程(二)

微信“红包照片”背后的技术难题

微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)

4、前端流量控制

发十亿红包,难在哪里?

  • 1)大量用户在同一时间发抢红包,瞬间产生每秒数万次的请求,除夕可能成百千万次;
  • 2)这个量级的请求如果不加以疏导处理直接到达后台,必定会导致后端服务过载甚至崩溃。

主要思路是缩短关键业务流程,分离可以通过异步、缓存等方式解决的问题,减轻系统压力,加快响应速度,在存储层前面建上一座大坝。

CGI无状态:

接入层无状态,逻辑层也无状态,可以方便地水平扩展。但依赖MySQL事务保证交易完整,保证红包系统的精简,减少瓶颈的存在。

资源静态化:

利用腾讯强大的基础资源优化部署,尽量把动态内容转为静态资源。静态资源和CGI分离,静态资源通过CDN就近接入,减少用户和CGI的交互,减少内网、访问延时和数据请求。

业务流程异步化:

微信红包的发、抢、拆背后都有多个内部环境,关键流程精简,非关键流程和后续业务逻辑进入异步队列进行处理,减少了用户的等待时间,也极大降低了峰值雪崩的概率。繁多的非关键链路也不会影响到主流程。

过载保护:

前端保护后端,能在前端处理,就不传递到后端:

  • 1)前端需要按后端能力做削峰限流;
  • 2)客户端、接入层、逻辑层逐层控制流量;
  • 3)前端更容易容错处理,全力保护存储层。

微信的过载保护在客户端已提前预埋了策略,在连接失败或超时情况下会有相应提示,减少用户重复请求次数。接入层针对频繁发出请求的客户端限制响应速度,并对系统负载划分出若干等级,达到不同阈值时引导客户端使用不同限速速率;在异常情况出现时,异步限流降速减轻服务器端压力防止过载。

多级读缓存:

发一个群红包,抢红包的请求量远大于发红包,如果已经领过完全可以拒绝。逻辑层增加缓存,类似可以缓存的请求都缓存起来,进一步减少存储层流量。

订单写缓存:

订单系统有很多请求不会真正完成全流量,创建这些废单不但浪费存储资源,还会挤占逻辑层和数据层的处理能力,影响其他交易。订单在完成支付前可以先落在缓存中,完成支付后再持久化。

5、存储层的高可用设计

在数百倍千倍的业务增长下,存储层很难简单无限扩容,一方面设备成倍增加的成本巨大,另一方面存储层瓶颈堆积不一定能解决问题。

读写分离:

写请求需要在主机上,实时读也需要走主机。有大量对延时不那么敏感,又影响性能的查询,完全可以放到从机。读写分离策略是MySQL分布式的入门,简洁地提高了系统容量。

水平切分:

数据的水平切分,实质就是分库分表;选取一张数据表按照主要纬度把数据拆分开。实现存储层的平行扩展。有效降低了单台数据库机器的负载,也减小了服务不可用的可能性。单台数据库宕机只会导致部分数据不能访问。主要需要考虑路由规则的选定,方便扩缩容以及数据的均衡分布。

垂直切分:

数据表除了水平切分,行内数据可以按属性进一步分开。核心表只保留最关键的字段,保证数据文件短小紧凑。以红包为例,昵称和祝福语这类较长的信息,不属于核心数据,完全可以切分到别的机器上,进一步提升核心数据库的容量。不同数据适合的存储类型也不一样,这类重复率高的长字符串更适合NoSQL存储,对存储空间和性能都是节约极大。

空间换时间:

按不同维度组织表,比如按订单属性和用户属性进行组织;适应不同的请求场景,避免复杂的查询。不同维度的表可以通过对账对齐,非核心表可以适当冗余,减少多次请求。

锁的优化:

多人争抢红包通过数据库事物来保证,必然存在竞争MySQL行锁。核心事物必须尽量精简,避免死锁。同一个订单的所有请求,尽量在逻辑层进程预排队后通过一个连接发送请求到数据库。

冷热分离:

核心数据库存放高频数据,其他数据可以定时移到成本低的冷数据库中。这样可以为核心数据库使用最好的SSD设备,快速设备容量较小较贵,不可能在全量数据上使用。同时可以保证数据表的容量不会一直积累,大表也会导致性能下降。

6、异地多活

当系统足够大时,就必须开始考虑异地部署的问题,让数据尽可能离用户更近。而且进一步的高可用不能局限在同一地域,必须跨数据中心跨城多活才能抵御系统性风险。因为跨城的几十毫秒延时,微信红包的异地活动设计为多数据中心相互独立。非灾难灰度不会将其他数据中心的数据导入到线上。

就近接入:

以微信红包系统的异步部署为例,第一个好处是用户就近接入,减少跨城的穿越流量。根据发送者的地域标志数据落地到不同数据中心,在不同地域实现业务闭环。

数据分离:

当前的网络技术限制,使用光纤也无法保证跨城数据的同步延时问题。所以微信红包的跨城数据中心并不进行数据实时同步。不同区域各自承载业务流量,地域上实现平衡,各地的订单数据各自独立存储。

异地容灾:

如果出现地域性故障,我们需要有机制去保证服务可用性。有了异步部署,假如深圳出现系统性故障,那么我们可以直接把请求接入上海。各数据中心独立部署,如果某地系统达到最大容量,可以进行跨地域分流。

7、有损服务和柔性降级

我们遇到最多的问题就是海量请求,通过分布式系统来实现海量请求,根据CAP理论不能同时保证一致性和高可用,必须有取舍。我们首先保证可用性,同时实现最终一致性。有以下原则。

有损服务:

要追求高可用性,可以牺牲部分数据一致性和完整性从而保证核心功能。在资源一定的前提下,满足用户的核心需求。微信红包的核心点是抢、拆红包,系统必须尽最大可能保证核心步骤流畅,但在瓶颈时立即降级防止引起系统雪崩。但是要保证数据能最终对齐,金融属性的系统数据安全硬要求。

柔性可用:

柔性可用是在有损服务价值观支持下的方法,结合具体场景提供不同级别的用户体验,保证尽可能成功返回关键数据。把握用户在每一个场景中的核心需求,设计不同层次满足核心诉求的办法。系统首先要实现容灾和自动切换;其次逻辑资源应该隔离;服务过载时必须自动快速拒绝。

8、结束语

本文简单介绍了微信红包的存储层服务设计准则,在业务从起步到小跑再到腾飞的过程中,背后的海量服务能力将对其最终成败有着越来越深远的影响。在互联网爆发性增长中,海量服务能力决定项目成败,必须在项目初期就做好海量服务的准备。

附录1:有关微信、QQ的文章汇总

微信朋友圈千亿访问量背后的技术挑战和实践总结

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

微信团队分享:微信移动端的全文检索多音字问题解决方案

腾讯技术分享:Android版手机QQ的缓存监控与优化实践

微信团队分享:iOS版微信的高性能通用key-value组件技术实践

微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

腾讯技术分享:Android手Q的线程死锁监控系统技术实践

微信团队原创分享:iOS版微信的内存监控系统技术实践

让互联网更快:新一代QUIC协议在腾讯的技术实践分享

iOS后台唤醒实战:微信收款到账语音提醒技术总结

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

微信团队分享:视频图像的超分辨率技术原理和应用场景

微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯团队分享:手机QQ中的人脸识别酷炫动画效果实现详解

腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享

微信团队分享:微信Android版小视频编码填过的那些坑

微信手机端的本地数据全文检索优化之路

企业微信客户端中组织架构数据的同步更新方案优化实战

微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

月活8.89亿的超级IM微信是如何进行Android端兼容测试的

以手机QQ为例探讨移动端IM中的“轻应用”

一篇文章get微信开源移动端数据库组件WCDB的一切!

微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化

微信后台基于时间序的海量数据冷热分级架构设计实践

微信团队原创分享:Android版微信的臃肿之困与模块化实践之路

微信后台团队:微信后台异步消息队列的优化升级实践分享

微信团队原创分享:微信客户端SQLite数据库损坏修复实践

腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率

腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)

腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(上篇)

微信Mars:微信内部正在使用的网络层封装库,即将开源

如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源

(本文已同步发布于:http://www.52im.net/thread-2568-1-1.html

posted @ 2025-01-24 16:28 Jack Jiang 阅读(43) | 评论 (0)编辑 收藏

本文来自腾讯ISUX设计团队,下文有修订和改动。

1、引言

2019年4月16日QQ语音消息新特性突然登上微博热搜,QQ铁粉瞬间集结。是什么让129万人为QQ花式彩虹屁?为何微信却被吃瓜群众疯狂艾特?现在,让我为你揭秘QQ语音消息改版的设计旅程。

关于腾讯ISUX团队:

腾讯社交用户体验设计,简称ISUX (Internet Social User Experience),成立于2011年1月11日,是腾讯集团核心、全球最具规模的UX设计团队,专业成员包括用户研究、交互设计、视觉设计、品牌设计、视频动画设计、UI开发、产品设计与市场研究等,至今ISUX分布于中国深圳总部、北京、上海、成都及韩国首尔。ISUX主要负责腾讯社交通讯与娱乐类产品服务的用户体验设计与研究,包括主要服务平台如QQ、QQ空间、QQ音乐、腾讯云、腾讯企点、QQ物联、腾讯课堂、兴趣部落、花样直播、全民K歌、全民影帝、企鹅FM、企鹅MV、天天P图、微云和来电等。

即时通讯网整理另一篇来自ISUX团队的文章,也可以一读:《感悟分享:在腾讯的八年,我的成长之路和职业思考》。

技术交流:

2、回归沟通:语音消息能否更方便

QQ已经陪伴了大家20年,但是我们仍然在持续思考怎样让用户的沟通更加高效。语音作为人与人之间最自然的交流方式,也不断引起我们对现有体验的反思。

是否语音消息只能采取这种经典的气泡体验?

现有的这些点击播放的语音气泡真的满足了所有用户需求吗?emm…

总结一下:发送语音一时爽,接收语音想撞墙。

针对这些用户声音,业内已有一些解决方案。但是其目标用户量和场景远没有QQ这样丰富。在此次改版中,我们回归QQ本身,探索在QQ语音消息场景中存在的那些痛点。

面对这些痛点,此次改版将需求聚焦在:

  • 1)长语音被打断可以重听;
  • 2)识别有效的语音片段;
  • 3)重点语音片段反复收听。

对于QQ 8.0此次对语音消息功能的改进目标:

  • 1)功能层面上,我们将通过提供语音的暂停和进度拖拽能力,并可视化音量,以满足语音接收者的使用效率需求;
  • 2)在体验层面上,语音作为用户的高频沟通操作,其设计必须满足QQ8.0中精致这一设计原则,给用户带来极致体验。

3、美好体验,从第一眼开始

3.1 易学性:让功能更加直觉化

“这么简单的操作,用户试一次就知道怎么用了吧!”

QQ拥有广泛的用户群,所有功能都要尽量降低用户的学习成本。更何况由于没有其他国民级APP的相似特性可以类比,对用户来说语音进度调节不只是一个新功能,更是一种新模式。在这种背景下,功能的易学性显得尤为重要。怎样让用户一眼就明白语音消息可以暂停并拖动呢?怎样让操作更加直觉化?我们不妨从用户熟悉的事物入手,进行联想。

暂停和拖动在语音中不常见,但它却是播放器的通用功能。在播放器设计中,有三个用户行为引导的关键元素:a.按钮—播放和暂停的指示 b.游标—拖动指示 c.颜色—进度指示。本次语音气泡的设计中,我们依旧沿用了按钮、游标、色彩作为指示性元素。

但是这些元素的加入无疑会加重气泡内的信息负担。并且当同时出现多个语音气泡时,我们更加需要保证聊天页面有适当的信息密度。因此在声纹样式设计中,降噪成为了关键。在发散了多种样式后,我们最终选择了这种简约的声纹形态。它既能很好的展示进度信息,又可以平衡气泡内的信息密度,让QQ多样化的用户群都能对语音进度拖拽有更直觉化的操作。

3.2 准确or美:直观体验至上

“声纹是程序直接生成的,难道还需要设计?”

盆友,买家秀和卖家秀了解一下?

呈现准确音量的声纹无法满足我们预期中的流畅视觉体验,反而会让用户感觉到多变声纹信息带来的压力。回归设计目标,声纹是为了帮助用户识别有效语音片段,因此有声音和无声音的声纹对比很重要。这也意味着对于正常音量区间的声音,我们可以适当牺牲准确性以确保良好的视觉体验。

在收集了大量用户真实语音声纹后,我们发现最“丑”声纹来自于两类声音。一类是当用户语音连续达到最大音量时,大量声纹达到最高高度并撑满语音气泡。这种现象常发生在用户对着手机收音孔处说话的场景中。为了解决这个问题,我们将达到最大音量的声纹高度进行削减。被削减的高度按照正弦曲线做随机值,再加回到这些声纹的上方。经过这样的优化后,所有达到最高值的声纹都能够在顶部产生流畅的曲线。

另一类“丑”声纹则来自于音量忽高忽低造成的声纹高度跳变。这是由于人们说话是非连续的,会存在语气词和用户思考的沉默点。解决这个问题的关键是让高声纹和低声纹之间的落差减少。因此我们定义当相邻声纹高度差超过50%时,就对这两个声纹高度做平滑处理,保证所有音量的声纹都有流畅的过渡。

经过与产品和开发团队的多轮参数调整后,这些精心优化后的声纹可以让用户无论怎样说话都能“看到”自己最美的语音。

4、不止拖拽,更要畅快感受

4.1 更大的响应区域

“点击拖拽是常规操作,调用系统交互就好了吧?”

拖拽的确常规,但是在功能之外,我们能否让用户的操作体验更畅快呢?

畅快意味着无拘无束,翻译成交互语言就是要赋予用户更大的操作区域。但是我们的手指宽度和控件大小有时难以匹配。例如,8.0UI改版后的语音气泡高度为118px,而成人手指的宽度范围则在110px-180px。如果拖拽只能在气泡范围内进行,就意味着用户需小心翼翼地去操作。为了实现“无拘无束”的拖拽体验,我们根据用户的行为阶段对响应范围进行了两次放大。

第一次放大:开始拖动阶段,放大触发拖动的范围。拖拽事件的触发范围由气泡本身扩大到气泡的外边缘区域。

第二次放大:拖拽中,拖动行为的响应范围扩大到全屏。一旦用户触发拖拽,系统将屏蔽聊天页面的所有操作,包括右滑返回、上下滚动和页面内的所有点击操作。确保用户在手指未离开屏幕的前提下,可以在整个页面范围内控制进度拖拽。一方面用户不再需要沿着气泡的小小区域去拖拽,体验更加顺畅;另一方面这也可以减少手指对于气泡的遮挡,让用户更好的看清楚当前进度。

4.2 更合理的气泡长度变化规则

-“语音越长,气泡越长,so easy~”

气泡越长代表语音越长。但你可能没注意过,其实气泡长度是随着语音时长呈线性变化。这个本来运行良好的规则在加入了拖拽功能后却出现了问题。从灰度用户的数据来看,大部分用户的语音时长在10s以内。此时语音气泡较短,十分不易于进行拖拽。所以我们既需要短语音气泡变长,又要保证用户可以感知到气泡始终随着时长增长而变长。在气泡最大长度无法改变的前提下,必须改变原有的线性变化规律,转变为更精细的分阶段的曲线变化。

 

  • [阶段1] 斜率逐渐增加的曲线。此阶段对应着短时长语音,也是用户的高频使用场景。因此该阶段气泡长度随语音时长的增长需要更加明显;
  • [阶段2] 斜率逐渐减小的曲线。此阶段对应的长语音是低频场景,此时气泡长度随语音时长变化的反馈可以适当放缓;
  • [阶段3] 达到气泡长度最大值,不再变化。此时为超长语音阶段,用户已经不需要通过气泡长度来判断语音时长。

运用更加精细的气泡长度变化规律,让用户的高频语音消息更好拖拽。

5、懂你所需,为你设计

-“结束了吗,有没有one more thing?”

至此,语音消息的改版设计似乎已经结束,但我们对于设计的追求不止于此。语音进度调节只是语音消息体验中的一个小小功能。我们希望通过这些精致贴心的体验设计,让用户产生一种感觉——QQ懂我。因为懂你,所以希望为你的沟通做更多事情。

关于语音消息,设计团队也在发散更多贴近用户真实生活的场景:

  • 1)更加贴近场景的体验:未来我们是否可以利用传感器检测到用户所处的环境和状态,根据不同的环境和用户行为状态,确定这些消息是以语音还是文本显示;
  • 2)更加丰富的语音表达:语音比文本承载了更多的情感信息,基于这个属性,我们能否通过特殊声音编辑、视觉化表达、手机触感等方式,帮助发送方传达更加丰富的信息;
  • 3)无障碍化体验:对于视障人群、运动障碍人群、老年人群体来说,语音是很好的沟通选择。我们是否能够更进一步,通过语音指令更好的协助他们使用QQ…

做最懂你的语音消息,我们还在继续。

6、未来可期:最美的QQ正在路上

QQ新版语音气泡iOS上线当天喜提微博热搜。看到用户们的花式夸奖,我们的心情美滋滋。但同时网络上也出现了一些负面的评价,这些声音也在鞭策设计团队持续打磨语音消息体验。

一花一世界,一树一菩提。语音消息气泡改版只是体验升级的第一步,但它可以折射出整个QQ8.0版本所遵循的设计原则:降噪、生机和精致。沿着这些原则,我们依旧在打造最美QQ的路上奋力前行。

什么,你还没有下载手机QQ8.0?那你岂不是没法体验到史上最简洁的QQ页面,也不能发现底部tab小惊喜了?你更没法知道我们的语音消息马上就支持<(ˉ^ˉ)><(ˉ^ˉ)><(ˉ^ˉ)>和(>▽<)(>▽<)(>▽<)两个超赞功能(顶级机密,手动打码)。

附录:更多即时通讯产品的实践总结、感悟分享

技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail

QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年

闲话即时通讯:腾讯的成长史本质就是一部QQ成长史

2017微信数据报告:日活跃用户达9亿、日发消息380亿条

腾讯开发微信花了多少钱?技术难度真这么大?难在哪?

技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码

技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史

技术往事:“QQ群”和“微信红包”是怎么来的?

开发往事:深度讲述2010到2015,微信一路风雨的背后

开发往事:微信千年不变的那张闪屏图片的由来

开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)

一个微信实习生自述:我眼中的微信开发团队

首次揭秘:QQ实时视频聊天背后的神秘组织

为什么说即时通讯社交APP创业就是一个坑?

微信七年回顾:历经多少质疑和差评,才配拥有今天的强大

前创始团队成员分享:盘点微信的前世今生——微信成功的必然和偶然

即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等

QQ的成功,远没有你想象的那么顺利和轻松

QQ现状深度剖析:你还认为QQ已经被微信打败了吗?

[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?

QQ和微信止步不前,意味着即时通讯社交应用创业的第2春已来? 

那些年微信开发过的鸡肋功能,及其带给我们的思考

读懂微信:从1.0到7.0版本,一个主流IM社交工具的进化史

同为IM社交产品中的王者,QQ与微信到底有什么区别

还原真实的腾讯:从最不被看好,到即时通讯巨头的草根创业史

QQ设计团队分享:新版 QQ 8.0 语音消息改版背后的功能设计思路

社交应用教父级人物的张小龙和马化腾的同与不同

专访马化腾:首次开谈个人经历、管理心得、技术创新、微信的诞生等

一文读懂微信之父张小龙:失败天才、颠覆者、独裁者、人性操控师

微信纯血鸿蒙版正式发布,295天走完微信14年技术之路!

>> 更多同类文章 ……


(本文已同步发布于:http://www.52im.net/thread-2539-1-1.html)

posted @ 2025-01-21 16:08 Jack Jiang 阅读(110) | 评论 (0)编辑 收藏

本文来自微信团队工程师方乐明的技术分享,由InfoQ编辑发布,下文有修订和改动。

一、引言

微信红包业务量级的高速发展,对后台系统架构的可用性要求越来越高。在保障微信红包业务体验的前提下,红包后台系统进行了一系列高可用方面的优化设计。

本次分享介绍了微信红包后台系统的高可用实践经验,主要包括后台的 set 化设计、异步化设计、订单异地存储设计、存储层容灾设计与平行扩缩容等。听众可以了解到微信红包后台架构的设计细节,共同探讨高可用设计实践上遇到的问题与解决方案。

补充说明:本文对应的演讲PPT详见《微信红包系统可用性设计实践(PPT) [附件下载]》。

技术交流:

二、分享者

方乐明:现任微信支付应用产品系统负责人,主要从事微信红包、微信转账、微信群收款等支付应用产品的系统设计、可用性提升、高性能解决方案设计等,曾连续多年负责春节微信红包系统的性能优化与稳定性提升,取得良好的效果。

三、系列文章

系列文章目录:

四、微信红包介绍

微信红包从 2014 年开始发展到现在,中间经历了几年时间。在这几年的时间里,整个系统可用性产生了很大的提升。2015 年年初的时候,每天晚上九点钟是微信红包的业务高峰期,系统经常性地出现性能问题。到了今天,即使在节假日高峰期,系统也不会出现问题。

▲ 红包印象 – 产品形态(点此查看本图出处

如上图所示,微信红包的业务包含包、发、抢、拆、查询发送红包和收红包数量,其中最关键的步骤是发红包和抢红包。

微信红包是微信支付的商户,微信红包这个商户出售的是钱。发红包用户在微信红包平台使用微信支付购买一份钱,微信红包将钱发放到相对应的微信群。群里的用户抢红包得到微信零钱。这个过程中,微信红包和微信支付之间的关系是商家和第三方支付平台的关系。

微信红包和微信支付之间的交互,与普通商家与微信支付的交互一样,需要经过六个步骤。用户发红包时,进入微信红包下一笔订单,系统记录发红包用户、发红包金额、红包数量和要发送到的用微信群。然后微信红包系统请求微信支付服务器进行下单,用户使用微信支付进行支付。

支付成功后,微信支付后台系统通知微信红包后台系统支付成功结果,微信红包后台系统收到通知后推送微信红包消息到微信群。微信群里用户便可抢红包。这就是微信红包和微信支付的关系以及交互过程。

五、微信红包系统架构

5.1 微信红包的系统流程

▲ 微信红包的系统流程(点此查看本图出处

上图是微信红包系统角度上的流程,业务主流程是包、发、抢、拆四个操作,每个操作包括几个关键步骤。

包红包:系统为每个红包分配一个唯一 ID,即红包发送订单号,然后将发红包用户、红包个数、红包数额写入存储,最后去微信支付下单。

发红包:用户使用微信支付完成付款,微信红包后台系统收到微信支付系统的支付成功通知。红包系统将红包发送订单状态更新为用户已支付,并写入用户发红包记录(用户发红包记录,就是微信钱包中,查看到的用户每一年总共发出及收到的红包记录)。最后微信红包后台系统发送微信红包消息到微信群。

抢红包:指微信群里的用户收到微信红包消息后,点开红包消息。这个过程,微信红包后台系统会检查红包是否已被抢完,是否已过期,是否已经抢过。

拆红包是最复杂的业务是操作,包括:

  • 1)查询这个红包发送订单,判断用户是否可拆,然后计算本次可拆到的红包金额;
  • 2)然后写入一条抢红包记录。如果把拆红包过程,类比为一个秒杀活动的过程,相当于扣库存与写入秒杀记录的过程;
  • 3)更新库存对应于更新红包发送订单,写入秒杀记录对应于写入这个红包的领取红包记录;
  • 4)另外,还要写入用户整体的红包领取记录;
  • 5)最后请求微信支付系统给拆到红包用户转入零钱,成功后更新抢红包的订单状态为已转账成功。

5.2 微信红包的整体架构

▲ 微信红包的系统架构(点此查看本图出处

上图所示,是微信红包的系统架构。包括微信统一接入层,下面是微信红包系统 API,包括发、抢、拆、查红包详情、查红包用户列表。再下面是封装微信红包关键业务的逻辑服务;最下面一层是数据存储层,微信红包最主要的数据是订单数据,包括发红包订单和拆红包订单两部分。业务逻辑和存储服务器之间是数据接入层,它最重要的作用是封装数据库操作的领域逻辑,使得业务逻辑服务不需要感知对 MySQL 的连接管理、性能、容灾等问题。

微信红包数据的访问热度,随着时间流逝会急剧降低,也就是数据的访问时间段非常集中,一般红包发出三天后,99% 的用户不会再去点开这个红包了。因此微信红包系统采取按时间做冷热数据分离,降低数据的存储成本,同时提升了热数据的访问性能。

数据平台用于对红包数据的分析计算,比如朋友圈里的文章,统计从 某年 1 月 1 日到 2017 年 1 月一个用户总共抢红包的金额,在全国的排名情况,发红包数最多的城市等。另外一个作用就是对账,红包的订单和微信支付的订单需要对账,以保证最终资金的一致性;订单的数据和订单的 cache 需要做对账,以保证数据的完整性;订单数据和用户的收发记录需要对账,以保证用户列表完整性。

六、微信红包系统可用性实践

6.1系统可用性影响因素

系统的可用性影响因素可分成两类:

  • 一类计划外;
  • 一类计划内。

计划外包含很多因素,系统用到的所有东西都可能产生故障,都可能成功影响可用性的因素。从这个角度上来讲,可以说故障是无法避免的,系统的运作一定会产生故障,尤其是服务器有成千上万个的时候。计划内的影响因素,主要有与升级相关、运维相关的操作,以及日常的备份等。这一类影响因素,通过精细地设计方案,是可以避免对可用性造成影响的。

6.2微信红包系统可用性设计方向

基于上面两个分析结论,可以总结出微信红包后台系统的可用性的设计方向。就是在不能避免意外故障的情况下,尽可能降低出现意外故障时对可用性的影响。另一方面,绝大多数计划内的日常维护可以通过方案的设计避免影响可用性,其中平行扩容特指关于存储层的平行扩容。

下面从降低故障影响和微信红包系统的平行扩容两方面进行分析。

首先是降低意外故障的影响,重点讲解订单存储层在订单 DB 故障的情况下如何降低对红包系统可用性的影响。

6.3业务逻辑层 - 部署方案设计

首先是业务逻辑层的部署方案。业务逻辑层是无状态的,微信红包系统的业务逻辑层,部署在两个城市,即两地部署,每一个城市部署至少三个园区,即三个 IDC。并且每个服务需要保证三个 IDC 的部署均衡。另外,三个 IDC 总服务能力需要冗余三分之一,当一个 IDC 出现故障时,服务能力仍然足够。从而达到 IDC 故障不会对可用性产生影响。

6.4业务逻辑层 - 异步化设计

▲ 业务逻辑层 – 异步化(点此查看本图出处

第二是异步化设计。如上图所示,微信红包的某些步骤不实时完成也不会影响用户对红包业务可用性的体验。比如拆红包,正常的业务流程很长,但关键步骤只有订单相关的几步。至于转零钱、写红包记录等操作不需要实时。用户抢到红包时,一般不会实时去钱包查看微信零钱,而是在微信群中点开消息查看本次抢到金额和他人抢红包金额。所以拆红包时只需要从 cache 查询用户是否拆过红包,然后写入拆红包的订单记录,更新发红包订单,其他的操作都可以异步化。当然,不是每个业务都可以进行异步化设计,需要进行业务分析,判断是否存在非关键步骤之外的事情可以将其异步化,并通过异步对账保证最终一致。

▲ 订单存储层 – 早期架构(点此查看本图出处

接下来是微信红包订单存储设计。上图是 2014 年微信红包存储层的模型。业务逻辑层请求数据层操作时,使用订单号 hash 路由到订单 SERVER。订单 SERVER 与每一组 MYSQL 数据库连接。

微信红包的订单号是在发红包时系统生成唯一标识,使用序列号服务生成唯一 ID,后面拼接三位微信红包的订单分库表的标识。所以,总共可以分一百个逻辑库,每个逻辑库含有十张表。一百个逻辑库均匀地分布到十组物理 DB,每组 DB 存十个逻辑库。

这个架构的最大问题是,一组 DB 故障时,会影响其他 DB。2014-2015 年期间,微信红包量涨得特别快,扩容速度跟不上业务增长速度。一组 DB 的性能出现瓶颈时,数据操作变慢, 拆红包的事务操作在 MYSQL 排队等待。由于所有十组 DB 机器与所有的订单 SERVER 连接,导致所有的订单 SERVER 都被拖住,从而影响红包整体的可用性。这个架构的另一个问题是扩容不方便,后面会介绍。

为解决 DB 间的相互影响,需要将 DB 间相互隔离,订单存储层 SET 化。SET 化指订单 DB 和订单接入 SERVER 垂直 stick 一起。业务逻辑层访问订单时,根据订单倒数第二、三位数字找到所属订单 SET,一个 SET 的请求不能路由到其他 SET。

找到对应的订单接入服务器之后,在服务器内的多个进程中找到指定进程,让同个红包的所有拆请求串行化。当一组 DB 出现故障,只会影响该组 DB 对应的 SERVER。

这里有一个问题,DB 故障拖住某些订单 SERVER,会不会也拖住更上层业务逻辑服务?业务逻辑层为什么不一起 SET 化?业务逻辑层承载了用户维度相关的业务操作,不可以按照订单的维度分业务逻辑,例如务逻辑层会请求用户的头像、昵称等,如果继续按照订单分业务逻辑,会导致跨地域调用。

微信红包系统采取的方案是,在订单 SERVER 服务端增加快速拒绝服务的能力。SERVER 主动监控 DB 的性能情况,DB 性能下降、自身的 CPU 使用升高,或者发现其他的监控维度超标时,订单 SERVER 直接向上层报错,不再去访问 DB,以此保证业务逻辑层的可用性。

一组 DB 故障不会影响整个系统的可用性。有影响的,只有十分之一,若扩成 100 组,影响便只有一百分之一。所以通过 SET 化得到的好处是,控制 DB 连接数、隔离故障影响和分流并发。

▲ 订单存储层 – 故障自愈(点此查看本图出处

完成 SET 化之后,DB 故障仍对业务有十分之一影响,那么这十分之一该怎么解决?通过对系统进行研究分析之后,发现 DB 可以做到故障自愈。

如上图所示,所设尾号 90-99 的 SET 故障时,如果业务逻辑服务后续不再生成属于这个 SET 的订单,那后续的业务就可以逐渐恢复。

也就是在发生故障时,业务逻辑层发布一个版本,屏蔽故障号段的单号生成,就可以恢复业务。进一步想,除了人为发版本,有没有方法可以让 DB 故障时自动恢复?在 DB 故障导致业务失败时,业务逻辑层可获取到故障 DB 的号段,在发红包时,将这些故障的号段,换一个可用的号段就可恢复业务。订单号除了最后三位,前面的部分已能保证该红包唯一性,后面的数字只代表着分库表信息,故障时只需要将最后三位换另外一个 SET 便可自动恢复。

完成这个设计后,即使 DB 出现故障,业务的可用性也不会有影响。这里还有一点,新的发红包请求可避免 DB 故障的影响,但那些故障之前已发出未被领取的红包,红包消息已发送到微信群,单号已确定,拆红包时还是失败。对这种情况,由于不会有增量,采用正常的主备切换解决即可。

6.5平行扩缩容设计

▲ 平行扩缩容 – 早期方案(点此查看本图出处

上图是微信红包早期的扩缩容方式。这个扩容方式,对扩容的机器数有限制。前面讲到,红包系统按红包单号后面两个数字分多 SET,为了使扩容后数据保持均衡,扩容只能由 10 组 DB 扩容到 20 组、50 组或者 100 组。另外,这个扩容方式,过程也比较复杂。首先,数据要先从旧数据库同步复制到新扩容的 DB,然后部署 DB 的接入 SERVER,最后在凌晨业务低峰时停服扩容。

这个扩容方式的复杂性,根本原因是数据需要从旧 SET 迁到新 SET。如果新产生数据与旧数据没关系,那么就可以省掉这部分的迁移动作,不需停服输。分析发现,需要把旧数据迁出来的原因是订单号段 00-99 已全部被用,每个物理数据库包含了 10 个逻辑库。如果将订单号重新设计,预留三位空间,三位数字每一个代表独立的物理 DB,原来 10 组 DB 分别为 000-009 号段。

这种设计,缩容时,比如要缩掉 000 这组,只需在业务逻辑服务上不生成订单号为 000 的红包订单。扩容时,比如扩为 11 组,只需多生成 010 的订单号,这个数据便自动写入新 DB。当然,缩容需要一个前提条件,也就是冷热分离,缩容后数据变为冷数据,可下线热数据机器。以上就是红包的平行扩缩容方案。

▲ 改进后的平行扩容(点此查看本图出处

七、写在最后

微信红包系统的可用性实践,主要包括了部署设计、SET 化设计、异步化设计、DB 故障自愈能力建设、平行扩容设计。在完成这些设计后,微信红包系统的可用性得到了很大提升,在 近几年的春节实现了 0 故障,在平常的运行中达到 99.99% 可用性。

(原文链接:点此进入

八、更多鹅厂技术文章汇总

微信朋友圈千亿访问量背后的技术挑战和实践总结

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

IM全文检索技术专题(二):微信移动端的全文检索多音字问题解决方案

腾讯技术分享:Android版手机QQ的缓存监控与优化实践

微信团队分享:iOS版微信的高性能通用key-value组件技术实践

微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

腾讯技术分享:Android手Q的线程死锁监控系统技术实践

微信团队原创分享:iOS版微信的内存监控系统技术实践

让互联网更快:新一代QUIC协议在腾讯的技术实践分享

iOS后台唤醒实战:微信收款到账语音提醒技术总结

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

微信团队分享:视频图像的超分辨率技术原理和应用场景

微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

腾讯信鸽技术分享:百亿级实时消息推送的实战经验

IPv6技术详解:基本概念、应用现状、技术实践(上篇)

IPv6技术详解:基本概念、应用现状、技术实践(下篇)

腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅

社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进

社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节

社交软件红包技术解密(四):微信红包系统是如何应对高并发的

社交软件红包技术解密(五):微信红包系统是如何实现高可用性的

社交软件红包技术解密(六):微信红包系统的存储层架构演进实践

社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等

社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术实践

社交软件红包技术解密(十一):解密微信红包随机算法(含代码实现)

社交软件红包技术解密(十三):微信团队首次揭秘微信红包算法,为何你抢到的是0.01元

(本文已同步发布于:http://www.52im.net/thread-2564-1-1.html

posted @ 2025-01-15 11:19 Jack Jiang 阅读(124) | 评论 (0)编辑 收藏

本文来自微信团队工程师方乐明的技术分享,由InfoQ编辑发布,下文收录时有修订和改动。

一、引言

每年节假日,微信红包的收发数量都会暴涨,尤以除夕为最。如此大规模、高峰值的业务需要,背后需要怎样的技术支撑?百亿级别的红包规模,如何保证并发性能与资金安全?

本文将为读者介绍微信百亿级别红包背后的高并发设计实践,内容包括微信红包系统的技术难点、解决高并发问题通常使用的方案,以及微信红包系统的所采用高并发解决方案。

技术交流:

二、分享者

方乐明:现任微信支付应用产品系统负责人,主要从事微信红包、微信转账、微信群收款等支付应用产品的系统设计、可用性提升、高性能解决方案设计等,曾连续多年负责春节微信红包系统的性能优化与稳定性提升,取得良好的效果。

三、系列文章

❶ 系列文章目录:

❷ 其它相关文章:

四、微信红包的两大业务特点

微信红包(尤其是发在微信群里的红包,即群红包),业务形态上很类似网上的普通商品“秒杀”活动。

就像下面这样:

  • 1)用户在微信群里发一个红包,等同于是普通商品“秒杀”活动的商品上架;
  • 2)微信群里的所有用户抢红包的动作,等同于“秒杀”活动中的查询库存;
  • 3)用户抢到红包后拆红包的动作,则对应“秒杀”活动中用户的“秒杀”动作。

不过除了上面的相同点之外,微信红包在业务形态上与普通商品“秒杀”活动相比,还具备自身的特点。

首先:微信红包业务比普通商品“秒杀”有更海量的并发要求。

微信红包用户在微信群里发一个红包,等同于在网上发布一次商品“秒杀”活动。假设同一时间有 10 万个群里的用户同时在发红包,那就相当于同一时间有 10 万个“秒杀”活动发布出去。10 万个微信群里的用户同时抢红包,将产生海量的并发请求。

其次:微信红包业务要求更严格的安全级别。

微信红包业务本质上是资金交易。微信红包是微信支付的一个商户,提供资金流转服务。

用户发红包时,相当于在微信红包这个商户上使用微信支付购买一笔“钱”,并且收货地址是微信群。当用户支付成功后,红包“发货”到微信群里,群里的用户拆开红包后,微信红包提供了将“钱”转入折红包用户微信零钱的服务。

资金交易业务比普通商品“秒杀”活动有更高的安全级别要求。普通的商品“秒杀”商品由商户提供,库存是商户预设的,“秒杀”时可以允许存在“超卖”(即实际被抢的商品数量比计划的库存多)、“少卖”(即实际被抢的商户数量比计划的库存少)的情况。但是对于微信红包,用户发 100 元的红包绝对不可以被拆出 101 元;用户发 100 元只被领取 99 元时,剩下的 1 元在 24 小时过期后要精确地退还给发红包用户,不能多也不能少。

以上是微信红包业务模型上的两大特点。

五、 微信红包系统的技术难点

在介绍微信红包系统的技术难点之前,先介绍下简单的、典型的商品“秒杀”系统的架构设计,如下图所示。

该系统由接入层、逻辑服务层、存储层与缓存构成:

  • 1)Proxy 处理请求接入;
  • 2)Server 承载主要的业务逻辑;
  • 3)Cache 用于缓存库存数量;
  • 4)DB 则用于数据持久化。

一个“秒杀”活动,对应 DB 中的一条库存记录。当用户进行商品“秒杀”时,系统的主要逻辑在于 DB 中库存的操作上。

一般来说,对 DB 的操作流程有以下三步:

  • 1)锁库存;
  • 2)插入“秒杀”记录;
  • 3)更新库存。

其中,锁库存是为了避免并发请求时出现“超卖”情况。同时要求这三步操作需要在一个事务中完成(所谓的事务,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行)。

“秒杀”系统的设计难点就在这个事务操作上。商品库存在 DB 中记为一行,大量用户同时“秒杀”同一商品时,第一个到达 DB 的请求锁住了这行库存记录。在第一个事务完成提交之前这个锁一直被第一个请求占用,后面的所有请求需要排队等待。同时参与“秒杀”的用户越多,并发进 DB 的请求越多,请求排队越严重。因此,并发请求抢锁,是典型的商品“秒杀”系统的设计难点。

微信红包业务相比普通商品“秒杀”活动,具有海量并发、高安全级别要求的特点。

在微信红包系统的设计上,除了并发请求抢锁之外,还有以下两个突出难点:

首先,事务级操作量级大:

上文介绍微信红包业务特点时提到,普遍情况下同时会有数以万计的微信群在发红包。这个业务特点映射到微信红包系统设计上,就是有数以万计的“并发请求抢锁”同时在进行。这使得 DB 的压力比普通单个商品“库存”被锁要大很多倍;

其次,事务性要求严格:

微信红包系统本质上是一个资金交易系统,相比普通商品“秒杀”系统有更高的事务级别要求。

六、解决高并发问题通常使用的方案

普通商品“秒杀”活动系统,解决高并发问题的方案,大体有以下几种。

6.1方案一:使用内存操作替代实时的 DB 事务操作

如图 2 所示,将“实时扣库存”的行为上移到内存 Cache 中操作,内存 Cache 操作成功直接给 Server 返回成功,然后异步落 DB 持久化。

这个方案的优点是用内存操作替代磁盘操作,提高了并发性能。

但是缺点也很明显,在内存操作成功但 DB 持久化失败,或者内存 Cache 故障的情况下,DB 持久化会丢数据,不适合微信红包这种资金交易系统。

6.2方案二:使用乐观锁替代悲观锁

所谓悲观锁,是关系数据库管理系统里的一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作对某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。对应于上文分析中的“并发请求抢锁”行为。

所谓乐观锁,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

商品“秒杀”系统中,乐观锁的具体应用方法,是在 DB 的“库存”记录中维护一个版本号。在更新“库存”的操作进行前,先去 DB 获取当前版本号。在更新库存的事务提交时,检查该版本号是否已被其他事务修改。如果版本没被修改,则提交事务,且版本号加 1;如果版本号已经被其他事务修改,则回滚事务,并给上层报错。

这个方案解决了“并发请求抢锁”的问题,可以提高 DB 的并发处理能力。

但是如果应用于微信红包系统,则会存在下面三个问题:

  • 1)如果拆红包采用乐观锁:那么在并发抢到相同版本号的拆红包请求中,只有一个能拆红包成功,其他的请求将事务回滚并返回失败,给用户报错,用户体验完全不可接受;
  • 2)如果采用乐观锁:将会导致第一时间同时拆红包的用户有一部分直接返回失败,反而那些“手慢”的用户,有可能因为并发减小后拆红包成功,这会带来用户体验上的负面影响;
  • 3)如果采用乐观锁的方式:会带来大数量的无效更新请求、事务回滚,给 DB 造成不必要的额外压力。

基于以上原因,微信红包系统不能采用乐观锁的方式解决并发抢锁问题。

七、微信红包系统的高并发解决方案

综合上面的分析,微信红包系统针对相应的技术难点,采用了下面几个方案,解决高并发问题。

7.1系统垂直 SET 化,分而治之

微信红包用户发一个红包时,微信红包系统生成一个 ID 作为这个红包的唯一标识。接下来这个红包的所有发红包、抢红包、拆红包、查询红包详情等操作,都根据这个 ID 关联。

红包系统根据这个红包 ID,按一定的规则(如按 ID 尾号取模等),垂直上下切分。切分后,一个垂直链条上的逻辑 Server 服务器、DB 统称为一个 SET。

各个 SET 之间相互独立,互相解耦。并且同一个红包 ID 的所有请求,包括发红包、抢红包、拆红包、查详情详情等,垂直 stick 到同一个 SET 内处理,高度内聚。通过这样的方式,系统将所有红包请求这个巨大的洪流分散为多股小流,互不影响,分而治之,如下图所示。

这个方案解决了同时存在海量事务级操作的问题,将海量化为小量。

7.2逻辑 Server 层将请求排队,解决 DB 并发问题

红包系统是资金交易系统,DB 操作的事务性无法避免,所以会存在“并发抢锁”问题。但是如果到达 DB 的事务操作(也即拆红包行为)不是并发的,而是串行的,就不会存在“并发抢锁”的问题了。

按这个思路,为了使拆红包的事务操作串行地进入 DB,只需要将请求在 Server 层以 FIFO(先进先出)的方式排队,就可以达到这个效果。从而问题就集中到 Server 的 FIFO 队列设计上。

微信红包系统设计了分布式的、轻巧的、灵活的 FIFO 队列方案。其具体实现如下:

首先,将同一个红包 ID 的所有请求 stick 到同一台 Server。

上面 SET 化方案已经介绍,同个红包 ID 的所有请求,按红包 ID stick 到同个 SET 中。不过在同个 SET 中,会存在多台 Server 服务器同时连接同一台 DB(基于容灾、性能考虑,需要多台 Server 互备、均衡压力)。

为了使同一个红包 ID 的所有请求,stick 到同一台 Server 服务器上,在 SET 化的设计之外,微信红包系统添加了一层基于红包 ID hash 值的分流,如下图所示。

其次,设计单机请求排队方案。

将 stick 到同一台 Server 上的所有请求在被接收进程接收后,按红包 ID 进行排队。然后串行地进入 worker 进程(执行业务逻辑)进行处理,从而达到排队的效果,如下图所示。

最后,增加 memcached 控制并发。

为了防止 Server 中的请求队列过载导致队列被降级,从而所有请求拥进 DB,系统增加了与 Server 服务器同机部署的 memcached,用于控制拆同一个红包的请求并发数。

具体来说,利用 memcached 的 CAS 原子累增操作,控制同时进入 DB 执行拆红包事务的请求数,超过预先设定数值则直接拒绝服务。用于 DB 负载升高时的降级体验。

通过以上三个措施,系统有效地控制了 DB 的“并发抢锁”情况。

7.3双维度库表设计,保障系统性能稳定

红包系统的分库表规则,初期是根据红包 ID 的 hash 值分为多库多表。随着红包数据量逐渐增大,单表数据量也逐渐增加。而 DB 的性能与单表数据量有一定相关性。当单表数据量达到一定程度时,DB 性能会有大幅度下降,影响系统性能稳定性。采用冷热分离,将历史冷数据与当前热数据分开存储,可以解决这个问题。

处理微信红包数据的冷热分离时,系统在以红包 ID 维度分库表的基础上,增加了以循环天分表的维度,形成了双维度分库表的特色。

具体来说,就是分库表规则像 db_xx.t_y_dd 设计,其中,xx/y 是红包 ID 的 hash 值后三位,dd 的取值范围在 01~31,代表一个月天数最多 31 天。

通过这种双维度分库表方式,解决了 DB 单表数据量膨胀导致性能下降的问题,保障了系统性能的稳定性。同时,在热冷分离的问题上,又使得数据搬迁变得简单而优雅。

综上所述:微信红包系统在解决高并发问题上的设计,主要采用了 SET 化分治、请求排队、双维度分库表等方案,使得单组 DB 的并发性能提升了 8 倍左右,取得了很好的效果。

八、本文小结

微信红包系统是一个高并发的资金交易系统,最大的技术挑战是保障并发性能与资金安全。

这种全新的技术挑战,传统的“秒杀”系统设计方案已不能完全解决。在分析了业界“秒杀”系统解决方案的基础上,微信红包采用了 SET 化、请求排队串行化、双维度分库表等设计,形成了独特的高并发、资金安全系统解决方案,并在平时节假日、春节红包雨实践中充分证明了可行性,取得了显著的效果。以2017 鸡年除夕夜为例,微信红包收发峰值达到 76 万每秒,收发微信红包 142 亿个,微信红包系统的表现稳定,实现了除夕夜系统零故障。

九、更多鹅厂技术文章汇总

微信朋友圈千亿访问量背后的技术挑战和实践总结

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

IM全文检索技术专题(二):微信移动端的全文检索多音字问题解决方案

腾讯技术分享:Android版手机QQ的缓存监控与优化实践

微信团队分享:iOS版微信的高性能通用key-value组件技术实践

微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

腾讯技术分享:Android手Q的线程死锁监控系统技术实践

微信团队原创分享:iOS版微信的内存监控系统技术实践

让互联网更快:新一代QUIC协议在腾讯的技术实践分享

(本文已同步发布于:http://www.52im.net/thread-2548-1-1.html

posted @ 2025-01-13 11:39 Jack Jiang 阅读(110) | 评论 (0)编辑 收藏

本文由腾讯技术团队原创分享于鹅厂黑板报,下文有排版优化。

1、写在前面

直至现在,「微信鸿蒙版」这五个字,依然被赋予着太多意义。

这是一款产品,也不仅仅是一款产品。开发它的本质,是让两个高速前进,相互影响的复杂系统,彼此磨合和熟悉,像是执行一场空中加油任务。

不管外界如何评价和鞭策,这款产品本身,依然需要研发团队一个键一个键敲出来,从内核,到架构,到内测,到公测,再到一轮一轮的 debug,他们要在不到一年的时间里,走完微信14 年的路。

回顾鹅厂所做过的产品里,也许从未有过一款,被如此放在放大镜下凝视。每一次上架,每一个 bug,乃至于每一个里程碑,几乎都预定当天热搜。

站在正式版发布的1 月 9 日,或许这一切都可以风轻云淡地说:the show must go on。但这过去的 295 天里,他们的经历,我们认为值得记录下来,分享给关心微信鸿蒙版的用户朋友们。

2、2024年3月,集结

鹅厂指派了从塞班(Symbian)时期就负责微信开发工作的团队,来主导微信鸿蒙版。从塞班到智能手表、车机、Linux PC 端的微信,这个团队在内部素以擅长攻克不同环境、不同语言的开发工作著称。

同样很重要的一点是,得益于智能手表端微信的研发工作,微信和华为的两个团队是老相识,这也让双方的对接更加顺畅紧密起来。从三月贯穿到四月,两边通过拉通会、分享会学习鸿蒙系统研发框架,不定时组织技术专题讨论。

双方都很清楚,这不是一场三天两夜就能解决的小规模战斗,而是旷日持久的兵团级战役。兵马未动,粮草先行,敲下第一行代码之前,还有许许多多的工作需要准备。

3、2024年4月,基建

万丈高楼平地起,基建是最重要的第一步。

搞基建,“三通一平”(通电/通路/通水/土地平整)是基本要求,进取一些,可以做到“五通一平”(加入通讯/排污),再进一步,还有“七通一平”(加入通气/有线电视),乃至于“十通一平”(加入宽带/铁路/暖气)。通得越多,越有利于后期扩展和长远发展。

经过塞班、手机、手表等各种终端上的长期打磨,这个团队积累了一套名为Alita(阿丽塔)的跨平台内核。这也为鸿蒙版微信的基建打下了基础。这个阶段的重中之重是,快速熟悉鸿蒙系统,移植基础库,让 Alita 内核能够在鸿蒙系统上运行起来,和华为一边沟通、一边验证推进。

4、2024年5月,架构

接下来考验的是架构能力。开发团队需要设计好鸿蒙微信客户端的架构、编写好各模块文档,支撑各业务进场后能够高效开发。

这一步的难点,在于充分预判到业务之间的复杂解耦,既要降低各业务之间的依赖性,又要提高整体的稳定性,还要留出高可扩展性,属于典型的“我全都要”难题。

这就好比从零开始建设一座城市,要预估到这座百年之后超级都市的人口规模、交通状况、人居需求、产业结构、商业发展等因素,以及提前平衡这些因素之间的关系,需要具备极大的前瞻视角。

技术团队继续摇人,招聘也快马加鞭推进。TAPD(腾讯敏捷产品研发平台)流程图里,他们的首个目标是做出一个基础版本,保证用户能实现收发消息、语音通话等最基础、也是最重要的功能。

5、2024年6月,磨合

进入了真正的手搓环节。flutter(跨平台应用程序开发框架)、liteapp(专为移动端设计的跨平台开发框架)等,都是这个阶段的关键工作。

为了这桌“年夜饭”,技术小哥们一边在厨房切菜烧饭,一边去客厅招呼各方沏茶倒水,让支付和VoIP(语音通话技术)等基础能力陆续凑上一桌。

除了内外部密切的技术沟通,微信和华为团队对彼此的技术标准保持了互相尊重。以相册选图发送功能为例,在 Android 系统上,选图需要获取整个相册权限,也就是说应用可以访问用户的所有照片。在鸿蒙上的选图功能,为了保障用户隐私,微信采用的是 Picker 控件的方式,相册照片的展示和选择逻辑都由 Picker 控件提供,微信只能读取到用户勾选的照片。

6、第一个里程碑:bug 如约而至

赶在6月21日前,团队做好了第一个内部体验版本,包含收发消息、通话功能。和2011年1月21日发布的 iOS和安卓版的微信1.0版本相比,多了语音消息发送。

你可能会不以为然:大动干戈这么久,就整了个这毛坯房?

其实这里蕴含的开发思路,是验证最小可用的原则,本质上是对第一阶段研究鸿蒙语言和系统的成果验收。重要的是把基本功练好,才能为后续的开枝散叶打好底子。

但即便是如此普通的版本,也出了个闪退型 bug,最后查出来是系统的底层 API 问题:同样的代码逻辑,在 iOS 和安卓上能用,但在鸿蒙上行不通。两边团队为此绞尽脑汁,交了两个星期的学费,最后还是靠着某位技术小哥灵光一现想到的。

这个 bug也像是一场结业考试,经此一役,开发进入了快节奏。

微信集合了众多产品功能,各功能间又有复杂的交互和依赖关系,比如小程序的开发就涉及到与支付功能的打通,而支付能力又需要与基础会话功能打通。在完成基建的前提下,基础、支付、小程序……能进场的业务模块都陆续进了场。一个共同的目标是——10月8号鸿蒙公测那天,做出一个新版本。这个版本,将新增微信支付、朋友圈等功能。

7、2024年10月8日:喜欢您来

10月8日,微信鸿蒙原生版开启内测邀请,尝鲜版本包含基础社交通讯音视频通话、朋友圈、微信支付的二维码收/付款等功能。

内测开启,意味着微信和其他所有适配原生鸿蒙的第三方App一样,从内测到应用尝鲜再到公测,走上了鸿蒙系统第三方软件开发的三部曲。

为什么要限量内测而不是一口气开放下载呢?

在全新的平台上,要支撑海量用户、高并发通讯需求,同时涉及支付、小程序、视频等多个大功能模块,还要满足极高频使用下的稳定性,是很大的挑战。

所以,用内测 → 找bug → 修bug → 加大内测的方式,是一个更符合软件开发规律的方式。

经历了4天紧张的测试和debug,包括微信支付在内的多个功能经过严格测试流程后,合入大版本,10 月 12 日,微信鸿蒙原生版正式开始公测。

8、2024年10月~11 月:这都能遇到灰产啊啊啊

公测放量过程中,有一次实际登陆人数不到放量总数的十分之一?某平台上竟然有人公然售卖测试名额?

一系列插曲打破了原定的放量节奏,双方共同排查后发现,原来有人把安装包拿去二手平台牟利。应用商店完善机制后,把漏洞补上。

安装包都能拿来卖,也堪称是国产软件开发史上浓墨重彩的一笔。

微信鸿蒙版在尝鲜专区上线了2万测试名额,但后台显示,登录数据一直较低,我们和华为一同复盘发现,因为有人用脚本去抢名额,触发了应用商店的安全机制,同时扰乱了应用商店的计数逻辑,导致大概90% 的放量被拦截,最终实际下载的用户只有 10%左右。

又是浓墨重彩的一笔......

如何让用户尽可能体验到微信测试版本?

在基本保障尝鲜专区不断档的情况下,11 月 6 日,双方紧急协商,华为将微信鸿蒙版的测试名额大幅扩容,微信再次邀请扩容后的用户分批有序参与内测,共同完善新版本的各种体验。

在不断收集用户反馈、历经数次迭代后,目前的版本已经可以使用视频号、聊天引用、发文件等功能,所有鸿蒙用户也都可以直接下载,更多功能在持续上线。

9、2025年1月9日:不止是微信

吸收了广大用户的反馈和多轮debug后,鸿蒙版微信顺利结束公测,1月9日正式版本上线。你除了能稳定下载和使用微信外,还可以用到 QQ、腾讯视频、腾讯新闻、QQ 音乐等App。

自今年起,腾讯20多款产品通过敏捷开发,实现鸿蒙系统的适配工作,更多腾讯的产品适配也在路上。

一个发生在2024年10月29日的插曲,某种程度上,可以反映微信鸿蒙版开发团队的工作情形和协作流程:

19:20,项目组微信支付团队发现,即将要上架的最新尝鲜版的微信,小部分用户转账入口出现bug,点击后无反应。

20:15,客服团队同步后台客诉情况。

20:57,微信支付团队初步定位,有问题的代码是今日合入导致的,疑似是LiteApp(跨端的框架,微信转账是鸿蒙第一个使用这个框架的功能)的问题。

21:31,进一步定位问题,发现在一些极端情况下, LiteApp的文件缓存写入被系统提示权限不足,联系华为技术团队一起定位。

21:47,支付技术团队完成最新内测版微信的修复,合入后,提交版本给测试团队。

22:32,支付技术团队复盘问题,提出后续改进措施。

22:41,微信基础技术团队向华为应用商店提审新版本内测包。

22:54,向华为应用商店提审尝鲜版。

23:30,最新尝鲜版微信通过审核,上架尝鲜专区,转账问题修复。

微信公众平台曾有一句 slogan 深入人心:再小的个体,也有自己的品牌。同样的,再小的问题,放在微信上,都会被亿量级地扩大。

我们知道,永远等不来“完美交付”这一天。灰度测试、持续迭代,让产品在和用户的互动中得到改进,是腾讯一直以来的产品理念。

感谢微信用户、鸿蒙用户始终跟我们站在一起,7x24小时反馈bug、提出优化意见。如果把新产品开发比做一场足球赛,那希望你们一直都在,做我们敏捷开发“球队”的第12人。

10、微信的其它故事

技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail

QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年

2017微信数据报告:日活跃用户达9亿、日发消息380亿条

腾讯开发微信花了多少钱?技术难度真这么大?难在哪?

技术往事:“QQ群”和“微信红包”是怎么来的?

开发往事:深度讲述2010到2015,微信一路风雨的背后

开发往事:微信千年不变的那张闪屏图片的由来

开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)

一个微信实习生自述:我眼中的微信开发团队

为什么说即时通讯社交APP创业就是一个坑?

微信七年回顾:历经多少质疑和差评,才配拥有今天的强大

前创始团队成员分享:盘点微信的前世今生——微信成功的必然和偶然

即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等

QQ现状深度剖析:你还认为QQ已经被微信打败了吗?

[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?

QQ和微信止步不前,意味着即时通讯社交应用创业的第2春已来?

那些年微信开发过的鸡肋功能,及其带给我们的思考

读懂微信:从1.0到7.0版本,一个主流IM社交工具的进化史

同为IM社交产品中的王者,QQ与微信到底有什么区别

社交应用教父级人物的张小龙和马化腾的同与不同

专访马化腾:首次开谈个人经历、管理心得、技术创新、微信的诞生等

一文读懂微信之父张小龙:失败天才、颠覆者、独裁者、人性操控师


(本文已同步发布于:http://www.52im.net/thread-4776-1-1.html

posted @ 2025-01-10 11:13 Jack Jiang 阅读(46) | 评论 (0)编辑 收藏

Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang