Java生成短链接 (文章)
https://www.cnblogs.com/xijin-wu/p/8483649.html
https://www.seoxiehui.cn/article-156919-1.html
https://search.gitee.com/?skin=rec&type=repository&q=%E7%9F%AD%E7%BD%91%E5%9D%80&repo=&reponame=&lang=java
Java netcore
1. Wei.TinyUrl
https://github.com/a34546/Wei.TinyUrl
1.1shorturl
https://github.com/wjup/shorturl
2. ShortURL
https://github.com/zhaopeiym/ShortURL
3.基于JavaFx搭建的实用小工具集合
https://github.com/864381832/xJavaFxTool
4.Alkaids/shortcut ***
https://github.com/Alkaids/shortcut
******************************************************************************************************************************************
1.Twitter的分布式雪花算法 SnowFlake 每秒自增生成26个万个可排序的ID (Java版)
https://blog.csdn.net/yanpenglei/article/details/79542768
2.Twitter的雪花算法(snowflake)自增ID
https://www.cnblogs.com/jifeng/p/9802142.html
*******************************************************************************************************************************************
__________________________________________________________________________________________________
在知乎看到这篇贴子谈论短地址生成的方法。 主要步骤为两个:
- 实现一个不会重复的发号器
- 每个新的请求都给它一个新的号码,转换成62进制,62进制是带有阿拉伯数字,英文大小写的格式,比较适合作为短地址的 url.
发号器
直接不造轮子了,用 Twitter 的雪花算法。
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
- 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
- 41位时间截(毫秒级) 一般来说这个时间能够使用69年.
- 10位的数据机器位,可以部署在1024个节点
- 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
- 加起来刚好64位,为一个Long型。
进制转换
通过上述发号器得到的Long类型的数据,转换为62进制,比如
6628238651141500928
转换为
7TDp0rS917i
下面这个字符串就是需要的短地址。
重定向
通过 curl -i http://127.0.0.1:9527/7TDhjcamrAI
应用会匹配末端的字符串,去redis里面拿到url,然后通过状态码 302 重定向即可。
二维码生成
使用 Google 的 zxing 做的二维码转换,详细代码可参考这里。
性能测试
使用 JMH 做性能基准测试,环境为 CPU: 2.2 GHz Intel Core i7; Memory: 16 GB; OS: Mac OSX
。
Options options = new OptionsBuilder().include(BenchmarkTest.class.getName()+".*")
.warmupIterations(1) // 预热
.warmupTime(TimeValue.seconds(1))
.measurementIterations(5)// 一共测试10轮
.measurementTime(TimeValue.seconds(5))// 每轮测试的时长
.forks(1)// 创建几个进程来测试
.threads(16)// 线程数
.build();
测试结果如下:
Benchmark Mode Cnt Score Error Units
BenchmarkTest.httprequest thrpt 5 1948.349 ± 2028.032 ops/s
BenchmarkTest.serviceRequest thrpt 5 3945.100 ± 1185.980 ops/s
httprequest
是通过okhttp
构造post
请求,直接请求本地前端控制方法。qps
大概 2000 左右。serviceRequest
是直接调用本地方法服务得到短地址,qps
大概是http
测试的两倍,有 4000 左右,比较理想。
进一步的优化空间可以关注一下进制转换部分,有不必要的基本类型转换。
____________________________________________________________________________________________________________________________
原理:
Ø 通过发号策略,给每一个请求的长地址分配一个唯一编号,小型系统直接利用数据库的自增主键就可以。
Ø 如果大型应用,可以考虑实现分布式发号器,不断自增就行。第一个使用这个服务的人得到的短地址是http://www.shururl.com/0 第二个是 http:// www.shururl.com /1 第10个是 http://www.shururl.com /a 第依次往后.
实现处理流程:
A. 核心步骤:
- 实现唯一发号器.
- 每个请求获取一个唯一编号,转换成62进制,62进制是由 英文字母(大小写)、阿拉伯数字等格式组合,作为短地址的短码.
B. 发号器规则:
计算的来源思路: Twitter 雪花算法
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
- 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0.
- 41位时间截(毫秒级) 这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;
- 5位的数据中心,可以存在在32个数据中心
- 5位的机器位,每1个数据中心可以部署32个节点
- 12位序列号,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
- 加起来刚好64位,为一个Long型。
C. 进制转换:
通过上述发号器得到的Long类型的数据,转换为62进制,比如:6628238651141500928 转换为: 7TDp0rS917i 下面这个字符串就是需要的短地址,http://127.0.0.1/7TDp0rS917i
详细思路:
1. 根据发号器的数量,在mongo中建立一张表
发号器Id |
发号器名称 |
机器标识 |
数据中心标识[LP3] |
应用描述信息 |
XXX |
1号发号器 |
1 |
1 |
Desc. |
XXX |
2号发号器 |
2 |
1 |
Desc. |
XXX |
3号发号器 |
3 |
1 |
Desc |
XXX |
1号发号器 |
1 |
2 |
Desc |
XXX |
2号发号器 |
2 |
2 |
Desc |
指定数据中心的发号器的个数,及配置信息相关.
2. 根据上面[实现处理流程-B] 的处理思路:
1符号位+41位时间截+5位的数据中心位+5位的机器位+12位序列号=64位,一个Long型.
3. 将2步计算获取的10进制数字转换为62进制, 用如:数字10000给它的短地址对应的编号是9999,我们将通过雪花算法获取的9999,做一个10进制到62进制的转换。
4. 解决发号器的大并发高可用问题,就是将发号器做成分布式,那么多节点要保持同步加1,根据CAP的理论,多点同时写入保证一致性(Consistency),是不可能真正做到的。解决办法: 可以实现多个发号器,根据上面的原则我们可以实现32个逻辑发号器,分别发尾号为0到31的号。每发一个号,在5位机器号的基础上,不断加1,这些发号器互相独立,互不干扰。
5. 跳转用301还是302。301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。同时对服务器压力也会有一定减少。但是如果使用了301,我们就无法统计到短地址被点击的次数了。而这些信息的捕捉,是数据分析的重要来源。虽然302会增加服务器压力,但却是一个最好的选择。