midwayjs / midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
https://www.midwayjs.org/
MIT License
7.4k stars 576 forks source link

node-zone 通过引入该模块,解决了这边用nodejs开发的一个痛点。参数嵌套传递。想咨询是否有什么没考虑到的冲突. #623

Closed caihaibin1991 closed 4 years ago

caihaibin1991 commented 4 years ago

https://www.npmjs.com/package/node-zone 通过该模块,实现了变量保存到"线程作用域"中,js在任意嵌套的情况下,可以通过简单的current.data.xx形式读取变量。 这边测试目前没发现什么问题,想了解下,这块东西是否会出现什么其它的冲突问题?该模块使用了 async_hooks.

caihaibin1991 commented 4 years ago

同时,该方式,在js转型成ts时,变量可以正常传递,解决session值无法在js里获取的问题。

caihaibin1991 commented 4 years ago

// import { current } from 'node-zone'
const {current} = require('node-zone');
// a zone has a name, a parent, and data
console.log(
    current.name,   // "<root>"
    current.parent, // null
    current.data    // { __proto__: null }
)

// create a new child zone
const myZone = current.fork('my zone')

console.log(
    myZone.name,   // "my zone"
    myZone.parent, // current
    myZone.data    // { __proto__: current.data }
)

// run some code in it
myZone.run(() => {
    console.log(current.name) // "my zone"

    // zone is preserved in async functions
    process.nextTick(() => {
        console.log(current.name) // "my zone"
    })
});

(async function () {

    // return;
// run some code in it
     myZone.run(async () => {
        const {current} = require('node-zone');
        console.log(current.name) // "my zone"

        // zone is preserved in async functions
        process.nextTick(() => {
            console.log(current.name) // "my zone"
        })
    })
})();
console.log(current.name) // "<root>"

目前是直接在中间件里使用,然后全局任意地方直接从“线程作用域”中取值。减少参数传递

caihaibin1991 commented 4 years ago

/src/app/middleware/commonRequest.ts

// import {Context} from 'egg';
import * as querystring from 'querystring';
import {v4} from 'uuid';

import {Context, EggAppConfig} from 'egg';

export default function (options: EggAppConfig['uuid']) {
    return async (ctx: Context, next: any): Promise<void> => {
        const name = ctx.app.config.uuid.name;
        let uuid = ctx.cookies.get(name, {signed: true});
        if (!uuid || uuid === 'null') {
            uuid = v4();
            ctx.cookies.set(name, uuid, {
                signed: true,
                maxAge: options.maxAge,
            });
        }
        ctx.locals.uuid = uuid;
        ctx.session._id = uuid;

        //解析出get的参数
        let query = ctx.originalUrl;
        if (!ctx.params) {
            ctx.params = {};
        }
        if (globalThis.common.stringHelper.stristr(query, '?')) {
            query = query.replace(/^.+\?/, '');
            let params = querystring.parse(query);
            ctx.params = Object.assign(params, ctx.params);
        }
        ctx.params = Object.assign(ctx.params, ctx.request.body);
        //取消注释会报错
        //@ts-ignore
        //ctx.db = ctx.model.Admin['SystemConfig'].db.db;
        try {
            const {current} = require('node-zone');
            let zone = current.fork(ctx.getSessReqKey());
            await zone.run(async function () {
                await next();
            });
            //全局日志记录
            const service = await ctx.requestContext.getAsync('admin/AdminLogService');
            service.record();
        } catch (e) {
            e = globalThis.gctx.parse(e);
            if (e && e.message.indexOf('Validation Failed') != -1 && e.errors && globalThis.common.isArray(e.errors) && e.errors.length) {
                return ctx.failure(ctx.__(e.errors[0].field) + '' + e.errors[0].message, e.errors);
            }
            //全局日志记录
            const service = await ctx.requestContext.getAsync('admin/AdminLogService');
            service.record(e.message);
            //异常捕获
            throw e;
        }
        if (!ctx.body) {
            throw new Error('响应内容为空或不存在,请检查控制器内容!');
        }
    };
}
czy88840616 commented 4 years ago

没太明白。。。这个希望达成啥效果?

caihaibin1991 commented 4 years ago

当前的上下文全部是围绕ctx, 但是在js重写成ts过程中,原来封装的大量库,大量函数,是无法直接读取到这个变量的。这样会出现个问题,如果js逐渐调整成ts里,一次性需要改动的工作量太大。很多地方需要扩展一个上下文变量。如果使用node-zone模块,可以在中间件启动这个“作用域”,任何地方直接: 存值:

const {current} = require('node-zone');
current.data['my_data'] =1;

取值:

const {current} = require('node-zone');
console.log(current.data['my_data']);

目前测试了瞬间1000次异步请求,多层嵌套混合读写,都能正常写和读。异步和同步线程,都可以很好的传递变量到深层。真的方便了很多。 这边在尝试启用中,想咨询async_hooks是否有什么影响这边没注意到。然后如果这个东西确实可以这么使用。这边再封装一个中间件进行读写。

czy88840616 commented 4 years ago

目前 async_hooks 有性能影响,20%~50% 不等,中后台问题不大。

caihaibin1991 commented 4 years ago

好的。谢谢。这边做下特殊处理。封装成单独中间件。避免没必要的性能消耗。[握手]

czy88840616 commented 4 years ago

midway 之前在 pandora 时期也是用的 async_hooks,后来觉得性能还是有一些影响,链路追踪设计了 IoC 的 requestContainer,减少了性能消耗。

caihaibin1991 commented 4 years ago

好的。谢谢