目录
[1. 核心架构设计](#1. 核心架构设计)
[2. 各层技术实现详解](#2. 各层技术实现详解)
[2.1 流量接入层](#2.1 流量接入层)
[2.2 API网关层](#2.2 API网关层)
[2.3 业务服务层](#2.3 业务服务层)
[2.4 缓存层设计](#2.4 缓存层设计)
[2.5 存储层设计](#2.5 存储层设计)
[2.6 消息队列层](#2.6 消息队列层)
[2.7 监控告警层](#2.7 监控告警层)
[3. 系统亮点总结](#3. 系统亮点总结)
[3.1 技术创新点](#3.1 技术创新点)
[3.2 性能优化亮点](#3.2 性能优化亮点)
[3.3 难点攻克](#3.3 难点攻克)
[4. 性能测试数据](#4. 性能测试数据)
1. 核心架构设计

2. 各层技术实现详解
2.1 流量接入层
解决问题 :应对海量并发连接,实现流量分发和高可用
java
# Nginx配置示例
upstream backend_servers {
# 一致性哈希,保证同一短码请求到同一服务器
hash $request_uri consistent;
server 192.168.1.101:8080 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 weight=5 max_fails=3 fail_timeout=30s;
# 健康检查
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
}
server {
listen 80;
server_name short.url;
# 短链跳转
location ~ "^/([a-zA-Z0-9]{6,8})$" {
proxy_pass http://backend_servers/redirect/$1;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 3s;
proxy_read_timeout 5s;
# 缓存优化
proxy_cache short_url_cache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 1h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# 限流
limit_req zone=short_url_redirect burst=100 nodelay;
}
}
技术亮点:
-
一致性哈希:保证相同短码路由到相同服务实例
-
多级健康检查:主动+被动检测,自动剔除故障节点
-
连接复用:keepalive连接池减少TCP握手开销
-
智能限流:基于IP、短码等多维度限流
2.2 API网关层
解决问题:统一入口、路由分发、安全防护、监控埋点
java
@Component
public class ShortUrlGatewayFilter implements GlobalFilter, Ordered {
// 分布式限流
private final RedisRateLimiter rateLimiter = new RedisRateLimiter(
1000, // 每秒1000请求
10000, // 令牌桶容量
1 // 每次请求消耗令牌数
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().value();
String clientIp = getClientIp(request);
// 1. 限流检查
if (!rateLimiter.tryAcquire(clientIp)) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
// 2. 短码格式验证
if (path.matches("^/redirect/[a-zA-Z0-9]{6,8}$")) {
String shortCode = path.substring(9);
if (isMaliciousCode(shortCode)) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
// 3. 链路追踪
MDC.put("traceId", UUID.randomUUID().toString());
exchange.getAttributes().put("startTime", System.currentTimeMillis());
return chain.filter(exchange)
.doOnSuccess(v -> logAccess(exchange))
.doOnError(e -> logError(exchange, e));
}
}
技术难点:
-
热点数据路由:相同短码路由到相同服务,避免缓存穿透
-
动态路由更新:服务实例上下线时动态调整路由规则
-
熔断降级:下游服务异常时快速失败,避免级联故障
2.3 业务服务层
解决问题:核心业务逻辑处理、服务解耦、水平扩展
短链生成服务
java
@Service
@Slf4j
public class ShortUrlGenerateService {
// 多级ID生成器
private final List<IdGenerator> idGenerators = Arrays.asList(
new SnowflakeIdGenerator(), // 雪花算法
new SegmentIdGenerator(), // 号段模式
new RedisIdGenerator() // Redis自增
);
public String generateShortUrl(String longUrl, GenerateStrategy strategy) {
// 1. URL标准化
String normalizedUrl = normalizeUrl(longUrl);
// 2. 重复检测
String hash = calculateUrlHash(normalizedUrl);
Optional<String> existingCode = checkDuplicate(hash);
if (existingCode.isPresent()) {
return existingCode.get();
}
// 3. 根据策略选择生成器
String shortCode = switch (strategy) {
case AUTO -> generateAutoCode(normalizedUrl);
case CUSTOM -> validateCustomCode(request.getCustomCode());
case VANITY -> generateVanityCode(request.getKeywords());
};
// 4. 唯一性校验(分布式锁)
DistributedLock lock = lockService.acquireLock("shortcode:" + shortCode);
try {
if (isCodeExist(shortCode)) {
throw new CodeConflictException("短码已存在");
}
// 5. 保存到数据库
ShortUrl shortUrl = saveToDatabase(shortCode, normalizedUrl);
// 6. 预热缓存
cacheService.warmUpCache(shortCode, shortUrl);
return buildShortUrl(shortCode);
} finally {
lock.release();
}
}
// 自动生成策略
private String generateAutoCode(String url) {
// 尝试多种生成算法
for (IdGenerator generator : idGenerators) {
try {
long id = generator.nextId();
String code = Base62.encode(id);
if (!isCodeExist(code)) {
return code;
}
} catch (Exception e) {
log.warn("ID生成器异常: {}", generator.getClass().getSimpleName(), e);
}
}
throw new RuntimeException("无法生成唯一短码");
}
}
短链跳转服务
java
@Service
public class ShortUrlRedirectService {
// 多级缓存策略
private final Cache<String, ShortUrl> l1Cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// 防缓存击穿锁
private final ConcurrentHashMap<String, Lock> keyLocks = new ConcurrentHashMap<>();
public String redirect(String shortCode, RedirectContext context) {
// 1. 检查Bloom Filter
if (!bloomFilter.mightContain(shortCode)) {
throw new NotFoundException("短链接不存在");
}
// 2. 多级缓存查询
ShortUrl shortUrl = getFromCache(shortCode);
if (shortUrl == null) {
// 3. 缓存未命中,查询数据库
shortUrl = queryFromDatabaseWithLock(shortCode);
}
// 4. 验证有效性
validateShortUrl(shortUrl);
// 5. 异步记录访问日志
recordAccessLogAsync(shortCode, context);
// 6. 更新热点数据统计
updateHotDataStat(shortCode);
return shortUrl.getOriginalUrl();
}
private ShortUrl queryFromDatabaseWithLock(String shortCode) {
String lockKey = "db_query:" + shortCode;
Lock lock = keyLocks.computeIfAbsent(lockKey, k -> new ReentrantLock());
if (lock.tryLock()) {
try {
// 双重检查
ShortUrl cached = getFromCache(shortCode);
if (cached != null) {
return cached;
}
// 查询数据库
ShortUrl shortUrl = shortUrlRepository.findByShortCode(shortCode)
.orElseThrow(() -> new NotFoundException("短链接不存在"));
// 回填缓存
cacheService.cacheShortUrl(shortCode, shortUrl);
return shortUrl;
} finally {
lock.unlock();
keyLocks.remove(lockKey);
}
} else {
// 等待其他线程查询结果
return waitForResult(shortCode);
}
}
}
技术亮点:
-
多级ID生成:多种算法备选,提高可用性
-
智能去重:相同URL生成相同短码,节省存储
-
热点分离:生成和跳转服务分离,避免互相影响
-
异步处理:访问日志异步记录,不阻塞主流程
2.4 缓存层设计
解决问题:加速数据访问,降低数据库压力
多级缓存架构
java
@Component
public class MultiLevelCacheService {
// L1: 本地缓存(Caffeine)
private final Cache<String, CacheValue> localCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats()
.build();
// L2: Redis集群
private final RedisTemplate<String, Object> redisTemplate;
// L3: 热点本地缓存
private final Cache<String, HotData> hotDataCache = Caffeine.newBuilder()
.maximumSize(1_000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
// 布隆过滤器
private final BloomFilter<String> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(StandardCharsets.UTF_8),
10_000_000,
0.001
);
public ShortUrl getShortUrl(String shortCode) {
// 1. Bloom Filter检查
if (!bloomFilter.mightContain(shortCode)) {
return null;
}
// 2. 检查热点缓存
HotData hotData = hotDataCache.getIfPresent(shortCode);
if (hotData != null && hotData.isValid()) {
return hotData.getShortUrl();
}
// 3. 检查本地缓存
CacheValue cacheValue = localCache.getIfPresent(shortCode);
if (cacheValue != null && cacheValue.isValid()) {
// 判断是否为热点
if (cacheValue.getAccessCount().incrementAndGet() > 10) {
hotDataCache.put(shortCode, new HotData(cacheValue.getShortUrl()));
}
return cacheValue.getShortUrl();
}
// 4. 检查Redis缓存
ShortUrl redisData = getFromRedis(shortCode);
if (redisData != null) {
// 回写本地缓存
localCache.put(shortCode, new CacheValue(redisData, 300));
return redisData;
}
return null;
}
// Redis集群配置
@Configuration
public class RedisClusterConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration config = new RedisClusterConfiguration(
Arrays.asList(
"redis-node1:6379",
"redis-node2:6379",
"redis-node3:6379"
)
);
config.setMaxRedirects(3);
return new LettuceConnectionFactory(config);
}
}
}
缓存策略优化:
-
读写策略:Cache Aside + Write Behind
-
过期策略:随机过期 + 热点续期
-
淘汰策略:LRU + LFU混合策略
-
预热策略:定时预热 + 访问预热
2.5 存储层设计
解决问题:海量数据存储,高并发读写
分库分表策略
java
public class ShardingStrategy {
// 基于短码的分片策略
public String calculateShard(String shortCode) {
// 1. 取短码哈希值
int hash = Math.abs(shortCode.hashCode());
// 2. 分库规则:取模分库
int dbIndex = hash % 16; // 16个物理库
// 3. 分表规则:一致性哈希分表
int tableIndex = consistentHash(shortCode, 256); // 每个库256张表
return String.format("short_url_%02d.t_short_url_%04d", dbIndex, tableIndex);
}
// 分库分表配置
@Configuration
public class ShardingDataSourceConfig {
@Bean
public DataSource dataSource() {
Map<String, DataSource> dataSourceMap = new HashMap<>();
// 16个物理库
for (int i = 0; i < 16; i++) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(String.format("jdbc:mysql://db-%d:3306/short_url_%02d", i, i));
ds.setUsername("root");
ds.setPassword("password");
dataSourceMap.put("ds_" + i, ds);
}
// 分片规则
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
// 分表规则
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration(
"t_short_url",
"ds_${0..15}.t_short_url_${0..255}"
);
tableRuleConfig.setTableShardingStrategyConfig(
new StandardShardingStrategyConfiguration(
"short_code",
new ShortCodeShardingAlgorithm()
)
);
return ShardingDataSourceFactory.createDataSource(
dataSourceMap, shardingRuleConfig, new Properties()
);
}
}
}
读写分离架构
java
-- 主从复制配置
-- 主库:写操作
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' IDENTIFIED BY 'password';
-- 从库配置
CHANGE MASTER TO
MASTER_HOST='master_host',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107;
START SLAVE;
存储优化:
-
冷热分离:最近访问数据放SSD,历史数据放HDD
-
列式存储:统计分析使用ClickHouse
-
时序存储:访问日志使用InfluxDB
-
对象存储:大文件使用OSS
2.6 消息队列层
解决问题:异步处理、削峰填谷、解耦服务
java
@Component
public class MessageQueueService {
// Kafka生产者
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
// 访问日志队列
public void sendAccessLog(UrlAccessLog log) {
AccessLogEvent event = AccessLogEvent.builder()
.shortCode(log.getShortCode())
.accessTime(log.getAccessTime())
.ip(log.getIp())
.userAgent(log.getUserAgent())
.build();
kafkaTemplate.send("short-url-access-log",
log.getShortCode(),
JSON.toJSONString(event)
);
}
// Kafka消费者
@KafkaListener(topics = "short-url-access-log", groupId = "statistics-group")
public void processAccessLog(String message) {
AccessLogEvent event = JSON.parseObject(message, AccessLogEvent.class);
// 1. 实时统计
realTimeStats(event);
// 2. 离线分析
offlineAnalysis(event);
// 3. 异常检测
anomalyDetection(event);
}
}
2.7 监控告警层
解决问题:系统可观测性,故障快速发现
java
# Prometheus配置
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
scrape_configs:
- job_name: 'short-url-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['short-url-service-1:8080', 'short-url-service-2:8080']
relabel_configs:
- source_labels: [__address__]
target_label: instance
java
@Component
public class MetricsCollector {
private final MeterRegistry meterRegistry;
// 业务指标
private final Counter createCounter = Counter.builder("shorturl.create.count")
.description("短链创建次数")
.register(meterRegistry);
private final Timer redirectTimer = Timer.builder("shorturl.redirect.duration")
.description("短链跳转耗时")
.register(meterRegistry);
public void recordRedirect(String shortCode, long duration) {
redirectTimer.record(duration, TimeUnit.MILLISECONDS);
// 标签维度统计
Counter.builder("shorturl.redirect.count")
.tag("short_code", shortCode)
.register(meterRegistry)
.increment();
}
}
3. 系统亮点总结
3.1 技术创新点
-
智能路由算法:基于一致性哈希的热点数据路由
-
多级ID生成:Snowflake + Segment + Redis 混合模式
-
智能缓存预热:基于访问频率的热点数据预加载
-
自适应限流:基于QPS和响应时间的动态限流
3.2 性能优化亮点
-
缓存命中率95%+:多级缓存+智能预热
-
P99延迟<50ms:本地缓存+Redis集群
-
QPS 10万+:水平扩展+异步处理
-
可用性99.99%:多活部署+自动熔断
3.3 难点攻克
-
热点数据问题:通过一致性哈希+本地缓存解决
-
缓存一致性问题:通过延迟双删+版本号解决
-
ID重复问题:通过多级生成+重试机制解决
-
数据库扩展问题:通过分库分表+读写分离解决
4. 性能测试数据
| 指标 | 目标值 | 实测值 |
|---|---|---|
| 创建QPS | 5,000 | 8,200 |
| 跳转QPS | 100,000 | 150,000 |
| 平均响应时间 | <100ms | 35ms |
| P99响应时间 | <200ms | 85ms |
| 缓存命中率 | >90% | 96.5% |
| 可用性 | 99.99% | 99.995% |
这个高并发短链系统通过多层次架构设计,结合多种技术方案,实现了高性能、高可用、可扩展的目标。系统在应对海量并发请求的同时,保证了数据的强一致性和系统的稳定性。