返回信息流想要在go里执行一些脚本语言。
1. 需要sandbox,因为脚本是用户写的。
2. 逻辑很简单,不需要很多library
3. lua、js之类的都行,更倾向js。
4. 并发执行脚本,脚本里会调go的函数发http请求
搜了一下基本2类:
1. 用cgo调用成熟的脚本解析器
js : https://github.com/ry/v8worker (这个居然是nodejs的作者写的。。)
2. 用go实现解析器
js:https://github.com/robertkrimen/otto
lua:https://github.com/yuin/gopher-lua
昨天简单测试了一下,v8worker那个办法,因为每次调用c好像都会启动一个线程,占用内存较大
otto的话cpu占用很多,但是内存很小。有看到别人说go不太适合做解析器,木有优化。几个go写的解析器执行效率好像都不高。
现在自己比较倾向于js使用otto或者v8worker了。主要是求稳定。
otto比lua的解析器感觉更新活跃一些。
v8worker cgo 那一层代码很少,应该会比较好维护。
求过来人分享经验。
这是一条镜像帖。来源:北邮人论坛 / golang / #554同步于 2016/8/20
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖
求教:Go 嵌入脚本语言(js,lua)
aiquestion
2016/8/20镜像同步7 回复
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
https://github.com/yuin/gopher-lua/issues/52
他说的。。。而且看benchmark耗时基本是c解析器的10倍。。
【 在 asm 的大作中提到: 】
: 为什么go不适合做解析器?
如果对脚本的功能需求不是很复杂的话,可不可以自己写一套DSL呢?
【 在 aiquestion 的大作中提到: 】
: 想要在go里执行一些脚本语言。
: 1. 需要sandbox,因为脚本是用户写的。
: 2. 逻辑很简单,不需要很多library
: ...................
恩,想过。
自己写成本比较高,加上从来没搞过又担心不稳定。
而且最后希望能让用户写脚本的,所以最好是js。。
【 在 asm 的大作中提到: 】
: 如果对脚本的功能需求不是很复杂的话,可不可以自己写一套DSL呢?
:
你说v8worker每调用一个C函数都创建一个线程,真的确定吗?这听起来很愚蠢,想不出谁会这样做(除了我上次要在JVM上实现含C frame的coroutine不得不这样做以外。但JS并没有coroutine,甚至还是单线程的)。你确定内存占用大的原因是“创建线程”而不是JIT编译器吗?另外v8的垃圾回收是一个generational GC,很快,但会预留内存,即使对象并没有那么多也会预留一些堆空间,以备将来分配。你是怎么看“内存占用”的?看到的会不会只是v8预留的gc heap空间?操作系统无法分辨出堆里面的空间是被占用的还是没被占用的,操作系统只知道“用户要求分配了这整块堆内存”。
看你的用途,如果逻辑很简单,又只是扩展你的主程序,做一些网络调用,那么即使不要JIT,性能也应该可以(v8只有JIT)。
看v8worker的叙述(https://github.com/ry/v8worker)“V8 will block a thread (goroutine) only while it computes javascript - it has no "syscalls" other than sending and receiving messages to Go.”这意味着如果用户写一个死循环,这个goroutine就阻塞了(不太清楚这对sandbox意味着什么。你需要终止“运行时间过长的js程序”吗)。
我倒是建议使用go实现的解释器。如果解释器本身就是一个go程序,那么一个解释器线程就是一个goroutine,等于直接把多线程交给go runtime了,还可以把GC也交给go runtime。otto是这样的解释器。另外,otto的CPU占用率高,很严重吗?在稳态执行的时候,解释器肯定会比JIT编译器慢(也可以理解成,做相同的事,CPU资源消耗高)。但这很重要吗?如果只是扩展主程序的功能,这一点代价我觉得还可以吧。
跑题一下:根本问题,我觉得是,需要一个好的虚拟机。v8是个相当不错的虚拟机(当然,不完美)
如果你不想“对每个js线程创建一个native线程”,那么你需要虚拟机支持。稳态下,高速执行的JS代码是被JIT编译成机器码,直接在CPU上执行的。如果想“暂停”这样一个正在执行的机器码程序,又不想用抢占式调度器(比如内核的线程调度器),而是让它“让出”CPU让其它JS线程来执行,那么,需要:
1. JIT编译器必须在机器码中的各处(主要是函数的入口或出口,和循环的入口或出口)插入一些“检测”代码,在恰当的时候让出CPU,“跳”到别的线程去执行。v8确实在这些地方插入了检测代码,但主要是为optimization/deoptimization和GC用的。
2. 所谓“跳”到别的线程,需要保存当前的栈和CPU状态,把CPU状态(包括PC和SP)换成另一个JS线程的上下文,再继续。
如果能做到上述两点,就能在JIT编译环境中轻量级的green thread。
当然,如果是解释器而不是JIT编译器,那么green thread更容易实现。但很不幸,v8根本没有解释器,只有一个baseline编译器和一个optimizing编译器。
所以,恭喜你,你碰到了编程语言实现中的几大难题的交集:轻量级多线程+JIT编译以节约CPU占用+有垃圾回收的脚本语言。目前没有很好的解决方案(虽然有人在研究)。
多谢暖神。
仔细看了看自己的测试代码。
v8worker线程多,有可能是因为我起了1000个v8worker所以才有1000个线程的原因吧。js里调用go的函数时候是阻塞的,所以如果想增加吞吐量,感觉应该要给js提供异步接口才对。
=。=感觉我测试代码有问题,otto的每次都会New一下。
打算先用otto试试~反正业务量不是很大~
看了下,两个应该都提供中止long running js的方法。
```
for i := 0; i < 1000; i++ {
go func() {
vm := v8worker.New(
func(msg string) {
atomic.AddInt64(&count, 1)
time.Sleep(time.Duration(1) * time.Second)
}, func(msg string) string {
return "a"
})
for {
vm.Load("test.js", `$send("tset");`)
}
}()
}
```
```
for i := 0; i < 1000; i++ {
go func() {
for {
vm := otto.New()
vm.Set("go", func(call otto.FunctionCall) otto.Value {
atomic.AddInt64(&count, 1)
//time.Sleep(time.Duration(1) * time.Second)
return otto.Value{}
})
_, err := vm.Run(`go();`)
if err != nil {
fmt.Println(err)
}
}
}()
}
```
【 在 nuanyangyang 的大作中提到: 】
: 你说v8worker每调用一个C函数都创建一个线程,真的确定吗?这听起来很愚蠢,想不出谁会这样做(除了我上次要在JVM上实现含C frame的coroutine不得不这样做以外。但JS并没有coroutine,甚至还是单线程的)。你确定内存占用大的原因是“创建线程”而不是JIT编译器吗?另外v8的垃圾回收是一个generational GC,很快,但会预留内存,即使对象并没有那么多也会预留一些堆空间,以备将来分配。你是怎么看“内存占用”的?看到的会不会只是v8预留的gc heap空间?操作系统无法分辨出堆里面的空间是被占用的还是没被占用的,操作系统只知道“用户要求分配了这整块堆内存”。
: 看你的用途,如果逻辑很简单,又只是扩展你的主程序,做一些网络调用,那么即使不要JIT,性能也应该可以(v8只有JIT)。
: 看v8worker的叙述(https://github.com/ry/v8worker)“V8 will block a thread (goroutine) only while it computes javascript - it has no "syscalls" other than sending and receiving messages to Go.”这意味着如果用户写一个死循环,这个goroutine就阻塞了(不太清楚这对sandbox意味着什么。你需要终止“运行时间过长的js程序”吗)。
: ...................