小編給大家分享一下Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù),希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
創(chuàng)新互聯(lián)公司專注于城東網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供城東營銷型網(wǎng)站建設(shè),城東網(wǎng)站制作、城東網(wǎng)頁設(shè)計、城東網(wǎng)站官網(wǎng)定制、小程序制作服務(wù),打造城東網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供城東網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
日常生活中能見到各種奇怪的短鏈接,每次點擊跳轉(zhuǎn)的時候,筆者都會覺得神奇,這短鏈?zhǔn)窃趺磳⒂脩粢龑?dǎo)到正確頁面的呢?
短鏈的原理就是以短博長,那么這個短的字符串怎么才能變成一長串鏈接呢?難道是靠某些神奇的加密算法?并不是,我們只需要依賴key/value的映射關(guān)系就能輕松實現(xiàn)這個看似神奇的以短博長。
用一張圖,大家就能清晰的看到我們訪問短鏈的整個過程了。
首先,我們會有一個長鏈接,通過短鏈服務(wù)的處理,通常會輸出一個只有一層目錄的URL,然后我們可以將獲取的URL進(jìn)行分發(fā)。
然后就到了用戶側(cè),用戶點擊短鏈之后,先到達(dá)的并不是目標(biāo)頁面,而是短鏈服務(wù)。
短鏈服務(wù)會截取鏈接上的pathname,并將其當(dāng)做key,到映射關(guān)系中查找對應(yīng)的value。
如果查到不到對應(yīng)的value,則表示這個短鏈不存在或者已失效;如果查詢成功,則會由短鏈服務(wù)直接302到value中的目標(biāo)鏈接,完成一次短鏈訪問。
原料: 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
中,等待短鏈接生成然后輸出。
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{ 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位隨機(jī)字符串,這個將會作為短鏈的pathname。
在進(jìn)行映射之前,我們需要對其進(jìn)行唯一性判斷,雖然出現(xiàn)的可能性不大,但是還是需要防范短鏈覆蓋這類的問題。本服務(wù)的解決方案是重試生成,如果短鏈值不幸重復(fù)時將會進(jìn)入重試分支,服務(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中,并輸出拼接好的短鏈接。
@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)的值,則會讀取其中的url
和type
字段,如果type為once則代表這個是一次性鏈接,會主動觸發(fā)刪除方法,最終都會返回目標(biāo)鏈接。
使用短鏈接時,大概率都會需要相關(guān)的數(shù)據(jù)統(tǒng)計,怎么樣在不使用數(shù)據(jù)庫的前提下進(jìn)行數(shù)據(jù)統(tǒng)計呢?
在本服務(wù)中,我們可以通過對落地日志文件的掃描,完成當(dāng)日短鏈訪問的報表。
在生成短鏈接的時候加上urlID字段進(jìn)行統(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
類型的日志進(jìn)行計數(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.port | 8000 | 服務(wù)啟動端口 |
redis.port | 6379 | redis端口 |
redis.host | 0.0.0.0 | redis服務(wù)地址 |
redis.db | 0 | redis具體儲存庫表 |
cacheType | redis | 短鏈儲存模式,接受memory/redis |
maxRetryTimes | 5 | 生成短鏈接最大重試次數(shù) |
defaultHost | http://localhost:8000/s | 短鏈接前綴 |
defaultIndex | http://localhost:8000/defaultIndex | 短鏈接失效后重定向地址 |
內(nèi)置接口
接口路由 | 請求方式 | 接口參數(shù) | 接口用途 |
---|---|---|---|
/s/createUrl | POST | url: string, type?: string | 短鏈接生成接口 |
/s/deleteUrl | POST | k: string | 刪除短鏈接接口 |
/s/:key | GET | none | 目標(biāo)鏈接獲取 |
shorturl是有本地儲存方案的,也就是說我們是可以監(jiān)聽Redis的狀態(tài),如果斷開連接時就臨時將數(shù)據(jù)儲存到內(nèi)存中,以達(dá)到服務(wù)降級的目的。當(dāng)然我們也可以直接使用內(nèi)存來儲存短鏈內(nèi)容,在config.ts
配置中可以進(jìn)行更改。
讓我們脫離短鏈接這個束縛,其實shorturl本身已經(jīng)是一個微型存儲服務(wù)了,我們完全可以進(jìn)行二次開發(fā),輸出更多的模塊以支撐更多樣的業(yè)務(wù)。
看完了這篇文章,相信你對“Nodejs+Nest如何實現(xiàn)的短鏈接服務(wù)”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!