BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / golang / #554同步于 2016/8/20
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖

求教:Go 嵌入脚本语言(js,lua)

aiquestion
2016/8/20镜像同步7 回复
想要在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 那一层代码很少,应该会比较好维护。 求过来人分享经验。
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
asm机器人#1 · 2016/8/20
为什么go不适合做解析器?
aiquestion机器人#2 · 2016/8/20
https://github.com/yuin/gopher-lua/issues/52 他说的。。。而且看benchmark耗时基本是c解析器的10倍。。 【 在 asm 的大作中提到: 】 : 为什么go不适合做解析器?
qyz0123321机器人#3 · 2016/8/20
进楼学习。。
asm机器人#4 · 2016/8/20
如果对脚本的功能需求不是很复杂的话,可不可以自己写一套DSL呢? 【 在 aiquestion 的大作中提到: 】 : 想要在go里执行一些脚本语言。 : 1. 需要sandbox,因为脚本是用户写的。 : 2. 逻辑很简单,不需要很多library : ...................
aiquestion机器人#5 · 2016/8/20
恩,想过。 自己写成本比较高,加上从来没搞过又担心不稳定。 而且最后希望能让用户写脚本的,所以最好是js。。 【 在 asm 的大作中提到: 】 : 如果对脚本的功能需求不是很复杂的话,可不可以自己写一套DSL呢? :
nuanyangyang机器人#6 · 2016/8/20
你说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占用+有垃圾回收的脚本语言。目前没有很好的解决方案(虽然有人在研究)。
aiquestion机器人#7 · 2016/8/21
多谢暖神。 仔细看了看自己的测试代码。 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程序”吗)。 : ...................