溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java中怎么將長鏈接轉換成短鏈接

發布時間:2021-06-21 15:19:51 來源:億速云 閱讀:597 作者:Leah 欄目:大數據
# 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();
    }
}

2.2 沖突處理方案

  1. 布隆過濾器:快速判斷URL是否已存在
  2. 重試機制:添加隨機鹽值重新哈希
  3. 數據庫唯一索引:確保最終一致性

三、基于分布式ID的方案

3.1 Snowflake算法實現

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

3.2 轉換BASE62

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

四、完整系統設計

4.1 架構圖

graph TD
    A[客戶端] --> B(API網關)
    B --> C[短鏈生成服務]
    C --> D{緩存層}
    D -->|緩存未命中| E[(數據庫)]
    E --> D
    B --> F[短鏈跳轉服務]
    F --> D

4.2 數據庫設計

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

4.3 緩存策略

  • Redis結構
    
    SET short:code:abc123 "https://original.long/url"
    EXPIRE short:code:abc123 86400
    

五、性能優化方案

5.1 批量預生成

// 預生成10萬個短碼
public void preGenerateCodes(int batchSize) {
    LongStream.range(0, batchSize)
        .parallel()
        .forEach(id -> {
            String code = Base62Converter.encode(id);
            redisTemplate.opsForValue().set("reserved:"+code, "1");
        });
}

5.2 熱點檢測

// 使用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; // 閾值判斷
}

六、安全防護措施

6.1 惡意URL檢測

public boolean isMalicious(String url) {
    // 1. 域名白名單校驗
    // 2. 調用第三方安全API
    // 3. 正則匹配可疑模式
    return false;
}

6.2 訪問控制

@RestController
public class UrlController {
    
    @RateLimiter(value = 100, key = "#ip") // 每IP每秒100次
    @PostMapping("/api/shorten")
    public ResponseEntity create(@RequestParam String url, 
                               @RequestHeader String ip) {
        // ...
    }
}

七、測試方案

7.1 單元測試用例

@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%
}

八、擴展功能實現

8.1 自定義短碼

public boolean customShortCode(String code, String longUrl) {
    if (redisTemplate.hasKey("custom:" + code)) {
        return false;
    }
    redisTemplate.opsForValue().set("custom:"+code, longUrl);
    return true;
}

8.2 數據統計功能

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字,此處展示核心內容框架,完整實現需包含更多細節代碼和說明)

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女