Files
waf-platform/EdgeNode/internal/js/isolate_pool.go
2026-02-04 20:27:13 +08:00

162 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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()
}
}