長URL轉短連接的簡單設計與實現

非常多的時候,我們不想在分享的時候採用長長的鏈接,主要的原因有:

  1. URL太長佔顯示空間、難於輸入,轉成二維碼點點小,難於識別
  2. 長的URL提供的信息太多,不利於信息安全,甚至容易造成倒庫
  3. 其他理由......

今天的理由不是重點,重點是如何用一點點代碼實現這個短鏈接的生成。

我們的注意點有:

  1. 生成的短鏈接地址長度可調整,畢竟不能因爲4位、5位、6位的長度變化就讓我們重新寫一套程序
  2. 要有防衝突處理機制,不能因爲算法導致兩個長鏈接地址生成的短地址一樣就把前一個覆蓋掉
  3. 有時候,一個地址用過之後覺得不需要使用了,哪麼這個地址失效之後,短地址可以有效收回。

首先設計接口

/**
 * Created by luoguo on 2017/3/16.
 */
public interface UrlShortener {
    /**
     * 設置產生短鏈接長度
     * @param length
     * @return
     */
    void setLength(int length);

    /**
     * 返回短鏈接長度
     * @return
     */
    int getLength();

    /**
     * 返回指定地址對應的短鏈接
     * @param url
     * @return
     */
    String get(String url);

    /**
     * 存儲對應關係
     * @param url
     * @param shortUrl
     */
    void put(String url, String shortUrl);
    /**
     * 到庫裏查看是不是存在映射,如果不存在返回null
     * @param shortUrl
     * @return
     */
    String seek(String shortUrl);

    /**
     * 據地址產生短地址
     * @param url
     * @return
     */
    String generate(String url);

    /**
     * 根據地址和種子產生一個短地址
     * @param url
     * @param seed
     * @return
     */
    String generate(String url, int seed);
    /**
     * 清除指定URL的短鏈接信息
     * @param url
     */
    void clean(String url);

    /**
     * 清除指定時間以前沒有使用的所有短鏈接
     * @param date
     */
    void clean(Date date);
}
只是個示例,註釋比較簡陋,接下來是抽象類實現,把公共的內容放這裏

import java.util.Random;

/**
 * Created by luoguo on 2017/3/16.
 */
public abstract class AbstractUrlShortener implements UrlShortener {
    public static char[] VALID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
    private static Random random = new Random(System.currentTimeMillis());
    protected int length = 4;

    public AbstractUrlShortener() {

    }

    public AbstractUrlShortener(int length) {
        this.length = length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getLength() {
        return length;
    }

    public String get(String url) {
        String sortUrl = seek(url);
        if (sortUrl == null) {
            sortUrl = generate(url);
            put(url, sortUrl);
        }
        return sortUrl;
    }

    public String generate(String url, int seed) {
        char[] sortUrl = new char[length];
        for (int i = 0; i < length; i++) {
            sortUrl[i] = VALID_CHARS[seed % VALID_CHARS.length];
            seed = random.nextInt(Integer.MAX_VALUE) % VALID_CHARS.length;
        }
        return new String(sortUrl);
    }

    public String generate(String url) {
        String shortUrl;
        shortUrl = generate(url, random.nextInt(Integer.MAX_VALUE));
        while (seek(shortUrl) != null) {
            shortUrl = generate(url, random.nextInt(Integer.MAX_VALUE));
        }
        put(url, shortUrl);
        return shortUrl;
    }

}
實際的需要在數據庫層實現,這裏在內存裏面存儲的實現一個意思一下

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 內存存儲的實現
 * <p>
 * Created by luoguo on 2017/3/16.
 */
public class UrlShortenerMemory extends AbstractUrlShortener {
    private Map<String, String> url2ShortUrl = new ConcurrentHashMap<String, String>();
    private Map<String, String> shortUrl2Url = new ConcurrentHashMap<String, String>();

    public UrlShortenerMemory() {
        super();
    }

    public UrlShortenerMemory(int length) {
        super(length);
    }

    public void put(String url, String shortUrl) {
        url2ShortUrl.put(url, shortUrl);
        shortUrl2Url.put(shortUrl, url);
    }

    public String seek(String shortUrl) {
        return shortUrl2Url.get(shortUrl);
    }

    public void clean(String url) {
        String sortUrl = url2ShortUrl.get(url);
        if (sortUrl != null) {
            url2ShortUrl.remove(url);
            shortUrl2Url.remove(sortUrl);
        }
    }

    public void clean(Date date) {
        throw new UnsupportedOperationException();
    }

}
弄點測試代碼,試試看效果怎麼樣

public class UrlShortenerTest {
    public static void main(String[] args) {
        for(int j=1;j<10;j++) {
            UrlShortener urlShortener = new UrlShortenerMemory(j);
            for (int i = 0; i < 5; i++) {
                System.out.println(urlShortener.get("http://www.tinygroup.org"));
            }
            System.out.println();
        }
    }
}
這裏是運行結果

T
Q
l
i
I

0R
VB
fN
so
Nw

fw1
fpu
WT7
pNL
M5V

0cZF
H09A
AjYM
1AMf
sdkz

USsgK
ZMn9U
7S48u
oBb1L
yB14g

DRVGWg
gh3oJa
BapdY7
yJgcZj
mivGN3

TEiQG2M
8SkokOY
NZww5GT
fxloZ0R
hkiOpID

3ZpdIYrF
eFlSSDyd
bB4RCFgE
25mceTMK
AAJ2Lp6S

5IUVoFSqZ
zHZeuZljY
njCGDBKVs
IgH21Tg5V
WvGCy5ZtE

感覺還可以,當然裏面還有一些不完善的地方

  1. 當產生的數量達到一定程度的時候,再獲取時由於衝突比較大,導致性能會降低
  2. 因此如果是小型系統,建議採用4個長度,大型系統建議採用6個長度
  3. 現在還沒有多長時間之後自動失效的API,建議添加
  4. 需要實現支持序列化的方案
  5. 現在沒有同步處理,實際實現中需要考慮

這個只是化幾分鐘展示一個思路,並沒有經過系統的思考的驗證,正式系統請慎重使用。

更多精彩技術博文,請看本人博客空間


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章