// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. //go:build script // +build script package js import ( "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/utils/goman" memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" "github.com/iwind/TeaGo/Tea" "time" ) type IsolatePool struct { hotIsolates []*Isolate // 当前正在使用的isolates coldIsolates []*Isolate // 等待回收的isolates coldTicker *time.Ticker coldIndex int maxSize int index int } func NewIsolatePool(maxSize int) (*IsolatePool, error) { if maxSize <= 0 { maxSize = 1 } if maxSize > 512 { maxSize = 512 } var pool = &IsolatePool{ hotIsolates: make([]*Isolate, maxSize), maxSize: maxSize, } err := pool.init() if err != nil { remotelogs.Error("SCRIPT", "create isolate pool failed: "+err.Error()) pool.Dispose() return nil, err } return pool, nil } func NewAutoIsolatePool() (*IsolatePool, error) { // 每 512M 内存一个isolate var totalMemory = memutils.SystemMemoryGB() var count = totalMemory * 2 if count <= 0 { count = 2 } // 防止在有些系统上OOM if count > 256 { count = 256 } return NewIsolatePool(count) } func (this *IsolatePool) init() error { for i := 0; i < this.maxSize; i++ { isolate, err := NewIsolate() if err != nil { return err } this.hotIsolates[i] = isolate } // run init functions if len(this.hotIsolates) > 0 { var isolate = this.hotIsolates[0] ctx, err := isolate.GetContext() if err == nil { _, err = ctx.Run("gojs.runOnce()", "utils.js") if err != nil { remotelogs.Error("SCRIPT", "run once functions failed: "+err.Error()) } isolate.PutContext(ctx) } } this.coldTicker = time.NewTicker(5 * time.Second) if Tea.IsTesting() { this.coldTicker = time.NewTicker(5 * time.Second) } goman.New(func() { for range this.coldTicker.C { this.tick() } }) return nil } func (this *IsolatePool) tick() { // dispose cold isolates if len(this.coldIsolates) > 0 { var newIndex = -1 for index, coldIsolate := range this.coldIsolates { if coldIsolate.IsUsing() { break } newIndex = index coldIsolate.Dispose() } if newIndex >= 0 { this.coldIsolates = this.coldIsolates[newIndex+1:] } } // add new isolate if this.coldIndex > this.maxSize-1 { this.coldIndex = 0 } var oldIsolate = this.hotIsolates[this.coldIndex] if oldIsolate.OverUses() { newIsolate, err := NewIsolate() if err != nil { remotelogs.Error("SCRIPT", "create isolate failed: "+err.Error()) return } this.hotIsolates[this.coldIndex] = newIsolate this.coldIsolates = append(this.coldIsolates, oldIsolate) } this.coldIndex++ } func (this *IsolatePool) GetContext() (*Context, error) { this.index++ // 不需要加锁,这里没必要非常严格 var index = this.index if index >= this.maxSize { index = 0 this.index = 0 } // TODO 未来实现循环查找可用的context,防止因单个context执行时间过长,阻塞整个isolate return this.hotIsolates[index].GetContext() } func (this *IsolatePool) PutContext(ctx *Context) { if ctx != nil { ctx.Isolate().PutContext(ctx) } } func (this *IsolatePool) MaxSize() int { return this.maxSize } func (this *IsolatePool) Dispose() { for _, isolate := range this.hotIsolates { isolate.Dispose() } }