首页 > 其他分享 >短 URL 系统是怎么设计的?

短 URL 系统是怎么设计的?

时间:2024-12-20 23:32:40浏览次数:10  
标签:return string err URL 系统 url func 设计 短链

短 URL 系统是怎么设计的?

1. 什么是短链系统

短链系统是一种将长的、复杂的网页链接转换为更短、更简洁的链接的服务。例如,一个原始的长链接:

https://www.example.com/articles/2023/detailed-report-on-technology-trends-in-artificial-intelligence

可以通过短链系统转换为:

https://short.link/abc123

2. 为什么需要短链系统

用户体验优化

  • 缩短链接长度,方便分享
  • 减少复杂链接的阅读和记忆负担
  • 适用于社交媒体、短信等字符受限的场景,在对内容长度有限制的平台发文,可编辑的文字就变多了

短链跳转的基本原理

短网址是如何形成的

短链好处多多,那么它是如何工作的呢。我们用curl -v看看

~ curl -v  GET 'http://127.0.0.1:8888/api/v1/shorturl/access?url=geRW0i1Jt5F'

* Could not resolve host: GET
* Closing connection
curl: (6) Could not resolve host: GET
*   Trying 127.0.0.1:8888...
* Connected to 127.0.0.1 (127.0.0.1) port 8888
> GET /api/v1/shorturl/access?url=geRW0i1Jt5F HTTP/1.1
> Host: 127.0.0.1:8888
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Location: http://www.baidu.com
< Traceparent: 00-96a3c00fbe0455de9814a229b93c7c8d-adb0403c96337c9a-00
< Date: Tue, 17 Dec 2024 14:55:19 GMT
< Content-Length: 0
<
* Connection #1 to host 127.0.0.1 left intact

可以看到请求后,返回了状态码 302(重定向)与 location 值为长链的响应,然后浏览器会再请求这个长链以得到最终的响应,整个交互流程图如下
在这里插入图片描述

sequenceDiagram
    participant Client as curl
    participant Server as Short URL Service
    participant Target as Target Website (baidu.com)

    Client->>Server: GET /api/v1/shorturl/access/?url=geRW0i1Jt5F
    Note over Client,Server: HTTP Request
    
    Server-->>Client: 302 Found
    Note over Server,Client: Redirect Response
    Server->>Client: Location: http://www.baidu.com
    Note right of Server: Set Redirect URL in Location Header

    alt Implicit Client Behavior
        Client->>Target: Follows 302 Redirect (not shown in diagram)
    end

主要步骤就是访问短网址后重定向访问 B,那么问题来了,301 和 302 都是重定向,到底该用哪个,这里需要注意一下 301 和 302 区别

  • 301代表 永久重定向,也就是说第一次请求拿到长链接后,下次浏览器再去请求短链的话,不会向短网址服务器请求了,而是直接从浏览器的缓存里拿,这样在 server 层面就无法获取到短网址的点击数了,如果这个链接刚好是某个活动的链接,也就无法分析此活动的效果。所以我们一般不采用 301。
  • 302临时重定向,也就是说每次去请求短链都会去请求短网址服务器(除非响应中用 Cache-Control 或 Expired 暗示浏览器缓存),这样就便于 server 统计点击数,所以虽然用 302 会给 server 增加一点压力,但在数据异常重要的今天,这点代码是值得的,所以推荐使用 302!
系统内部短链生成流程

在这里插入图片描述

flowchart LR
   Start[开始] --> GetShortCode["根据长码从缓存获取短码,检查短码是否已存在"]
   GetShortCode --> IsShortCodeExists{短码是否已存在?}
   IsShortCodeExists -->|否| GenerateShortCode["使用算法生成新的短码"]
   GenerateShortCode --> StoreShortCode["将新生成的短码保存在存储中"]
   IsShortCodeExists -->|是| CheckShortCodeLegality{保存的短码关系是否合法?}
   CheckShortCodeLegality -->|否| ReturnShortCode[返回短码]
   CheckShortCodeLegality -->|是| ReturnShortCode

短链生成的几种方法

哈希算法生成
MD5哈希
func generateByMD5(longURL string) string {
    hash := md5.Sum([]byte(longURL))
    // 取前6-8位作为短链接
    shortCode := hex.EncodeToString(hash[:])[:8]
    return shortCode
}
优点:
  • 实现简单
  • 冲突概率较低
缺点:
  • 可能存在哈希碰撞
  • 生成的短码不可预测
MurmurHash算法
func murmur64() uint64 {
	return murmur3.Sum64([]byte(str))
}
优点:
  • 冲突率更低
  • 计算速度快
自增ID生成
数据库自增
func generateByAutoIncrement(longURL string) string {
    // 数据库中保存长链接,并获取自增ID
    id := saveURLAndGetID(longURL)
    
    // 将ID转换为62进制
    return base62Encode(id)
}

// base62编码  , 62进制编码缩短域名
func base62Encode(id int64) string {
    const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    if id == 0 {
        return "0"
    }
    
    var encoding string
    for id > 0 {
        encoding = string(base[id%62]) + encoding
        id /= 62
    }
    return encoding
}
优点:
  • 保证唯一性
  • 可以直接通过短码还原ID
缺点:
  • 需要数据库支持
  • 性能相对较低
雪花算法(Snowflake)生成
type Snowflake struct {
    sequence      int64
    lastTimestamp int64
    machineID     int64
}

func (s *Snowflake) NextID() int64 {
    timestamp := time.Now().UnixNano() / 1e6

    if timestamp < s.lastTimestamp {
        panic("Clock moved backwards")
    }

    if timestamp == s.lastTimestamp {
        s.sequence = (s.sequence + 1) & 4095
        if s.sequence == 0 {
            timestamp = s.waitNextMillis(timestamp)
        }
    } else {
        s.sequence = 0
    }

    s.lastTimestamp = timestamp

    return ((timestamp - 1609459200000) << 22) |
           (s.machineID << 17) |
           (s.sequence)
}

func generateBySnowflake(longURL string) string {
    sf := &Snowflake{machineID: 1}
    id := sf.NextID()
    // 62进制编码缩短域名
    return base62Encode(id)
}
优点:
  • 高性能
  • 分布式环境下唯一
  • 可以包含时间戳信息
随机token生成
func generateByRandomToken(longURL string, length int) string {
    const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
    b := make([]byte, length)
    for i := range b {
        b[i] = charset[rand.Intn(len(charset))]
    }
    
    return string(b)
}
优点:
  • 生成简单
  • 短码更随机
缺点:
  • 冲突风险较高
  • 需要额外的唯一性检查

综合评估

方法唯一性性能可读性适用场景
哈希算法小规模系统
自增ID中小型系统
雪花算法大规模分布式
随机Token短期临时链接

最佳实践建议

  • 选择合适的算法: murmur3 转换为 63进制
url = base62Encode(murmur3.Sum64([]byte(url)))
func base62Encode(id uint64) string {
	const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	if id == 0 {
		return "0"
	}
	var encoding string
	for id > 0 {
		encoding = string(base[id%62]) + encoding
		id /= 62
	}
	return encoding
}
  • 增加防重复机制: 已存在则重复生成
func (s *urlManager) UrlToShortUrl(url string) string {
	url = base62Encode(murmur3.Sum64([]byte(url)))
	count, err := s.shortUrlRepo.CountShortUrl(s.db, url)
	if err != nil || count > 0 {
		var sid string
		for i := 0; i < 3; i++ {
			sid, err = shortid.Generate()
			if err == nil && sid != "" {
				break
			}
		}
		url = url + sid
	}
	return url
}
  • 使用缓存提高性能
func newLocalCache() (cache.Cache, error) {
	memCache, err := cache.NewRistrettoCache(cache.RistrettoCacheConfig{
		Capacity:    2147483648, // bytes, max mem:2G
		NumCounters: 200000000,  // max keys
		CostFunc:    cache.CostMemoryUsage,
	}, codec.NewSonicCodec())
	if err != nil {
		return nil, err
	}
	return memCache, nil
}
  • 定期清理过期短链

惰性删除缓存

if shortUrl.ExpireAt.UTC().Unix() < time.Now().UTC().Unix() {
		gosafe.GoSafe(context.WithoutCancel(l.ctx), func() {
			_ = l.svcCtx.LocalCache.Delete(context.WithoutCancel(l.ctx), req.Url)
			_ = l.shortUrlManager.DelShortUrl(&model.ShortUrl{ShortUrl: shortUrl.ShortUrl})
		})
		return nil, code.UrlNotExist
	}

数据追踪与分析

  • 可以统计链接点击量
  • 追踪用户来源和行为
  • 提供详细的链接访问数据

func AccessShortUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req types.AccessShortUrlRequest
		if err := httpy.Parse(r, &req); err != nil {
			httpy.ResultCtx(r, w, nil, err)
			return
		}
		l := shorturl.NewAccessShortUrlLogic(r.Context(), svcCtx)
		resp, err := l.AccessShortUrl(&req)
		if err != nil {
			httpy.ResultCtx(r, w, nil, err)
			return
		}
		// 设置 302 重定向和 Location 头
		w.Header().Set("Location", resp.Localtion)
		w.WriteHeader(http.StatusFound) // 302 状态码
		accessLog := &model.UrlAccessLog{
			ShortUrl:  req.Url,
			Ip:        sql.NullString{String: r.RemoteAddr, Valid: true},
			UserAgent: sql.NullString{String: r.UserAgent(), Valid: true},
			Referrer:  sql.NullString{String: r.Referer(), Valid: true},
		}
		_ = l.SaveAccessLog(accessLog)
	}
}

安全性

  • 可以隐藏原始链接,防止暴露复杂的 URL 结构
  • 可以增加链接跳转前的安全检查

总结

短链系统是一个集数据追踪、用户体验优化于一体的实用性服务,通过巧妙的算法和高效的存储机制,为用户提供便捷的链接服务。

标签:return,string,err,URL,系统,url,func,设计,短链
From: https://blog.csdn.net/baidu_32452525/article/details/144621212

相关文章