溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù)

發(fā)布時間:2021-04-30 11:10:29 來源:億速云 閱讀:222 作者:小新 欄目:web開發(fā)

小編給大家分享一下Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù),希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

日常生活中能見到各種奇怪的短鏈接,每次點擊跳轉(zhuǎn)的時候,筆者都會覺得神奇,這短鏈?zhǔn)窃趺磳⒂脩粢龑?dǎo)到正確頁面的呢?

短鏈原理

短鏈的原理就是以短博長,那么這個短的字符串怎么才能變成一長串鏈接呢?難道是靠某些神奇的加密算法?并不是,我們只需要依賴key/value的映射關(guān)系就能輕松實現(xiàn)這個看似神奇的以短博長。

用一張圖,大家就能清晰的看到我們訪問短鏈的整個過程了。

Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù)

首先,我們會有一個長鏈接,通過短鏈服務(wù)的處理,通常會輸出一個只有一層目錄的URL,然后我們可以將獲取的URL進行分發(fā)。

然后就到了用戶側(cè),用戶點擊短鏈之后,先到達的并不是目標(biāo)頁面,而是短鏈服務(wù)。

短鏈服務(wù)會截取鏈接上的pathname,并將其當(dāng)做key,到映射關(guān)系中查找對應(yīng)的value。

如果查到不到對應(yīng)的value,則表示這個短鏈不存在或者已失效;如果查詢成功,則會由短鏈服務(wù)直接302到value中的目標(biāo)鏈接,完成一次短鏈訪問。

具體實現(xiàn)

原料: Fast-Nest腳手架、Redis

整個實現(xiàn)分拆成3個部分:

① 接收長鏈接

@Post('/createUrl')
async createUrl(
    @Body('url') url: string,
    @Body('type') type: string,
) {
    const shortUrl = await this.shorturlService.createUrl(url, type);
    return {
        shortUrl,
    };
}

在服務(wù)中創(chuàng)建一個createUrl接口,接收url已經(jīng)type字段,并將其傳入shorturlService中,等待短鏈接生成然后輸出。

② 生成shortKey

async createUrl(url: string, type: string = 'normal') {
    const urlKey = await this.handleUrlKey();
    const dataStr = JSON.stringify({
        url,
        type
    });
    await this.client.set(urlKey, dataStr, type === 'permanent' ? -1 : 300);
    return `${Config.defaultHost}/${urlKey}`;
}

private async handleUrlKey(count?: number): Promise<string> {
    const _count = count || 1;
    const maxCount = Config.maxRetryTimes;
    if (_count >= maxCount) throw new HttpException('超過重試次數(shù),請重新生成鏈接', HttpStatus.INTERNAL_SERVER_ERROR);
    const urlKey: string = Math.random().toString(36).slice(-4);
    const _url = await this.client.get(urlKey);
    if (_url) {
        return await this.handleUrlKey(_count + 1);
    }
    return urlKey;
}

首先通過Math.random().toString(36).slice(-4)獲取4位隨機字符串,這個將會作為短鏈的pathname。

在進行映射之前,我們需要對其進行唯一性判斷,雖然出現(xiàn)的可能性不大,但是還是需要防范短鏈覆蓋這類的問題。本服務(wù)的解決方案是重試生成,如果短鏈值不幸重復(fù)時將會進入重試分支,服務(wù)將會內(nèi)置可重試次數(shù),如果重試的次數(shù)超過配置的字?jǐn)?shù),本次轉(zhuǎn)換將會返回失敗。

除了url,createUrl方法還接受一個type字段,這里涉及特殊短鏈的特性。我們短鏈有三種模式:

  • normal - 普通短鏈接,將會在規(guī)定時間內(nèi)失效

  • once - 一次性短鏈接,將會在規(guī)定時間內(nèi)失效,被訪問后自動失效

  • permanent - 長期短鏈接,不會自動失效,只接受手動刪除

生成urlKey之后,將會與type一起轉(zhuǎn)成字符串儲存到redis中,并輸出拼接好的短鏈接。

③ 接收短鏈接并完成目標(biāo)重定向

@Get('/:key')
@Redirect(Config.defaultIndex, 302)
async getUrl(
        @Param('key') key: string,
    ) {
    if (key) {
        const url = await this.shorturlService.getUrl(key);
        return {
            url
        }
    }
}

// this.shorturlService.getUrl
async getUrl(k: string) {
    const dataStr = await this.client.get(k);
    if (!dataStr) return;
    const { url, type } = JSON.parse(dataStr);
    if (type === 'once') {
        await this.client.del(k);
    }
    return url;
}

用戶側(cè)會獲得一個類似http://localhost:8000/s/ku6a的鏈接,點擊之后相當(dāng)于是給短鏈接服務(wù)發(fā)送了一個GET請求。

服務(wù)接收到請求之后獲取鏈接中key字段的值,也就是ku6a這個字符串,利用它查找Redis中的映射關(guān)系。

這里有兩個分支,一個是在Redis中無法查詢到相關(guān)的值,服務(wù)則認(rèn)為短鏈接已經(jīng)失效會直接return,因為getUrl返回了空值,重定向裝飾器會將本次請求重定向到默認(rèn)的目標(biāo)鏈接中。

如果在Redis中順利查到相關(guān)的值,則會讀取其中的urltype字段,如果type為once則代表這個是一次性鏈接,會主動觸發(fā)刪除方法,最終都會返回目標(biāo)鏈接。

額外功能

利用日志系統(tǒng)輸出報表

使用短鏈接時,大概率都會需要相關(guān)的數(shù)據(jù)統(tǒng)計,怎么樣在不使用數(shù)據(jù)庫的前提下進行數(shù)據(jù)統(tǒng)計呢?

在本服務(wù)中,我們可以通過對落地日志文件的掃描,完成當(dāng)日短鏈訪問的報表。

在生成短鏈接的時候加上urlID字段進行統(tǒng)計區(qū)分并主動輸出日志,如下:

async createUrl(url: string, type: string = 'normal') {
    const urlKey = await this.handleUrlKey();
    const urlID = UUID.genV4().toString();
    const dataStr = JSON.stringify({
        urlID,
        url,
        type
    });
    this.myLogger.log(`createUrl**${urlID}`, 'createUrl', false);
    await this.client.set(urlKey, dataStr, type === 'permanent' ? -1 : 300);
    return `${Config.defaultHost}/${urlKey}`;
}

然后在用戶點擊短鏈接時獲取該短鏈接的urlID字段,并主動輸出日志,如下:

async getUrl(k: string) {
    const dataStr = await this.client.get(k);
    if (!dataStr) return;
    const { url, type, urlID } = JSON.parse(dataStr);
    if (type === 'once') {
        await this.client.del(k);
    }
    this.myLogger.log(`getUrl**${urlID}`, 'getUrl', false);
    return url;
}

這么一來我們將能夠在服務(wù)的logs目錄中獲得類似這樣的日志:

2021-04-25 22:31:03.306	INFO	[11999]	[-]	createUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:38.323	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:39.399	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:40.281	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:40.997	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:41.977	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:42.870	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:43.716	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf
2021-04-25 22:31:44.614	INFO	[11999]	[-]	getUrl**3f577625-474a-4e30-9933-e469ce3b0dcf

之后我們只需要以createUrl的日志為索引,對getUrl類型的日志進行計數(shù),即可完成鏈接與點擊數(shù)的報表,如果還需要其他維度的報表只需要在輸出日志的時候帶上即可,或者修改日志中間件中的日志范式。

使用方式

根據(jù)上述的流程,筆者寫了一個比較簡易的短鏈服務(wù),大家可以開箱即用。

shorturl(歡迎大家Star????)

具體啟動方式

首先請確保有可用的redis,否則無法順利啟動服務(wù)。

git clone https://github.com/mykurisu/shorturl.git

cd shorturl

npm install

npm start

可用配置修改

與短鏈相關(guān)的配置收束在根目錄的config.ts中。

serverConfig: {
    port: 8000,
},
redis: {
    port: 6379,
    host: '0.0.0.0',
    db: 0,
},
cacheType: 'redis',
defaultHost: 'http://localhost:8000/s',
defaultIndex: 'http://localhost:8000/defaultIndex',
配置默認(rèn)值配置用途
serverConfig.port8000服務(wù)啟動端口
redis.port6379redis端口
redis.host0.0.0.0redis服務(wù)地址
redis.db0redis具體儲存庫表
cacheTyperedis短鏈儲存模式,接受memory/redis
maxRetryTimes5生成短鏈接最大重試次數(shù)
defaultHosthttp://localhost:8000/s短鏈接前綴
defaultIndexhttp://localhost:8000/defaultIndex短鏈接失效后重定向地址

內(nèi)置接口

接口路由請求方式接口參數(shù)接口用途
/s/createUrlPOSTurl: string, type?: string短鏈接生成接口
/s/deleteUrlPOSTk: string刪除短鏈接接口
/s/:keyGETnone目標(biāo)鏈接獲取

拓展

① 儲存降級策略

shorturl是有本地儲存方案的,也就是說我們是可以監(jiān)聽Redis的狀態(tài),如果斷開連接時就臨時將數(shù)據(jù)儲存到內(nèi)存中,以達到服務(wù)降級的目的。當(dāng)然我們也可以直接使用內(nèi)存來儲存短鏈內(nèi)容,在config.ts配置中可以進行更改。

② 不僅僅是短鏈接服務(wù)

讓我們脫離短鏈接這個束縛,其實shorturl本身已經(jīng)是一個微型存儲服務(wù)了,我們完全可以進行二次開發(fā),輸出更多的模塊以支撐更多樣的業(yè)務(wù)。

看完了這篇文章,相信你對“Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù)”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI