一个企业级高并发短链接服务项目分享

这个项目我在企业做过的一个高并发服务,功能很简单,但有很多技术难点和技术细节。没有做过企业级项目的同学可以好好看看,想想怎么挖掘自己项目的亮点。也欢迎各位同学跟我讨论,大家理解透彻后,完全可以实操一下这个项目,写在简历中。

1 目标

生产级别的互联网应用的架构、设计思路、原理及调优、演进思路。
涉及到三高:高可用、高性能、高并发。
广度和深度覆盖全面,能够全面体现出程序员的能力水平。

2 使用场景

电商推广:短信、邮件 一般来说,一条短信最多70个汉字,140个字节。如果超出一般会被运营商自动拆分为2条短信,增加了运营成本。
社交网络分享:微博和Twitter都有140字数的限制,如果分享一个长链接,很容易就超出限制。短链接服务可以把一个长链接变成短链接,方便在社交网络上传播。

3 真实案例

alt
我们看到短信里面的URI是数字加子母组成的字符,跟我们平时写代码时的URI不太一样。
实际上这就是一个短链接,其实原始的内部长链接可能长这样:

https://dpubstatic.udache.com/static/dpubimg/d4c0e518e95afb4e669affa6eb15d1d6/index.html  

3.1 工作流程

用户点击这个短链接后,会跳转到内部的长链接。基本流程图:
alt
客户端-->发出短链接请求--> 重定向跳转--->长链接

3.1.1 为什么使用302重定向

重定向 时效性 请求方式 优点
301 永久 第一次会重定向,下次直接从浏览器缓存拿到长链接就可跳转 效率高
302 临时 每次请求都会请求短链接服务器,浏览器不会缓存 方便统计入口链接的访问次数,短链接服务商主要盈利方式之一

4 软件详细设计

4.1 功能性需求

1.给定原始的Long URL,短链服务能生成比它短且唯一的Short URL
2.用户点击Short URL, 能跳转到Long URL
3.有效期/过期机制
4.Short URL越短越好

4.2 非功能性需求

1.高可用:服务不能存在单点故障,用冗余来解决高可用问题。
2.高性能:生成Short URL以及从Short URL 跳转到Long URL 要近实时。读写比预估100:1
3.安全:短链不可被猜测(防止被攻击和滥用,引发不可知的情况)

4.3 短链生成

短链接的原理其实就是:将长链接通过一定的算法生成一个短链接。访问短链接时实际访问的是短链接服务器,然后根据短链接的参数找回对应的长链接重定向跳转。短URL包含一个短链接网站+短连接key。 系统本质上包含一个短链算法模块和一个Hash表。
长URL通过短链算法生成短URL。将短URL作为key,长URL作为value,保存到Hash表中。
用户输入短URL,直接从Hash表中查找并返回长URL。

4.4 算法选择

4.4.1 算法要求

  1. 生成值比原始值短
  2. 唯一性
  3. 高效、低CPU内存消耗
  4. 安全不可预测

4.4.2 常见Hash算法

4.4.2.1 MD5

准确说它是一种信息摘要算法,从一个字符串或一个文件中按照一定的规律生成一个特殊的字符串。
生成的字符串是满足唯一性的,但输出是128位的,还是比较长

4.4.2.2 SHA

SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。
生成的字符串是满足唯一性的。输出长度还是较大,而且这类算法属于加密型HASH算法,本质是为了数据加密传输,后续需要能够逆向得到原始字符串,相应的算法性能比较低。
评价一个哈希算法的好坏,人们通常会引用 SMHasher 测试集的运行结果。 Smhasher 测试Hash函数的功能,测试包括以下几个方面:

  • Sanity 是不是可以使用的
  • Performance 完成一个散列需要多长时间
  • Differentials 产生相同哈希的概率,可能导致相同的的最小差异
  • Keysets 分布均匀程度
    一系列的测试方式具体可参考:https://github.com/aappleby/smhasher/wiki/SMHasher
4.4.2.3 MurmurHash

随机分布特征表现更良好,发生 Hash 碰撞的几率更低。比起 MD5它的性能至少提升了一个数量级。生成的结果为32bit或64bit相比MD5的长度缩短了一个量级。
MurmurHash在很多现代KV型存储中间件中被广泛使用,如在Redis中当字典被用作数据库的底层实现或者哈希键的底层实现时,Redis 使用 MurmurHash2 算法来计算键的哈希值。
算法主页: http://code.google.com/p/smhasher/

4.4.3 如何做到最短

1000万用户平均每个月10条短信 每个月基本上1亿个短链接即可。 预估未来五年增长到5000万用户,5亿短链接基本够用。
通常短链是用 [0-9], [a-z], [A-Z] 这 62 个字符的组合来表示的,我们可以选择用这62个字符对MurmurHash的结果做base62编码,那么长度是 N 的短链可以映射的 URL 数量如下图: alt
5位长度基本满足未来五年需求,6位一定满足,按照6位来设计(扩展性更强,余地更多)。

4.4.4 如何解决HASH冲突

4.4.4.1 冲突识别

方案1:先根据短链接查询数据库,如果不存在则插入;
问题:先查再插入,涉及两次网络请求及可能的磁盘调度
方案2:短链接字段建立唯一索引,直接插入,通过数据库唯一索引检测判断重复
方案3:通过布隆过滤器快速判断短链不存在,不需要唯一索引,可消除磁盘IO调度影响
但这个看似挺好,实际由于布隆过滤器数据易丢失,造成短链生成冲突

4.4.4.2 解决冲突

尽管 MurmurHash 算法,冲突的概率非常低。但是,一旦冲突,就会导致两个原始链接被转化成同一个短链接。可以给原始链接拼接一串特殊字符,比如“[REPEAT]”,然后跟再重新计算哈希值,两次哈希计算都冲突的概率,显然是非常低的。

4.4.5 如何解决重复长地址转换攻击

用户连续发起对同样的长地址转换请求
服务端处理逻辑:
1)长地址 -> 短地址
2)短地址冲突
3)长地址叠加随机值 -> 短地址
4)入库
在大量请求下,一个长地址对应多个短地址,消耗数据库资源,并造成数据库操作效率降低。
解决方案:
1)给长地址也加唯一索引
有没更高效的办法?
2)使用布隆过滤器存储长地址,每次转换前先判断下长地址是否已经转换过。
这种方法效率高,虽然有布隆过滤器数据丢失后造成长地址对应多个短地址,但在高可用Redis集群模式下,丢失的不会太多,对应多个短地址也不影响用户使用

4.4.6 总结

采用计算速度快、冲突概率小的Murmur-Hash 算法,通过hash得到10进制数,然后转化成 62 进制表示法,进一步减小短链接的长度。对于哈希算法的哈希冲突问题,我们通过给原始网址添加特殊前缀字符,重新计算哈希值的方法来解决。

4.5 短链接过期解决方案

短链接数据多为短时效存储数据,不需要永久保存,如优惠券、促销活动等,之前的设计中短地址是永久保存在数据库和分布式缓存中,会带来数据库和Redis的存储资源容量持续增长,最终导致访问数据库速度下降以及Redis的OOM问题。
解决方案:设置过期时间,惰性删除 + 定时删除
1、接口层面
需要再重载一个接口,提供timeout字段可让用户自行决定保留时间,没有提供timeout可根据公司的业务场景给定一个默认保留时间
2、Redis层面
数据入Redis时设置过期时间,按照二八原则定律,百分之八十的数据会在存入数据后的百分之二十的过期时间内访问。
1)为减少内存消耗,Redis中数据的过期时间为数据库中相应数据剩余保留时间的一定比例
2)为避免缓存雪崩问题,保留比例设置为(0.2, 0.5)范围内的随机值。同时为避免Redis崩溃带来的雪崩问题,对Redis集群应做高可用处理
3、数据库层面
需加入createdTime,expiredTime两个数据类型为时间戳的字段, 参考Redis的数据过期处理原理 =》惰性+定时删除相结合。
1)数据库数据惰性删除
查询数据时判断已过期,则同步删除,返回提示用户短地址已失效。 2)数据库数据定时删除
通过定时任务,如每天凌晨扫描数据库中expiredTime < now()的数据。 为避免锁表:读已提交隔离级别 + expiredTime设置索引。
3)定时任务框架选择:
1)JDK自带ScheduledExecutorService
微服务多节点部署下重复执行的问题,不会出现业务问题,但浪费数据库性能
2)分布式任务调度框架
框架选型可参考:https://cloud.tencent.com/developer/article/2046586
考虑到Saturn、ElasticJob需要额外依赖Zookeeper增加维护成本,XXL-JOB作为初学者部署及开发难度相对较大,
数据库定时删除对于可用性和性能要求较低,这里选用Quartz作为调度框架

4.6 数据库设计

4.6.1 建表语句

alt

4.6.2 分库分表

单表按照1800W数据来计算,3.6亿/1800W = 20张表。 单库按照8000W数据计算,3.6亿/8000W = 5个库。 url_mapping 分表的 key 可以使用短链 Key 或创建时间来计算 hash,通过一致性 Hash 算法把记录路由到对应的分表。
url_0 url_mapping_0 ~ url_mapping_3
url_1 url_mapping_4 ~ url_mapping_7
url_2 url_mapping_8 ~ url_mapping_11
url_3 url_mapping_12 ~ url_mapping_15
url_4 url_mapping_16 ~ url_mapping_19

4.7 性能极致优化

4.7.1 全局自增ID

所谓的全局自增ID:我们的服务是集群模式,全局自增意味着此ID在该集群内唯一且自增。
alt
去掉Hash的过程,同时预生成一批短链接,请求来了直接建立映射关系。
不同于 Hash 算法,全局自增 ID 的方案不需要对长 URL 进行 Hash 转换。因为 ID 全局自增且唯一,能确保一个 ID 只映射一个长 URL,不存在Hash 算法中存在的多个长 URL 可能映射到同一个短链的问题。基于全局自增 ID 的方案,依赖自增ID产生的效率。
可以选用无需即时生成的分布式ID解决方案,如美团的leaf开源组件,百度Uidgenerator开源组件,都有批量号段缓存及缓存预加载机制,ID的获取操作就是一个简单的数据GET操作,轻松可以达到十万级生成效率。
缺点:生成的短链接是自增的,用户方可以猜测到业务的交易流水量,流水量对于很多互联网公司是机密敏感数据,会带来不安全性。可以考虑通过接口字段让用户自定义选择短链生成方式,同时短链服务本身也得做好功能可扩展性的设计。

4.7.2 查询优化

一般的短链系统,基本都是读多写少,对于高频操作(读),如果每次都从数据库取,开销较大,通常采用缓存技术来解决。 alt

4.8 服务高可用设计

短链接服务是一个高并发项目,需保证高性能的同时,也要保证服务不间断。
总体思路: Keepalive + Nginx + 多节点容器化部署 + Redis Sentinel + Mysql半同步

4.8.1 负载均衡器选择

4.8.1.1 DNS轮询

优点

  1. 低成本:只需要在DNS服务器上把域名绑定多个A记录即可。域名注册商一般都免费提供此类解析服务。
  2. 部署简单:部署多个web应用实例,然后在DNS服务器上添加A记录。

缺点

  1. 可靠性低:业务机器出现问题,没有故障转移机制
  2. 负载分配问题:缓存绑定,负载不均衡
4.8.1.2 硬件负载

优点

  1. 高性能:硬件负载均衡器可以处理大量的网络请求,具有很高的性能,可以满足高流量负载的需求。
  2. 高可靠性:硬件负载均衡器通常具有冗余的硬件和软件,可以提供高可靠性,防止单点故障导致整个系统崩溃。
  3. 高扩展性:硬件负载均衡器可以轻松地扩展和升级,以应对增加的网络负载。
  4. 安全性:硬件负载均衡器通常具有内置的安全特性,如DDoS攻击防御、SSL加速和安全连接等,可以保护网络安全。

缺点

  1. 成本高:硬件负载均衡器通常价格昂贵,需要更多的预算。
  2. 管理复杂:硬件负载均衡器的安装、配置和管理需要技术人员进行,需要较高的技术水平和时间投入。
  3. 灵活性差:硬件负载均衡器通常需要进行预配置,而且不太灵活,无法快速适应变化的网络环境。
4.8.1.3 LVS

LVS是Linux Virtual Server的缩写,是一个基于Linux内核的负载均衡器软件,旨在为高性能、高可用性和可扩展性的应用程序提供负载均衡和高可用性服务。
优点

  1. LVS是一个内核级别的负载均衡器,运行时不存在用户态、内核态上下文切换的消耗,因此具有很高的性能和吞吐量。
  2. LVS支持多种负载均衡算法和协议,可以根据实际需求进行配置。
  3. LVS具有较好的可靠性和可扩展性,可以通过增加后端服务器来增加负载容量。

缺点:

  1. LVS的安装和配置比较复杂,使用门槛较高,需要有一定的Linux系统和网络知识。
  2. LVS的管理和维护成本比较高,需要专门的管理人员来进行操作和维护。
  • LVS的负载均衡器只能工作在内核空间中,不支持灵活的配置。
4.8.1.4 NGINX

优点

  1. 灵活性:Nginx支持多种负载均衡策略,可以根据请求的源IP地址、目标IP地址、源端口号、目标端口号等信息来判断请求的目标服务器,并将请求转发到合适的服务器上。
  2. 易于配置:Nginx的配置文件简单易懂,可以快速配置和部署,同时支持热部署,可以在不停止服务的情况下更新配置文件。
  3. 支持缓存。
4.8.1.5 总结

短链接服务预期QPS不会过万,Nginx作为负载均衡已经足够了

4.8.2 负载均衡器高可用

Nginx作为负载均衡器,挂掉则服务整体不可用。这里选择用KeepAlive软件做Nginx高可用
alt
基本原理:通过建立主备两台服务器,保证一台服务挂了后另外一台还能提供服务。

4.8.2.1 宕机检测

主节点和从节点维护心跳,从节点通过心跳检测主节点挂机,主节点挂机后,从节点升级为主节点

4.8.2.2 故障转移

alt
从节点升级为主节点后,如何将用户访问转到新的主节点呢。用户访问的是短链接域名,通过DNS协议映射到负载均衡器节点。这里主备服务器会通过虚拟网卡技术生成同样的虚拟IP,DNS映射为该虚拟IP,那么理论上在IP协议层ARP协议会同时广播到这两台机器,但KeepAlive软件会只让主节点回复ARP包。这样当原主节点挂机后,用户访问的就是新的主节点。

4.8.3 Redis高可用

为避免单机Redis挂机导致缓存雪崩等问题,需建立Redis集群机制,常见的解决方案是搭建Redis Sentinel集群来做节点宕机检测及故障转移。这里注意不同的哨兵最好部署在不同的机房,进行隔离,以免没有足够的哨兵存活(<quorun),无法进行故障转移。
alt

4.8.4 数据库高可用

常见的方案为建立主从集群,但注意数据库默认的复制为异步复制。主节点磁盘损坏,从节点未来得及复制时,数据会出现丢失。
alt 可采取数据同步复制方案,主节点将数据同步到从节点,从节点返回数据重放成功后,主节点才返回用户成功。数据在主或从数据丢失后,用户是能实时感知的,可以通过选择重试来保证数据的可靠性。
在企业级生产中,一般会采取同步加异步复制相结合的方案,简称半同步复制。主节点和同步复制的从节点会部署在同一地点的不同机房,异步复制的从节点部署在另外的机房,这样可以避免极低概率下同一地点的数据中心出现着火、网络中断等数据中断异常。

#项目##面试##校招##面经#
全部评论
欢迎各位同学跟我一起讨论技术呀
3 回复
分享
发布于 2023-11-17 10:32 四川
写的太棒了
3 回复
分享
发布于 2023-11-17 13:31 湖北
滴滴
校招火热招聘中
官网直投
这个项目链接在哪呀,大佬能分享不
点赞 回复
分享
发布于 2023-12-18 14:51 浙江
项目在哪
点赞 回复
分享
发布于 01-21 11:37 广东

相关推荐

#晒一晒我的offer#&nbsp;bg:&nbsp;211本这段期间面试了不少公司,如腾讯,美团,淘天,字节,腾讯offer,淘天hr说清明节后给结果,美团也是.算是积累了不少面试经验,就给兄弟们分享一下吧,项目有两个,一个是马哥知识星球的短链接项目,一个办公系统.以腾讯为主吧.腾讯时间线3.8号投递 + 测评 -> 3.11 约一面(3.13) -> 3.15 约二面(3.16) -> 3.22 约GM面(3.22) -> 3.22 三面完1h左右约hr面(本来当天,快开始改成周一了) -> 3.28 进评估 + 云证 ->&nbsp;4.2&nbsp;oc&nbsp;+&nbsp;offer一面(2h)内容太多了,看帖子吧也可以直接进我主页看,主要就是计网和操作系统的拷打链接:&nbsp;&nbsp;https://www.nowcoder.com/feed/main/detail/770bf802bec54d57a3fc866b15392855?sourceSSR=users二面(1h10min)全程拷打项目(项目用的马哥的短链接+课程一个项目,短链接现在写在简历上的人挺多的,可见项目还是很不错的,星球里会更新短链接项目的面经,感觉这部分最值。面试中大部分问题都可以覆盖,可以节省挺多时间。时间充足的友友可以自己找开源)1.短链接实现的原理2.分表过后依旧不够用怎么办?(给出几个解决方案)3.如果放在数据库集群中,怎么保证能够正确的访问目标服务器4.如果sessin存放的服务器和请求访问的服务器不在一个服务器上怎么解决5.负载均衡算法,实现特定的QPS怎么做,例如a服务器60%,b服务器30%,c服务器10%,给出几种解决方案,并说明优缺点6.布隆过滤器的原理,优缺点,在项目中如何运用7.关于缓存击穿,穿透等的项目的解决方案8.用rabbitmq和用rpc直接调用接口相比有什么好处9.rabbitmq如何确保消息不能重复消费,具体原理10.对于一个订单创建,实现把请求放入消息队列还是先写入数据库,为什么.11.限流策略有哪些,不局限于后端12.我的数据存在数据库中,但是查不到有哪些原因,不局限于后端.能想到的就这些,其实还很多.剩下的给大家弄成图片吧,字数超了.祝大家早点oc!也希望能给大家一点帮助 #腾讯#
点赞 评论 收藏
转发
31 250 评论
分享
牛客网
牛客企业服务