q-koa 13.8.58 → 13.8.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/core/app.js +188 -147
  2. package/package.json +1 -1
package/core/app.js CHANGED
@@ -458,154 +458,88 @@ class APP {
458
458
  })
459
459
  dirList = dirList.filter((item) => includeList.some((i) => i.app === item))
460
460
 
461
- let dbFlag = false
462
- for (const appName of dirList) {
463
- console.log(
464
- chalk.green(`==================== 启动 ${appName} ====================`)
465
- )
466
- const routerConfig = {
467
- prefix: `/${appName}`,
468
- }
469
- const router = new Router(routerConfig)
470
- Serve(
471
- path.resolve(
472
- __dirname,
473
- `${rootDirectory}/${APP_DIR}/${appName}/public`
474
- ),
475
- router
476
- )
477
-
478
- const { push } = require('./file/plugins/log/service')
461
+ const { push } = require('./file/plugins/log/service')
462
+
463
+ // 所有 app 共用同一份 redis 配置,全局只连接一对 client+subscriber 供各 app 复用,
464
+ // 避免每个 app 各自建一套连接(2N 条连接 -> 2 条)
465
+ let sharedRedisClient = null
466
+ let sharedSubscriber = null
467
+ let redisError = null
468
+ if (
469
+ this.config.redis &&
470
+ this.config.redis.host &&
471
+ this.config.redis.port
472
+ ) {
479
473
  try {
480
- const target = includeList.find((i) => i.app === appName)
481
- const dbName =
482
- (target ? target.db : '') || this.config.db.name || appName
483
- const db = connectDatabase(dbName)(this.config.db)
484
- if (!is_dev) {
485
- if (!dbFlag) {
486
- console.log(chalk.green(`${appName}检查数据库${dbName}`))
487
- await db.authenticate()
488
- dbFlag = true
489
- }
490
- } else {
491
- console.log(chalk.green(`${appName}检查数据库${dbName}`))
492
- await db.authenticate()
493
- }
494
- const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
495
- const configFile = path.resolve(__dirname, configPath)
496
- const configExist = fs.existsSync(configFile)
497
-
498
- if (
499
- this.config.redis &&
500
- this.config.redis.host &&
501
- this.config.redis.port
502
- ) {
503
- let subscriber
504
- let client
505
- try {
506
- client = redis.createClient({
507
- socket: {
508
- host: this.config.redis.host,
509
- port: this.config.redis.port,
510
- reconnectStrategy: false,
511
- },
512
- ...(this.config.redis.database
513
- ? {
514
- database: this.config.redis.database,
515
- }
516
- : {}),
517
- })
518
-
519
- client.on('error', (err) => console.log('Redis Client Error', err))
520
-
521
- await client.connect()
522
-
523
- subscriber = client.duplicate()
524
- await subscriber.connect()
525
-
526
- if (!is_dev) {
527
- push({
528
- appName,
529
- message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
530
- })
531
- }
532
- } catch (e) {
533
- client = null
534
-
535
- push({
536
- appName,
537
- message: `${appName}连接redis失败,${e.message}`,
538
- })
539
- }
540
-
541
- this.app[appName] = {
542
- sequelize: db,
543
- ...(client
544
- ? {
545
- redisClient: client,
546
- subscriber,
547
- }
548
- : {}),
549
- appConfig: _.defaultsDeep(
550
- configExist ? require(path.resolve(__dirname, configPath)) : {},
551
- this.config.app,
552
- { appName, ip: this.config.ip, port: this.port }
553
- ),
554
- }
555
- } else {
556
- this.app[appName] = {
557
- sequelize: db,
558
- redisClient: redisVirtual,
559
- appConfig: _.defaultsDeep(
560
- configExist ? require(path.resolve(__dirname, configPath)) : {},
561
- this.config.app,
562
- { appName, ip: this.config.ip, port: this.port }
563
- ),
564
- }
565
- }
474
+ sharedRedisClient = redis.createClient({
475
+ socket: {
476
+ host: this.config.redis.host,
477
+ port: this.config.redis.port,
478
+ reconnectStrategy: false,
479
+ },
480
+ ...(this.config.redis.database
481
+ ? {
482
+ database: this.config.redis.database,
483
+ }
484
+ : {}),
485
+ })
566
486
 
567
- // const proxyConfig = _.get(this.app[appName], 'appConfig.proxy');
487
+ sharedRedisClient.on('error', (err) =>
488
+ console.log('Redis Client Error', err)
489
+ )
568
490
 
569
- // if (proxyConfig) {
570
- // this.app.use(proxy(`/${appName}`, proxyConfig));
571
- // }
491
+ await sharedRedisClient.connect()
572
492
 
573
- const initFileConfig = _.get(
574
- this.app[appName],
575
- 'appConfig.initFile',
576
- false
577
- )
578
- if (initFileConfig) {
579
- this.initFile(appName)
580
- }
581
- await this.initPlugin(appName)
493
+ sharedSubscriber = sharedRedisClient.duplicate()
494
+ await sharedSubscriber.connect()
495
+ } catch (e) {
496
+ sharedRedisClient = null
497
+ sharedSubscriber = null
498
+ redisError = e
499
+ }
500
+ }
582
501
 
583
- const initEvent = _.get(this.app[appName], 'appConfig.initEvent', false)
584
- if (initEvent) {
585
- this.app[appName].event = new EventEmitter()
502
+ // 数据库连接校验:生产只校验一次(保持原有行为);开发环境按 dbName 去重,同库只校验一次
503
+ let prodAuthPromise = null
504
+ const dbAuthCache = new Map()
505
+ const authenticateDb = (db, dbName, appName) => {
506
+ if (!is_dev) {
507
+ if (!prodAuthPromise) {
508
+ console.log(chalk.green(`${appName}检查数据库${dbName}`))
509
+ prodAuthPromise = db.authenticate()
586
510
  }
511
+ return prodAuthPromise
512
+ }
513
+ if (!dbAuthCache.has(dbName)) {
514
+ console.log(chalk.green(`${appName}检查数据库${dbName}`))
515
+ dbAuthCache.set(dbName, db.authenticate())
516
+ }
517
+ return dbAuthCache.get(dbName)
518
+ }
587
519
 
588
- const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
589
- max: 100,
590
- maxAge: 1000 * 60 * 60 * 24,
591
- })
592
-
593
- this.app[appName].cache = new LRUCache({
594
- max: cacheConfig.max,
595
- ttl: cacheConfig.maxAge,
520
+ const routers = new Map()
521
+
522
+ // 各 app 的重初始化(数据库校验/插件/模型等)互相独立,并行执行
523
+ const results = await Promise.allSettled(
524
+ dirList.map((appName) =>
525
+ this._initSingleApp(appName, includeList, {
526
+ push,
527
+ routers,
528
+ sharedRedisClient,
529
+ sharedSubscriber,
530
+ redisError,
531
+ authenticateDb,
596
532
  })
533
+ )
534
+ )
535
+ const failed = results.find((r) => r.status === 'rejected')
536
+ if (failed) {
537
+ throw failed.reason
538
+ }
597
539
 
598
- await this.initModel(appName)
599
- // const models = Object.keys(this.app[appName].model).join(' / ')
600
- } catch (e) {
601
- push({
602
- appName,
603
- message: `${appName}数据库连接失败`,
604
- })
605
- console.log(`${appName}数据库连接失败`)
606
- console.log(e)
607
- throw new Error(e.message)
608
- }
540
+ // 按原有顺序挂载路由,保证中间件注册顺序确定
541
+ for (const appName of dirList) {
542
+ const router = routers.get(appName)
609
543
  if (
610
544
  this.app[appName] &&
611
545
  this.app[appName].appConfig &&
@@ -654,10 +588,121 @@ class APP {
654
588
  starter.emit('loadAll')
655
589
  }
656
590
 
657
- initFile(appName) {
591
+ async _initSingleApp(appName, includeList, ctx) {
592
+ const {
593
+ push,
594
+ routers,
595
+ sharedRedisClient,
596
+ sharedSubscriber,
597
+ redisError,
598
+ authenticateDb,
599
+ } = ctx
600
+ console.log(
601
+ chalk.green(`==================== 启动 ${appName} ====================`)
602
+ )
603
+ const routerConfig = {
604
+ prefix: `/${appName}`,
605
+ }
606
+ const router = new Router(routerConfig)
607
+ Serve(
608
+ path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}/public`),
609
+ router
610
+ )
611
+ routers.set(appName, router)
612
+
613
+ try {
614
+ const target = includeList.find((i) => i.app === appName)
615
+ const dbName = (target ? target.db : '') || this.config.db.name || appName
616
+ const db = connectDatabase(dbName)(this.config.db)
617
+ await authenticateDb(db, dbName, appName)
618
+
619
+ const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
620
+ const configFile = path.resolve(__dirname, configPath)
621
+ const configExist = fs.existsSync(configFile)
622
+ const appConfig = _.defaultsDeep(
623
+ configExist ? require(configFile) : {},
624
+ this.config.app,
625
+ { appName, ip: this.config.ip, port: this.port }
626
+ )
627
+
628
+ if (sharedRedisClient) {
629
+ if (!is_dev) {
630
+ push({
631
+ appName,
632
+ message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
633
+ })
634
+ }
635
+ this.app[appName] = {
636
+ sequelize: db,
637
+ redisClient: sharedRedisClient,
638
+ subscriber: sharedSubscriber,
639
+ appConfig,
640
+ }
641
+ } else if (
642
+ this.config.redis &&
643
+ this.config.redis.host &&
644
+ this.config.redis.port
645
+ ) {
646
+ // redis 配置了但连接失败:保持原有行为(不挂 redisClient),仅记录日志
647
+ push({
648
+ appName,
649
+ message: `${appName}连接redis失败,${
650
+ redisError ? redisError.message : '未知错误'
651
+ }`,
652
+ })
653
+ this.app[appName] = {
654
+ sequelize: db,
655
+ appConfig,
656
+ }
657
+ } else {
658
+ this.app[appName] = {
659
+ sequelize: db,
660
+ redisClient: redisVirtual,
661
+ appConfig,
662
+ }
663
+ }
664
+
665
+ const initFileConfig = _.get(
666
+ this.app[appName],
667
+ 'appConfig.initFile',
668
+ false
669
+ )
670
+ if (initFileConfig) {
671
+ await this.initFile(appName)
672
+ }
673
+ await this.initPlugin(appName)
674
+
675
+ const initEvent = _.get(this.app[appName], 'appConfig.initEvent', false)
676
+ if (initEvent) {
677
+ this.app[appName].event = new EventEmitter()
678
+ }
679
+
680
+ const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
681
+ max: 100,
682
+ maxAge: 1000 * 60 * 60 * 24,
683
+ })
684
+
685
+ this.app[appName].cache = new LRUCache({
686
+ max: cacheConfig.max,
687
+ ttl: cacheConfig.maxAge,
688
+ })
689
+
690
+ await this.initModel(appName)
691
+ } catch (e) {
692
+ push({
693
+ appName,
694
+ message: `${appName}数据库连接失败`,
695
+ })
696
+ console.log(`${appName}数据库连接失败`)
697
+ console.log(e)
698
+ throw new Error(e.message)
699
+ }
700
+ }
701
+
702
+ async initFile(appName) {
658
703
  const dirExist = fsExtra.existsSync(path.resolve(__dirname, './file'))
659
704
  if (dirExist) {
660
- fsExtra.copySync(
705
+ await fsExtra.copy(
661
706
  path.resolve(__dirname, './file'),
662
707
  path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}`),
663
708
  {
@@ -964,12 +1009,8 @@ class APP {
964
1009
  a[b] = []
965
1010
  return a
966
1011
  }, {})
967
- this.app[appName] = _.defaultsDeep(
968
- {
969
- include,
970
- },
971
- this.app[appName]
972
- )
1012
+ // 直接赋值,避免对整个 app 对象做 defaultsDeep 深合并(且保持对象引用不变)
1013
+ this.app[appName].include = include
973
1014
  for (const model of modelList) {
974
1015
 
975
1016
  // if (!this.app[appName].include || !this.app[appName].include[model]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q-koa",
3
- "version": "13.8.58",
3
+ "version": "13.8.59",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {