您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么用Java設計一個短鏈接生成系統”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么用Java設計一個短鏈接生成系統”吧!
相信大家在生活中,特別是最近的雙十一活動期間,會收到很多短信,而那些短信都有兩個特征,第一個是幾乎都是垃圾短信,這個特點此處可以忽略不計,第二個特點是鏈接很短,比如下面這個:
我們知道,短信有些是有字數限制的,直接放一個帶滿各種參數的鏈接,不合適,另外一點是,不想暴露參數。好處無非以下:
太長的鏈接容易被限制長度
短鏈接看著簡潔,長鏈接看著容易懵
安全,不想暴露參數
可以統一鏈接轉換,當然也可以實現統計點擊次數等操作
那背后的原理是什么呢?怎么實現的?讓你實現這樣的系統,你會怎么設計呢?【來自于某鵝場面試官】
短鏈接展示的邏輯
這里最重要的知識點是重定向,先復習一下http的狀態碼:
分類 | 含義 |
---|---|
1** | 服務器收到請求,需要請求者繼續執行操作 |
2** | 成功,操作被成功接收并處理 |
3** | 重定向,需要進一步的操作以完成請求 |
4** | 客戶端錯誤,請求包含語法錯誤或無法完成請求 |
5** | 服務器錯誤,服務器在處理請求的過程中發生了錯誤 |
那么以 3 開頭的狀態碼都是關于重定向的:
300:多種選擇,可以在多個位置存在
301:永久重定向,瀏覽器會緩存,自動重定向到新的地址
302:臨時重定向,客戶端還是會繼續使用舊的URL
303:查看其他的地址,類似于301
304:未修改。所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。
305:需要使用代理才能訪問到資源
306:廢棄的狀態碼
307:臨時重定向,使用Get請求重定向
整個跳轉的流程:
1.用戶訪問短鏈接,請求到達服務器
2.服務器將短鏈接裝換成為長鏈接,然后給瀏覽器返回重定向的狀態碼301/302
301永久重定向會導致瀏覽器緩存重定向地址,短鏈接系統統計訪問次數會不正確
302臨時重定向可以解決次數不準的問題,但是每次都會到短鏈接系統轉換,服務器壓力會變大。
3.瀏覽器拿到重定向的狀態碼,以及真正需要訪問的地址,重定向到真正的長鏈接上。
從下圖可以看出,確實鏈接被302重定向到新的地址上去,返回的頭里面有一個字段Location就是所要重定向的地址:
全局發號器
肯定我們第一點想到的是壓縮,像文件壓縮那樣,壓縮之后再解壓還原到原來的鏈接,重定向到原來的鏈接,但是很不幸的是,這個是行不通的,你有見過什么壓縮方式能把這么長的數字直接壓縮到這么短么?事實上不可能。就像是Huffman樹,也只能對那種重復字符較多的字符串壓縮時效率較高,像鏈接這種,可能帶很多參數,而且各種不規則的情況都有,直接壓縮算法不現實。
那https://dx.10086.cn/tzHLFw與https://gd.10086.cn/gmccapp/webpage/payPhonemoney/index.html?channel=之間的裝換是怎么樣的呢?前面路徑不變,變化的是后面,也就是tzHLFw與gmccapp/webpage/payPhonemoney/index.html?channel=之間的轉換。
實際也很簡單,就是數據庫里面的一條數據,一個id對應長鏈接(相當于全局的發號器,全局唯一的ID):
id | url |
---|---|
1 | gd.10086.cn/gmccapp/web… |
這里用到的,也就是我們之前說過的分布式全局唯一ID,如果我們直接用id作為參數,貌似也可以:https://dx.10086.cn/1,訪問這個鏈接時,去數據庫查詢獲得真正的url,再重定向。
單機的唯一ID很簡單,用原子類AtomicLong就可以,但是分布式的就不行了,簡單點可以用 redis,或者數據庫自增,或者可以考慮Zookeeper之類的。
id 轉換策略
但是直接用遞增的數字,有兩個壞處:
數字很大的時候,還是很長
遞增的數字,不安全,規律性太強了
明顯我們平時看到的鏈接也不是數字的,一般都是大小寫字母加上數字。為了縮短鏈接的長度,我們必須把id轉換掉,比如我們的短鏈接由a-z,A-Z,0-9組成,相當于62進制的數字,將id轉換成為62進制的數字:
public class ShortUrl { private static final String BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static String toBase62(long num) { StringBuilder result = new StringBuilder(); do { int i = (int) (num % 62); result.append(BASE.charAt(i)); num /= 62; } while (num > 0); return result.reverse().toString(); } public static long toBase10(String str) { long result = 0; for (int i = 0; i < str.length(); i++) { result = result * 62 + BASE.indexOf(str.charAt(i)); } return result; } public static void main(String[] args) { // tzHLFw System.out.println(toBase10("tzHLFw")); System.out.println(toBase62(27095455234L)); } }
id轉 62位的key 或者key裝換成為id都已經實現了,不過計算還是比較耗時的,不如加個字段存起來,于是數據庫變成了:
id | key | url |
---|---|---|
27095455234 | tzHLFw | gd.10086.cn/gmccapp/web… |
但是這樣還是很容易被猜出這個id和key的對應關系,要是被遍歷訪問,那還是很不安全的,如果擔心,可以隨機將短鏈接的字符順序打亂,或者在適當的位置加上一些隨機生成的字符,比如第1,4,5 位是隨機字符,其他位置不變,只要我們計算的時候,將它對應的關系存到數據庫,我們就可以通過連接的key找到對應的url。(值得注意的是,key必須是全局唯一的,如果沖突,必須重新生成)
一般短鏈接都有過期時間,那么我們也必須在數據庫里面加上對應的字段,訪問的時候,先判斷是否過期,過期則不給予重定向。
性能考慮
如果有很多短鏈接暴露出去了,數據庫里面數據很多,這個時候可以考慮使用緩存優化,生成的時候順便把緩存寫入,然后讀取的時候,走緩存即可,因為一般短鏈接和長鏈接的關系不會修改,即使修改,也是很低頻的事情。
如果系統的id用完了怎么辦?這種概率很小,如果真的發生,可以重用舊的已經失效的id號。
如果被人瘋狂請求一些不存在的短鏈接怎么辦?其實這就是緩存穿透,緩存穿透是指,緩存和數據庫都沒有的數據,被大量請求,比如訂單號不可能為-1,但是用戶請求了大量訂單號為-1的數據,由于數據不存在,緩存就也不會存在該數據,所有的請求都會直接穿透到數據庫。如果被惡意用戶利用,瘋狂請求不存在的數據,就會導致數據庫壓力過大,甚至垮掉。
針對這種情況,一般可以用布隆過濾器過濾掉不存在的數據請求,但是我們這里id本來就是遞增且有序的,其實我們范圍大致都是已知的,更加容易判斷,超出的肯定不存在,或者請求到的時候,緩存里面放一個空對象也是沒有問題的。
感謝各位的閱讀,以上就是“怎么用Java設計一個短鏈接生成系統”的內容了,經過本文的學習后,相信大家對怎么用Java設計一個短鏈接生成系統這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。