短链生成

背景

当前短地址发号使用 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 文件中
    • 短转长的场景
      • 经产品确认,该场景不受白名单限制,无论域名是否还在白名单中,短转长都应该正常进行

主要的方案有两种

  1. hash 算法: hash 长地址得到一个短地址
  2. 发号器:通过发号器得到一个整数,再转为 62 进制

两种方案的优缺点

  1. hash 方案的优点: 生成的短链不可遍历

  2. hash 方案的缺点:位数较短时,生成的短链容易冲突,碰撞的概率可由如下公式得出:
    image.png
    当生成的短链为 8 位,且为 62 进制时,在 hash 了 3kw 次时,碰撞的概率为 1/2, 我们要求生成的短链接为 6 位,且目前每日的增长量为 200w,显然不符合我们的要求

  3. 发号器的优点:生成的短链必定不会碰撞

  4. 发号器的缺点:同一个 url 分两次不同的时间,生成结果不一致,且 url 容易被遍历

最终采取的方案

选择发号器,且做了一些方案上的优化解决了发号器的缺点:

  1. 预先生成一亿个短链接,并随机打乱插入到 mysql,这样通过发号器拿到的 id 去更新数据库的对应主键短链,解决了短链容易被遍历,且为将来的横向扩展做了准备,当数据量即将一亿时,再重新准备插入一亿的纪录到新的 mysql 实例
  2. 用 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 上升时,需要立刻查看日志排查问题

短地址部署容量

image.png
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 多亿后才会出现重复的可能。

https://alicharles.oss-cn-hangzhou.aliyuncs.com/static/images/mp_qrcode.jpg
文章目录
  1. 背景
  2. 方案
  3. 具体代码流程
  4. 修改背景
  5. 主要的方案有两种
  6. 两种方案的优缺点
  7. 最终采取的方案
  8. 短地址店铺隔离改造
  9. 短地址监控
  10. 短地址部署容量