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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM