Jadual Kandungan
短链原理
具体实现
① 接收长链接
② 生成shortKey
③ 接收短链接并完成目标重定向
额外功能
利用日志系统输出报表
使用方式
拓展
① 储存降级策略
② 不仅仅是短链接服务
小结
Rumah hujung hadapan web tutorial js 详解Nodejs+Nest实现的短链接服务

详解Nodejs+Nest实现的短链接服务

Apr 30, 2021 am 10:48 AM
nest nodejs

本篇文章给大家介绍一下基于Node框架Nest实现短链接服务的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

详解Nodejs+Nest实现的短链接服务

推荐学习:《nodejs 教程

日常生活中能见到各种奇怪的短链接,每次点击跳转的时候,笔者都会觉得神奇,这短链是怎么将用户引导到正确页面的呢?

短链原理

短链的原理就是以短博长,那么这个短的字符串怎么才能变成一长串链接呢?难道是靠某些神奇的加密算法?并不是,我们只需要依赖key/value的映射关系就能轻松实现这个看似神奇的以短博长

用一张图,大家就能清晰的看到我们访问短链的整个过程了。

未命名文件.png

首先,我们会有一个长链接,通过短链服务的处理,通常会输出一个只有一层目录的URL,然后我们可以将获取的URL进行分发。

然后就到了用户侧,用户点击短链之后,先到达的并不是目标页面,而是短链服务。

短链服务会截取链接上的pathname,并将其当做key,到映射关系中查找对应的value。

如果查到不到对应的value,则表示这个短链不存在或者已失效;如果查询成功,则会由短链服务直接302到value中的目标链接,完成一次短链访问。

具体实现

原料: Fast-Nest脚手架、Redis

整个实现分拆成3个部分:

① 接收长链接

1

2

3

4

5

6

7

8

9

10

@Post('/createUrl')

async createUrl(

    @Body('url') url: string,

    @Body('type') type: string,

) {

    const shortUrl = await this.shorturlService.createUrl(url, type);

    return {

        shortUrl,

    };

}

Salin selepas log masuk

在服务中创建一个createUrl接口,接收url已经type字段,并将其传入shorturlService中,等待短链接生成然后输出。

② 生成shortKey

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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;

}

Salin selepas log masuk

首先通过Math.random().toString(36).slice(-4)获取4位随机字符串,这个将会作为短链的pathname。

在进行映射之前,我们需要对其进行唯一性判断,虽然出现的可能性不大,但是还是需要防范短链覆盖这类的问题。本服务的解决方案是重试生成,如果短链值不幸重复时将会进入重试分支,服务将会内置可重试次数,如果重试的次数超过配置的字数,本次转换将会返回失败。

除了urlcreateUrl方法还接受一个type字段,这里涉及特殊短链的特性。我们短链有三种模式:

  • normal - 普通短链接,将会在规定时间内失效
  • once - 一次性短链接,将会在规定时间内失效,被访问后自动失效
  • permanent - 长期短链接,不会自动失效,只接受手动删除

生成urlKey之后,将会与type一起转成字符串储存到redis中,并输出拼接好的短链接。

③ 接收短链接并完成目标重定向

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@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;

}

Salin selepas log masuk

用户侧会获得一个类似http://localhost:8000/s/ku6a的链接,点击之后相当于是给短链接服务发送了一个GET请求。

服务接收到请求之后获取链接中key字段的值,也就是ku6a这个字符串,利用它查找Redis中的映射关系。

这里有两个分支,一个是在Redis中无法查询到相关的值,服务则认为短链接已经失效会直接return,因为getUrl返回了空值,重定向装饰器会将本次请求重定向到默认的目标链接中。

如果在Redis中顺利查到相关的值,则会读取其中的urltype字段,如果type为once则代表这个是一次性链接,会主动触发删除方法,最终都会返回目标链接。

额外功能

利用日志系统输出报表

使用短链接时,大概率都会需要相关的数据统计,怎么样在不使用数据库的前提下进行数据统计呢?

在本服务中,我们可以通过对落地日志文件的扫描,完成当日短链访问的报表。

在生成短链接的时候加上urlID字段进行统计区分并主动输出日志,如下:

1

2

3

4

5

6

7

8

9

10

11

12

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}`;

}

Salin selepas log masuk

然后在用户点击短链接时获取该短链接的urlID字段,并主动输出日志,如下:

1

2

3

4

5

6

7

8

9

10

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;

}

Salin selepas log masuk

这么一来我们将能够在服务的logs目录中获得类似这样的日志:

1

2

3

4

5

6

7

8

9

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

Salin selepas log masuk

之后我们只需要以createUrl的日志为索引,对getUrl类型的日志进行计数,即可完成链接与点击数的报表,如果还需要其他维度的报表只需要在输出日志的时候带上即可,或者修改日志中间件中的日志范式。

使用方式

根据上述的流程,笔者写了一个比较简易的短链服务,大家可以开箱即用。

shorturl(欢迎大家Star⭐️⭐️)

具体启动方式

首先请确保有可用的redis,否则无法顺利启动服务。

1

2

3

4

5

6

7

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

 

cd shorturl

 

npm install

 

npm start

Salin selepas log masuk

可用配置修改

与短链相关的配置收束在根目录的config.ts中。

1

2

3

4

5

6

7

8

9

10

11

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',

Salin selepas log masuk
配置 默认值 配置用途
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)

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

Atas ialah kandungan terperinci 详解Nodejs+Nest实现的短链接服务. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat panas Tag

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Perbezaan antara nodejs dan tomcat Perbezaan antara nodejs dan tomcat Apr 21, 2024 am 04:16 AM

Perbezaan antara nodejs dan tomcat

Perbezaan antara nodejs dan vuejs Perbezaan antara nodejs dan vuejs Apr 21, 2024 am 04:17 AM

Perbezaan antara nodejs dan vuejs

Adakah nodejs rangka kerja bahagian belakang? Adakah nodejs rangka kerja bahagian belakang? Apr 21, 2024 am 05:09 AM

Adakah nodejs rangka kerja bahagian belakang?

Apakah perbezaan antara fail npm dan npm.cmd dalam direktori pemasangan nodejs? Apakah perbezaan antara fail npm dan npm.cmd dalam direktori pemasangan nodejs? Apr 21, 2024 am 05:18 AM

Apakah perbezaan antara fail npm dan npm.cmd dalam direktori pemasangan nodejs?

Adakah terdapat perbezaan besar antara nodejs dan java? Adakah terdapat perbezaan besar antara nodejs dan java? Apr 21, 2024 am 06:12 AM

Adakah terdapat perbezaan besar antara nodejs dan java?

Adakah nodejs bahasa pembangunan bahagian belakang? Adakah nodejs bahasa pembangunan bahagian belakang? Apr 21, 2024 am 05:09 AM

Adakah nodejs bahasa pembangunan bahagian belakang?

Mana satu untuk dipilih antara nodejs dan java? Mana satu untuk dipilih antara nodejs dan java? Apr 21, 2024 am 04:40 AM

Mana satu untuk dipilih antara nodejs dan java?

Apakah pembolehubah global dalam nodejs Apakah pembolehubah global dalam nodejs Apr 21, 2024 am 04:54 AM

Apakah pembolehubah global dalam nodejs

See all articles