您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java中怎么将长链接转换成短链接
## 引言
在互联网应用中,短链接服务(如Bit.ly、TinyURL等)已成为提升用户体验的重要技术。本文将深入探讨在Java中实现长链接转短链接的完整方案,涵盖算法原理、系统设计、代码实现和性能优化等内容。
---
## 一、短链接技术原理
### 1.1 短链接的核心价值
- 节省字符空间(特别是Twitter等有字数限制的场景)
- 提高点击率(更简洁美观)
- 便于统计跟踪(埋点分析)
### 1.2 技术实现方式对比
| 实现方式 | 优点 | 缺点 |
|----------------|-----------------------|-----------------------|
| 哈希算法 | 实现简单 | 可能冲突 |
| 自增ID | 绝对唯一 | 需存储映射关系 |
| 预生成编码 | 高性能 | 需要初始化资源 |
---
## 二、基于哈希算法的实现方案
### 2.1 基础实现(MD5示例)
```java
import java.math.BigInteger;
import java.security.MessageDigest;
public class ShortUrlGenerator {
public static String[] generate(String longUrl) {
// 1. 计算MD5
String md5 = getMD5(longUrl);
// 2. 分组处理
String[] resUrl = new String[4];
for (int i = 0; i < 4; i++) {
String sub = md5.substring(i * 8, i * 8 + 8);
BigInteger num = new BigInteger(sub, 16);
// 3. 取30位(避免负数)
num = num.and(new BigInteger("3fffffff", 16));
String code = encodeBase62(num.longValue());
resUrl[i] = code;
}
return resUrl;
}
private static String getMD5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes());
BigInteger bigInt = new BigInteger(1, digest);
return bigInt.toString(16);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String encodeBase62(long num) {
String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder();
while (num > 0) {
sb.append(chars.charAt((int)(num % 62)));
num /= 62;
}
return sb.reverse().toString();
}
}
public class IdWorker {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long timestampShift = sequenceBits + workerIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public IdWorker(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("workerId不合法");
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨异常");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampShift)
| (workerId << workerIdShift)
| sequence;
}
}
public class Base62Converter {
private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String encode(long num) {
StringBuilder sb = new StringBuilder();
do {
sb.insert(0, BASE62.charAt((int)(num % 62)));
num /= 62;
} while (num > 0);
return sb.toString();
}
}
graph TD
A[客户端] --> B(API网关)
B --> C[短链生成服务]
C --> D{缓存层}
D -->|缓存未命中| E[(数据库)]
E --> D
B --> F[短链跳转服务]
F --> D
CREATE TABLE short_url (
id BIGINT PRIMARY KEY,
short_code VARCHAR(10) UNIQUE,
original_url VARCHAR(2048) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expire_time TIMESTAMP,
INDEX idx_short_code (short_code)
);
SET short:code:abc123 "https://original.long/url"
EXPIRE short:code:abc123 86400
// 预生成10万个短码
public void preGenerateCodes(int batchSize) {
LongStream.range(0, batchSize)
.parallel()
.forEach(id -> {
String code = Base62Converter.encode(id);
redisTemplate.opsForValue().set("reserved:"+code, "1");
});
}
// 使用Redis统计访问频率
public boolean isHotspot(String code) {
String key = "counter:" + code;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, 1, TimeUnit.MINUTES);
}
return count > 1000; // 阈值判断
}
public boolean isMalicious(String url) {
// 1. 域名白名单校验
// 2. 调用第三方安全API
// 3. 正则匹配可疑模式
return false;
}
@RestController
public class UrlController {
@RateLimiter(value = 100, key = "#ip") // 每IP每秒100次
@PostMapping("/api/shorten")
public ResponseEntity create(@RequestParam String url,
@RequestHeader String ip) {
// ...
}
}
@Test
public void testGenerateShortUrl() {
String longUrl = "https://example.com/very/long/url";
String[] shorts = ShortUrlGenerator.generate(longUrl);
assertEquals(4, shorts.length);
assertTrue(shorts[0].length() <= 8);
}
@Test
public void testCollisionRate() {
Set<String> codes = new HashSet<>();
for (int i = 0; i < 100000; i++) {
String url = "https://example.com/"+UUID.randomUUID();
codes.add(ShortUrlGenerator.generate(url)[0]);
}
assertTrue(codes.size() > 99900); // 冲突率<0.1%
}
public boolean customShortCode(String code, String longUrl) {
if (redisTemplate.hasKey("custom:" + code)) {
return false;
}
redisTemplate.opsForValue().set("custom:"+code, longUrl);
return true;
}
public class VisitStats {
private String code;
private Long totalVisits;
private Map<String, Long> byRegion;
private Map<LocalDate, Long> dailyVisits;
// getters/setters
}
本文详细讲解了Java中实现短链接服务的多种技术方案。实际应用中建议: 1. 高并发场景采用分布式ID+预生成方案 2. 普通场景可使用哈希算法+冲突检测 3. 务必做好安全防护和监控
完整示例代码已上传GitHub:示例仓库链接 “`
(注:实际字数约3500字,此处展示核心内容框架,完整实现需包含更多细节代码和说明)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。