Nest 的功能都是大多通过装饰器来使用的,这节我们就把所有的装饰器过一遍。
+
我们创建个新的 nest 项目:
+
nest new all-decorator -p npm
+
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1824b5769a7c4cd4a1862918af0dffe8~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
Nest 提供了一套模块系统,通过 @Module声明模块:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/350bf6ae1f1d425aba1e30a2112c75f4~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
通过 @Controller、@Injectable 分别声明其中的 controller 和 provider:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/835bb7e52eb24497bec4a6c97a682307~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/63c2e0a4e2e04d638fd510e658429265~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这个 provider 可以是任何的 class:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a32aed0be559462d8734925deebc8e1f~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
注入的方式可以是构造器注入:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/97763942ca1843eab617d82a2b6ee164~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
或者属性注入:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b66d2ff31184e689a22bef888eb4b77~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
属性注入要指定注入的 token,可能是 class 也可能是 string。
+
你可以通过 useFactory、useValue 等方式声明 provider:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/035c9f0ee8e540aaa5e1ceadb1ce9aa2~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这时候也需要通过 @Inject 指定注入的 token:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/16e14a5849b64701b3f74162046c6f38~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/97c08058adb944cabe6367d070c01546~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这些注入的依赖如果没有的话,创建对象时会报错。但如果它是可选的,你可以用 @Optional 声明一下,这样没有对应的 provider 也能正常创建这个对象。
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b9742d2a930b47018c07a627b57cfdb3~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
如果模块被很多地方都引用,为了方便,可以用 @Global 把它声明为全局的,这样它 exports 的 provider 就可以直接注入了:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/16ce92233e484b4e974c9af63f24a8bc~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
filter 是处理抛出的未捕获异常的,通过 @Catch 来指定处理的异常:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4cd95a5fb6b44d17869c3ae45fda9467~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
然后通过 @UseFilters 应用到 handler 上:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f824522eee2d4c9f96b38b1784879280~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73b48391b96245438e60872b913d5108~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
除了 filter 之外,interceptor、guard、pipe 也是这样用:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/54b937a0d54a40a19b81624eb8e82a1b~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
当然,pipe 更多还是单独在某个参数的位置应用:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5fced92c2344495b86524871d8ed9cfa~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这里的 @Query 是取 url 后的 ?bbb=true,而 @Param 是取路径中的参数,比如 /xxx/111 种的 111
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f61a443880944b1bb1aff47d2e77e769~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/493ea39f11f1488ba3bd53dc6f4ee405~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
此外,如果是 @Post 请求,可以通过 @Body 取到 body 部分:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a10d3521580a486ca1348b3f9b7bdde8~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
我们一般用 dto 的 class 来接受请求体里的参数:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/58338c7cc0634a388c58bde39f5bc8b6~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
nest 会实例化一个 dto 对象:
+
用 postman 发个 post 请求:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a959786094d4690832d0af884921c12~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
可以看到 nest 接受到了 body 里的参数:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4bfb3d526abb4b478ccd3bc9988d486f~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
除了 @Get、@Post 外,还可以用 @Put、@Delete、@Patch、@Options、@Head 装饰器分别接受 put、delete、patch、options、head 请求:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf5a32293fde40beb8e9f19d91e81329~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
handler 和 class 可以通过 @SetMetadata 指定 metadata:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/937ac8e44f2d4fedb9818a0b6c8e70c5~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
然后在 guard 或者 interceptor 里取出来:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/27163078cd944d68b10c13068dc08145~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5971233494fb4a1bb6968501878dd66a~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
你可以通过 @Headers 装饰器取某个请求头 或者全部请求头:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/59578d6bc6a64764a276c3fb8abbb1e8~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/583883749dc14df0b5d1ed7efbffee1c~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
通过 @Ip 拿到请求的 ip:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0dadc94181774a94aa3913d8d793909f~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
通过 @Session 拿到 session 对象:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1f8c71364e3849e3b6fd1b9b9dacaeea~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
但要使用 session 需要安装一个 express 中间件:
+
npm install express-session
+
+
在 main.ts 里引入并启用:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5971294374b641c7b099f95471b4f6e6~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
指定加密的密钥和 cookie 的存活时间。
+
然后刷新页面:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d3b62447a4041e6b9f666da1d8e2705~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
会返回 set-cookie 的响应头,设置了 cookie,包含 sid 也就是 sesssionid。
+
之后每次请求都会自动带上这个 cookie:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/466415bd1d9941a38053e08a61d53d89~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这样就可以在 session 对象里存储信息了。
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f5bb63a915e4218aa6e1a997e8e4db0~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/83805b26ffed403fb45106412479d766~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
@HostParam 用于取域名部分的参数:
+
我们再创建个 controller:
+
nest g controller aaa --no-spec --flat
+
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a30b153d260842c4b0a21aa9ab37bd46~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这样指定 controller 的生效路径:
+
import { Controller, Get, HostParam } from '@nestjs/common';
+
+@Controller({ host: ':host.0.0.1', path: 'aaa' })
+export class AaaController {
+ @Get('bbb')
+ hello() {
+ return 'hello';
+ }
+}
+
+
controller 除了可以指定某些 path 生效外,还可以指定 host:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0acf98f2b82041df81cb8a7e92f639b2~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
然后再访问下:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e44328d1d6d74a01914d0ac5187a8bc4~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
这时候你会发现只有 host 满足 xx.0.0.1 的时候才会路由到这个 controller。
+
host 里的参数就可以通过 @HostParam 取出来:
+
import { Controller, Get, HostParam } from '@nestjs/common';
+
+@Controller({ host: ':host.0.0.1', path: 'aaa' })
+export class AaaController {
+ @Get('bbb')
+ hello(@HostParam('host') host) {
+ return host;
+ }
+}
+
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a059c8c10ae74b80862238fdf7935043~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
前面取的这些都是 request 里的属性,当然也可以直接注入 request 对象:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9f2bd6c37caf46b6b1e4b96f29e78291~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
通过 @Req 或者 @Request 装饰器,这俩是同一个东西:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4041e1d300b14403bc05d1bc296829f4~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
注入 request 对象后,可以手动取任何参数:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2bfdaeb455a34a6a8d1bf655fdc979c4~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
当然,也可以 @Res 或者 @Response 注入 response 对象,只不过 response 对象有点特殊:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0069b293e87e44a8af1df6a6150ff511~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
当你注入 response 对象之后,服务器会一直没有响应:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a328cef97c9242fb8c2b93e50feb9b9c~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
因为这时候 Nest 就不会再把 handler 返回值作为响应内容了。
+
你可以自己返回响应:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b22ecf0abbab49eca095d05d8a03644e~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b940ec0f01b4a268b32ac10f7d15842~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
Nest 这么设计是为了避免你自己返回的响应和 Nest 返回的响应的冲突。
+
如果你不会自己返回响应,可以通过 passthrough 参数告诉 Nest:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/404c6fe6d28947de89e1b94d3b535e5c~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b940ec0f01b4a268b32ac10f7d15842~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
除了注入 @Res 不会返回响应外,注入 @Next 也不会:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b696471d11644cc8dc4a07efd697546~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
当你有两个 handler 来处理同一个路由的时候,可以在第一个 handler 里注入 next,调用它来把请求转发到第二个 handler:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f74e4f19b09e434496a5dfe35caedc66~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
Nest 不会处理注入 @Next 的 handler 的返回值。
+
handler 默认返回的是 200 的状态码,你可以通过 @HttpCode 修改它:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7148d5f586494f9891b1d4a43cebc103~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2e4d8c9ee3b4598b6bfdd53f6d1c61d~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
当然,你也可以修改 response header,通过 @Header 装饰器:
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0ce211852e9e4d5c9d9f954355f5c60b~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00ef498cad0d4670821b46eb64a12731~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
此外,你还可以通过 @Redirect 装饰器来指定路由重定向的 url:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ee4edf4a78b4fb683fb4f48d91270a5~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d563d9e5836f49038f9ad7db40702f85~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
或者在返回值的地方设置 url:
+
@Get('xxx')
+@Redirect()
+async jump() {
+ return {
+ url: 'https://www.baidu.com',
+ statusCode: 302
+ }
+}
+
+
你还可以给返回的响应内容指定渲染引擎,不过这需要先这样设置:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3dc3a9722af1467eb26f7b21f028abfe~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+import { NestExpressApplication } from '@nestjs/platform-express';
+import { join } from 'path';
+
+async function bootstrap() {
+ const app = await NestFactory.create<NestExpressApplication>(AppModule);
+
+ app.useStaticAssets(join(__dirname, '..', 'public'));
+ app.setBaseViewsDir(join(__dirname, '..', 'views'));
+ app.setViewEngine('hbs');
+
+ await app.listen(3000);
+}
+bootstrap();
+
+
+
分别指定静态资源的路径和模版的路径,并指定模版引擎为 handlerbars。
+
当然,还需要安装模版引擎的包 hbs:
+
npm install --save hbs
+
+
然后准备图片和模版文件:
+
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3eab9c35c1534e2b9a173302480cda32~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5afa60e3c49c4ac7af3fe65f1f52a96c~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
在 handler 里指定模版和数据:
+
![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dcd6047993244f07a6ac050aaf654573~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
就可以看到渲染出的 html 了:
+
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b59a367d570e4a5f84332ee02b8ac0d8~tplv-k3u1fbpfcp-jj-mark:1512:0:0:0:q75.awebp)
+
案例代码在小册仓库。
+
总结
+
这节我们梳理了下 Nest 全部的装饰器
+
+- @Module: 声明 Nest 模块
+- @Controller:声明模块里的 controller
+- @Injectable:声明模块里可以注入的 provider
+- @Inject:通过 token 手动指定注入的 provider,token 可以是 class 或者 string
+- @Optional:声明注入的 provider 是可选的,可以为空
+- @Global:声明全局模块
+- @Catch:声明 exception filter 处理的 exception 类型
+- @UseFilters:路由级别使用 exception filter
+- @UsePipes:路由级别使用 pipe
+- @UseInterceptors:路由级别使用 interceptor
+- @SetMetadata:在 class 或者 handler 上添加 metadata
+- @Get、@Post、@Put、@Delete、@Patch、@Options、@Head:声明 get、post、put、delete、patch、options、head 的请求方式
+- @Param:取出 url 中的参数,比如 /aaa/:id 中的 id
+- @Query: 取出 query 部分的参数,比如 /aaa?name=xx 中的 name
+- @Body:取出请求 body,通过 dto class 来接收
+- @Headers:取出某个或全部请求头
+- @Session:取出 session 对象,需要启用 express-session 中间件
+- @HostParm: 取出 host 里的参数
+- @Req、@Request:注入 request 对象
+- @Res、@Response:注入 response 对象,一旦注入了这个 Nest 就不会把返回值作为响应了,除非指定 passthrough 为true
+- @Next:注入调用下一个 handler 的 next 方法
+- @HttpCode: 修改响应的状态码
+- @Header:修改响应头
+- @Redirect:指定重定向的 url
+- @Render:指定渲染用的模版引擎
+
+
把这些装饰器用熟,就掌握了 nest 大部分功能了。
diff --git a/src/Node/Event Loop.md b/src/Node/Event Loop.md
new file mode 100644
index 0000000..aa3402a
--- /dev/null
+++ b/src/Node/Event Loop.md
@@ -0,0 +1,68 @@
+---
+ title: 'Event Loop'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2023-07-18
+ category: ''
+ tag: 'node'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+ # 事件循环
+
+## 官网简化图
+
+```
+ ┌───────────────────────────┐
+┌─>│ timers │
+│ └─────────────┬─────────────┘
+│ ┌─────────────┴─────────────┐
+│ │ pending callbacks │
+│ └─────────────┬─────────────┘
+│ ┌─────────────┴─────────────┐
+│ │ idle, prepare │
+│ └─────────────┬─────────────┘ ┌───────────────┐
+│ ┌─────────────┴─────────────┐ │ incoming: │
+│ │ poll │<─────┤ connections, │
+│ └─────────────┬─────────────┘ │ data, etc. │
+│ ┌─────────────┴─────────────┐ └───────────────┘
+│ │ check │
+│ └─────────────┬─────────────┘
+│ ┌─────────────┴─────────────┐
+└──┤ close callbacks │
+ └───────────────────────────┘
+
+```
+
+## 概念理解
+
+![Alt text](image.png)
+
+- 事件循环在node服务启动的时候就会运行
+
+- node中的异步处理,是交给libuv来处理的
+
+- 只有在调用堆栈为空后,事件循环才会开始发挥作用。
+
+
+
+## 事件循环中,执行顺序遵循的一定规则
+
+![Alt text](image-1.png)
+
+- 微任务队列中,nextTick的队列优先级大于promise队列
+
+- 进行一次循环前,会优先执行微任务队列。且每个阶段完成后也会执行微任务队列
+
+## 参考文章
+
+1. [A Complete Visual Guide to Understanding the Node.js Event Loop](https://www.builder.io/blog/visual-guide-to-nodejs-event-loop)
diff --git "a/src/Node/FS\347\233\270\345\205\263.md" "b/src/Node/FS\347\233\270\345\205\263.md"
new file mode 100644
index 0000000..d8ff53f
--- /dev/null
+++ "b/src/Node/FS\347\233\270\345\205\263.md"
@@ -0,0 +1,28 @@
+---
+ title: 'FS相关'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2020-05-15
+ category: ''
+ tag: 'node'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+ fs模块是唯一一个同时提供同步和异步API的模块
+
+- fs.readdirSync(__dirname) 同步读取一个文件
+- fs.readdir('.',function(err, files){}) 异步读取文件
+
+
+### Stream
+流,比起使用fs.readFile和rs.writeFile方法对文件进行读写操作,
+使用Stream API来进行读写操作可以避免想上面的方法一样需要一次分配内存来进行操作,stream可以分段处理,这样在读取大文件或者上传大文件时有足够的操作空间。
diff --git a/src/Node/image-1.png b/src/Node/image-1.png
new file mode 100644
index 0000000..33e7db9
Binary files /dev/null and b/src/Node/image-1.png differ
diff --git a/src/Node/image-2.png b/src/Node/image-2.png
new file mode 100644
index 0000000..1d78125
Binary files /dev/null and b/src/Node/image-2.png differ
diff --git a/src/Node/image-3.png b/src/Node/image-3.png
new file mode 100644
index 0000000..67c8278
Binary files /dev/null and b/src/Node/image-3.png differ
diff --git a/src/Node/image-4.png b/src/Node/image-4.png
new file mode 100644
index 0000000..9e7d918
Binary files /dev/null and b/src/Node/image-4.png differ
diff --git a/src/Node/image-5.png b/src/Node/image-5.png
new file mode 100644
index 0000000..3e7fcae
Binary files /dev/null and b/src/Node/image-5.png differ
diff --git a/src/Node/image-6.png b/src/Node/image-6.png
new file mode 100644
index 0000000..9f52215
Binary files /dev/null and b/src/Node/image-6.png differ
diff --git a/src/Node/image.png b/src/Node/image.png
new file mode 100644
index 0000000..e5d5035
Binary files /dev/null and b/src/Node/image.png differ
diff --git a/src/Node/node_modules.md b/src/Node/node_modules.md
new file mode 100644
index 0000000..fde614c
--- /dev/null
+++ b/src/Node/node_modules.md
@@ -0,0 +1,97 @@
+---
+ title: 'node_modules'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2022-03-23
+ category: ''
+ tag: 'node'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+
+原文链接[https://zhuanlan.zhihu.com/p/137535779](https://zhuanlan.zhihu.com/p/137535779)
+### package
+包含了package.json,使用package.json定义的一个package。通常是对应一个module,也可以不包含module。
+### module
+能被require的,就是一个module,只有当module里面包含package.json的时候,它才叫package。
+
+### Dependency Hell
+依赖地狱。
+当A,C都依赖B,A依赖的是B的1.0.0版本。C依赖的是B的2.0.0版本。在B版本能支持多版本共存的情况下,npm如何解决保证让A,C都加载到自己想要的版本?
+npm的解决方式是通过加载依赖时路径的查找算法和node_modules的目录结构这两者来配合解决的
+#### 查找算法
+递归向上查找node_modules里面的package
+> eg.
+> 如果在 `'/home/ry/projects/foo.js'` 文件里调用了 `require('bar.js')`,则 Node.js 会按以下顺序查找:
+> - `/home/ry/projects/node_modules/bar.js`
+> - `/home/ry/node_modules/bar.js`
+> - `/home/node_modules/bar.js`
+> - `/node_modules/bar.js`
+
+-- 递归向上
+-- 就近原则
+#### node_modules的目录结构
+![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1589538479785-9b9033a3-d0b6-40bb-95d7-bf9cdcbf86c5.png#height=310&id=z1aGJ&name=image.png&originHeight=982&originWidth=460&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66172&status=done&style=none&title=&width=145)
+
+### nest mode(npm v2)
+根据上面dependency hell的解决方案,我们可以想到如果acorn-jsx和acorn-dynamic-import同时依赖一个package的不同版本,只要在他们自己的目录下维护就好了。因为是就近原则。但是如果此时有另外一个模块,也依赖这个同acorn-jsx相同版本的package,那么就会导致同时存在两个相同版本的package。
+如果依赖的过多,会导致大量空间被浪费。这就是臭名昭著的node_modules hell
+
+### flat mode(npm v3)
+同样,这个模式,是利用向上递归查找的原则,解决nest mode的重复依赖问题。它把重复的依赖提取为公共依赖,放到上一层的node_modules。
+但是,如果有四个模块,其中两个依赖了1.0.0版本,另外两个依赖了2.0.0版本,那么不论是把1.0.0放到上一层还是把2.0.0放到上一层,都会造成某个版本依赖两次。这时你可能会想:为啥不把1.0.0和2.0.0都放到上一层,这不就只要install一次吗。如果都放到上一层,我怎么保证我拿到的是1.0.0版本还是2.0.0版本? 这叫做doppelgangers
+### 版本重复问题
+版本重复及同时存在多个版本,会出现什么问题?
+#### 全局types冲突
+一些package会修改全局的类型定义,全局的types形成了命名冲突。解决方式就是自己控制包含哪些加载的
+#### 破坏单例模式
+
+### Phantom dependecy
+对比以上flat mode会比nest mode节省很多空间,同时也带来了phantom dependecy的问题。什么是phantom dependecy?
+我们把一个库使用了不属于其dependencies里的package称之为phantom dependecy。我理解:A,C模块依赖1.0.0,现在把1.0.0提升一层,那么在AC的dependencies里面肯定没有1.0.0
+另外,在同一个库里面,有可能引用的依赖不在dependencies里面而是在devDependencies里面,我们本地开发运行没有问题,但是发布的话别人下载安装依赖就会有问题了。
+并且在使用monorepo管理项目的情况下,问题更加严重。一个package不但可能引入dev环境下的phantom dependecy,也有可能引入其他package的依赖。
+**在基于npm或者yarn的node_modules的结构下,doppelganger 和 phantom dependency这两个问题似乎并没有太好的解决方式。**
+
+### Semver(语意化版本)
+semver的提出主要用于控制每个依赖package的影响范围,能实现系统的平滑升级和过度。
+![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1589783983490-06c804e9-d48f-485c-a09f-45500df54c76.png#height=80&id=PFxRL&name=image.png&originHeight=80&originWidth=388&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7724&status=done&style=none&title=&width=388)
+前面加个^表示npm install 的时候都会安装符合0.18.0约束的最新依赖。
+问题是。并不是所有的库都会遵循。所以。。。
+如果直接写死axios的版本依赖,但是不能保证axios的依赖也是写死。所以,packge-lock.json和yarn的lock文件就是实现这样的方式。
+如上图的package.json里面声明的axios依赖,我们在生成的package-lock.json文件中可以看到。axios所有的依赖及其依赖的依赖的版本都在lock文件中锁定了。这样其他人来使用这个package就能复现版本。
+**但是当我们第一次安装创建项目时或者第一次安装某个依赖的时候,此时即使第三方库里含有lock文件。但是npm install 并不会去读取三方依赖的lock,所以还是有可能触发bug。**
+> ### Resolutions 救火队长
+> 如果你某天安装了一个新的webpack-cli,却发现这个webpack-cli并不能正常工作,经过一番定位发现,是该cli的一个上游依赖portfinder的最近一个版本有bug,但是该cli的作者在休假,没办法及时修复这个cli,但项目赶着上线该怎么处理?yarn提供了一个叫做[https://classic.yarnpkg.com/en/docs/selective-version-resolutions/](https://classic.yarnpkg.com/en/docs/selective-version-resolutions/)的机制,使得你可以忽略dependency的限制,强行将portfinder锁定为某个没有bug的版本,以解燃眉之急
+> npm本身没有提供resolution机制,但是可以通过`npm-froce-resolution`这个库实现类似机制
+
+
+### determinism
+determinism指的是在给定package.json和lock文件下,每次重新install都会得到同样的node_modules的拓扑结构。
+
+### PNPM
+相比于yarn尽可能的将package放到root level,pnpm则是只将显式写明的dependency的依赖写入root-level的node_modules,这避免了业务里错误的引入隐式依赖的问题,即解决了phantom dependency
+
+pnpm不仅仅能保证一个项目里的所有package的每个版本是唯一的,甚至能保证你使得你不同的项目之间也可以公用唯一的版本(只需要公用store即可),这样可以极大的节省了磁盘空间。核心就在于pnpm不再依赖于node的递归向上查找node_modules的算法,因为该算法强依赖于node_modules的物理拓扑结构,这也是导致不同项目的项目难以复用node_modules的根源。(还有一种干法,就是使用代码的地方写死依赖的版本号,这是deno的干法)
+
+### cargo(全局store的包管理系统)
+
+## CJM/ESM
+
+1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
+2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
+3. CommonJs 是单个值导出,ES6 Module可以导出多个
+4. CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
+5. CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined
+
+
+
diff --git "a/src/Node/\344\270\255\351\227\264\344\273\266.md" "b/src/Node/\344\270\255\351\227\264\344\273\266.md"
new file mode 100644
index 0000000..af8e407
--- /dev/null
+++ "b/src/Node/\344\270\255\351\227\264\344\273\266.md"
@@ -0,0 +1,26 @@
+---
+ title: '中间件'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2020-05-15
+ category: ''
+ tag: 'node'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+ ### connect
+对node http模块的封装,能更简洁更方便的调用功能
+
+### express
+对connect的封装,基于更上层的调用
+
+
diff --git "a/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md" "b/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md"
new file mode 100644
index 0000000..a1fc22b
--- /dev/null
+++ "b/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md"
@@ -0,0 +1,431 @@
+---
+ title: '如何在node中更好的使用集群'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2023-09-01
+ category: ''
+ tag: 'node'
+ sticky: 10
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+ # 如何通过集群扩展 Node.js 应用程序
+
+- [原文链接](https://www.digitalocean.com/community/tutorials/how-to-scale-node-js-applications-with-clustering#prerequisites)
+- [演示代码](https://github.com/Capchen/cluster_demo)
+
+## 介绍
+
+当你在一个多核CPU的系统上运行一个node程序,默认情况下会以单核的模式去创建一个进程。因为Node.js是以单线程的方式执行javascript代码,所以应用的所有请求都必须由单核上的线程去处理。如果应用程序有 CPU 密集型任务,操作系统必须安排它们共享单个 CPU,直到完成。如果单个进程收到太多请求,可能会导致该进程不堪重负,从而导致性能下降。如果进程崩溃了,用户也不能继续访问你的应用了。
+
+Node.js引入了cluster模块去解决这个问题,它会在同一台计算机上创建同一个应用程序的多个副本并让它们同时运行。同时它也使用了[round-robin 算法](https://en.wikipedia.org/wiki/Round-robin_scheduling)去实现负载均衡。如果一个实例崩溃了,剩下运行中的实例依然可以为用户提供服务。得益于负载均衡,应用的性能也会显著提高。
+
+在本教程汇总,你将会在一台拥有四个或更多个 CPU 的机器上,使用 Node.js 的集群(cluster)模块来扩展一个应用程序。您将创建一个不使用集群的应用程序,然后将该应用改进到使用集群模块。你还将使用 pm2 模块来将应用程序扩展到多个 CPU。你将使用负载测试工具来比较使用集群和不使用集群的应用程序的性能,以及评估 pm2 模块的表现。
+
+## 准备
+
+要跟随学习本教程,你需要:
+
+1. 大于或等于4核系统
+
+ a. 如果您使用的是 Ubuntu 22.04 远程服务器,您可以按照我们的[初始服务器设置](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04)来设置您的系统.
+
+2. 在您的开发环境中设置 Node.js(最好大于16)。
+
+3. 对express的基本了解
+
+## 步骤一:创建目录
+
+在这一步中,你将创建项目的目录并下载应用程序所需的依赖。在第二步中,你将使用 Express 构建应用程序。然后在第三步中,你将使用内置的 node-cluster 模块将其扩展到多个 CPU,在第四步中会使用 loadtest 软件包进行压力测试。接着你将使用 pm2 软件包扩展当前应用,并在第五步中再次进行压力测试。
+
+首先,创建一个目录。您可以将其命名为 cluster_demo 或您喜欢的任何目录名称,然后进入目录,接着对其初始化
+
+``` bash
+# 创建目录
+mkdir cluster_demo
+
+# 进入目录
+cd cluster_demo
+
+# 初始化
+npm init -y
+```
+
+-y 选项告诉 NPM 接受所有默认选项。
+
+``` javascript
+{
+ "name": "cluster_demo",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
+```
+
+这些属性需要与项目保持一致:
+
+- name:npm 包的名称。
+- version:你的包的版本号。
+- main:项目的入口点。
+
+在 package.json 文件中,添加启用对 ES 模块的支持:
+
+``` javascript
+{
+ ...
+ "author": "",
+ "license": "ISC",
+ "type": "module"
+}
+
+```
+
+接着,下载一些依赖包
+
+- express:一个用于在 Node.js 中构建 Web 应用程序的框架。
+- loadtest:一种负载测试工具,可用于生成应用程序的流量以测量其性能。
+- pm2:一种自动将应用程序扩展到多个 CPU 的工具。
+
+``` zsh
+npm install express
+
+npm install -g loadtest pm2
+```
+
+## 步骤二:创建不使用集群的应用
+
+在这一步中,您将创建一个包含单个路由的示例程序,该路由将在每个用户访问时启动CPU密集型任务。该程序不会使用集群模块,因此您可以访问在一个CPU上运行应用程序的单个实例的性能影响。在本教程的后面,您将比较这种方法与集群模块的性能。
+
+首先,创建`index.js`文件
+
+``` js
+import express from "express";
+
+const port = 3000;
+const app = express();
+
+console.log(`worker pid=${process.pid}`);
+```
+
+在第一行,导入express包。在第二行中,将端口变量设置为端口3000,应用程序的服务器将侦听该端口。接下来,将app变量设置为Express的实例。之后,您可以使用内置的进程模块在控制台中记录应用程序进程的进程ID。
+
+然后,新增一个路由,它是一个CPU 密集的循环操作
+
+``` js
+...
+app.get("/heavy", (req, res) => {
+ let total = 0;
+ for (let i = 0; i < 5_000_000; i++) {
+ total++;
+ }
+ res.send(`The result of the CPU intensive task is ${total}\n`);
+});
+```
+
+在/ heavy路径中,你定义了一个循环,将总变量增加了500万次。然后使用res. send ()方法发送包含总变量中的值的响应。虽然CPU受限任务的示例是任意的,但它演示了CPU受限任务,而没有增加复杂性。你也可以为路由使用其他名称,但本教程使用/ heavy表示繁重的性能任务。
+
+接下来,调用 Express 模块的 Listen() 方法,让服务器监听存储在 port 变量中的端口 3000:
+
+```js
+...
+app.listen(port, () => {
+ console.log(`App listening on port ${port}`);
+});
+```
+
+`index.js`完整代码如下:
+
+```js
+import express from "express";
+
+const port = 3000;
+const app = express();
+
+console.log(`worker pid=${process.pid}`);
+
+app.get("/heavy", (req, res) => {
+ let total = 0;
+ for (let i = 0; i < 5_000_000; i++) {
+ total++;
+ }
+ res.send(`The result of the CPU intensive task is ${total}\n`);
+});
+
+app.listen(port, () => {
+ console.log(`App listening on port ${port}`);
+});
+```
+启动服务
+
+`node index.js`
+
+输出显示正在运行的进程的进程 ID 以及确认服务器正在侦听端口 3000 的消息。 要测试应用程序是否正常工作,请打开另一个终端并运行以下命令:
+
+```bash
+worker pid=11023
+App listening on port 3000
+```
+
+可以在浏览器中直接打开
+`http://localhost:3000/heavy`
+
+也可以在终端中执行curl
+`curl http://localhost:3000/heavy`
+
+当你使用node命令运行index. js文件时,操作系统(OS)会创建一个进程。进程是操作系统为运行程序所做的抽象。操作系统为程序分配内存,并在进程列表中创建一个对应运行程序的进程ID。
+
+程序二进制文件会被定位并加载到为进程分配的内存中。从那里开始执行。运行时,它对系统中的其他进程没有任何感知,并且在该进程中发生的任何事情都不会影响其他进程。
+
+由于您在具有多个CPU的服务器上运行Node.js应用程序只有一个进程,因此它将接收并处理所有传入请求。在此图中,所有传入请求都被定向到在单个CPU上运行的进程,而其他CPU保持空闲:
+
+![Alt text](image-2.png)
+
+## 步骤三:创建集群应用
+
+在这一步中,你将添加集群(cluster)模块,以创建同一程序的多个实例,以处理更多的负载并提高性能。当你使用集群模块运行进程时,你可以在你的机器上的每个 CPU 上运行多个进程:
+
+![Alt text](image-3.png)
+
+在这个图示中,请求通过主进程中的负载均衡器,然后使用循环轮询算法将请求分发到各个进程之间。
+
+现在来创建`primary.js`文件
+
+``` js
+import cluster from "cluster";
+import os from "os";
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+```
+
+在前两行中,导入了集群(cluster)和操作系统(os)模块。在接下来的两行中,导入了 dirname 和 fileURLToPath,它们用于将 __dirname 变量的值设置为执行 index.js 文件所在目录的绝对路径。因为当使用 ES 模块时,__dirname 并未定义,它默认只在 CommonJS 模块中定义。
+
+接下来,添加以下代码来引用index.js文件:
+
+```js
+const cpuCount = os.cpus().length;
+
+console.log(`The total number of CPUs is ${cpuCount}`);
+console.log(`Primary pid=${process.pid}`);
+cluster.setupPrimary({
+ exec: __dirname + "/index.js",
+});
+```
+
+首先,我们将 cpuCount 变量设置为你的计算机上的 CPU 数量,应该是四个或更多。接下来,在控制台中打印了 CPU 数量和主进程的进程 ID,这个主进程将接收所有的请求,并使用负载均衡器将它们分发给工作进程。
+
+随后,你使用集群(cluster)模块的 setupPrimary() 方法引用了 index.js 文件,以便在每个工作进程中执行。
+
+然后,添加以下代码来创建进程:
+
+```js
+...
+for (let i = 0; i < cpuCount; i++) {
+ cluster.fork();
+}
+cluster.on("exit", (worker, code, signal) => {
+ console.log(`worker ${worker.process.pid} has been killed`);
+ console.log("Starting another worker");
+ cluster.fork();
+});
+```
+
+以上代码会循环迭代cpu的数量,并在每次迭代中调用集群(cluster)模块的 fork() 方法。同时使用集群模块的 on() 方法附加了 exit 事件,以便监听进程何时发出 exit 事件,通常是当进程终止时。当触发 exit 事件时,你会记录已终止的工作进程的进程 ID,然后调用 fork() 方法创建一个新的工作进程,以替换已终止的进程。
+
+完整代码如下:
+
+```js
+import cluster from "cluster";
+import os from "os";
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+const cpuCount = os.cpus().length;
+
+console.log(`The total number of CPUs is ${cpuCount}`);
+console.log(`Primary pid=${process.pid}`);
+cluster.setupPrimary({
+ exec: __dirname + "/index.js",
+});
+
+for (let i = 0; i < cpuCount; i++) {
+ cluster.fork();
+}
+cluster.on("exit", (worker, code, signal) => {
+ console.log(`worker ${worker.process.pid} has been killed`);
+ console.log("Starting another worker");
+ cluster.fork();
+});
+```
+
+现在可以`node primary.js`启动服务,观察输出.可以看到服务以集群的方式启动了。
+![Alt text](image-4.png)
+
+## 步骤四:使用测试工具对比性能
+
+在这一步中,你将使用 loadtest 软件包对你构建的两个程序生成流量。你将比较使用集群(cluster)模块的 primary.js 程序与不使用集群的 index.js 程序的性能。你会注意到使用集群模块的程序在特定时间内执行得更快,可以处理更多的请求,而不使用集群的程序则不如此。
+
+### 对单核的测试
+
+首先,先启动单核服务`node index.js`
+
+接下来,在终端中运行测试命令(在步骤一中已经下载了全局loadtest)
+
+```bash
+loadtest -n 1200 -c 200 -k http://localhost:3000/heavy
+
+# -n 1200:这个参数指定了要发送的请求数量,即压测将模拟发送 1200 个请求到指定的 URL。
+
+#-c 200:这个参数指定了并发连接数,即同时发送的请求数量。在这个命令中,将同时发送 200 个请求。
+
+#-k:这是一个选项,表示使用 HTTP Keep-Alive 连接。Keep-Alive 允许单个连接复用,而不必为每个请求建立新的连接。
+```
+
+可以看到测试报告:
+![Alt text](image-5.png)
+
+> Max requests: 1200:设置的最大请求数。
+> Concurrency level: 200:并发连接数,即同时发送的请求数量。
+> Running on cores: 4:表示测试是在一个拥有 4 个 CPU 核心的机器上运行的。
+> Agent: keepalive:测试时使用的代理(Agent),这里是 HTTP Keep-Alive 连接。
+> Completed requests: 1200:完成的请求数量,与设置的最大请求数一致。
+> Total errors: 0:总共的错误请求数量,这里是 0,表示没有错误的请求。
+> Total time: 2.486 s:总共花费的时间,单位是秒。
+> Mean latency: 1100.3 ms:平均延迟,即请求从发送到接收的平均时间,单位是毫秒。
+> Effective rps: 483:每秒的有效请求数,即成功完成的请求数。
+> Percentage of the requests served within a certain time:显示在特定时间内完成的请求的百分比和相应的时间。
+> 50% 1240 ms:50% 的请求在 1240 毫秒内完成。
+> 90% 1596 ms:90% 的请求在 1596 毫秒内完成。
+> 95% 1607 ms:95% 的请求在 1607 毫秒内完成。
+> 99% 1680 ms:99% 的请求在 1680 毫秒内完成。
+> 100% 1723 ms (longest request):所有请求中,最长的请求花费了 1723 毫秒。
+
+### 对多核的测试
+
+先启动单核服务`node index.js`,同样执行
+
+```bash
+loadtest -n 1200 -c 200 -k http://localhost:3000/heavy
+```
+
+可以将测试报告的结果数据和单核模式下对比。
+
+这个响应证实了扩展已经生效,你的应用程序可以在短时间内处理更多的请求而无需延迟。如果你将计算机升级为更多的 CPU,该应用程序将自动扩展到相应数量的 CPU,并进一步提高性能。
+
+需要提醒的是,由于你的网络和处理器速度的不同,终端输出中的指标会有所不同。总时间和平均延迟会显著下降,总时间会迅速增加。
+
+在下一步中,我们将使用 pm2 代替集群模块。
+
+## 步骤五:使用 pm2
+
+### 使用pm2启动
+
+到目前为止,你已经使用集群(cluster)模块根据你计算机上的 CPU 数量创建了工作进程。你还添加了在工作进程终止时重新启动它的能力。在这一步中,你将设置一个替代方案,通过使用建立在集群模块之上的 pm2 进程管理器来自动扩展你的应用程序。这个进程管理器包含一个负载均衡器,并可以自动创建与你计算机上的 CPU 数量相同的工作进程。它还允许你监控这些进程,并且如果有一个进程终止,它可以自动产生一个新的工作进程。
+
+在终端中,使用以下命令启动 pm2 集群:
+
+```bash
+pm2 start index.js -i 0
+```
+
+-i 选项接受你想要 pm2 创建的工作进程数量。如果你传递参数 0,pm2 将会自动创建与你计算机上的 CPU 数量相同的工作进程。
+
+启动后可以看到类似表格
+![Alt text](image-6.png)
+
+表格包含了每个工作进程的进程 ID、状态、CPU 利用率和内存消耗,你可以用它来了解进程的行为。
+
+当使用 pm2 启动集群时,该软件包会在后台运行,并且甚至在重新启动系统后会自动重新启动。
+
+如果你想要从工作进程中读取日志,你可以使用以下命令:
+
+`pm2 logs`
+
+如果你想要检查进程的状态,你可以使用以下命令:
+`pm2 ls`
+
+使用pm2启动服务之后,你可以再尝试下运行测试命令,去查看服务的性能。
+
+### 使用配置文件
+
+为了改进你使用 pm2 的工作流程,你可以生成一个配置文件,以传递应用程序的配置设置。这种方法将允许你在启动或重新启动集群时无需传递选项。
+
+为了使用配置文件,删除当前的集群:
+
+```bash
+pm2 delete index.js
+```
+
+接下来,生成配置文件:
+
+```bash
+pm2 ecosystem
+```
+
+可以看到`ecosystem.config.js`文件在当前目录被生成。
+需要注意的是,需要修改这个文件的后缀名来启动对ES模块的支持。
+
+```cjs
+// ecosystem.config.cjs
+module.exports = {
+ apps : [{
+ script: 'index.js',
+ watch: '.',
+ name: "cluster_app",
+ instances: 0,
+ exec_mode: "cluster",
+ }],
+
+ deploy : {
+ production : {
+ user : 'SSH_USERNAME',
+ host : 'SSH_HOSTMACHINE',
+ ref : 'origin/master',
+ repo : 'GIT_REPOSITORY',
+ path : 'DESTINATION_PATH',
+ 'pre-deploy-local': '',
+ 'post-deploy' : 'npm install && pm2 reload ecosystem.config.cjs --env production',
+ 'pre-setup': ''
+ }
+ }
+};
+```
+
+启动命令:`pm2 start ecosystem.config.cjs`
+
+其他指令:
+
+| Command | Description |
+| ---- | ---- |
+| pm2 start app_name | 启动 |
+| pm2 restart app_name | 先删除再启动 |
+| pm2 reload app_name | 重启集群 |
+| pm2 stop app_name | 停止集群 |
+| pm2 delete app_name | 删除集群 |
+
+ok,您现在可以使用 pm2 模块和 cluster 模块扩展您的应用程序了。
+
+## 总结
+
+在本教程中,你使用了集群(cluster)模块来扩展你的应用程序。首先,你创建了一个不使用集群模块的程序。然后,你创建了一个使用集群模块的程序,将应用程序扩展到你机器上的多个 CPU。随后,你对比了使用集群模块和不使用集群模块的应用程序的性能。最后,你使用了 pm2 软件包作为集群模块的替代方案,将应用程序扩展到多个 CPU 上。
+
+要进一步学习,你可以访问集群模块的文档页面,了解更多关于该模块的信息。
+
+Node.js 还附带了 worker_threads 模块,允许你将 CPU 密集型任务分配给工作线程,以便它们可以更快地完成。你可以尝试我们关于如何在 Node.js 中使用多线程的教程。你还可以通过专门的 Web Workers 在前端优化 CPU 绑定的任务,你可以通过遵循如何使用 Web Workers 处理 CPU 绑定任务的教程来实现。如果你想学习如何避免 CPU 绑定任务影响应用程序的请求/响应循环,请查阅如何使用 Node.js 和 BullMQ 处理异步任务的教程。
diff --git "a/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md" "b/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md"
new file mode 100644
index 0000000..7f3a829
--- /dev/null
+++ "b/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md"
@@ -0,0 +1,50 @@
+---
+ title: '深入浅出'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2023-02-08
+ category: ''
+ tag: 'node'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+ ## 文件模块
+node引入模块三个步骤:
+
+1. 路径解析
+2. 文件定位
+3. 编译执行
+
+---
+
+例如require('element') 时,在node_modules中找到的是个文件夹,node会将目录当一个包来处理。
+(查找的兜底是index文件名)
+
+1. 在当前目录下找到package.json
+2. 通过JSON.parse()解析出包描述对象,从中取出main属性制定的文件名
+
+---
+
+## 异步I/O
+### 系统的异步io
+非阻塞i/o会让CPU处理状态判断。通过轮询处理
+轮询的演进:
+
+1. read
+2. select
+3. poll
+4. epoll
+
+通过线程池来完成异步I/O
+### node的异步io
+在进程启动的时候,node便会创建一个类似while(true)的循环,每一次循环为一次tick
+
diff --git "a/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md"
new file mode 100644
index 0000000..7422d94
--- /dev/null
+++ "b/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md"
@@ -0,0 +1,223 @@
+---
+ title: '5分钟上手GSAP'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2023-11-27
+ category: ''
+ tag: 'js annimation'
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+## 什么是GSAP
+
+动效库
+
+> GSAP 允许您轻松地为 JS 可以触摸的任何东西设置动画。提供丝般流畅的性能和无与伦比的支持,让您可以专注于有趣的事情。
+
+- [速查表](https://gsap.com/community/cheatsheet/)
+
+### Core
+
+基础的动效控制,能满足大部分动效场景。例如旋转,缩放,抛物线运动
+
+### Plugins
+
+能力拓展,能满足特殊动效场景。例如跟随鼠标滚轮的滚动动效
+
+## 动起来
+
+在 GSAP 中,Tweens 是指在动画中改变一个或多个属性的过程。以下是一个简单的 Tween 示例:
+
+```js
+gsap.to('.box', {
+ x: 200, // 将元素的 x 坐标移动到 200
+ opacity: 0.5, // 改变元素的不透明度
+ duration: 1, // 动画持续时间
+ ease: 'power2.inOut', // 缓动函数
+});
+```
+
+to/from/fromTo这三个方法都能返回一个Tween实例
+
+假设现在`.box`这个dom节点,在x轴上的偏移量为0`{x: 0}`
+
+### to
+
+偏移量从0到200
+
+```js
+gsap.to(".box", { x: 200 })
+```
+
+### from
+
+偏移量从200回到0
+
+```js
+gsap.from(".box", { x: 200 })
+```
+
+### fromTo
+
+```js
+gsap.fromTo(".box", { x: 200 }, {x: 0})
+```
+
+偏移量从200到0
+
+### set
+
+```js
+gsap.set(".box", {x: 100});
+```
+
+设置偏移量为100
+
+set 是一瞬间的事
+
+## Easing
+
+控制动画运动的速率
+
+[配置字典](https://gsap.com/resources/getting-started/Easing)
+
+## Staggers
+
+错落有致,控制一组元素动画的交错配置
+
+```js
+gsap.from(".box", {
+ x: -150,
+ duration: 1,
+ opacity: 0,
+ stagger: 0.2,
+})
+```
+
+## Apple Watch 实战
+
+[示例网站](https://www.apple.com.cn/apple-watch-series-9/)
+
+1. 字往上提的渐入效果
+
+## 排队动起来(timelines)
+
+如果我们需要控制一系列复杂的动画,涉及到多个元素,
+仅仅依靠延迟和上面说到的交错配置,其实是不够的。
+
+那么Timelines来了。
+
+Timelines 允许你将多个 Tweens 组织在一起,以便在时间上进行更复杂的控制。以下是一个创建 Timeline 的示例:
+
+```js
+const timeline = gsap.timeline();
+timeline.to('.box', { x: 200, duration: 1 })
+ .to('.box', { rotation: 360, duration: 1, ease: 'elastic' });
+```
+
+[查看官网例子](https://gsap.com/resources/getting-started/timelines)
+
+## 控制动画(control)
+
+从上面的示例可以看到,进行一个动画,我们没办法更精细的控制动画什么时候开始,什么时候结束。但我们可以拿到一个基础的Tween实例,一个timeline实例
+
+有这两个实例,我们就可以控制对应的动画了。
+
+可以在官网上看,这两个动画实例支持什么方法
+[实例方法文档](https://gsap.com/docs/v3/GSAP/Timeline)
+
+### 基础控制
+
+```js
+// store the tween or timeline in a variable
+let tween = gsap.to("#logo", {duration: 1, x: 100});
+
+//pause
+tween.pause();
+
+//resume (honors direction - reversed or not)
+tween.resume();
+
+//reverse (always goes back towards the beginning)
+tween.reverse();
+
+//jump to exactly 0.5 seconds into the tween
+tween.seek(0.5);
+
+//jump to exacty 1/4th into the tween 's progress:
+tween.progress(0.25);
+
+//make the tween go half-speed
+tween.timeScale(0.5);
+
+//make the tween go double-speed
+tween.timeScale(2);
+
+//immediately kill the tween and make it eligible for garbage collection
+tween.kill();
+
+// You can even chain control methods
+// Play the timeline at double speed - in reverse.
+tween.timeScale(2).reverse();
+
+```
+
+### 回调
+
+- onComplete: 当动画完成时调用。
+- onStart: 当动画开始时调用。
+- onUpdate: 每次动画更新时调用(在动画活动期间的每一帧)。
+- onRepeat: 每次动画重复时调用。
+- onReverseComplete: 当动画在反向播放时再次达到起点时调用。
+
+```js
+gsap.to(".class", {
+ duration: 1,
+ x: 100,
+ // arrow functions are handy for concise callbacks
+ onComplete: () => console.log("the tween is complete")
+})
+
+// If your function doesn't fit neatly on one line, no worries.
+// you can write a regular function and reference it
+gsap.timeline({onComplete: tlComplete}); // <- no () after the reference!
+
+function tlComplete() {
+ console.log("the tl is complete");
+ // more code
+}
+```
+
+## 花式的动起来(plugins)
+
+插件提供了额外的能力,我们也可以按需引入
+
+[插件一览](https://gsap.com/resources/Plugins/)
+
+需要注意的是,这里区分收费版,高级的使用需要付费。
+
+### scrollTrigger
+
+看官网代码
+
+> 辅助线 markers: true
+
+### Draggable
+
+看官网代码
+
+## 总结
+
+优秀的JS动画库,对现代浏览器更友好。
+
+一些高级功能需要付费,但是基础的动画支持已经满足大部分场景。
+
+上手简单,应用不难,值得star
diff --git "a/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md" "b/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md"
new file mode 100644
index 0000000..89ed4e7
--- /dev/null
+++ "b/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md"
@@ -0,0 +1,318 @@
+---
+ title: 'Hello算法笔记'
+ shortTitle: ''
+ description: ''
+ icon: ''
+ author:
+ name: 'Song'
+ isOriginal: true
+ date: 2023-01-16
+ category: ''
+ tag: '算法'
+ sticky: 1
+ star: false
+ article: true
+ timeline: true
+ image: ''
+ editLink: false
+---
+
+### 数据结构
+
+> 可以根据逻辑结构和物理结构来区分
+
+- 按照逻辑结构
+
+ 1. 线性数据结构: 数组、链表、栈、队列、哈希表