1. 程式人生 > >網頁短連結實現原理探究

網頁短連結實現原理探究

事情是這樣的,今天一人問我一個問題,但是我懶得在說,就在網上找了一篇部落格通過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;
}

?>

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

摘要演算法

實現思路:

  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)

短連結重定向的執行過程:

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