使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):四、生成code算法
章節(jié)
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):一、基本原理
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):二、架構(gòu)設(shè)計(jì)
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):三、項(xiàng)目目錄結(jié)構(gòu)設(shè)計(jì)
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):四、生成code算法
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):五、添加和獲取短鏈接
?使用Go語(yǔ)言開(kāi)發(fā)一個(gè)短鏈接服務(wù):六、鏈接跳轉(zhuǎn)
??Gitee https://gitee.com/alxps/short_link
Github https://github.com/1911860538/short_link
上一篇介紹了項(xiàng)目目錄接口,這篇將實(shí)現(xiàn)短鏈接code算法。
前言
??假如某個(gè)用戶(hù),有個(gè)長(zhǎng)鏈接為:https://github.com/gin-gonic/gin/blob/master/internal/bytesconv/bytesconv_1.19.go,我們短鏈接服務(wù)域名為https://a.b.c,為這個(gè)長(zhǎng)鏈接生成對(duì)應(yīng)的短鏈接為https://a.b.c/N26jas。這里code便是Na6jas。
??code必須滿(mǎn)足以下幾個(gè)要求:
????1、只包含,大小寫(xiě)字母或數(shù)字
????2、短
????3、唯一
??大小字母加上數(shù)字,共62個(gè),當(dāng)code長(zhǎng)度為N時(shí),code可以有62的N次方個(gè)。N為5、6、7,code個(gè)數(shù)大約為1600萬(wàn), 568億, 3.5萬(wàn)億。586億夠用,因此系統(tǒng)code長(zhǎng)度為6,夠短。
如何生成唯一code
??這還不簡(jiǎn)單!生成6位隨機(jī)大小寫(xiě)字母或數(shù)字的字符,數(shù)據(jù)庫(kù)存在,重新隨機(jī)生成,遞歸直到不沖突。本篇完……,當(dāng)然不是。
??至于如何生成6位隨機(jī)大小寫(xiě)字母或數(shù)字,可以看看這篇文章:Golang 生成隨機(jī)字符串的八種方式與性能測(cè)試。文章隨機(jī)字符串只有大小寫(xiě)字母,我們?cè)诖嘶A(chǔ)上稍作修改,代碼如下:
package service
import (
"math/rand"
"time"
"unsafe"
)
const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var src = rand.NewSource(time.Now().UnixNano())
const (
// 6 bits to represent a character index
charIdBits = 6
// All 1-bits as many as charIdBits
charIdMask = 1<<charIdBits - 1
// 由于現(xiàn)在包含了62個(gè)字符,計(jì)算新的charIdMax
charIdMax = 63 / charIdBits
// 字符集的大小
charsetSize = len(letters)
)
func randStr(n int) string {
b := make([]byte, n)
for i, cache, remain := n-1, src.Int63(), charIdMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), charIdMax
}
if idx := int(cache & charIdMask); idx < charsetSize {
b[i] = letters[idx]
i--
}
cache >>= charIdBits
remain--
}
return unsafe.String(unsafe.SliceData(b), len(b))
}
??但是,我想法是,code要根據(jù)用戶(hù)id和長(zhǎng)鏈接url推算出來(lái),即相同的user_id+long_url,每次得到的code都一樣。先上代碼,再講思路
package service
import (
"crypto/md5"
"encoding/hex"
"hash/fnv"
"io"
"unsafe"
"github.com/1911860538/short_link/config"
)
const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var confCodeLen = config.Conf.Core.CodeLen
// GenCode
func GenCode(userId string, longUlr string, salt string) (string, error) {
// 首先對(duì)userId+longUrl+salt md5 主要為了防止longUrl包含漢字等字符串
hasher := md5.New()
if _, err := io.WriteString(hasher, userId+longUlr+salt); err != nil {
return "", err
}
hashStr := hex.EncodeToString(hasher.Sum(nil))
stepLen := len(hashStr) / confCodeLen
remain := len(hashStr) % confCodeLen
if remain > 0 {
stepLen += 1
}
lettersLen := uint32(len(letters))
b := make([]byte, confCodeLen)
for i := 0; i < confCodeLen; i++ {
// 根據(jù)要生成的code長(zhǎng)度,切分md5字符串
var piece string
if remain > 0 && i == confCodeLen-1 {
piece = hashStr[i*stepLen : i*stepLen+remain]
} else {
piece = hashStr[i*stepLen : i*stepLen+stepLen]
}
// 為切片元素生成對(duì)應(yīng)的整形數(shù)值
h := fnv.New32a()
pieceBytes := unsafe.Slice(unsafe.StringData(piece), len(piece))
if _, err := h.Write(pieceBytes); err != nil {
return "", err
}
pieceHash32 := h.Sum32()
// 切片字符的整形,取len(letters)余數(shù),并取letters索引為該余數(shù)的letter
letterIdx := pieceHash32 % lettersLen
b[i] = letters[letterIdx]
}
return unsafe.String(unsafe.SliceData(b), len(b)), nil
}
??比如user_id為"1f70a466-1449-4676-b2d7-2037341c718e",long_url為"https://i.cnblogs.com/posts/edit;postId=18090256":
????1、將user_id+long_url字符串,生成md5的哈希字符串,這一步是為了得到固定長(zhǎng)度的字符。結(jié)果為,"101360eb993b977d9f6969813ee84338"
????2、根據(jù)要生成的code長(zhǎng)度,切分步驟1的字符串。我們要得到6位code,因而我們將得到的字符切片結(jié)果為,["101360", "eb993b", "977d9f", "696981", "3ee843", "38"]
????3、接著我們對(duì)步驟2每個(gè)字符切片元素,使用fnv哈希,獲得uint32,結(jié)果為[791694210, 3988549581, 2254501405, 2706880430, 3291227237, 2414894606]
????4、對(duì)步驟3,uint32切片,每個(gè)元素,取letters長(zhǎng)度余數(shù),獲得余數(shù)切片,[28, 53, 55, 48, 17, 0]
????5、取letters索引為步驟4余數(shù)切片的字母或數(shù)字字符,得到最終結(jié)果,"2RTMra"
??步驟3/4參考了一致性哈希。當(dāng)然,上面兩種獲取6位code方式憑個(gè)人想法哈,網(wǎng)上也有其它算法實(shí)現(xiàn)可參考。本項(xiàng)目中使用上面說(shuō)的后者。
總結(jié)
??本篇講了短鏈接code生成邏輯,下一篇講添加和獲取短鏈接邏輯。

浙公網(wǎng)安備 33010602011771號(hào)