網頁短鏈接實現原理探究


事情是這樣的,今天一人問我一個問題,但是我懶得在說,就在網上找了一篇博客通過QQ發送給他,但是在發送鏈接時我發現之前很長的鏈接變成了短鏈接,且這個短鏈接能夠正常訪問之前的長鏈接,好奇之下就有了這篇文章.

什么是短鏈接?

我的理解就是通過一定的算法和技術實現將原本很長的網址轉換為較短的網址,從而便於用戶記憶和在互聯網上的傳播.常用於有字數約束的微博,二維碼等場景.

現在很多公司都提供了短鏈接服務,比如百度,新浪微博等等,以供用戶自由方便的生成短鏈接.

短鏈接的大致整體流程

今天上午我找的原本鏈接是這個:

https://blog.wpjam.com/m/scripts-and-plugins-for-analyzing-website-traffic-stats/
生成的短鏈接(長期的話可能會無效):
http://url.cn/5r8GoSZ
大致流程是這樣的:我復制(輸入)了一個長鏈接,通過騰訊服務器的轉化后得到一個以http://url.cn開頭的短鏈接,然后我可以將該網址在互聯網上進行分享和傳播,其他人在訪問該短網址可以進入到之前原本長網址對應的頁面.

所以要想將生成短鏈接,我們需要注意兩個問題:

  1. 如何將任意長的字符串轉化為較短長或者固定長的字符串.
  2.  如何將短鏈接還原成之前的長鏈接,使之能夠訪問.

算法實現

Hash實現

通過一定方式將任意長的文本轉化為一個固定長的字符串,只要目標文本長度適當,那么我們對於不同的輸入通過哈希幾乎(注意是幾乎)不可能得到對應同一個字符串.通過對長鏈接進行Hash運算,將Hash值作為這個長鏈接的唯一標示.但是通過Hash實現可能會造成碰撞.不一樣的長網址縮短成了同一個短網址,那也就做不到復原了.

對於碰撞問題,有一種緩沖方法就是在呈現碰撞了以后后邊在增加隨機字符,隨機字符的增加能夠緩解碰撞的疑問,但是這終究是一種緩沖的辦法,沒有徹底解決碰撞. 

自增序列算法(永不重復算法)

我們可以設置一個自增id,對於每一個新的長鏈接給他一個不重復的id.

原理:當服務器接收到一個網址時首先檢驗這個網址在服務器中是否再存,如果不存在,存儲這個新網址並分發一個id,這個id設置成自增,保證了每一個存儲的網址的id都是唯一標示.比如上面的,當一個鏈接過來時,給這個鏈接發一個0,再有一個鏈接過來時,給后面這個鏈接一個1,以此類推.

數據實現:我們發現短鏈接后面的參數好像都是定長的,但是如果通過id進行時,參數不定長,且隨着id的自增,可能會出現這種情況:url.cn/10000000.我們可以將十進制的id轉化為多進制,比如在以'0-9a-z'這36個字符表示的36進制中,一億可以被表示為1njchs,基本實現不重復夠用.如果數據量更大,我們可以采用62進制進行轉化:

短址的長度一般設為 6 位,而每一位是由 [a - z, A - Z, 0 - 9] 總共 62 個字母組成的,所以 6 位的話,總共會有 62^6 ~= 568億種組合。

存儲實現:

對於小型系統,簡單的mysql系統的表自增索引即可實現(注意自增id數據類型,int只能到65535)

大型系統可以搭建分布式key-value系統進行存儲.

我使用mysql簡單建了一張表,用於保存長網址的數據,只有兩個字段,一個是主鍵用於保存id,一個url字段用於存放原始的長網址.在進行長網址轉換時,先檢查數據表中是否存在該長網址,如果存在直接獲取該記錄的id,否在創建一條新的記錄並返回該記錄的id,對於這id進行進制轉化處理后拼接到准備好的域名后面得到一個對應的短網址返回給用戶.

這里我簡單模仿了一個轉換短鏈接的功能:

url.php:用於模擬數據庫存儲

<?php

return array(
    'http://test1.com/12345vn6',
    'http://test2.com/1234gf56',
    'http://teat1.com/123ssgg456',
    'http://test1.com/1234svss56',
    'http://tefasfd1.com/123456',
    'http://tesddt1.com/12sss3456',
    'http://tehghdgst1.com/123dsaf456',
    'http://tedddst1.com/12SDsd3456',
    'http://testaa1bb.com/1234ccgfryh56',
    'http://testaa1dfd.com/1234ccgfryh56',
    'http://testaa1.com/1234ccgfryh56',
    'http://testaa1.cssom/1234ccgfryh56',
    'http://testaa1.com/1234ccgfryh56',
    'http://testraa1.com/1234ccgfryzh56',
    'http://teffstaa1.com/1234ccgfsryh56',
    'http://testaxxa1.com/1234ccgfrsryh56',
    'http://testaa1ll.com/1234ccgyrfryh56',
    'http://testaa1.com/1234ccgfryyh56',
    'http://tesbbtaa1.com/1ss234cjcgfryh56',
)

?>
get.php:用於模擬生成短鏈接
<?php

    require './jinzhi.php';
    $arr = include('./url.php');

    $host = 'http://url.cn/';

    $url = $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];

    $keyId = in_array($url, $arr) ? array_search($url, $arr) : (array_push($arr, $url) - 1);

    $toKey = get_char($keyId);

    echo $host . $toKey;

?>
jinzhi.php:模擬進制轉化
<?php

/**
 * @desc  im:十進制數轉換成三十六機制數
 * @param (int)$num 十進制數
 * return 返回:三十六進制數
*/
function get_char($num) {
    $num = intval($num);
    if ($num <= 0)
        return false;
    $charArr = array("0","1","2","3","4","5","6","7","8","9",'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z');
    $char = '';
    do {
        $key = ($num - 1) % 36;
        $char= $charArr[$key] . $char;
        $num = floor(($num - $key) / 36);
    } while ($num > 0);
    return $char;
}

/**
 * @desc  im:三十六進制數轉換成十機制數
 * @param (string)$char 三十六進制數
 * return 返回:十進制數
 */
function get_num($char){
    $array=array("0","1","2","3","4","5","6","7","8","9","A", "B", "C", "D","E", "F", "G", "H", "I", "J", "K", "L","M", "N", "O","P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y","Z");
    $len=strlen($char);
    for($i=0;$i<$len;$i++){
        $index=array_search($char[$i],$array);
        $sum+=($index+1)*pow(36,$len-$i-1);
    }
    return $sum;
}

?>
進行路由請求:http://localhost:4000/get.php/10000000

輸出:http://url.cn/J

至於解析短鏈接跳轉至原有鏈接,只是對上面思路進行取反.

摘要算法

實現思路:

  1. 將長網址 md5 生成 32 位簽名串,分為 4 段, 每段 8 個字節
  2. 對這四段循環處理, 取 8 個字節, 將他看成 16 進制串與 0x3fffffff(30位1) 與操作, 即超過 30 位的忽略處理
  3. 這 30 位分成 6 段, 每 5 位的數字作為字母表的索引取得特定字符, 依次進行獲得 6 位字符串
  4. 總的 md5 串可以獲得 4 個 6 位串,取里面的任意一個就可作為這個長 url 的短 url 地址

這種算法雖然會生成四個短鏈接,但是存在重復幾率.

算法對比

采用自增序列的好處就是簡單好理解易操作.但是由於id隨着增大長度不固定,但是這個問題可以通過讓id從指定的數字開始遞增即可以解決.還有一個問題就是我們使用的短碼是有序的,可能會存在安全方面的問題.當然相關的防護手法也有很多,比如簽名驗證之類的安全策略;我們也可以自己實現安全手法,比如從一個隨機中心值進行開端計數,然后選用一些校檢位算法計算出固定位的校檢碼,將其連接起來,得到固定長不遞增的短碼.

第二種算法存在碰撞的問題,雖然產生重復(碰撞)的幾率很小.但是也采用這種算法也有一個好處就是短碼的位數是固定的,不會從一位到多位.

所以這兩種算法各有千秋,如果事務所需要的短鏈接有效期較短,那么通過批處理定期清洗,那么用摘要算法也不錯.而自增算法能夠確保任何懇求量都不會呈現沖突也不失一種非常好的解決算法.

重定向的問題(301還是302)

短鏈接重定向的執行過程:

  1. 用戶訪問短鏈接:https://dwz.cn/9WnR9Qcx
  2. 短鏈接服務器dwz.cn收到請求,根據URL路徑9WnR9Qcx獲取到原始的長鏈接:http://www.lishanlei.cn/
  3. 服務器返回狀態碼,將響應頭中的Location設置為:http://www.lishanlei.cn/
  4. 瀏覽器重新向http://www.lishanlei.cn/發送請求
  5. 返回響應
Request URL: https://dwz.cn/9WnR9Qcx
Request Method: GET
Status Code: 302 Found
Remote Address: 220.181.164.108:443
Referrer Policy: no-referrer-when-downgrade

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin,Accept,Content-Type,X-Requested-With
Access-Control-Allow-Methods: POST,GET,PUT,PATCH,DELETE,HEAD
Access-Control-Allow-Origin: 
Content-Length: 47
Content-Type: text/html; charset=utf-8
Date: Wed, 03 Oct 2018 05:42:18 GMT
Location: http://www.lishanlei.cn/
那么服務器在返回狀態碼時應該選取301還是302呢?

301是永久重定向,而302是臨時重定向.

如果選取301,短鏈接生成以后就不會變化,所以用301符合http語義,這樣對服務器的壓力會有所減少.但是這樣一來,我們就無法統計短地址被點擊的次數了.

而選擇302會增加服務器的壓力,但是我們可以統計短鏈接被點擊的次數,這些數據可能對於公司的發展規划非常重要.

綜上所述,我認為更好的應該選擇302

 

End


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM