用 MurmurHash + Base62 生成短链接
短链接你有没有遇到过这种情况想在朋友圈分享一个链接结果一粘贴——好家伙一长串参数占了半屏还带一堆 ?utm_sourcexxxrefyyy…… 别人一看就烦自己都懒得点。更别说在短信、海报、二维码等空间有限的场景下了。这时候就需要一个短链接比如把texthttps://example.com/article?id12345sourcewechatutm_campaignspring_sale变成texthttps://ex.co/aB3k9这类短链接简洁美观易于传播并且可隐藏原始逻辑用起来还是挺方便的。哈希 编码 短码要生成短链接关键在于将任意长度的原始 URL 映射为一个固定长度、唯一且紧凑的字符串标识符即“短码”。这里采用两步法第一步哈希使用非加密型哈希函数如 MurmurHash将原始 URL 转换为一个固定长度的整数通常是 32 位。为什么不用 MD5 或 SHA因为它们输出太长MD5 是 32 位十六进制字符串而我们需要的是短MurmurHash 的优势高性能计算速度快适合高并发场景均匀分布冲突率低保证不同 URL 生成不同哈希值固定种子Guava 提供的 murmur3_32_fixed() 使用固定种子确保跨 JVM、跨机器结果一致非加密不用于安全场景正适合做 ID 生成第二步编码将哈希得到的整数可能为负数转换为Base62 字符串。Base62 是什么字符集0–910个 A–Z26个 a–z26个 共 62 个字符优点URL 安全不含 , /, 等特殊字符可直接拼接到域名后对比 Base64Base64 含 和 /在 URL 中需转义不适合做短链最终流程text长 URL → MurmurHash → 32位整数 → 转无符号 long → Base62 编码 → 5~6位短码核心代码首先在 Maven 项目中引入 Google Guava 库提供了稳定高效的 MurmurHash 实现xml!-- https://mvnrepository.com/artifact/com.google.guava/guava --dependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion33.5.0-jre/version/dependency哈希测试看 MurmurHash 输出什么java// import com.google.common.hash.Hashing;// import java.nio.charset.StandardCharsets;public static void main(String[] args) {String url https://tse1-mm.cn.bing.net/th/id/OIP-C.wb-bFBTpIZDy_1jcvMY_5QHaE8?w286h191c7r0o7cbucfimg2dpr1.1pid1.7rm3ucfimg1;int hash Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8).asInt();System.out.println(hash: hash); // 可能为负数如 -904567778long unsignedHash hash 0xFFFFFFFFL; // 转为无符号 long如 3390399518System.out.println(unsignedHash: unsignedHash);}texthash: -904567778unsignedHash: 3390399518Java 的 int 是有符号的直接对负数做 Base62 编码会导致错误比如模运算异常或空字符串因此须先转为无符号 long在很多业务中同一个链接对不同用户可能有不同的行为或权限就需要对短码进行区分生成因此可以在生成短码时将用户唯一标识如 user_id、设备 ID与原始 URL 拼接再进行哈希javacreate(url | userId)因为输入变了哈希结果就变了短码自然也不同。完整代码带注释放心抄javapackage io.jiangbyte.app.biz.urls.utils;import com.google.common.hash.Hashing;import java.nio.charset.StandardCharsets;/*** 1. 使用 Guava 的 Murmur3_32_fixed 哈希算法对输入字符串计算 32 位哈希值* 2. 将有符号 int 转换为无符号 long避免负数问题* 3. 将该数值使用 Base62 编码字符集0-9, A-Z, a-z输出为紧凑字符串*/public class MurmurHashUtils {/*** Base62 编码字符集按标准顺序排列* - 数字 0 到 910 个* - 大写字母 A 到 Z26 个* - 小写字母 a 到 z26 个*/private static final char[] CHARS buildBase62Chars();/*** Base62 的基数值为 62* 用于进制转换计算*/private static final int BASE CHARS.length;/*** 构建 Base62 字符数组* 按照标准顺序依次填充数字、大写字母、小写字母** return 长度为 62 的字符数组索引即对应数值如 CHARS[0]0, CHARS[10]A*/private static char[] buildBase62Chars() {char[] chars new char[62];int index 0;// 填充数字 0 ~ 9 for (char c 0; c 9; c) {chars[index] c;}// 填充大写字母 A ~ Z for (char c A; c Z; c) {chars[index] c;}// 填充小写字母 a ~ z for (char c a; c z; c) {chars[index] c;}return chars;}/*** 将一个非负长整型数值转换为 Base62 编码字符串* 不断对 BASE 取模获取最低位字符再除以 BASE直到数值为 0, 最后将字符序列反转得到高位在前的标准表示** param n 待编码的非负 long 值* return Base62 编码后的字符串*/private static String base62(long n) {if (n 0) {return 0; // 特殊情况0 编码为 0 }StringBuilder sb new StringBuilder();while (n 0) {sb.append(CHARS[(int) (n % BASE)]); // 取模得到当前最低位对应的字符索引n / BASE; // 整除进入下一位}// 由于是从低位到高位追加需反转得到正确顺序return sb.reverse().toString();}/*** 对输入字符串进行哈希并生成 Base62 短字符串。* 使用 Murmur3_32_fixed 算法Guava 提供的固定种子版本保证跨 JVM 一致性* 将结果转为无符号 32 位整数再进行 Base62 编码** param input 原始输入字符串* return Base62 编码的短字符串*/public static String create(String input) {// 使用 UTF-8 编码计算 Murmur3_32 哈希值固定种子int hash Hashing.murmur3_32_fixed().hashString(input, StandardCharsets.UTF_8).asInt();// 将有符号 int 转换为无符号 long避免负数导致 base62 逻辑异常// -1 → 0xFFFFFFFFL 4294967295long unsignedHash hash 0xFFFFFFFFL;return base62(unsignedHash);}/*** 生成带用户隔离的短链标识。* 若提供 userId则将 URL 与 userId 拼接后再哈希* 使得同一 URL 对不同用户生成不同短链* 若 userId 为 null则退化为普通模式** param url 原始长链接* param userId 用户唯一标识* return 用户隔离或通用的 Base62 短字符串*/public static String create(String url, String userId) {if (userId ! null) {// 拼接格式原始URL 分隔符 | 用户IDreturn create(url | userId);} else {// 无用户隔离直接哈希原始 URLreturn create(url);}}}测试输出javapublic static void main(String[] args) {String url https://tse1-mm.cn.bing.net/th/id/OIP-C.wb-bFBTpIZDy_1jcvMY_5QHaE8?w286h191c7r0o7cbucfimg2dpr1.1pid1.7rm3ucfimg1;int hash Hashing.murmur3_32_fixed().hashString(url, StandardCharsets.UTF_8).asInt();System.out.println(hash: hash); // 有符号 int逻辑出错为空String base62_hash base62(hash);System.out.println(base62_hash: base62_hash);long unsignedHash hash 0xFFFFFFFFL;System.out.println(unsignedHash: unsignedHash);String base62_unsignedHash base62(unsignedHash);System.out.println(base62_unsignedHash: base62_unsignedHash);System.out.println(create: create(url));System.out.println(create: create(url, 1234));System.out.println(create: create(url, 1234));System.out.println(create: create(url, 12345));}texthash: -904567778base62_hash:unsignedHash: 3390399518base62_unsignedHash: 3hRlnCcreate: 3hRlnCcreate: 4YmoRucreate: 4YmoRucreate: GsQoj原文链接https://juejin.cn/post/7585574047075418112
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2513795.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!