亿级用户中心的设计与实践

亿级⽤户中⼼的设计与实践
⽤户中⼼是互联⽹最为基础的核⼼系统,随着业务和⽤户的增长,势必会带来不断的挑战。如何在亿级的情况下保证系统的⾼可⽤,⾼性能以及⾼安全,本⽂能够给你⼀套实践⽅案。
注1:本⽂讨论的是微服务框架下的⽤户中⼼,不涉及授权等功能;
注2:本⽂所涉及的⽤户中⼼设计与vivo⾃⾝业务⽆关。
⽤户中⼼,顾名思义就是管理⽤户的地⽅,⼏乎是所有互联⽹公司最为核⼼的⼦系统之⼀。它的核⼼功能是登录与注册,主要功能是修改密码、换绑⼿机号码、获取⽤户信息、修改⽤户信息和⼀些延伸服务,同时还有登录之后⽣成Token以及校验Token的功能。下⾯我们从⼏个维度来拆解⽤户中⼼。
⼀、服务架构
夫妻生活指南⽤户中⼼既需要为⽤户提供服务,也会承担其他业务的频繁调⽤;既然需要为⽤户提供服务,它就会⾃带⼀些业务逻辑,⽐如⽤户在登录过程中需要风控或短信的校验,那么就会存在不可⽤的风险。⽽⽐如获取⽤户信息的接⼝,则没有那么多的依赖,可能只需要调⽤数据库或者缓存就可以。获取⽤户信息接⼝要求稳定,⽽核⼼的登录注册接⼝也需要稳定,但是当我们在接⼝层⾯加⼀些策略或者修改的时候,不希望因为上线问题导致整个服务不可⽤,⽽且上线后,需要对整个服务功能做全量的回归,导致资源
严重浪费。
因此,基于业务特性,我们可以将⽤户中⼼拆成3个独⽴的微服务: ⽹关服务,核⼼服务,异步消费者服务。⽹关服务,提供http服务,聚合了各种业务逻辑和服务调⽤,⽐如登录时候需要校验的风控或者短信;核⼼服务,处理简单的业务逻辑以及数据存储,核⼼服务处在调⽤链路的终端,⼏乎不依赖调⽤其他服务,⽐如校验Token或者获取⽤户信息,他们就只依赖于redis或者数据库;⽽异步消费者服务,则处理并消费异步消息。下⽂会详细介绍。
image
这样的设计之后,当有新功能上线时,核⼼服务和异步消费服务⼏乎不需要重新发布,只需要发布⽹关服务,依赖我们核⼼服务的第三⽅⾮常放⼼,层级也⾮常的清晰。当然,这样做的代价就是服务的调⽤链路变长了。由于涉及到⽹关和核⼼服务,就需要发布两个服务,⽽且要做兼容性测试。
⼆、接⼝设计
⽤户中⼼的接⼝涉及到⽤户的核⼼信息,安全性要求⾼;同时,承接了较多第三⽅的调⽤,可⽤性要求也⾼。因此,对⽤户中⼼的接⼝做以下设
科学小怪蛋计:
⾸先,接⼝可以拆分为⾯向Web和⾯向App的接⼝。Web接⼝需要做到跨域情况下的单点登录,加密、验签和token校验的⽅式也同App端的不⼀样。
其次,对核⼼接⼝做特殊处理。⽐如登录接⼝,在逻辑和链路上做了⼀些优化。为什么要对这些接⼝做特殊处理呢?假如⽤户不能登录,⽤户会⾮常恐慌,客诉量会⽴马上来。
那怎么做呢?⼀⽅⾯,我们将⽤户核⼼信息表做简单。⽤户的信息当中会包含userId、⼿机号码、密
血凝素码、头像、昵称等字段,假如把⽤户的这些所有信息都保存在⼀张表中,那么这张表将会异常庞⼤,变更字段变得异常困难。因此,需要将⽤户表拆分,将核⼼的信息保存在⽤户表中,⽐如userId、username、⼿机号码、密码、盐值(随机⽣成)等;⽽⼀些如性别,头像,昵称等信息保存在⽤户资料表中。
另⼀⽅⾯,我们需要将登录的核⼼链路做短,短到只依赖于读库。⼀般情况下,⽤户登录后,需要记录⽤户登录信息,调⽤风控或者短信等服务。对于登录链路来说,任何⼀个环节出现问题都有可能导致⽤户⽆法登录,那么怎么样才能做到最短的链路呢?⽅法就是依赖的服务可⾃动降级。⽐如说反欺诈校验出问题了,那么它⾃动降级后使⽤它的默认策略,极端情况下只做密码校验,主库挂了之后还能到从库读取⽤户信息。
最后就是接⼝的安全性校验。对App接⼝我们需要做防重放和验签。验签可能⼤家⽐较熟悉,但是对防重放这个概念可能相对陌⽣。防重放,顾名思义就是防⽌请求重复发送。⽤户请求在特定时间段内只能请求⼀次。即使⽤户请求被攻击者挟持,在⼀段时间内也⽆法重复请求。如果攻击者想要篡改⽤户请求再发送,对不起,请求不会通过。得益于⼤数据的⽀持,结合终端,我们还可以把每个⽤户⾏为画像存储在系统中(或者调⽤第三⽅服务)。⽤户发起请求后,我们的接⼝会根据⽤户画像对⽤户进⾏诸如⼿机号码校验、实名认证、⼈脸或者活体校验。
image四面墙内
三、分库分表
随着⽤户的增长,数据超过了1亿,怎么办?常见的办法就是分库分表。我们来分析⼀下⽤户中⼼常见的⼀些表结构:⽤户信息表,第三⽅登录关联表,⽤户事件表。从上述表中可以看出来,⽤户相关的数据表增长相对缓慢,因为⽤户增长是有天花板的。⽤户事件表的增长是呈指数级增长,因为每个⽤户登录、变更等密码及变更⼿机号码等操作是不限次数。
因此,⾸先我们可以先把⽤户信息表垂直切分。正如上⾯说的,将⽤户ID、密码、⼿机号、盐值等常见字段从⽤户信息表中拆分,其他⽤户相关的信息⽤单独⼀张表。另外,把⽤户事件表迁移⾄其他库中。相⽐于⽔平切分,垂直切分的代价相对较少,操作起来相对简单。⽤户核⼼信息表由于数据量相对较少,即使是亿级别的数据,利⽤数据库缓存的机制,也能够解决性能问题。
梅山降糖神茶
其次,我们可以利⽤前后台业务的特性采⽤不同的⽅式来区别对待。对于⽤户侧前台访问:⽤户通过username/mobile登录或者通过uid来查询⽤户信息。⽤户侧信息的访问通常是单条数据的查询,我们可以通过索引多次查询来解决⼀致性和⾼可⽤问题。对于运营侧后台访问:根据年龄、性别、登录时间段、注册时间段等来进⾏查询,基本上都是批量分页查询。但是由于是内部系统,查询量低,对⼀致性要求低。如果⽤户侧和运营侧的查询采⽤同⼀个数据库,那么运营侧的排序查询会导致整个库的CPU上升,查询效率下降,影响到⽤户侧。因此,运营侧使⽤的数据库可以是和⽤户侧同样的MySQL离线库,如果想要增加运营侧的查询效率,可以采⽤ES⾮关系型数据库。ES⽀持分⽚与复制,⽅便⽔平分割和扩展,复制保证了ES的⾼可⽤与⾼吞吐,同时能够满⾜运营侧的查询需求。
最后,如果还是要⽔平切分来保证系统的性能,那么我们采取什么样的切分⽅式呢?常见的⽅法有索引表法和基因法。索引表法的思路主要是UID 能够直接定位到库,但是⼿机号码或者username是⽆法直接定位到库的,需要建⽴⼀个索引表来记录mobile与UID或者username与UID的映射关系的⽅式来解决这个问题。通常这类数据⽐较少,可以不⽤分库分表,但是相⽐直接查询,多了⼀次数据库查询
的同时,在新增数据的时候还多了⼀次映射关系的插⼊,事务变⼤。基因法的思路是我们将username或者mobile融⼊到UID中。具体做法如下:、
1. ⽤户注册时,根据⽤户的⼿机号码,利⽤函数⽣成N bit的基因mobile_gen,使得mobile_gen=f(mobile);
2. ⽣成M bit全局唯⼀的id,作为⽤户标识;
3. 拼接M和N,作为UID赋给⽤户;
4. 根据N bit来取余来插⼊到特定数据库;
5. 查⽤户数据的时候,将⽤户UID的后N bit取余来落到最终的库中。
从上述过程中看,基因法只适⽤于某类经常查询的场景,⽐如⽤⼿机号码登录,如果⽤户使⽤username登录就⽐较⿇烦了。因此⼤家以根据⾃⼰的业务场景来选择不同的⽅式⽔平切分。
四、Token之柔性降级
⽤户登录之后,另⼀个重要的事情就是Token的⽣成与校验。⽤户的Token分为两类, ⼀类是web端登
陆⽣成的Token, 这个Token可以和Cookie结合, 达到单点登陆的效果,在此不细说了。另外⼀类就是APP端登录⽣成的Token。⽤户在我们的APP输⼊⽤户名密码之后,服务端会对⽤户的⽤户名密码进⾏校验,成功之后从系统配置中⼼获取加密算法的版本以及秘钥,并按照⼀定的格式排列⽤户ID,⼿机号、随机码以及过期时间,经过⼀系列的加密之后,⽣成了Token之后并将其存⼊Redis缓存。⽽Token的校验就是把⽤户ID和Token组合并校验是否在Redis中存在。那么假如Redis不可⽤了怎么办呢?这⾥有⼀个⾼可⽤和⾃动降级的设计。当Redis不可⽤的时候, 服务端会⽣成⼀个特殊格式的Token。当校验Token的时候,会对Token的格式进⾏⼀个判断。
假如判断为Redis不可⽤时⽣成的Token,那么服务端会对Token进⾏解密,⽽Token的⽣成是由⽤户ID,⼿机号、随机码和过期时间等数据按照特定顺序排列并加密⽽来的, 那么解密出来的数据中也包含了ID,⼿机号码,随机码和过期时间。服务端会根据获取到的数据查询数据库, ⽐对之后告诉⽤户是否登录成功。由于内存缓存redis和数据库缓存性能的差距,在redis不可⽤的情况下,降级有可能会导致数据库⽆法及时响应,因此需要在降级的⽅法上加⼊限流。
image
五、数据安全
数据安全对⽤户中⼼来说⾮常重要。敏感数据需要脱敏处理,对密码更是要做多重的加密处理。应⽤虽然有⾃⼰的安全策略,但如果把⿊客限制在登录之前,那应⽤的安全性将得到⼤幅度的提升。互联⽹上⽤户明⽂数据遭到泄露的案件屡屡发⽣,因此各⼤企业对数据安全的认识也提到了前所未有的⾼度。⽽即使使⽤了MD5和salt的加密⽅式,依然可以使⽤彩虹表的⽅式来破解。那么⽤户中⼼对⽤户信息是怎么保存的呢?李尙福 国防科技大学
⾸先,正如上⽂中提到的⽤户密码、⼿机号等登录信息和其他的信息分离,⽽且在不同的数据库中。其次,对⽤户设置的密码进⾏了⿊名单校验,只要符合条件的弱密码,都会拒绝提交,因为不管使⽤了什么加密⽅式的弱密码,都极其容易破解。为什么呢?因为⼈的记性很差,⼤部分⼈总是最倾向于选择⽣⽇,单词等来当密码。6位纯数字可以⽣成100万个不同的密码,8位⼩写字母和数字的组合⼤概可以⽣成2.8万亿个不同的密码。⼀个规模为7.8万亿的密码库⾜以覆盖⼤部分⽤户的密码,对于不同的加密算法都可以拥有这样⼀个密码库,这也就是为什么⼤部分⽹站都建议⽤户使⽤8位以上数字加字母密码的原因。当然,如果⼀⽅⾯加了盐值,另⼀⽅⾯对密钥分开保管,破解难度会指数级增加。
最后,可以⽤bcrypt/scrypt的⽅式来加密。bcrypt算法是基于Blowfish块密钥算法来实现的,bcrypt内部实现了随机加盐处理,使⽤bcrypt 之后每次加密后的密⽂都不⼀样,同时还会使⽤内存初始化hash过程。由于使⽤内存,虽然在CPU上运⾏很快,但是在GPU上并⾏运算并不快。随着新的FPGA集成了⼤型RAM,解决了内存密集IO的问题,但是破解难度依然不⼩。⽽scrypt算法弥补了bcrypt算法的不⾜,它将CPU计算与内存使⽤开销都指数级提升了。bcrypt和scrypt算法能够有效抵御彩虹表,但是安全性的提升带来了⽤户登录性能的下降。⽤户登录注册并不是⼀个⾼并发的接⼝,所以影响并不会特别⼤。因此在安全和性能⽅⾯需要依据业务类型和⼤⼩来做平衡,并不是所有的应⽤都需要使⽤这种加密⽅式来保护⽤户密码。
六、异步消费设计
此处的异步消费,就是上⽂提到的异步消费服务。⽤户在做完登录注册等操作后,需要记录⽤户的操作⽇志。同时,⽤户注册登录完毕后,下游业务需要对⽤户增加积分,赠送礼券等奖励操作。这些系统如果都同步依赖于⽤户中⼼,那么整个⽤户中⼼将异常庞⼤,链路⾮常冗长,也不符合业内的“⼤系统做⼩“的原则。依赖的服务不可⽤之后将会造成⽤户⽆法登录注册。因此,⽤户中⼼在⽤户操作完之后,将⽤户事件⼊库后发送⾄MQ,第三⽅业务监听⽤户事件。⽤户中⼼和下游业务解耦,同时⽤户操作事件⼊库后,在MQ不可⽤或者消息丢失的时候可做补偿处理。⽤户的画像数据也在很⼤程度上来源于此处的数据。
七、灵活多样的监控
⽤户中⼼涉及到⽤户的登录注册更改密码等核⼼功能,能否及时发现系统的问题成为关键指标,因此对业务的监控显得尤为重要。需要对⽤户中⼼重要接⼝的QPS、机器的内存使⽤量、垃圾回收的时间、服务的调⽤时间等做详细的监控。当某个接⼝的调⽤量下降的时候,监控会及时发出报警。除了这些监控之外,还有对数据库Binlog的写⼊,前端组件,以及基于ZipKin全链路调⽤时间的监控,实现从⽤户发起端到结束端的全⾯监控,哪怕出现⼀点问题,监控随时会告诉你哪⾥出问题了。⽐如运营互动推⼴注册量下降的时候,⽤户中⼼就会发出报警,可以及时通知业务⽅改正问题,挽回损失。
⼋、总结
本⽂从服务架构设计,接⼝设计,token降级,数据安全和监控等⽅⾯介绍了亿级⽤户中⼼的设计,当然⽤户中⼼的设计远不⽌这些,还会包含⽤户数据的分库分表,熔断限流,第三⽅登录等,在本⽂中就不⼀⼀赘述。尽管本⽂中设计的⽤户中⼼能够满⾜⼤部分公司的需求,但是还存在⼀些⽐较⼤的挑战:在鉴权服务增长的情况下,如何平滑的从⽤户中⼼剥离;监控的侵⼊性以及监控的粒度的完善;另外服务的安全性、可⽤性、性能的提升永远都没有尽头,也是我们孜孜追求的⽬标。在未来的⽇⼦⾥,希望能够通过⼤家的努⼒,使⽤户中⼼的技术体系更上⼀层楼。
作者:vivo 游戏技术团队

本文发布于:2024-09-22 12:56:17,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/635079.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:服务   登录   需要   信息   密码   数据   问题
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议