162 lines
3.3 KiB
Go
162 lines
3.3 KiB
Go
// 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()
|
||
}
|
||
}
|