高并发短链系统架构设计

目录

[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));
    }
}

技术难点

  1. 热点数据路由:相同短码路由到相同服务,避免缓存穿透

  2. 动态路由更新:服务实例上下线时动态调整路由规则

  3. 熔断降级:下游服务异常时快速失败,避免级联故障

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);
        }
    }
}

缓存策略优化

  1. 读写策略:Cache Aside + Write Behind

  2. 过期策略:随机过期 + 热点续期

  3. 淘汰策略:LRU + LFU混合策略

  4. 预热策略:定时预热 + 访问预热

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;

存储优化

  1. 冷热分离:最近访问数据放SSD,历史数据放HDD

  2. 列式存储:统计分析使用ClickHouse

  3. 时序存储:访问日志使用InfluxDB

  4. 对象存储:大文件使用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 技术创新点

  1. 智能路由算法:基于一致性哈希的热点数据路由

  2. 多级ID生成:Snowflake + Segment + Redis 混合模式

  3. 智能缓存预热:基于访问频率的热点数据预加载

  4. 自适应限流:基于QPS和响应时间的动态限流

3.2 性能优化亮点

  1. 缓存命中率95%+:多级缓存+智能预热

  2. P99延迟<50ms:本地缓存+Redis集群

  3. QPS 10万+:水平扩展+异步处理

  4. 可用性99.99%:多活部署+自动熔断

3.3 难点攻克

  1. 热点数据问题:通过一致性哈希+本地缓存解决

  2. 缓存一致性问题:通过延迟双删+版本号解决

  3. ID重复问题:通过多级生成+重试机制解决

  4. 数据库扩展问题:通过分库分表+读写分离解决

4. 性能测试数据

指标 目标值 实测值
创建QPS 5,000 8,200
跳转QPS 100,000 150,000
平均响应时间 <100ms 35ms
P99响应时间 <200ms 85ms
缓存命中率 >90% 96.5%
可用性 99.99% 99.995%

这个高并发短链系统通过多层次架构设计,结合多种技术方案,实现了高性能、高可用、可扩展的目标。系统在应对海量并发请求的同时,保证了数据的强一致性和系统的稳定性。

相关推荐
点点滴滴的记录2 小时前
积分系统架构设计
系统架构
郑州光合科技余经理2 小时前
可独立部署的Java同城O2O系统架构:技术落地
java·开发语言·前端·后端·小程序·系统架构·uni-app
一匹电信狗3 小时前
【C++】CPU的局部性原理
开发语言·c++·系统架构·学习笔记·c++11·智能指针·新特性
每天的每一天18 小时前
基于DDD开发的KYC用户实名认证
系统架构
kicikng1 天前
走在智能体前沿:智能体来了(西南总部)的AI Agent指挥官与AI调度官实践
人工智能·系统架构·智能体协作·ai agent指挥官·ai调度官·应用层ai
C澒1 天前
前端技术核心领域与实践方向
前端·系统架构
犀思云2 天前
如何通过网络即服务平台实现企业数字化转型?
运维·网络·人工智能·系统架构·机器人
liu****2 天前
4.Qt窗口开发全解析:菜单栏、工具栏、状态栏及对话框实战
数据库·c++·qt·系统架构
Python_Study20252 天前
面向工程材料行业的数据采集系统架构设计与选型指南
系统架构