[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron":2089,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron":2115},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":2085,"stem":2086,"id":2087,"extension":2088},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron","定时任务","@nestjs\u002Fschedule 基于 cron 表达式调度任务。","技能小册",[12,13],"nestjs","cron","2023-04-12",false,{"type":17,"value":18,"toc":2077},"minimark",[19,22,33,57,60,75,86,235,238,246,249,441,478,493,500,503,661,668,671,828,835,847,854,1321,1332,1336,1349,1356,1367,1370,1398,1807,1815,1822,1994,2010,2014,2017,2073],[20,21,8],"h1",{"id":8},[23,24,25,29,30,32],"p",{},[26,27,28],"code",{},"@nestjs\u002Fschedule"," 基于 ",[26,31,13],{}," 表达式调度任务。",[23,34,35,36,38,39,41,42,49,50,52,53,56],{},"NestJS 的 ",[26,37,28],{}," 模块，是一个基于装饰器和 ",[26,40,13],{},"（",[43,44,48],"a",{"href":45,"rel":46},"https:\u002F\u002Fgithub.com\u002Fkelektiv\u002Fnode-cron",[47],"nofollow","kelektiv\u002Fnode-cron","，npm 包名为 ",[26,51,13],{},"，注意与另一个同名不同源的 ",[26,54,55],{},"node-cron"," 包区分）库的轻量级任务调度工具。",[23,58,59],{},"它通过声明式装饰器和动态调度 API，为 Cron、Interval 和 Timeout 三类任务提供了开箱即用的支持。",[61,62,64,65,68,69,68,72],"h3",{"id":63},"一声明式任务cron-interval-timeout","一、声明式任务：",[26,66,67],{},"@Cron",", ",[26,70,71],{},"@Interval",[26,73,74],{},"@Timeout",[23,76,77,78,81,82,85],{},"在使用前，需要在根模块（通常是 ",[26,79,80],{},"AppModule","）中导入并初始化 ",[26,83,84],{},"ScheduleModule","：",[87,88,93],"pre",{"className":89,"code":90,"language":91,"meta":92,"style":92},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark","import { Module } from \"@nestjs\u002Fcommon\";\nimport { ScheduleModule } from \"@nestjs\u002Fschedule\";\n\n@Module({\n  imports: [ScheduleModule.forRoot()],\n  \u002F\u002F ...\n})\nexport class AppModule {}\n","typescript","",[26,94,95,132,154,161,177,202,209,218],{"__ignoreMap":92},[96,97,100,104,108,112,115,118,122,126,129],"span",{"class":98,"line":99},"line",1,[96,101,103],{"class":102},"sVHd0","import",[96,105,107],{"class":106},"sP7_E"," {",[96,109,111],{"class":110},"su5hD"," Module",[96,113,114],{"class":106}," }",[96,116,117],{"class":102}," from",[96,119,121],{"class":120},"sjJ54"," \"",[96,123,125],{"class":124},"s_sjI","@nestjs\u002Fcommon",[96,127,128],{"class":120},"\"",[96,130,131],{"class":106},";\n",[96,133,135,137,139,142,144,146,148,150,152],{"class":98,"line":134},2,[96,136,103],{"class":102},[96,138,107],{"class":106},[96,140,141],{"class":110}," ScheduleModule",[96,143,114],{"class":106},[96,145,117],{"class":102},[96,147,121],{"class":120},[96,149,28],{"class":124},[96,151,128],{"class":120},[96,153,131],{"class":106},[96,155,157],{"class":98,"line":156},3,[96,158,160],{"emptyLinePlaceholder":159},true,"\n",[96,162,164,167,171,174],{"class":98,"line":163},4,[96,165,166],{"class":106},"@",[96,168,170],{"class":169},"sGLFI","Module",[96,172,173],{"class":110},"(",[96,175,176],{"class":106},"{\n",[96,178,180,184,187,190,193,196,199],{"class":98,"line":179},5,[96,181,183],{"class":182},"skxfh","  imports",[96,185,186],{"class":106},":",[96,188,189],{"class":110}," [ScheduleModule",[96,191,192],{"class":106},".",[96,194,195],{"class":169},"forRoot",[96,197,198],{"class":110},"()]",[96,200,201],{"class":106},",\n",[96,203,205],{"class":98,"line":204},6,[96,206,208],{"class":207},"sutJx","  \u002F\u002F ...\n",[96,210,212,215],{"class":98,"line":211},7,[96,213,214],{"class":106},"}",[96,216,217],{"class":110},")\n",[96,219,221,224,228,232],{"class":98,"line":220},8,[96,222,223],{"class":102},"export",[96,225,227],{"class":226},"sbsja"," class",[96,229,231],{"class":230},"sbgvK"," AppModule",[96,233,234],{"class":106}," {}\n",[23,236,237],{},"初始化完成后，便可通过以下三种装饰器来定义任务：",[239,240,242,243,245],"h4",{"id":241},"_1-croncron-表达式任务","1. ",[26,244,67],{},"：Cron 表达式任务",[23,247,248],{},"这是最常用、最强大的任务调度方式，通过标准的 Cron 表达式定义执行周期。在下面的例子中，任务每 10 秒触发一次：",[87,250,252],{"className":89,"code":251,"language":91,"meta":92,"style":92},"import { Injectable, Logger } from \"@nestjs\u002Fcommon\";\nimport { Cron } from \"@nestjs\u002Fschedule\";\n\n@Injectable()\nexport class TasksService {\n  private readonly logger = new Logger(TasksService.name);\n\n  @Cron(\"*\u002F10 * * * * *\") \u002F\u002F Cron 表达式\n  handleCron() {\n    this.logger.debug(\"Cron 任务执行\");\n  }\n}\n",[26,253,254,281,302,306,316,328,360,364,387,398,429,435],{"__ignoreMap":92},[96,255,256,258,260,263,266,269,271,273,275,277,279],{"class":98,"line":99},[96,257,103],{"class":102},[96,259,107],{"class":106},[96,261,262],{"class":110}," Injectable",[96,264,265],{"class":106},",",[96,267,268],{"class":110}," Logger",[96,270,114],{"class":106},[96,272,117],{"class":102},[96,274,121],{"class":120},[96,276,125],{"class":124},[96,278,128],{"class":120},[96,280,131],{"class":106},[96,282,283,285,287,290,292,294,296,298,300],{"class":98,"line":134},[96,284,103],{"class":102},[96,286,107],{"class":106},[96,288,289],{"class":110}," Cron",[96,291,114],{"class":106},[96,293,117],{"class":102},[96,295,121],{"class":120},[96,297,28],{"class":124},[96,299,128],{"class":120},[96,301,131],{"class":106},[96,303,304],{"class":98,"line":156},[96,305,160],{"emptyLinePlaceholder":159},[96,307,308,310,313],{"class":98,"line":163},[96,309,166],{"class":106},[96,311,312],{"class":169},"Injectable",[96,314,315],{"class":110},"()\n",[96,317,318,320,322,325],{"class":98,"line":179},[96,319,223],{"class":102},[96,321,227],{"class":226},[96,323,324],{"class":230}," TasksService",[96,326,327],{"class":106}," {\n",[96,329,330,333,336,340,344,347,350,353,355,358],{"class":98,"line":204},[96,331,332],{"class":226},"  private",[96,334,335],{"class":226}," readonly",[96,337,339],{"class":338},"sucvu"," logger",[96,341,343],{"class":342},"smGrS"," =",[96,345,346],{"class":342}," new",[96,348,268],{"class":349},"sVXei",[96,351,352],{"class":110},"(TasksService",[96,354,192],{"class":106},[96,356,357],{"class":110},"name)",[96,359,131],{"class":106},[96,361,362],{"class":98,"line":211},[96,363,160],{"emptyLinePlaceholder":159},[96,365,366,369,372,374,376,379,381,384],{"class":98,"line":220},[96,367,368],{"class":106},"  @",[96,370,371],{"class":169},"Cron",[96,373,173],{"class":110},[96,375,128],{"class":120},[96,377,378],{"class":124},"*\u002F10 * * * * *",[96,380,128],{"class":120},[96,382,383],{"class":110},") ",[96,385,386],{"class":207},"\u002F\u002F Cron 表达式\n",[96,388,390,393,396],{"class":98,"line":389},9,[96,391,392],{"class":349},"  handleCron",[96,394,395],{"class":106},"()",[96,397,327],{"class":106},[96,399,401,405,407,410,412,415,417,419,422,424,427],{"class":98,"line":400},10,[96,402,404],{"class":403},"s39Yj","    this",[96,406,192],{"class":106},[96,408,409],{"class":110},"logger",[96,411,192],{"class":106},[96,413,414],{"class":169},"debug",[96,416,173],{"class":182},[96,418,128],{"class":120},[96,420,421],{"class":124},"Cron 任务执行",[96,423,128],{"class":120},[96,425,426],{"class":182},")",[96,428,131],{"class":106},[96,430,432],{"class":98,"line":431},11,[96,433,434],{"class":106},"  }\n",[96,436,438],{"class":98,"line":437},12,[96,439,440],{"class":106},"}\n",[442,443,444],"ul",{},[445,446,447,451,452],"li",{},[448,449,450],"strong",{},"Cron 表达式参考","：\n",[442,453,454,460,466,472],{},[445,455,456,459],{},[26,457,458],{},"* * * * * *","：每一秒",[445,461,462,465],{},[26,463,464],{},"45 * * * * *","：每分钟的第 45 秒",[445,467,468,471],{},[26,469,470],{},"0 10 * * * *","：每小时的第 10 分钟",[445,473,474,477],{},[26,475,476],{},"0 *\u002F30 9-17 * * *","：每天 9 点到 17 点之间，每 30 分钟",[23,479,480,481,484,485,488,489,492],{},"此外，还支持 ",[26,482,483],{},"Date"," 对象和内置枚举 ",[26,486,487],{},"CronExpression","（如 ",[26,490,491],{},"CronExpression.EVERY_DAY_AT_10AM","）。",[239,494,496,497,499],{"id":495},"_2-interval固定间隔任务","2. ",[26,498,71],{},"：固定间隔任务",[23,501,502],{},"适用于不需要精确到秒的、固定周期执行的任务。下面的代码会每 10 秒执行一次。",[87,504,506],{"className":89,"code":505,"language":91,"meta":92,"style":92},"import { Injectable, Logger } from \"@nestjs\u002Fcommon\";\nimport { Interval } from \"@nestjs\u002Fschedule\";\n\n@Injectable()\nexport class TasksService {\n  private readonly logger = new Logger(TasksService.name);\n\n  @Interval(10000) \u002F\u002F 单位：毫秒，即 10 秒\n  handleInterval() {\n    this.logger.debug(\"固定间隔任务执行\");\n  }\n}\n",[26,507,508,532,553,557,565,575,597,601,619,628,653,657],{"__ignoreMap":92},[96,509,510,512,514,516,518,520,522,524,526,528,530],{"class":98,"line":99},[96,511,103],{"class":102},[96,513,107],{"class":106},[96,515,262],{"class":110},[96,517,265],{"class":106},[96,519,268],{"class":110},[96,521,114],{"class":106},[96,523,117],{"class":102},[96,525,121],{"class":120},[96,527,125],{"class":124},[96,529,128],{"class":120},[96,531,131],{"class":106},[96,533,534,536,538,541,543,545,547,549,551],{"class":98,"line":134},[96,535,103],{"class":102},[96,537,107],{"class":106},[96,539,540],{"class":110}," Interval",[96,542,114],{"class":106},[96,544,117],{"class":102},[96,546,121],{"class":120},[96,548,28],{"class":124},[96,550,128],{"class":120},[96,552,131],{"class":106},[96,554,555],{"class":98,"line":156},[96,556,160],{"emptyLinePlaceholder":159},[96,558,559,561,563],{"class":98,"line":163},[96,560,166],{"class":106},[96,562,312],{"class":169},[96,564,315],{"class":110},[96,566,567,569,571,573],{"class":98,"line":179},[96,568,223],{"class":102},[96,570,227],{"class":226},[96,572,324],{"class":230},[96,574,327],{"class":106},[96,576,577,579,581,583,585,587,589,591,593,595],{"class":98,"line":204},[96,578,332],{"class":226},[96,580,335],{"class":226},[96,582,339],{"class":338},[96,584,343],{"class":342},[96,586,346],{"class":342},[96,588,268],{"class":349},[96,590,352],{"class":110},[96,592,192],{"class":106},[96,594,357],{"class":110},[96,596,131],{"class":106},[96,598,599],{"class":98,"line":211},[96,600,160],{"emptyLinePlaceholder":159},[96,602,603,605,608,610,614,616],{"class":98,"line":220},[96,604,368],{"class":106},[96,606,607],{"class":169},"Interval",[96,609,173],{"class":110},[96,611,613],{"class":612},"srdBf","10000",[96,615,383],{"class":110},[96,617,618],{"class":207},"\u002F\u002F 单位：毫秒，即 10 秒\n",[96,620,621,624,626],{"class":98,"line":389},[96,622,623],{"class":349},"  handleInterval",[96,625,395],{"class":106},[96,627,327],{"class":106},[96,629,630,632,634,636,638,640,642,644,647,649,651],{"class":98,"line":400},[96,631,404],{"class":403},[96,633,192],{"class":106},[96,635,409],{"class":110},[96,637,192],{"class":106},[96,639,414],{"class":169},[96,641,173],{"class":182},[96,643,128],{"class":120},[96,645,646],{"class":124},"固定间隔任务执行",[96,648,128],{"class":120},[96,650,426],{"class":182},[96,652,131],{"class":106},[96,654,655],{"class":98,"line":431},[96,656,434],{"class":106},[96,658,659],{"class":98,"line":437},[96,660,440],{"class":106},[239,662,664,665,667],{"id":663},"_3-timeout一次性延迟任务","3. ",[26,666,74],{},"：一次性延迟任务",[23,669,670],{},"它只会在应用启动后的指定时间后运行一次。",[87,672,674],{"className":89,"code":673,"language":91,"meta":92,"style":92},"import { Injectable, Logger } from \"@nestjs\u002Fcommon\";\nimport { Timeout } from \"@nestjs\u002Fschedule\";\n\n@Injectable()\nexport class TasksService {\n  private readonly logger = new Logger(TasksService.name);\n\n  @Timeout(5000) \u002F\u002F 5 秒后执行一次\n  handleTimeout() {\n    this.logger.debug(\"一次性任务执行\");\n  }\n}\n",[26,675,676,700,721,725,733,743,765,769,786,795,820,824],{"__ignoreMap":92},[96,677,678,680,682,684,686,688,690,692,694,696,698],{"class":98,"line":99},[96,679,103],{"class":102},[96,681,107],{"class":106},[96,683,262],{"class":110},[96,685,265],{"class":106},[96,687,268],{"class":110},[96,689,114],{"class":106},[96,691,117],{"class":102},[96,693,121],{"class":120},[96,695,125],{"class":124},[96,697,128],{"class":120},[96,699,131],{"class":106},[96,701,702,704,706,709,711,713,715,717,719],{"class":98,"line":134},[96,703,103],{"class":102},[96,705,107],{"class":106},[96,707,708],{"class":110}," Timeout",[96,710,114],{"class":106},[96,712,117],{"class":102},[96,714,121],{"class":120},[96,716,28],{"class":124},[96,718,128],{"class":120},[96,720,131],{"class":106},[96,722,723],{"class":98,"line":156},[96,724,160],{"emptyLinePlaceholder":159},[96,726,727,729,731],{"class":98,"line":163},[96,728,166],{"class":106},[96,730,312],{"class":169},[96,732,315],{"class":110},[96,734,735,737,739,741],{"class":98,"line":179},[96,736,223],{"class":102},[96,738,227],{"class":226},[96,740,324],{"class":230},[96,742,327],{"class":106},[96,744,745,747,749,751,753,755,757,759,761,763],{"class":98,"line":204},[96,746,332],{"class":226},[96,748,335],{"class":226},[96,750,339],{"class":338},[96,752,343],{"class":342},[96,754,346],{"class":342},[96,756,268],{"class":349},[96,758,352],{"class":110},[96,760,192],{"class":106},[96,762,357],{"class":110},[96,764,131],{"class":106},[96,766,767],{"class":98,"line":211},[96,768,160],{"emptyLinePlaceholder":159},[96,770,771,773,776,778,781,783],{"class":98,"line":220},[96,772,368],{"class":106},[96,774,775],{"class":169},"Timeout",[96,777,173],{"class":110},[96,779,780],{"class":612},"5000",[96,782,383],{"class":110},[96,784,785],{"class":207},"\u002F\u002F 5 秒后执行一次\n",[96,787,788,791,793],{"class":98,"line":389},[96,789,790],{"class":349},"  handleTimeout",[96,792,395],{"class":106},[96,794,327],{"class":106},[96,796,797,799,801,803,805,807,809,811,814,816,818],{"class":98,"line":400},[96,798,404],{"class":403},[96,800,192],{"class":106},[96,802,409],{"class":110},[96,804,192],{"class":106},[96,806,414],{"class":169},[96,808,173],{"class":182},[96,810,128],{"class":120},[96,812,813],{"class":124},"一次性任务执行",[96,815,128],{"class":120},[96,817,426],{"class":182},[96,819,131],{"class":106},[96,821,822],{"class":98,"line":431},[96,823,434],{"class":106},[96,825,826],{"class":98,"line":437},[96,827,440],{"class":106},[61,829,831,832],{"id":830},"二动态调度schedulerregistry","🎮 二、动态调度：",[26,833,834],{},"SchedulerRegistry",[23,836,837,839,840,842,843,846],{},[26,838,84],{}," 的核心在于它的 ",[26,841,834],{},"，它不仅管理着由装饰器创建的任务，还能让你在程序运行时",[448,844,845],{},"动态地创建、启动、停止或删除","任务。",[23,848,849,850,853],{},"下面的例子演示了如何",[448,851,852],{},"动态添加一个 Cron 任务","。",[87,855,857],{"className":89,"code":856,"language":91,"meta":92,"style":92},"import { Injectable, OnModuleInit } from \"@nestjs\u002Fcommon\";\nimport { SchedulerRegistry } from \"@nestjs\u002Fschedule\";\nimport { CronJob } from \"cron\";\n\n@Injectable()\nexport class DynamicTaskService {\n  constructor(private schedulerRegistry: SchedulerRegistry) {}\n\n  addDynamicCronJob(cronId: string, seconds: string) {\n    \u002F\u002F 1. 创建 CronJob 实例\n    const job = new CronJob(`${seconds} * * * * *`, () => {\n      console.log(`动态任务 ${cronId} 执行！`);\n    });\n\n    \u002F\u002F 2. 添加到注册表\n    this.schedulerRegistry.addCronJob(cronId, job);\n    job.start();\n    console.log(`动态任务 ${cronId} 已添加并启动。`);\n  }\n\n  getCronJob(cronId: string) {\n    return this.schedulerRegistry.getCronJob(cronId);\n  }\n\n  deleteCronJob(cronId: string) {\n    this.schedulerRegistry.deleteCronJob(cronId);\n    console.log(`动态任务 ${cronId} 已删除。`);\n  }\n}\n",[26,858,859,884,905,926,930,938,949,971,975,1004,1009,1050,1083,1093,1098,1104,1131,1146,1177,1182,1187,1205,1231,1236,1241,1259,1281,1311,1316],{"__ignoreMap":92},[96,860,861,863,865,867,869,872,874,876,878,880,882],{"class":98,"line":99},[96,862,103],{"class":102},[96,864,107],{"class":106},[96,866,262],{"class":110},[96,868,265],{"class":106},[96,870,871],{"class":110}," OnModuleInit",[96,873,114],{"class":106},[96,875,117],{"class":102},[96,877,121],{"class":120},[96,879,125],{"class":124},[96,881,128],{"class":120},[96,883,131],{"class":106},[96,885,886,888,890,893,895,897,899,901,903],{"class":98,"line":134},[96,887,103],{"class":102},[96,889,107],{"class":106},[96,891,892],{"class":110}," SchedulerRegistry",[96,894,114],{"class":106},[96,896,117],{"class":102},[96,898,121],{"class":120},[96,900,28],{"class":124},[96,902,128],{"class":120},[96,904,131],{"class":106},[96,906,907,909,911,914,916,918,920,922,924],{"class":98,"line":156},[96,908,103],{"class":102},[96,910,107],{"class":106},[96,912,913],{"class":110}," CronJob",[96,915,114],{"class":106},[96,917,117],{"class":102},[96,919,121],{"class":120},[96,921,13],{"class":124},[96,923,128],{"class":120},[96,925,131],{"class":106},[96,927,928],{"class":98,"line":163},[96,929,160],{"emptyLinePlaceholder":159},[96,931,932,934,936],{"class":98,"line":179},[96,933,166],{"class":106},[96,935,312],{"class":169},[96,937,315],{"class":110},[96,939,940,942,944,947],{"class":98,"line":204},[96,941,223],{"class":102},[96,943,227],{"class":226},[96,945,946],{"class":230}," DynamicTaskService",[96,948,327],{"class":106},[96,950,951,954,956,959,963,965,967,969],{"class":98,"line":211},[96,952,953],{"class":226},"  constructor",[96,955,173],{"class":106},[96,957,958],{"class":226},"private",[96,960,962],{"class":961},"s99_P"," schedulerRegistry",[96,964,186],{"class":342},[96,966,892],{"class":230},[96,968,426],{"class":106},[96,970,234],{"class":106},[96,972,973],{"class":98,"line":220},[96,974,160],{"emptyLinePlaceholder":159},[96,976,977,980,982,985,987,991,993,996,998,1000,1002],{"class":98,"line":389},[96,978,979],{"class":349},"  addDynamicCronJob",[96,981,173],{"class":106},[96,983,984],{"class":961},"cronId",[96,986,186],{"class":342},[96,988,990],{"class":989},"sZMiF"," string",[96,992,265],{"class":106},[96,994,995],{"class":961}," seconds",[96,997,186],{"class":342},[96,999,990],{"class":989},[96,1001,426],{"class":106},[96,1003,327],{"class":106},[96,1005,1006],{"class":98,"line":400},[96,1007,1008],{"class":207},"    \u002F\u002F 1. 创建 CronJob 实例\n",[96,1010,1011,1014,1018,1020,1022,1024,1026,1029,1032,1034,1037,1040,1042,1045,1048],{"class":98,"line":431},[96,1012,1013],{"class":226},"    const",[96,1015,1017],{"class":1016},"s_hVV"," job",[96,1019,343],{"class":342},[96,1021,346],{"class":342},[96,1023,913],{"class":169},[96,1025,173],{"class":182},[96,1027,1028],{"class":120},"`${",[96,1030,1031],{"class":110},"seconds",[96,1033,214],{"class":120},[96,1035,1036],{"class":124}," * * * * *",[96,1038,1039],{"class":120},"`",[96,1041,265],{"class":106},[96,1043,1044],{"class":106}," ()",[96,1046,1047],{"class":226}," =>",[96,1049,327],{"class":106},[96,1051,1052,1055,1057,1060,1062,1064,1067,1070,1072,1074,1077,1079,1081],{"class":98,"line":437},[96,1053,1054],{"class":110},"      console",[96,1056,192],{"class":106},[96,1058,1059],{"class":169},"log",[96,1061,173],{"class":182},[96,1063,1039],{"class":120},[96,1065,1066],{"class":124},"动态任务 ",[96,1068,1069],{"class":120},"${",[96,1071,984],{"class":110},[96,1073,214],{"class":120},[96,1075,1076],{"class":124}," 执行！",[96,1078,1039],{"class":120},[96,1080,426],{"class":182},[96,1082,131],{"class":106},[96,1084,1086,1089,1091],{"class":98,"line":1085},13,[96,1087,1088],{"class":106},"    }",[96,1090,426],{"class":182},[96,1092,131],{"class":106},[96,1094,1096],{"class":98,"line":1095},14,[96,1097,160],{"emptyLinePlaceholder":159},[96,1099,1101],{"class":98,"line":1100},15,[96,1102,1103],{"class":207},"    \u002F\u002F 2. 添加到注册表\n",[96,1105,1107,1109,1111,1114,1116,1119,1121,1123,1125,1127,1129],{"class":98,"line":1106},16,[96,1108,404],{"class":403},[96,1110,192],{"class":106},[96,1112,1113],{"class":110},"schedulerRegistry",[96,1115,192],{"class":106},[96,1117,1118],{"class":169},"addCronJob",[96,1120,173],{"class":182},[96,1122,984],{"class":110},[96,1124,265],{"class":106},[96,1126,1017],{"class":110},[96,1128,426],{"class":182},[96,1130,131],{"class":106},[96,1132,1134,1137,1139,1142,1144],{"class":98,"line":1133},17,[96,1135,1136],{"class":110},"    job",[96,1138,192],{"class":106},[96,1140,1141],{"class":169},"start",[96,1143,395],{"class":182},[96,1145,131],{"class":106},[96,1147,1149,1152,1154,1156,1158,1160,1162,1164,1166,1168,1171,1173,1175],{"class":98,"line":1148},18,[96,1150,1151],{"class":110},"    console",[96,1153,192],{"class":106},[96,1155,1059],{"class":169},[96,1157,173],{"class":182},[96,1159,1039],{"class":120},[96,1161,1066],{"class":124},[96,1163,1069],{"class":120},[96,1165,984],{"class":110},[96,1167,214],{"class":120},[96,1169,1170],{"class":124}," 已添加并启动。",[96,1172,1039],{"class":120},[96,1174,426],{"class":182},[96,1176,131],{"class":106},[96,1178,1180],{"class":98,"line":1179},19,[96,1181,434],{"class":106},[96,1183,1185],{"class":98,"line":1184},20,[96,1186,160],{"emptyLinePlaceholder":159},[96,1188,1190,1193,1195,1197,1199,1201,1203],{"class":98,"line":1189},21,[96,1191,1192],{"class":349},"  getCronJob",[96,1194,173],{"class":106},[96,1196,984],{"class":961},[96,1198,186],{"class":342},[96,1200,990],{"class":989},[96,1202,426],{"class":106},[96,1204,327],{"class":106},[96,1206,1208,1211,1214,1216,1218,1220,1223,1225,1227,1229],{"class":98,"line":1207},22,[96,1209,1210],{"class":102},"    return",[96,1212,1213],{"class":403}," this",[96,1215,192],{"class":106},[96,1217,1113],{"class":110},[96,1219,192],{"class":106},[96,1221,1222],{"class":169},"getCronJob",[96,1224,173],{"class":182},[96,1226,984],{"class":110},[96,1228,426],{"class":182},[96,1230,131],{"class":106},[96,1232,1234],{"class":98,"line":1233},23,[96,1235,434],{"class":106},[96,1237,1239],{"class":98,"line":1238},24,[96,1240,160],{"emptyLinePlaceholder":159},[96,1242,1244,1247,1249,1251,1253,1255,1257],{"class":98,"line":1243},25,[96,1245,1246],{"class":349},"  deleteCronJob",[96,1248,173],{"class":106},[96,1250,984],{"class":961},[96,1252,186],{"class":342},[96,1254,990],{"class":989},[96,1256,426],{"class":106},[96,1258,327],{"class":106},[96,1260,1262,1264,1266,1268,1270,1273,1275,1277,1279],{"class":98,"line":1261},26,[96,1263,404],{"class":403},[96,1265,192],{"class":106},[96,1267,1113],{"class":110},[96,1269,192],{"class":106},[96,1271,1272],{"class":169},"deleteCronJob",[96,1274,173],{"class":182},[96,1276,984],{"class":110},[96,1278,426],{"class":182},[96,1280,131],{"class":106},[96,1282,1284,1286,1288,1290,1292,1294,1296,1298,1300,1302,1305,1307,1309],{"class":98,"line":1283},27,[96,1285,1151],{"class":110},[96,1287,192],{"class":106},[96,1289,1059],{"class":169},[96,1291,173],{"class":182},[96,1293,1039],{"class":120},[96,1295,1066],{"class":124},[96,1297,1069],{"class":120},[96,1299,984],{"class":110},[96,1301,214],{"class":120},[96,1303,1304],{"class":124}," 已删除。",[96,1306,1039],{"class":120},[96,1308,426],{"class":182},[96,1310,131],{"class":106},[96,1312,1314],{"class":98,"line":1313},28,[96,1315,434],{"class":106},[96,1317,1319],{"class":98,"line":1318},29,[96,1320,440],{"class":106},[23,1322,1323,1324,1327,1328,1331],{},"类似地，通过 ",[26,1325,1326],{},"addInterval","、",[26,1329,1330],{},"addTimeout"," 等方法也能动态添加对应的任务。",[61,1333,1335],{"id":1334},"三分布式锁防止多实例重复执行","🌐 三、分布式锁：防止多实例重复执行",[23,1337,1338,1340,1341,1344,1345,1348],{},[26,1339,28],{}," 模块本身在 ",[448,1342,1343],{},"PM2"," 等集群模式下",[448,1346,1347],{},"不提供防止任务重复执行的机制","。因此，当应用同时运行多个实例时，同一个定时任务会在所有实例上被触发。",[23,1350,1351,1352,1355],{},"为了解决这个问题，实践中通常采用分布式锁（",[448,1353,1354],{},"Redis 分布式锁","）的方案。",[239,1357,1359,1360,1363,1364],{"id":1358},"️-方案一集成-nestjsbull-与-bull","🛠️ 方案一：集成 ",[26,1361,1362],{},"@nestjs\u002Fbull"," 与 ",[26,1365,1366],{},"bull",[23,1368,1369],{},"这是一种非常健壮的方案。它将任务逻辑拆分为两个部分：",[1371,1372,1373,1382],"ol",{},[445,1374,1375,1378,1379,1381],{},[448,1376,1377],{},"任务调度器","：使用 ",[26,1380,67],{}," 装饰器，但内部只是将任务信息添加到队列中。",[445,1383,1384,1387,1388,1391,1392,1397],{},[448,1385,1386],{},"队列处理器","：由 ",[26,1389,1390],{},"Bull"," 队列负责，而 ",[448,1393,1394,1396],{},[26,1395,1390],{}," 基于 Redis，天然就具备分布式特性","，能保证一个任务只被一个消费者进程处理。",[87,1399,1401],{"className":89,"code":1400,"language":91,"meta":92,"style":92},"\u002F\u002F cron-job.service.ts (调度器)\nimport { Injectable } from \"@nestjs\u002Fcommon\";\nimport { Cron } from \"@nestjs\u002Fschedule\";\nimport { InjectQueue } from \"@nestjs\u002Fbull\";\nimport { Queue } from \"bull\";\n\n@Injectable()\nexport class CronJobService {\n  constructor(@InjectQueue(\"my-queue\") private myQueue: Queue) {}\n\n  @Cron(\"*\u002F5 * * * * *\") \u002F\u002F 定时触发\n  async scheduleJob() {\n    await this.myQueue.add(\"my-job\", {\n      \u002F* 任务数据 *\u002F\n    }); \u002F\u002F 将任务加入队列\n  }\n}\n\n\u002F\u002F my-queue.processor.ts (处理器)\nimport { Processor, Process } from \"@nestjs\u002Fbull\";\nimport { Job } from \"bull\";\n\n@Processor(\"my-queue\")\nexport class MyQueueProcessor {\n  @Process(\"my-job\")\n  async handleCronJob(job: Job) {\n    console.log(\"开始处理队列中的任务...\", job.id);\n    \u002F\u002F 在这里执行真正的业务逻辑\n  }\n}\n",[26,1402,1403,1408,1428,1448,1469,1490,1494,1502,1513,1547,1551,1571,1583,1613,1618,1630,1634,1638,1642,1647,1673,1694,1698,1715,1726,1743,1763,1793,1798,1802],{"__ignoreMap":92},[96,1404,1405],{"class":98,"line":99},[96,1406,1407],{"class":207},"\u002F\u002F cron-job.service.ts (调度器)\n",[96,1409,1410,1412,1414,1416,1418,1420,1422,1424,1426],{"class":98,"line":134},[96,1411,103],{"class":102},[96,1413,107],{"class":106},[96,1415,262],{"class":110},[96,1417,114],{"class":106},[96,1419,117],{"class":102},[96,1421,121],{"class":120},[96,1423,125],{"class":124},[96,1425,128],{"class":120},[96,1427,131],{"class":106},[96,1429,1430,1432,1434,1436,1438,1440,1442,1444,1446],{"class":98,"line":156},[96,1431,103],{"class":102},[96,1433,107],{"class":106},[96,1435,289],{"class":110},[96,1437,114],{"class":106},[96,1439,117],{"class":102},[96,1441,121],{"class":120},[96,1443,28],{"class":124},[96,1445,128],{"class":120},[96,1447,131],{"class":106},[96,1449,1450,1452,1454,1457,1459,1461,1463,1465,1467],{"class":98,"line":163},[96,1451,103],{"class":102},[96,1453,107],{"class":106},[96,1455,1456],{"class":110}," InjectQueue",[96,1458,114],{"class":106},[96,1460,117],{"class":102},[96,1462,121],{"class":120},[96,1464,1362],{"class":124},[96,1466,128],{"class":120},[96,1468,131],{"class":106},[96,1470,1471,1473,1475,1478,1480,1482,1484,1486,1488],{"class":98,"line":179},[96,1472,103],{"class":102},[96,1474,107],{"class":106},[96,1476,1477],{"class":110}," Queue",[96,1479,114],{"class":106},[96,1481,117],{"class":102},[96,1483,121],{"class":120},[96,1485,1366],{"class":124},[96,1487,128],{"class":120},[96,1489,131],{"class":106},[96,1491,1492],{"class":98,"line":204},[96,1493,160],{"emptyLinePlaceholder":159},[96,1495,1496,1498,1500],{"class":98,"line":211},[96,1497,166],{"class":106},[96,1499,312],{"class":169},[96,1501,315],{"class":110},[96,1503,1504,1506,1508,1511],{"class":98,"line":220},[96,1505,223],{"class":102},[96,1507,227],{"class":226},[96,1509,1510],{"class":230}," CronJobService",[96,1512,327],{"class":106},[96,1514,1515,1517,1520,1523,1525,1527,1530,1532,1534,1536,1539,1541,1543,1545],{"class":98,"line":389},[96,1516,953],{"class":226},[96,1518,1519],{"class":106},"(@",[96,1521,1522],{"class":169},"InjectQueue",[96,1524,173],{"class":110},[96,1526,128],{"class":120},[96,1528,1529],{"class":124},"my-queue",[96,1531,128],{"class":120},[96,1533,383],{"class":110},[96,1535,958],{"class":226},[96,1537,1538],{"class":961}," myQueue",[96,1540,186],{"class":342},[96,1542,1477],{"class":230},[96,1544,426],{"class":106},[96,1546,234],{"class":106},[96,1548,1549],{"class":98,"line":400},[96,1550,160],{"emptyLinePlaceholder":159},[96,1552,1553,1555,1557,1559,1561,1564,1566,1568],{"class":98,"line":431},[96,1554,368],{"class":106},[96,1556,371],{"class":169},[96,1558,173],{"class":110},[96,1560,128],{"class":120},[96,1562,1563],{"class":124},"*\u002F5 * * * * *",[96,1565,128],{"class":120},[96,1567,383],{"class":110},[96,1569,1570],{"class":207},"\u002F\u002F 定时触发\n",[96,1572,1573,1576,1579,1581],{"class":98,"line":437},[96,1574,1575],{"class":226},"  async",[96,1577,1578],{"class":349}," scheduleJob",[96,1580,395],{"class":106},[96,1582,327],{"class":106},[96,1584,1585,1588,1590,1592,1595,1597,1600,1602,1604,1607,1609,1611],{"class":98,"line":1085},[96,1586,1587],{"class":102},"    await",[96,1589,1213],{"class":403},[96,1591,192],{"class":106},[96,1593,1594],{"class":110},"myQueue",[96,1596,192],{"class":106},[96,1598,1599],{"class":169},"add",[96,1601,173],{"class":182},[96,1603,128],{"class":120},[96,1605,1606],{"class":124},"my-job",[96,1608,128],{"class":120},[96,1610,265],{"class":106},[96,1612,327],{"class":106},[96,1614,1615],{"class":98,"line":1095},[96,1616,1617],{"class":207},"      \u002F* 任务数据 *\u002F\n",[96,1619,1620,1622,1624,1627],{"class":98,"line":1100},[96,1621,1088],{"class":106},[96,1623,426],{"class":182},[96,1625,1626],{"class":106},";",[96,1628,1629],{"class":207}," \u002F\u002F 将任务加入队列\n",[96,1631,1632],{"class":98,"line":1106},[96,1633,434],{"class":106},[96,1635,1636],{"class":98,"line":1133},[96,1637,440],{"class":106},[96,1639,1640],{"class":98,"line":1148},[96,1641,160],{"emptyLinePlaceholder":159},[96,1643,1644],{"class":98,"line":1179},[96,1645,1646],{"class":207},"\u002F\u002F my-queue.processor.ts (处理器)\n",[96,1648,1649,1651,1653,1656,1658,1661,1663,1665,1667,1669,1671],{"class":98,"line":1184},[96,1650,103],{"class":102},[96,1652,107],{"class":106},[96,1654,1655],{"class":110}," Processor",[96,1657,265],{"class":106},[96,1659,1660],{"class":110}," Process",[96,1662,114],{"class":106},[96,1664,117],{"class":102},[96,1666,121],{"class":120},[96,1668,1362],{"class":124},[96,1670,128],{"class":120},[96,1672,131],{"class":106},[96,1674,1675,1677,1679,1682,1684,1686,1688,1690,1692],{"class":98,"line":1189},[96,1676,103],{"class":102},[96,1678,107],{"class":106},[96,1680,1681],{"class":110}," Job",[96,1683,114],{"class":106},[96,1685,117],{"class":102},[96,1687,121],{"class":120},[96,1689,1366],{"class":124},[96,1691,128],{"class":120},[96,1693,131],{"class":106},[96,1695,1696],{"class":98,"line":1207},[96,1697,160],{"emptyLinePlaceholder":159},[96,1699,1700,1702,1705,1707,1709,1711,1713],{"class":98,"line":1233},[96,1701,166],{"class":106},[96,1703,1704],{"class":169},"Processor",[96,1706,173],{"class":110},[96,1708,128],{"class":120},[96,1710,1529],{"class":124},[96,1712,128],{"class":120},[96,1714,217],{"class":110},[96,1716,1717,1719,1721,1724],{"class":98,"line":1238},[96,1718,223],{"class":102},[96,1720,227],{"class":226},[96,1722,1723],{"class":230}," MyQueueProcessor",[96,1725,327],{"class":106},[96,1727,1728,1730,1733,1735,1737,1739,1741],{"class":98,"line":1243},[96,1729,368],{"class":106},[96,1731,1732],{"class":169},"Process",[96,1734,173],{"class":110},[96,1736,128],{"class":120},[96,1738,1606],{"class":124},[96,1740,128],{"class":120},[96,1742,217],{"class":110},[96,1744,1745,1747,1750,1752,1755,1757,1759,1761],{"class":98,"line":1261},[96,1746,1575],{"class":226},[96,1748,1749],{"class":349}," handleCronJob",[96,1751,173],{"class":106},[96,1753,1754],{"class":961},"job",[96,1756,186],{"class":342},[96,1758,1681],{"class":230},[96,1760,426],{"class":106},[96,1762,327],{"class":106},[96,1764,1765,1767,1769,1771,1773,1775,1778,1780,1782,1784,1786,1789,1791],{"class":98,"line":1283},[96,1766,1151],{"class":110},[96,1768,192],{"class":106},[96,1770,1059],{"class":169},[96,1772,173],{"class":182},[96,1774,128],{"class":120},[96,1776,1777],{"class":124},"开始处理队列中的任务...",[96,1779,128],{"class":120},[96,1781,265],{"class":106},[96,1783,1017],{"class":110},[96,1785,192],{"class":106},[96,1787,1788],{"class":110},"id",[96,1790,426],{"class":182},[96,1792,131],{"class":106},[96,1794,1795],{"class":98,"line":1313},[96,1796,1797],{"class":207},"    \u002F\u002F 在这里执行真正的业务逻辑\n",[96,1799,1800],{"class":98,"line":1318},[96,1801,434],{"class":106},[96,1803,1805],{"class":98,"line":1804},30,[96,1806,440],{"class":106},[239,1808,1810,1811,1814],{"id":1809},"方案二使用-redislock-装饰器","⚡ 方案二：使用 ",[26,1812,1813],{},"@RedisLock()"," 装饰器",[23,1816,1817,1818,1821],{},"对于不需要用到任务队列的场景，可以使用 ",[26,1819,1820],{},"nestjs-simple-redis-lock"," 这个第三方模块。它能以简洁的装饰器形式，为方法添加分布式锁的能力（具体参数以其官方文档为准，不同第三方库的 API 差异较大，接入前请仔细核对版本说明）。",[87,1823,1825],{"className":89,"code":1824,"language":91,"meta":92,"style":92},"import { Injectable } from \"@nestjs\u002Fcommon\";\nimport { RedisLock, RedisLockService } from \"nestjs-simple-redis-lock\";\n\n@Injectable()\nexport class MyService {\n  constructor(private readonly lockService: RedisLockService) {}\n\n  \u002F\u002F 参数依次为：锁名称、过期时间（毫秒，默认 60000）、重试间隔（毫秒）、最大重试次数\n  @RedisLock(\"critical-task\", 10000)\n  async criticalTask() {\n    console.log(\"执行关键任务，此过程被分布式锁保护\");\n    \u002F\u002F ... 任务逻辑\n  }\n}\n",[26,1826,1827,1847,1873,1877,1885,1896,1917,1921,1926,1949,1960,1981,1986,1990],{"__ignoreMap":92},[96,1828,1829,1831,1833,1835,1837,1839,1841,1843,1845],{"class":98,"line":99},[96,1830,103],{"class":102},[96,1832,107],{"class":106},[96,1834,262],{"class":110},[96,1836,114],{"class":106},[96,1838,117],{"class":102},[96,1840,121],{"class":120},[96,1842,125],{"class":124},[96,1844,128],{"class":120},[96,1846,131],{"class":106},[96,1848,1849,1851,1853,1856,1858,1861,1863,1865,1867,1869,1871],{"class":98,"line":134},[96,1850,103],{"class":102},[96,1852,107],{"class":106},[96,1854,1855],{"class":110}," RedisLock",[96,1857,265],{"class":106},[96,1859,1860],{"class":110}," RedisLockService",[96,1862,114],{"class":106},[96,1864,117],{"class":102},[96,1866,121],{"class":120},[96,1868,1820],{"class":124},[96,1870,128],{"class":120},[96,1872,131],{"class":106},[96,1874,1875],{"class":98,"line":156},[96,1876,160],{"emptyLinePlaceholder":159},[96,1878,1879,1881,1883],{"class":98,"line":163},[96,1880,166],{"class":106},[96,1882,312],{"class":169},[96,1884,315],{"class":110},[96,1886,1887,1889,1891,1894],{"class":98,"line":179},[96,1888,223],{"class":102},[96,1890,227],{"class":226},[96,1892,1893],{"class":230}," MyService",[96,1895,327],{"class":106},[96,1897,1898,1900,1902,1904,1906,1909,1911,1913,1915],{"class":98,"line":204},[96,1899,953],{"class":226},[96,1901,173],{"class":106},[96,1903,958],{"class":226},[96,1905,335],{"class":226},[96,1907,1908],{"class":961}," lockService",[96,1910,186],{"class":342},[96,1912,1860],{"class":230},[96,1914,426],{"class":106},[96,1916,234],{"class":106},[96,1918,1919],{"class":98,"line":211},[96,1920,160],{"emptyLinePlaceholder":159},[96,1922,1923],{"class":98,"line":220},[96,1924,1925],{"class":207},"  \u002F\u002F 参数依次为：锁名称、过期时间（毫秒，默认 60000）、重试间隔（毫秒）、最大重试次数\n",[96,1927,1928,1930,1933,1935,1937,1940,1942,1944,1947],{"class":98,"line":389},[96,1929,368],{"class":106},[96,1931,1932],{"class":169},"RedisLock",[96,1934,173],{"class":110},[96,1936,128],{"class":120},[96,1938,1939],{"class":124},"critical-task",[96,1941,128],{"class":120},[96,1943,265],{"class":106},[96,1945,1946],{"class":612}," 10000",[96,1948,217],{"class":110},[96,1950,1951,1953,1956,1958],{"class":98,"line":400},[96,1952,1575],{"class":226},[96,1954,1955],{"class":349}," criticalTask",[96,1957,395],{"class":106},[96,1959,327],{"class":106},[96,1961,1962,1964,1966,1968,1970,1972,1975,1977,1979],{"class":98,"line":431},[96,1963,1151],{"class":110},[96,1965,192],{"class":106},[96,1967,1059],{"class":169},[96,1969,173],{"class":182},[96,1971,128],{"class":120},[96,1973,1974],{"class":124},"执行关键任务，此过程被分布式锁保护",[96,1976,128],{"class":120},[96,1978,426],{"class":182},[96,1980,131],{"class":106},[96,1982,1983],{"class":98,"line":437},[96,1984,1985],{"class":207},"    \u002F\u002F ... 任务逻辑\n",[96,1987,1988],{"class":98,"line":1085},[96,1989,434],{"class":106},[96,1991,1992],{"class":98,"line":1095},[96,1993,440],{"class":106},[23,1995,1996,1997,1999,2000,2003,2004,2006,2007,2009],{},"总而言之，",[26,1998,28],{}," 是一款功能全面、设计优雅的任务调度模块。对于绝大多数单实例的应用，用其提供的",[448,2001,2002],{},"声明式 API"," 就足够了；而当面对多实例部署的挑战时，",[26,2005,1390],{}," 和 ",[26,2008,1820],{}," 等分布式方案也提供了成熟的应对思路。",[2011,2012,2013],"h2",{"id":2013},"参考文献",[23,2015,2016],{},"以下链接在编写时均可正常访问：",[2018,2019,2020,2033],"table",{},[2021,2022,2023],"thead",{},[2024,2025,2026,2030],"tr",{},[2027,2028,2029],"th",{},"资料",[2027,2031,2032],{},"说明",[2034,2035,2036,2049,2061],"tbody",{},[2024,2037,2038,2046],{},[2039,2040,2041],"td",{},[43,2042,2045],{"href":2043,"rel":2044},"https:\u002F\u002Fdocs.nestjs.com\u002F",[47],"NestJS 文档",[2039,2047,2048],{},"官方",[2024,2050,2051,2058],{},[2039,2052,2053],{},[43,2054,2057],{"href":2055,"rel":2056},"https:\u002F\u002Fdocs.nestjs.com\u002Ftechniques\u002Ftask-scheduling",[47],"Task scheduling",[2039,2059,2060],{},"本章主题",[2024,2062,2063,2070],{},[2039,2064,2065],{},[43,2066,2069],{"href":2067,"rel":2068},"https:\u002F\u002Fdocs.nestjs.com\u002Ffaq\u002Frequest-lifecycle",[47],"Request lifecycle",[2039,2071,2072],{},"执行顺序",[2074,2075,2076],"style",{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":92,"searchDepth":134,"depth":134,"links":2078},[2079,2081,2083,2084],{"id":63,"depth":156,"text":2080},"一、声明式任务：@Cron, @Interval, @Timeout",{"id":830,"depth":156,"text":2082},"🎮 二、动态调度：SchedulerRegistry",{"id":1334,"depth":156,"text":1335},{"id":2013,"depth":134,"text":2013},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fcron","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fcron.md","md",{"items":2090},[2091,2095,2099,2103,2107,2111],{"path":2092,"title":2093,"description":2094,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth","认证与授权","认证（Authentication）：确认「你是谁」（如 JWT、Session）。 - 授权（Authorization）：确认「你能做什么」（如 RBAC、策略检查）。",{"path":2096,"title":2097,"description":2098,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcache","缓存","@nestjs\u002Fcache-manager 统一缓存 API，存储实现可插拔。",{"path":2100,"title":2101,"description":2102,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fservice","提供者与服务","Service 是最常见的 Provider，封装业务逻辑。见 module。",{"path":2104,"title":2105,"description":2106,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fguards","守卫 (Guards) 与授权","守卫决定是否放行请求，常用于认证与授权。见 auth。",{"path":2108,"title":2109,"description":2110,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig","配置管理（Config 模块）","@nestjs\u002Fconfig 加载 .env 并提供 ConfigService。见 工程化 env。",{"path":2112,"title":2113,"description":2114,"category":10,"score":204},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdatabase","数据库集成（以 TypeORM 为例）","Nest 通过 @nestjs\u002Ftypeorm 等包集成 ORM；生产环境用 migration，慎用 synchronize。亦可选用 Prisma、MikroORM 等（见 官方 Database）。",{"series":2116,"title":2117,"items":2118,"index":179,"prev":2179,"next":2180},"booklet\u002Fend\u002Fnestjs\u002Fnew","new",[2119,2121,2125,2127,2129,2133,2134,2138,2140,2144,2148,2150,2154,2158,2162,2166,2169,2173,2175],{"path":2092,"title":2093,"date":2120,"pinned":15},"2025-12-23",{"path":2122,"title":2123,"date":2124,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fbase","NestJS 基础","2021-03-09",{"path":2096,"title":2097,"date":2126,"pinned":15},"2025-07-04",{"path":2108,"title":2109,"date":2128,"pinned":15},"2023-08-26",{"path":2130,"title":2131,"date":2132,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller","控制器与路由","2020-07-18",{"path":7,"title":8,"date":14,"pinned":15},{"path":2135,"title":2136,"date":2137,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcustom-decorators","自定义装饰器","2021-09-19",{"path":2112,"title":2113,"date":2139,"pinned":15},"2023-05-26",{"path":2141,"title":2142,"date":2143,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdeploy","部署与运维","2023-01-24",{"path":2145,"title":2146,"date":2147,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Ferror","异常处理","2020-01-28",{"path":2104,"title":2105,"date":2149,"pinned":15},"2023-09-09",{"path":2151,"title":2152,"date":2153,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fintercept","拦截器 (Interceptors)","2021-04-26",{"path":2155,"title":2156,"date":2157,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Flogger","日志记录","2022-10-10",{"path":2159,"title":2160,"date":2161,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmiddleware","中间件 (Middleware)","2021-11-26",{"path":2163,"title":2164,"date":2165,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmodule","模块系统与依赖注入","2022-06-15",{"path":2167,"title":2168,"date":2132,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fpipes","管道 (Pipes) 与数据验证",{"path":2170,"title":2171,"date":2172,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fqueue","队列（Bull）","2021-07-01",{"path":2100,"title":2101,"date":2174,"pinned":15},"2024-12-02",{"path":2176,"title":2177,"date":2178,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fwebsocket","WebSocket（网关）","2022-12-14",{"path":2130,"title":2131,"date":2132,"pinned":15},{"path":2135,"title":2136,"date":2137,"pinned":15}]