用golang的map實現(xiàn)短鏈接服務(wù)的核心是通過兩個map維護長短鏈雙向映射,配合自增id生成base62短串,并通過http接口提供生成和跳轉(zhuǎn)功能。1. 使用shorttolong和longtoshort兩個map實現(xiàn)雙向映射,避免重復(fù)生成相同短鏈;2. 通過自增id結(jié)合base62編碼生成唯一短標識符;3. 利用net/http庫創(chuàng)建shorten接口接收長鏈接生成短鏈、redirect接口根據(jù)短鏈跳轉(zhuǎn)原鏈接;4. 數(shù)據(jù)存儲于內(nèi)存適合原型開發(fā),但需注意重啟丟失、并發(fā)安全及沖突風(fēng)險。
短鏈接服務(wù)的核心是將一個長 URL 映射成一個簡短的字符串,用戶訪問這個短串就能跳轉(zhuǎn)到原始地址。如果你只是想快速實現(xiàn)一個基礎(chǔ)版本,用 golang 的 map 做內(nèi)存存儲是一個簡單又直接的辦法。
下面我們就來看看怎么一步步用 Golang + map 實現(xiàn)一個最簡單的短鏈接服務(wù)。
1. 設(shè)計數(shù)據(jù)結(jié)構(gòu)和映射關(guān)系
我們使用兩個 map 來保存長短鏈接之間的雙向映射:
立即學(xué)習(xí)“go語言免費學(xué)習(xí)筆記(深入)”;
- 一個用于短鏈 → 長鏈(供跳轉(zhuǎn)時查找)
- 一個用于長鏈 → 短鏈(避免重復(fù)生成)
var shortToLong = make(map[string]string) var longToShort = make(map[string]string)
這樣設(shè)計的好處是,當用戶提交相同的長鏈接時,我們可以直接返回之前生成的短鏈接,而不是每次都生成新的。
2. 生成短鏈接標識符
短鏈接的關(guān)鍵在于生成一個唯一的、較短的字符串作為標識。常見做法是使用 Base62 編碼(0-9a-zA-Z),也可以結(jié)合遞增 ID 或隨機生成。
這里我們采用一個簡單的自增 ID 方式,每次生成后轉(zhuǎn)換為 Base62:
func generateShortKey(id int) string { const base62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" result := "" for id > 0 { id-- result = string(base62[id%62]) + result id /= 62 } return result }
比如 ID=1 會生成 “a”,ID=10000 會生成 “27g”,足夠簡潔且不易沖突。
3. 處理請求邏輯
我們使用 Go 的標準庫 net/http 搭建一個簡單的 HTTP 服務(wù),包含兩個接口:
- /shorten:接收長鏈接,返回短鏈接
- /{key}:根據(jù)短鏈接 key 跳轉(zhuǎn)到原鏈接
接口一:生成短鏈接
var counter = 1 func shortenHandler(w http.ResponseWriter, r *http.Request) { longURL := r.URL.Query().Get("url") if longURL == "" { http.Error(w, "Missing 'url' parameter", http.StatusBadRequest) return } if short, exists := longToShort[longURL]; exists { fmt.Fprintf(w, "Short URL: http://localhost:8080/%sn", short) return } short := generateShortKey(counter) counter++ shortToLong[short] = longURL longToShort[longURL] = short fmt.Fprintf(w, "Short URL: http://localhost:8080/%sn", short) }
接口二:跳轉(zhuǎn)處理
func redirectHandler(w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) < 2 || parts[1] == "" { http.Error(w, "Invalid short URL", http.StatusBadRequest) return } key := parts[1] if longURL, exists := shortToLong[key]; exists { http.Redirect(w, r, longURL, http.StatusFound) } else { http.NotFound(w, r) } }
然后注冊路由并啟動服務(wù):
func main() { http.HandleFunc("/shorten", shortenHandler) http.HandleFunc("/", redirectHandler) fmt.Println("Starting server at :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
4. 測試一下
-
生成短鏈接:
curl "http://localhost:8080/shorten?url=https://example.com/really-long-path?query=abc"
-
訪問短鏈接跳轉(zhuǎn):
curl -v http://localhost:8080/a
注意事項和優(yōu)化空間
雖然上面的方法已經(jīng)能跑起來,但還是有些需要注意的地方:
- 重啟數(shù)據(jù)丟失:因為數(shù)據(jù)存在內(nèi)存里,程序重啟就會清空。如果要持久化,可以考慮加個文件或數(shù)據(jù)庫寫入。
- 并發(fā)安全問題:多個請求同時操作 map 可能會有競態(tài)條件。可以用 sync.Mutex 或者換成 sync.Map。
- 短鏈沖突風(fēng)險:如果使用隨機生成而不是自增 ID,需要檢查是否已存在該 key。
- 性能限制:內(nèi)存存儲適合小規(guī)模使用,如果數(shù)據(jù)量大了就需要上 redis 這樣的緩存系統(tǒng)。
基本上就這些。用 map 實現(xiàn)短鏈接服務(wù)雖然簡單,但很適合練手或做原型開發(fā)。等你熟悉流程后,再擴展功能、換存儲方式都不難。