您好,登錄后才能下訂單哦!
小編給大家分享一下Nodejs+Nest如何實現的短鏈接服務,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
日常生活中能見到各種奇怪的短鏈接,每次點擊跳轉的時候,筆者都會覺得神奇,這短鏈是怎么將用戶引導到正確頁面的呢?
短鏈的原理就是以短博長,那么這個短的字符串怎么才能變成一長串鏈接呢?難道是靠某些神奇的加密算法?并不是,我們只需要依賴key/value的映射關系就能輕松實現這個看似神奇的以短博長。
用一張圖,大家就能清晰的看到我們訪問短鏈的整個過程了。
首先,我們會有一個長鏈接,通過短鏈服務的處理,通常會輸出一個只有一層目錄的URL,然后我們可以將獲取的URL進行分發。
然后就到了用戶側,用戶點擊短鏈之后,先到達的并不是目標頁面,而是短鏈服務。
短鏈服務會截取鏈接上的pathname,并將其當做key,到映射關系中查找對應的value。
如果查到不到對應的value,則表示這個短鏈不存在或者已失效;如果查詢成功,則會由短鏈服務直接302到value中的目標鏈接,完成一次短鏈訪問。
原料: Fast-Nest腳手架、Redis
整個實現分拆成3個部分:
@Post('/createUrl') async createUrl( @Body('url') url: string, @Body('type') type: string, ) { const shortUrl = await this.shorturlService.createUrl(url, type); return { shortUrl, }; }
在服務中創建一個createUrl
接口,接收url
已經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<string> { const _count = count || 1; const maxCount = Config.maxRetryTimes; if (_count >= maxCount) throw new HttpException('超過重試次數,請重新生成鏈接', 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。
在進行映射之前,我們需要對其進行唯一性判斷,雖然出現的可能性不大,但是還是需要防范短鏈覆蓋這類的問題。本服務的解決方案是重試生成,如果短鏈值不幸重復時將會進入重試分支,服務將會內置可重試次數,如果重試的次數超過配置的字數,本次轉換將會返回失敗。
除了url
,createUrl
方法還接受一個type
字段,這里涉及特殊短鏈的特性。我們短鏈有三種模式:
normal - 普通短鏈接,將會在規定時間內失效
once - 一次性短鏈接,將會在規定時間內失效,被訪問后自動失效
permanent - 長期短鏈接,不會自動失效,只接受手動刪除
生成urlKey
之后,將會與type
一起轉成字符串儲存到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; }
用戶側會獲得一個類似http://localhost:8000/s/ku6a
的鏈接,點擊之后相當于是給短鏈接服務發送了一個GET請求。
服務接收到請求之后獲取鏈接中key字段的值,也就是ku6a
這個字符串,利用它查找Redis中的映射關系。
這里有兩個分支,一個是在Redis中無法查詢到相關的值,服務則認為短鏈接已經失效會直接return,因為getUrl
返回了空值,重定向裝飾器會將本次請求重定向到默認的目標鏈接中。
如果在Redis中順利查到相關的值,則會讀取其中的url
和type
字段,如果type為once則代表這個是一次性鏈接,會主動觸發刪除方法,最終都會返回目標鏈接。
使用短鏈接時,大概率都會需要相關的數據統計,怎么樣在不使用數據庫的前提下進行數據統計呢?
在本服務中,我們可以通過對落地日志文件的掃描,完成當日短鏈訪問的報表。
在生成短鏈接的時候加上urlID字段進行統計區分并主動輸出日志,如下:
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; }
這么一來我們將能夠在服務的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
類型的日志進行計數,即可完成鏈接與點擊數的報表,如果還需要其他維度的報表只需要在輸出日志的時候帶上即可,或者修改日志中間件中的日志范式。
根據上述的流程,筆者寫了一個比較簡易的短鏈服務,大家可以開箱即用。
shorturl(歡迎大家Star????)
具體啟動方式
首先請確保有可用的redis,否則無法順利啟動服務。
git clone https://github.com/mykurisu/shorturl.git cd shorturl npm install npm start
可用配置修改
與短鏈相關的配置收束在根目錄的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',
配置 | 默認值 | 配置用途 |
---|---|---|
serverConfig.port | 8000 | 服務啟動端口 |
redis.port | 6379 | redis端口 |
redis.host | 0.0.0.0 | redis服務地址 |
redis.db | 0 | redis具體儲存庫表 |
cacheType | redis | 短鏈儲存模式,接受memory/redis |
maxRetryTimes | 5 | 生成短鏈接最大重試次數 |
defaultHost | http://localhost:8000/s | 短鏈接前綴 |
defaultIndex | http://localhost:8000/defaultIndex | 短鏈接失效后重定向地址 |
內置接口
接口路由 | 請求方式 | 接口參數 | 接口用途 |
---|---|---|---|
/s/createUrl | POST | url: string, type?: string | 短鏈接生成接口 |
/s/deleteUrl | POST | k: string | 刪除短鏈接接口 |
/s/:key | GET | none | 目標鏈接獲取 |
shorturl是有本地儲存方案的,也就是說我們是可以監聽Redis的狀態,如果斷開連接時就臨時將數據儲存到內存中,以達到服務降級的目的。當然我們也可以直接使用內存來儲存短鏈內容,在config.ts
配置中可以進行更改。
讓我們脫離短鏈接這個束縛,其實shorturl本身已經是一個微型存儲服務了,我們完全可以進行二次開發,輸出更多的模塊以支撐更多樣的業務。
看完了這篇文章,相信你對“Nodejs+Nest如何實現的短鏈接服務”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。