zoukankan      html  css  js  c++  java
  • k6 集成goja 的部分集成说明

    k6 对于goja 的集成还是比较强大的,支持了es6(基于babel 的编译能力),同时对于默认的js engine 进行了扩展(基于core-js)
    同时对于require以及module ,exports 也是支持的,只是对于exports 是自己定义了变量,同时对于一些内置的模块(k6 开头的进行了
    特殊处理,直接内置),对于require 的支持是基于了 github.com/spf13/afero 一个很不错的通用文件系统抽象以及自定义了一个require
    函数,同时暴露到goja js engine 的全局对象中,同时k6 扩展了console的支持

    es6编译处理

    会将babel 的Transform 映射为一个golang 函数,编译就是利用了bable 的能力,为了防止多起加载,使用了sync.Once
    同时会通过goja 的parser 解析代码为ast然后在编译(为了兼容,支持了多种模式的编译)

     
    func (c *Compiler) Compile(src, filename, pre, post string,
        strict bool, compatMode lib.CompatibilityMode) (*goja.Program, string, error) {
        code := pre + src + post
        ast, err := parser.ParseFile(nil, filename, code, 0)
        if err != nil {
            if compatMode == lib.CompatibilityModeExtended {
                code, _, err = c.Transform(src, filename)
                if err != nil {
                    return nil, code, err
                }
                // the compatibility mode "decreases" here as we shouldn't transform twice
                return c.Compile(code, filename, pre, post, strict, lib.CompatibilityModeBase)
            }
            return nil, code, err
        }
        pgm, err := goja.CompileAST(ast, strict)
        return pgm, code, err
    }

    babel 浏览器集成方法(一个参考)

    var output = Babel.transform(content, { presets: ['env'] }).code;

    require 支持

    实际上就是利用了js hack 的模式
    https://github.com/loadimpact/k6/blob/master/js/initcontext.go#L117

     
    func (i *InitContext) Require(arg string) goja.Value {
        switch {
        case arg == "k6", strings.HasPrefix(arg, "k6/"):
            // Builtin or external modules ("k6", "k6/*", or "k6/x/*") are handled
            // specially, as they don't exist on the filesystem. This intentionally
            // shadows attempts to name your own modules this.
            v, err := i.requireModule(arg)
            if err != nil {
                common.Throw(i.runtime, err)
            }
            return v
        default:
            // Fall back to loading from the filesystem.
            v, err := i.requireFile(arg)
            if err != nil {
                common.Throw(i.runtime, err)
            }
            return v
        }
    }

    spf13/afero 参考使用

    package main
    import (
        "io/ioutil"
        "log"
        "net/http"
        "github.com/spf13/afero"
    )
    func httpFs() {
        var appFs = afero.NewMemMapFs()
        appFs.Mkdir("/", 0755)
        fh, _ := appFs.Create("/demoapp.js")
        fh.WriteString("This is a test")
        fh.Close()
        httpFs := afero.NewHttpFs(appFs)
        fileserver := http.FileServer(httpFs.Dir("/"))
        http.Handle("/", fileserver)
        http.ListenAndServe(":8090", nil)
    }
    func main() {
        httpFs()
    }

    为了方便跨平台的支持,提供了一个通用的基于afero 的文件系统实现,同时支持网络文件系统

    func CreateFilesystems() map[string]afero.Fs {
        // We want to eliminate disk access at runtime, so we set up a memory mapped cache that's
        // written every time something is read from the real filesystem. This cache is then used for
        // successive spawns to read from (they have no access to the real disk).
        // Also initialize the same for `https` but the caching is handled manually in the loader package
        osfs := afero.NewOsFs()
        if runtime.GOOS == "windows" {
            // This is done so that we can continue to use paths with /|"" through the code but also to
            // be easier to traverse the cachedFs later as it doesn't work very well if you have windows
            // volumes
            osfs = fsext.NewTrimFilePathSeparatorFs(osfs)
        }
        return map[string]afero.Fs{
            "file":  fsext.NewCacheOnReadFs(osfs, afero.NewMemMapFs(), 0),
            "https": afero.NewMemMapFs(),
        }
    } 

    网络文件加载模式也是支持的,首先第一次是进行http 请求缓存,然后进行读取(基于NewMemMapFs)

    if scheme == "https" {
            var finalModuleSpecifierURL = &url.URL{}
            switch {
            case moduleSpecifier.Opaque != "": // This is loader
                finalModuleSpecifierURL, err = resolveUsingLoaders(logger, moduleSpecifier.Opaque)
                if err != nil {
                    return nil, err
                }
            case moduleSpecifier.Scheme == "":
                logger.Warningf(`The moduleSpecifier "%s" has no scheme but we will try to resolve it as remote module. `+
                    `This will be deprecated in the future and all remote modules will `+
                    `need to explicitly use "https" as scheme.`, originalModuleSpecifier)
                *finalModuleSpecifierURL = *moduleSpecifier
                finalModuleSpecifierURL.Scheme = scheme
            default:
                finalModuleSpecifierURL = moduleSpecifier
            }
            var result *SourceData
            result, err = loadRemoteURL(logger, finalModuleSpecifierURL)
            if err == nil {
                result.URL = moduleSpecifier
                // TODO maybe make an afero.Fs which makes request directly and than use CacheOnReadFs
                // on top of as with the `file` scheme fs
                _ = afero.WriteFile(filesystems[scheme], pathOnFs, result.Data, 0644)
                return result, nil
            }
            if moduleSpecifier.Scheme == "" || moduleSpecifier.Opaque == "" {
                // we have an error and we did remote module resolution without a scheme
                // let's write the coolest error message to try to help the lost soul who got to here
                return nil, noSchemeRemoteModuleResolutionError{err: err, moduleSpecifier: originalModuleSpecifier}
            }
            return nil, errors.Errorf(httpsSchemeCouldntBeLoadedMsg, originalModuleSpecifier, finalModuleSpecifierURL, err)
        }

    自定义模块的开发

    k6已经内置了一些以k6开头的模块,使用了类似databasedriver 的开发模式,一个简单参考

    func init() {
        modules.Register("k6", New())
    }
    type K6 struct{}
    // ErrGroupInInitContext is returned when group() are using in the init context
    var ErrGroupInInitContext = common.NewInitContextError("Using group() in the init context is not supported")
    // ErrCheckInInitContext is returned when check() are using in the init context
    var ErrCheckInInitContext = common.NewInitContextError("Using check() in the init context is not supported")
    func New() *K6 {
        return &K6{}
    }

    说明

    通过学习k6集成goja 的模式,发现比goja 社区的模块模式简单点(但是核心没变),提供k6支持es6 的模式设计的挺不错的
    (以前自己有基于browserify +babel 编译模式集成的模式,发现不是很好,而且没有k6集成模式支持的特性多,而且简单)

    参考资料

    https://github.com/loadimpact/k6/blob/master/js/common/bridge.go
    https://github.com/loadimpact/k6/blob/master/js/initcontext.go#L117
    https://godoc.org/github.com/dop251/goja#Runtime.ToValue
    https://github.com/spf13/afero
    https://github.com/loadimpact/k6/blob/master/js/compiler/compiler.go#L74
    https://github.com/zloirock/core-js
    https://babeljs.io/docs/en/babel-standalone.html
    https://github.com/loadimpact/k6/blob/master/loader/loader.go#L205

  • 相关阅读:
    微信公众平台开发学习系列(四):微信分享内容修改与分享之后才能访问某页面
    微信公众平台开发学习系列(三):网页授权获取用户基本信息
    微信公众平台开发学习系列(二):微信公众平台接收消息与发送消息
    微信公众平台开发学习系列(一):公众平台测试号申请与自定义菜单创建
    EF结合SqlBulkCopy在项目中的使用
    用python实现【五猴分桃】问题
    基于pandas进行数据预处理
    【转载】各类排序算法的对比及实现
    牛客网2017校招真题在线编程之合唱团问题——动态规划问题首秀
    动态规划(DP)算法
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/14236601.html
Copyright © 2011-2022 走看看