怎么用Java设计一个短链接生成系统

发布时间:2021-12-11 15:47:49 作者:iii
来源:亿速云 阅读:426
# 怎么用Java设计一个短链接生成系统

## 目录
1. [系统概述](#系统概述)
2. [核心功能需求](#核心功能需求)
3. [技术选型](#技术选型)
4. [数据库设计](#数据库设计)
5. [核心算法实现](#核心算法实现)
6. [系统架构设计](#系统架构设计)
7. [详细代码实现](#详细代码实现)
8. [性能优化](#性能优化)
9. [安全考虑](#安全考虑)
10. [扩展功能](#扩展功能)

---

## 系统概述
短链接系统是将长URL转换为短字符串的服务(如bit.ly/abc123),具有:
- 节省字符空间(短信/社交媒体场景)
- 便于统计访问数据
- 隐藏原始URL等优势

典型技术指标要求:
- 每秒千级写入能力
- 毫秒级响应时间
- 99.9%可用性

---

## 核心功能需求
| 功能模块       | 详细说明                                                                 |
|----------------|--------------------------------------------------------------------------|
| URL缩短        | 将长URL转换为6-8字符的短码                                               |
| URL重定向      | 访问短链接时302跳转到原始URL                                             |
| 自定义短码     | 允许用户指定特定短码(需校验唯一性)                                      |
| 访问统计       | 记录访问时间、IP、UserAgent等信息                                        |
| 有效期控制     | 支持设置永久/临时链接                                                    |

---

## 技术选型
```mermaid
graph TD
    A[Spring Boot] --> B[Web MVC]
    A --> C[Spring Data JPA]
    D[Redis] --> E[缓存热点数据]
    F[MySQL] --> G[持久化存储]
    H[Zookeeper] --> I[分布式ID生成]

选型理由: - Spring Boot:快速构建RESTful服务 - Redis:应对高并发查询(10万+ QPS) - MySQL:可靠持久化存储 - Zookeeper:分布式环境协调


数据库设计

主要表结构

CREATE TABLE `short_url` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `short_code` varchar(8) NOT NULL UNIQUE,
  `original_url` varchar(2048) NOT NULL,
  `create_time` datetime NOT NULL,
  `expire_time` datetime DEFAULT NULL,
  `creator_ip` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  INDEX `idx_short_code` (`short_code`)
);

CREATE TABLE `access_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `short_code` varchar(8) NOT NULL,
  `access_time` datetime NOT NULL,
  `user_agent` varchar(512) DEFAULT NULL,
  `ip_address` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  INDEX `idx_code_time` (`short_code`, `access_time`)
);

核心算法实现

短码生成方案对比

方案 优点 缺点
自增ID+Base62 无碰撞风险 需暴露数字ID
Hash算法 分布式友好 可能冲突(需解决碰撞)
预生成池 高性能 需要维护池状态

推荐实现

// Base62编码示例
public class Base62Encoder {
    private static final String CHARACTERS = 
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    
    public static String encode(long num) {
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            sb.insert(0, CHARACTERS.charAt((int)(num % 62)));
            num /= 62;
        }
        return sb.toString();
    }
}

// 使用Snowflake生成分布式ID
public class ShortCodeGenerator {
    public String generate() {
        long id = Snowflake.nextId(); // 分布式ID
        return Base62Encoder.encode(id);
    }
}

系统架构设计

sequenceDiagram
    participant Client
    participant API
    participant Cache
    participant DB
    
    Client->>API: POST /api/shorten (原始URL)
    API->>DB: 保存映射关系
    DB-->>API: 返回短码
    API->>Cache: 写入缓存
    API-->>Client: 返回短链接
    
    Client->>API: GET /s/短码
    API->>Cache: 查询原始URL
    alt 缓存命中
        Cache-->>API: 返回URL
    else 缓存未命中
        API->>DB: 查询原始URL
        DB-->>API: 返回URL
        API->>Cache: 回填缓存
    end
    API-->>Client: 302重定向

详细代码实现

1. 控制器层

@RestController
@RequestMapping("/api")
public class ShortUrlController {
    
    @Autowired
    private ShortUrlService service;

    @PostMapping("/shorten")
    public ResponseEntity<ShortUrlResponse> createShortUrl(
            @RequestBody ShortenRequest request) {
        
        String shortCode = service.createShortUrl(
            request.getUrl(), 
            request.getCustomCode());
        
        return ResponseEntity.ok(
            new ShortUrlResponse(shortCode));
    }

    @GetMapping("/s/{code}")
    public void redirect(
            @PathVariable String code, 
            HttpServletResponse response) throws IOException {
        
        String originalUrl = service.getOriginalUrl(code);
        response.sendRedirect(originalUrl);
    }
}

2. 服务层关键逻辑

@Service
public class ShortUrlServiceImpl implements ShortUrlService {
    
    @Autowired
    private ShortUrlRepository repository;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String CACHE_PREFIX = "short_url:";
    private static final int MAX_RETRY = 3;

    @Override
    @Transactional
    public String createShortUrl(String originalUrl, String customCode) {
        // 校验URL合法性
        validateUrl(originalUrl);
        
        String shortCode;
        if (StringUtils.isNotBlank(customCode)) {
            // 自定义短码处理
            if (repository.existsByShortCode(customCode)) {
                throw new BusinessException("短码已被占用");
            }
            shortCode = customCode;
        } else {
            // 自动生成短码(带重试机制)
            int retryCount = 0;
            do {
                shortCode = ShortCodeGenerator.generate();
                retryCount++;
            } while (repository.existsByShortCode(shortCode) 
                    && retryCount < MAX_RETRY);
        }
        
        // 持久化存储
        ShortUrl entity = new ShortUrl();
        entity.setOriginalUrl(originalUrl);
        entity.setShortCode(shortCode);
        entity.setCreateTime(LocalDateTime.now());
        repository.save(entity);
        
        // 写入缓存
        redisTemplate.opsForValue().set(
            CACHE_PREFIX + shortCode, 
            originalUrl, 
            7, TimeUnit.DAYS);
        
        return shortCode;
    }
}

性能优化

  1. 缓存策略

    • 热点数据永不过期
    • LRU缓存淘汰策略
    // Spring Cache配置示例
    @Configuration
    @EnableCaching
    public class CacheConfig {
       @Bean
       public CacheManager cacheManager() {
           return new RedisCacheManager(
               RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory()),
               RedisCacheConfiguration.defaultCacheConfig()
                   .entryTtl(Duration.ofHours(1))
                   .disableCachingNullValues()
           );
       }
    }
    
  2. 批量处理

    // 使用Spring Batch进行日志批量插入
    @Bean
    public JdbcBatchItemWriter<AccessLog> logWriter() {
       return new JdbcBatchItemWriterBuilder<AccessLog>()
           .sql("INSERT INTO access_log VALUES (...)")
           .dataSource(dataSource)
           .build();
    }
    

安全考虑

  1. 防护措施
    • URL黑名单过滤(钓鱼网站)
    • 频率限制(Guava RateLimiter)
    ”`java private final RateLimiter limiter = RateLimiter.create(100.0); // 100QPS

@GetMapping(“/s/{code}”) public ResponseEntity<?> redirect(…) { if (!limiter.tryAcquire()) { throw new TooManyRequestsException(); } // … }


2. **敏感操作审计**:
   ```java
   @Aspect
   @Component
   public class AuditAspect {
       @AfterReturning("execution(* com..ShortUrlService.create*(..))")
       public void auditLog(JoinPoint jp) {
           // 记录操作日志
       }
   }

扩展功能

  1. 可视化面板

    // 使用ECharts展示访问趋势
    myChart.setOption({
       xAxis: { data: ['Mon', 'Tue', 'Wed'] },
       series: [{ data: [120, 200, 150] }]
    });
    
  2. API限流方案对比

    方案 实现复杂度 精确度
    令牌桶
    固定窗口
    滑动日志 极高

最佳实践建议: 1. 生产环境建议使用分布式Redis集群 2. 短码长度建议6-8字符(62^6≈568亿组合) 3. 重要业务链接建议使用HTTPS 4. 定期归档冷数据到对象存储 “`

推荐阅读:
  1. 短网址生成链接接口平台推荐--新浪T.CN短链接 腾讯URL短链接生成地址
  2. java学生成绩管理系统设计与实现

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:SpringBoot中常用的注解有哪些

下一篇:HyperLedger Fabric 2.0-release如何测试网络部署

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》