• Feeds

  • Archive for the ‘架构’ Category


    微信红包金额分配的算法

    虽然春节已经过去一段时间,但不少微信群里面依旧乐此不疲的在玩发红包活动,用户自发的将最初的一个春节拜年的场景功能慢慢演化成一个长尾功能。

    用户在微信中抢红包时分成抢包和拆包两个操作。抢包决定红包是否还有剩余金额,但如果行动不够迅速,在拆包阶段可能红包已经被其他用户抢走的情况。

    红包的金额是在什么时候算? 据某架构群腾讯财付通专家反馈,红包的金额是拆的时候实时计算,而不是预先分配,实时计算基于内存,不需要额外存储空间,并且实时计算效率也很高。每次拆红包时,系统取0.01到剩余平均值*2之间作为红包的金额。

    为了保证每次操作的原子性,拆包过程中使用了CAS,确保每次只有一个并发用户拆包成功。拆包CAS失败的用户可以由系统自动进行重试。但也有可能在重试过程中被别的用户抢得先机而空手而归,因此严格意义拆包的调用也未能保证用户先到先得。

    基于上面的原因,当时在群中提到这种算法有些复杂,微信红包为了减少存储,每次进行了一个理解稍复杂的实时计算。对比大部分架构师想到的预分配金额的做法,预先分配金额需要将金额保存在一个内存队列中,如果红包的份额较多,则需要较大的存储空间。而微信红包仅保存 count:balance 这样2个数字。count指还剩几个人可以抢,balance只还剩下的金额。

    但是预分配金额也并不是非得需要额外存储。比如利用随机算法,在种子相同的情况下,随机数实际上返回的随机序列也是固定的。如以下Python代码,对于给定的seed 1024,每次执行返回的结果都是相同的。
    >>> import random
    >>> random.seed(1024)
    >>> random.randint(1,100)
    80
    >>> random.randint(1,100)
    49
    >>> random.randint(1,100)
    39
    >>> random.randint(1,100)
    83
    >>> random.randint(1,100)
    88

    因此预分配金额也只需要额外存储一个种子,或利用一些红包id做加密变换做seed达到零存储。而在发放红包时候,无需进行CAS操作,而只需要对剩余红包count做一个DECR操作。当count<0时,表示红包被拆包抢完。由于DECR是原子操作,无需加锁,用简单的方法达到了先拆包先得,原理上不存在早拆包但由于并发冲突失败而抢不到红包的情况。 每个人分配的金额是:total * random(n) / random_total,不需要重复计算。 random(1)..random(n)不需要保存,因为对于给定的seed,random(1)到random(n)返回是固定的。 以上算法评论与对比,与Tim所在雇主的红包算法无关,特此声明。

    部分细节下面列表已做说明,未做详细阐述。

    Reference:
    1、微信红包的架构设计简介
    2、网友周航老师基于聊天记录整理的微信红包架构图(点击查看大图)
    wechat architecture
    3、微信红包实现原理

    对于上文中提到的架构群感兴趣的朋友可以关注Tim公众号“TimYang_net”后回复“arch”获取进群方式。

    Feed架构-我们做错了什么

    在过去几年,所在的微博技术团队在一定程度成功解决了feed架构的扩展性与性能的问题,大部分精力已经从应对峰值性能或者数据扩展中解放出来。

    feed arch

    几天前,拿着上面这张架构图问内部一些架构师,目前完成的工作及存在的主要问题是什么?

    完成的工作不出意料,大家的观点比较类似,主要在架构的工程成熟度方面。

    • 解决了数据规模大且长尾访问的问题,通过按时间线的冷热分区设计,在MySQL等数据库上成功实现了低成本的长尾分区实现。由于社交产品中大部分访问(90%以上)发生在最近的数据,而较旧的数据(长尾数据)相对访问偏少,因此在存储上利用时间维度进行不同存储区域区分,来达到性能及成本的收益。
    • 解决了数据存储可扩展的问题,可以满足3年左右数据扩展的需要。这个主要利用成熟的数据库拆分方案,简单的实现通常设计成倍扩容,在最初建表时候预留足够多的分表,当容量增长时将这些表迁移到更多服务器上去。
    • 解决了百万QPS访问的问题,主要依赖缓存分区及复制机制的成功应用,通过缓存复制及分级,可以让每秒上十万次访问的数据获得非常好的访问性能,同时数据复制机制可以提高可用性,防止单点机器故障时给数据库带来范围压力。
    • 解决了可用性及错误隔离问题,得益于历年来建设的SLA体系,服务之间有良好的服务契约及错误隔离手段,因此核心功能 有99.99%+的可用性。

    但是对于不足呢?如果架构重新再来,你会怎么做?这个问题回答比较多元,经过整理一些主要观点如下。

    首先是从解决架构的问题说起,上述feed架构主要解决了在社交媒体环境中,实时及可扩展的解决基于关注关系的数据分发问题。

    由于微博是一个实时的网络,人们常把Twitter/微博这种社交媒体产品比作是地球的脉搏,它确实无时不刻都在产生海量的数据,并且产生着不可预测的大量随机峰值访问,业界也是首次随着社交网络的出现遭遇这种架构上的scalability问题,记得在2009-2010年左右,Twitter由于压力过大经常出现鲸鱼页面错误。经过几年的努力,包括微博在内的业界主要公司都已经成功解决了关系模型的数据分发架构问题。

    但在用户的层面,使用社交媒体产品依旧被信息过载的问题所困扰。

    • 虽然每天收到大量信息,但是大部分不是自己感兴趣的信息。
    • 基于用户关注维度的兴趣阅读效率不高,用户关注了一个兴趣领域的同行,但这个同行却大部分时候在谈风花雪月。
    • 由于传播的低成本,大号在过度的使用消息通道,生产大量用户未必感兴趣的低质内容。
    • 广告及普通内容的界限很模糊。
    • 由于利益关系,僵尸帐号及内容通过变换形式在大行其道。
    • 内容同质化及雷同问题,内容被精英占领,小人物声音不能得到有效传递。

    在社交网络中,上述问题,不可能全部像Google的使命那样,通过技术手段得到解决。但是大部分又跟技术密切相关。

    • 基于用户维度组织内容高效满足兴趣阅读的难度。在上图中,绿色的框中是数据流经的主要通道,但在传统的SNS架构中,这些数据的组织都是基于用户纬度。从用户纬度组织数据较好解决了传统feed模型基于关系聚合数据的问题。但正是由于数据是从关系维度来组织的结构,造成并不能给改进上述阅读效率问题比如兴趣维度的阅读目的带来便利。由于在一定程度信息过载,兴趣阅读是发展方向,但是在架构上目前并没有清晰的解法。
    • 信息识别及低质内容鉴定的技术挑战。虽然业界已经在大数据、自然语言处理等方面积累了不少经验,但在具体场景中,信息识别仍然还在比较初级的阶段。低质内容不一定是垃圾广告,只是当前浏览者不感兴趣。
    • 反垃圾算法的难度。僵尸及垃圾数据变换着出现的形式,目前,至少从实时性算法上识别垃圾及僵尸仍然有很大挑战。

    社交网络的信息架构和搜索引擎有很大的不同,在浏览feed是并不像使用搜索有明确的搜索意图,Facebook曾经尝试使用EdgeRank来解决newsfeed算法的效率问题,但是结果并不如预期理想。由于社交关系的存在,用户对出现(及不出现)什么内容的可解释性非常敏感,不能接受好友的信息在排序结果不能看到。除了技术,还有用户体验的问题需要考虑。

    综上所述,feed架构虽然取得了架构及工程在scalability方面的一些问题,但更大的问题及依旧在思考及解决的途中。

    当谈系统重构时,我们谈什么

    weibo team

    又一次团队沙龙,这是一个每周一期的活动,探讨技术或团队一些问题。

    首先,介绍一下本次讨论的背景,参与讨论的是一个多团队的部门,每个团队有不同的职责方向。因此,大部分跨团队的各种技术或协作问题也会碰到,典型的一些场景:

    • 需要升级一个公共库,但是通常并没有一个职能部门来负责公共库升级事项(即使成立也可能最终成为“全国假日办”之类的机构)。
    • 或者是当你负责一个被依赖的模块,当你需要升级时候,发现不少使用方对老版本的feature依赖比较严重,而他们没有精力(或兴趣)迎合升级做大的修改。
    • 团队多了之后,缺少全局的依赖管理,调用之间可能会出现循环依赖的情况(还有间接的循环依赖更复杂)
    • 引入新的第三方库,可能有些团队觉得方便就直接引入了,但又造成了内部使用方法不统一的问题。如果部署在相同的节点,还会对别的模块带来污染的问题。
    • 部分相似的功能大家重复开发,比如一些开关降级工具、发号器等。另外一个层面,A team在开关降级方面新开发了一些实用的feature,但是B team也用不上
    • 从team leader的角度,首要关心的问题是项目的进度及交付目标。因此软件结构的合理性、代码的优雅性以及系统的可扩展性等层面,可能出现优先级未得到保证的情况。“等忙过这一阵,一定要好好重构”,但由于拖延症,长期得不到实现。

    在正式展开讨论之前,介绍一下《人件》原书第三版(感谢华章)中的一个黑衣团队章节。说的是一公司,软件在客户那里出现了很多问题。但由于开发工程师是一群非常有个性也聪明的人,从来不认为自己的代码有问题。因此解决这个问题,公司引入了一群非常有才能的测试工程师,为了更加有个性,他们开始都穿上黑色的衣服,系统一旦有BUG他们就可怕地笑起来,他们的测试根本不是在支持开发人员,而是乐于将软件与工程师放到一种不是测试而是折磨的工序下面;他们还经常聚在一起研究出十分可怕的测试策略,他们一些变态的想法与测试方法让开发工程师望而生畏,欲哭无泪。由于这样一个机制,软件工程师在交付之前自己会进行各种极端的健壮性及正确性判断,最终这个团体逐渐形成自已的个性,也发展了一种渴望并期待发现产品缺陷的哲学。这个机制在不断有人离开的情况下仍然在团队内完好保存。

    但是在重构这个问题,如上面几点所说,团队找不到一种类似凯文·凯利《失控》中描述的机制,不健康的问题第一时间得到了处理。这个可能也是在不少公司同样存在。因此一些主要提出的方案观点如下。

    体检及手术式

    由于上面提到的各种问题,技术体系的健康遭到侵蚀,各个团队还承担各自业务压力,大家的人力资源宽裕程度也不一,因此第一种观点对这些不能及时消除的隐患的存在表示理解,也能意识彻底解决问题的复杂性,但隐患也不能长期不理,因此提出跨团队巡检队定期检查的思路,并将发现的问题列举出来,在约定的时间内完全解决。

    这个做法的逻辑有点类似生活在帝都的IT民工们,在糟糕的雾霾环境下承担着工作的重压,身体健康状况自然不好保证,但也无力改变现状,因此只好通过每年体检来发现身体大的隐患,并尽早排除。

    改变导向式

    这种观点的出发点是人都有惰性,即使有合理的重构理由,在系统还可以勉强运作的前提下,人是没有充足动力去做改变。即使这时候有人跳出来大声疾呼,也不能得到充分的支持与理解,造成了一种机制上的潜在障碍,让健康问题不能第一时间得到解决。

    《人件》第34章标题就是改变成为可能,其中提到,因为人们都不喜欢改变,而你想促成改变的发生,所以你就站在了人性的对立面,自然你就会受到他人的挑战。

    “People hate change…
    and that’s because people hate change…
    I want to be sure that you get my point.
    People really hate change.
    They really, really do.”
    – Steve McMenamin, The Atlantic Systems Guild, 1996

    为了避免这个情况出现,团队需要实行一种“改变为先”的共识 — 只要有人提出一种新的升级(新版本、框架、工具等),团队其他成员原则上不可以提出反对意见。尤其在公共工具库方面,倾向采用在框架配置层面强制升级,再让所有成员被动升级的做法。

    satir_change_model
    《人件》中有关改变的观点则引用萨提亚模型(Satir model),其主要观点是,改变需要至少经过4个阶段,其中的3、4阶段如果没有经历,改变不可能真正落地。因此对于软件开发这样一个以人为本的实践活动,充分意识到混乱也是新事务出现以后的一个必经之路,但混乱不是终点站,有很多团队在做改变时候,从3直接又回去2了。

    多数派决策式

    这种观点的出发点是大的系统、背后大的团队以及成员的关注点及诉求点的多元性。比如

    张三:关注点在存储的成本及效率,由于精力原因,对于公共组件库采用跟随策略,并期望最小的参与成本。
    李四:喜欢尝试新框架,如果团队引入了某个公共的开源框架,对社区每个小的升级迭代充满兴趣,希望将每个版本即时引入到公共库中,并希望所有团队调整自己的代码,跟随及时升级。

    在认可团队多元化价值观的前提下,这个问题不能简单总结成采用某种“稳健型”或者“积极参与型”的思路来决策。但为了团队重构的机制能够存在并运作,可以由各团队选出一名技术代表,每周来讨论一次公共的技术重构事项,并按照多数派的意见进行执行。

    当然上面只是部分主要观点,由于本次讨论各方参与激烈,本文章不做结论,欢迎大家留言介绍你们团队在系统重构方面的做法及机制,希望能从中找到类似上文《人件》或《失控》中描述方法类似效果。

    对微博平台技术沙龙感兴趣的朋友,可以在微博关注话题 #平台技术沙龙# 或微信搜索公众号“WeiboArch”(a.k.a 微博平台架构)来参与。