首頁  >  文章  >  web前端  >  詳解Nodejs+Nest實作的短連結服務

詳解Nodejs+Nest實作的短連結服務

青灯夜游
青灯夜游轉載
2021-04-30 10:48:022818瀏覽

本篇文章為大家介紹一下基於Node框架Nest實作短連結服務的方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解Nodejs+Nest實作的短連結服務

推薦學習:《nodejs 教學

日常生活中能見到各種奇怪的短鏈接,每次點擊跳轉的時候,筆者都會覺得神奇,這短鍊是怎麼引導使用者到正確頁面的呢?

短鏈原理

短鏈的原理就是以短博長,那麼這個短的字串怎麼能變成一長串連結呢?難道是靠某些神奇的加密演算法?並不是,我們只需要依賴key/value的映射關係就能輕鬆實現這個看似神奇的以短博長

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

詳解Nodejs+Nest實作的短連結服務

首先,我們會有一個長鏈接,透過短鏈服務的處理,通常會輸出一個只有一層目錄的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中,等待短連結產生然後輸出。

② 產生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(&#39;超过重试次数,请重新生成链接&#39;, 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。

在進行映射之前,我們需要對其進行唯一性判斷,雖然出現的可能性不大,但是還是需要防範短鏈覆蓋這類的問題。本服務的解決方案是重試生成,如果短鏈值不幸重複時將會進入重試分支,服務將內建可重試次數,如果重試的次數超過配置的字數,本次轉換將會回傳失敗。

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

  • normal - 普通短鏈接,將會在規定時間內失效
  • once - 一次性短鏈接,將會在規定時間內失效,被訪問後自動失效
  • permanent - 長期短鏈接,不會自動失效,只接受手動刪除
##生成

urlKey之後,將會與type一起轉成字串儲存到redis中,並輸出拼接好的短連結。

③ 接收短鏈接並完成目標重定向

@Get(&#39;/:key&#39;)
@Redirect(Config.defaultIndex, 302)
async getUrl(
        @Param(&#39;key&#39;) 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 === &#39;once&#39;) {
        await this.client.del(k);
    }
    return url;
}

用戶側會獲得一個類似

http://localhost:8000/s/ku6a的鏈接,點擊之後相當於是給短連結服務發送了一個GET請求。

服務接收到請求之後取得連結中key欄位的值,也就是

ku6a這個字串,利用它來找出Redis中的對應關係。

這裡有兩個分支,一個是在Redis中無法查詢到相關的值,服務則認為短連結已經失效會直接return,因為

getUrl傳回了空值,重定向裝飾器會將本次請求重定向到預設的目標連結。

如果在Redis中順利查到相關的值,則會讀取其中的

urltype字段,如果type為once則代表這個是一次性鏈接,會主動觸發刪除方法,最終都會返回目標鏈接。

額外功能

利用日誌系統輸出報表

使用短連結時,大機率都會需要相關的數據統計,怎麼樣在不使用資料庫的前提下進行數據統計呢?

在本服務中,我們可以透過落地日誌檔​​案的掃描,完成當日短鏈存取的報表。

在產生短連結的時候加上

urlID欄位進行統計區分並主動輸出日誌,如下:

async createUrl(url: string, type: string = &#39;normal&#39;) {
    const urlKey = await this.handleUrlKey();
    const urlID = UUID.genV4().toString();
    const dataStr = JSON.stringify({
        urlID,
        url,
        type
    });
    this.myLogger.log(`createUrl**${urlID}`, &#39;createUrl&#39;, false);
    await this.client.set(urlKey, dataStr, type === &#39;permanent&#39; ? -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 === &#39;once&#39;) {
        await this.client.del(k);
    }
    this.myLogger.log(`getUrl**${urlID}`, &#39;getUrl&#39;, 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: &#39;0.0.0.0&#39;,
    db: 0,
},
cacheType: &#39;redis&#39;,
defaultHost: &#39;http://localhost:8000/s&#39;,
defaultIndex: &#39;http://localhost:8000/defaultIndex&#39;,
配置 默认值 配置用途
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本身已经是一个微型存储服务了,我们完全可以进行二次开发,输出更多的模块以支撑更多样的业务。

小结

整个短链接服务其实非常简单,麻烦的是服务的搭建,也就是迈出的第一步。笔者也是在无数次最初一步中挣扎,最终积累了fast-nest这么一个脚手架,希望能帮助到有同样境遇的同学。

另外,附上本文的服务源码 -- shorturl(欢迎大家Star)

更多编程相关知识,请访问:编程教学!!

以上是詳解Nodejs+Nest實作的短連結服務的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除