背景
当前短地址发号使用 hbase 的 increament 指令, 每个转短请求都会做一次操作, 当 qps 超过 1k 时, 会造成 hbase 机器报警, 需要减少 hbase 的 increament 指令调用
方案
修改为每个请求在本地取号, 当本地无号可取时请求 hbase 发号, 每次在 hbase 中调用 increament 时取 100 个号, 回到本地
具体代码流程
- 加全局锁, 所有转短请求在取号前通过全局锁串行
- 尝试本地取号, 如果不成功, 去 hbase 取号 100 个, 放入本地缓存
- 解锁, 并继续原有转短流程
待确认的点,全局锁是否需要公平 公平锁的性能是否满足需求
修改背景
商家有需求需要定制自己的域名不使用有赞的默认域名。
定制的过程涉及到一个 VIP 域名的发布,这个发布涉及到前端,后端,运维的一个 VIP 域名的维护,现在是 iron,node,后端,运维都维护了一个 VIP 名单,存储的位置也是各自维护的,之前发布过程每次发布都会有白名单不同步问题导致 VIP 商户的功能挂掉。
上午讨论的结果同步:
上午和前端,后端,运维的同事讨论了下,使用 apollo 统一维护一份域名,每次更改这份配置将自动同步到其他不同的端,保证数据的维护的一致性。
- 短地址修改方案
短地址白名单修改为由动态白名单和静态白名单合并得到,动态白名单从 apollo 中获取,静态白名单在启动时配置。
apollo 配置格式为 json 格式,数据结构为 map,key 为 kdtid,value 为对应域名
- 可能影响的场景
- 静态白名单的有效性
- 静态白名单中配置的域名应该始终可以成功进行转短链
- 动态白名单的有效性
- 动态白名单中配置的域名应该可以成功转短链
- 当 apollo 中配置的动态白名单添加域名后,新增加的域名应该可以成功转短链
- 当 apollo 中配置的动态白名单减少部分域名后,如减少的域名不在静态白名单中,则该域名应该转短链失败
- 当 apollo 中配置的动态白名单减少部分域名后,如减少的域名在静态白名单中,则该域名应该可以成功转短链
- 当 apollo 中白名单配置为空时,应该只有静态白名单中的域名可以成功转短链
- 动态白名单无法获取或是失效场景
- 当 apollo 中配置的动态白名单不符合 json 的 map 格式时,视为动态白名单配空,与 2.e 场景一致
- 当 apollo 无法正常读取到白名单配置项时,将最后一次成功读取的值视为动态白名单的值,该值保存在 apollo-config.json 文件中
- 短转长的场景
- 经产品确认,该场景不受白名单限制,无论域名是否还在白名单中,短转长都应该正常进行
- 静态白名单的有效性
主要的方案有两种
- hash 算法: hash 长地址得到一个短地址
- 发号器:通过发号器得到一个整数,再转为 62 进制
两种方案的优缺点
hash 方案的优点: 生成的短链不可遍历
hash 方案的缺点:位数较短时,生成的短链容易冲突,碰撞的概率可由如下公式得出:
当生成的短链为 8 位,且为 62 进制时,在 hash 了 3kw 次时,碰撞的概率为 1/2, 我们要求生成的短链接为 6 位,且目前每日的增长量为 200w,显然不符合我们的要求发号器的优点:生成的短链必定不会碰撞
发号器的缺点:同一个 url 分两次不同的时间,生成结果不一致,且 url 容易被遍历
最终采取的方案
选择发号器,且做了一些方案上的优化解决了发号器的缺点:
- 预先生成一亿个短链接,并随机打乱插入到 mysql,这样通过发号器拿到的 id 去更新数据库的对应主键短链,解决了短链容易被遍历,且为将来的横向扩展做了准备,当数据量即将一亿时,再重新准备插入一亿的纪录到新的 mysql 实例
- 用 redis 缓存了最近一天的长链,每次生成短链先去 redis 查,这样同一个长链接在一天内反复生成的短链相同,并且每次查询成功时,更新 key 的过期时间,所以热门的短链很大可能上生成的都是同一个短链。
短地址店铺隔离改造
- 改造背景:为规避腾讯风控,短地址改为不同商家使用不同的域名访问,同时增加对不同调用方的接入管理
- 改造方案:
- 长转短:
- 接口输入参数为 长地址,访问限制域名,调用方 app_name,版本号
- 按照原有方案生成短地址,将短地址和长地址以及对应域名的关系存入 hbase,以供短转长时校验
- 短转长:
- 接口输入参数为短地址,调用方 app_name, 版本号
- hbase 中查询到长地址和访问限制域名,校验 hbase 中的访问限制域名与参数中短地址访问的域名是否一致,如一致,按原流程返回,否则报错
- 接入管理:
- 前期先通过人工接入,后续改造接入 ops
- 长转短:
短地址监控
监控接口 curl -H “Host: kdt.im“ “http://127.0.0.1/shorturl_status
监控分四个维度:
- redis
a. hit: 缓存的命中次数
b. miss:缓存的 miss 次数,当 miss 上升时需引起注意
c. error:error 次数,任何非正常情况下的错误,当 error 上升时,需要立刻查看日志排查问题 - mysql
a. update: 短链的更新次数
b. select: 缓存失效,数据库查询的次数,当次数上升时需关注
c. error: error 次数,任何非正常情况下的错误,当 error 上升时,需要立刻查看日志排查问题 - lock
a. get: 缓存失效时,获取的锁,当次数上升时需关注
b. error: 任何非正常情况下的错误,当 error 上升时,需要立刻查看日志排查问题 - 发号器
a. current: 发号器当前的号,当发号即将达到预设的一亿、二亿时,需要进行扩容
b. error: 任何非正常情况下的错误,当 error 上升时,需要立刻查看日志排查问题
短地址部署容量
nginx 的 qps 单机过万,umem 的 qps 单机过万,只有 udb 的 tps 单机只有 800, 目前整个系统的瓶颈在 udb
# /opt/wrk/bin/wrk -t 4 -c 4 -d 3600s --script get.lua http://127.0.0.1
Running 60m test @ http://127.0.0.1
4 threads and 4 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 19.86ms 62.22ms 1.00s 93.87%
Req/Sec 198.29 59.82 474.00 84.81%
2714028 requests in 60.00m, 832.96MB read
Requests/sec: 753.88
Transfer/sec: 236.93KB
/data/shorturl/src/config.lua
因为没有配置解析器,不要配置有域名,直接换成 ip,tengine 要换成 openresty
短链链接使用 6 位字符,62^6 = 568 亿,a-zA-Z0-9 这 62 位取 6 位组合,可产生 500 多亿个组合数量。把数字和字符组合做一定的映射,就可以产生唯一的字符串,如第 62 个组合就是 aaaaa9,第 63 个组合就是 aaaaba,再利用洗牌算法,把原字符串打乱后保存,那么对应位置的组合字符串就会是无序的组合。
把长网址存入数据库,取返回的 id,找出对应的字符串,例如返回 ID 为 1,那么对应上面的字符串组合就是 bbb,同理 ID 为 2 时,字符串组合为 bba,依次类推,直至到达 64 种组合后才会出现重复的可能,所以如果用上面的 62 个字符,任意取 6 个字符组合成字符串的话,你的数据存量达到 500 多亿后才会出现重复的可能。