golang支持并发事件处理的嵌入式脚本语言插件ecal的使用
Golang支持并发事件处理的嵌入式脚本语言插件ECAL的使用
ECAL简介
ECAL是一种用于并发事件处理的ECA(事件条件动作)语言。ECAL可以使用由事件触发的规则定义基于事件的系统。ECAL旨在嵌入到其他软件中,提供一个易于使用的脚本语言,可以对外部事件做出反应。
主要特性
- 简单直观的语法
- 极简的基础语言(默认只支持写入日志)
- 语言可以轻松扩展,通过自动生成到Go函数的桥接适配器或在标准库(stdlib)中添加自定义函数
- 外部事件可以轻松推送到解释器中,用ECAL编写的脚本可以对这些事件做出反应
- 简单但功能强大的基于事件的并发处理,支持优先级和控制流范围
- 处理事件规则可以匹配事件状态,规则可以相互抑制
快速开始
基本使用
启动交互式会话:
./ecal
编写简单的一行语句并求值:
>>>a:=2;b:=a*4;a+b
10
>>>"Result is {{a+b}}"
Result is 10
示例项目
examples/fib
目录中有2个ECAL文件:
lib.ecal
# Library for fib
/*
fib calculates the fibonacci series using recursion.
*/
func fib(n) {
if (n <= 1) {
return n
}
return fib(n-1) + fib(n-2)
}
fib.ecal
import "lib.ecal" as lib
for a in range(2, 20, 2) {
log("fib({{a}}) = ", lib.fib(a))
}
运行程序:
sh run.sh
输出示例:
2000/01/01 12:12:01 fib(2) = 1
2000/01/01 12:12:01 fib(4) = 3
2000/01/01 12:12:01 fib(6) = 8
2000/01/01 12:12:01 fib(8) = 21
2000/01/01 12:12:01 fib(10) = 55
2000/01/01 12:12:01 fib(12) = 144
2000/01/01 12:12:02 fib(14) = 377
2000/01/01 12:12:02 fib(16) = 987
2000/01/01 12:12:02 fib(18) = 2584
2000/01/01 12:12:02 fib(20) = 6765
嵌入ECAL和使用事件处理
ECAL的主要目的是成为一种简单的多用途语言,可以嵌入到其他软件中:
- 它具有最小化(相当通用)的语法
- 默认情况下,语言只能通过返回值、注入事件或日志记录到达解释器外部
- 外部系统可以通过事件与代码交互,这些事件可能在具有不同复杂性的接收器系统中处理
- 标准库函数可以轻松创建,通过生成标准Go函数的代理代码或添加简单的直接函数对象
嵌入示例
ECAL解释器的核心是运行时提供者对象,它使用给定的记录器和导入定位器构造:
logger := util.NewStdOutLogger()
importLocator := &util.FileImportLocator{Root: "/somedir"}
rtp := interpreter.NewECALRuntimeProvider("Some Program Title", importLocator, logger)
解析ECAL代码为抽象语法树:
ast, err := parser.ParseWithRuntime("sourcefilename", code, rtp)
执行代码:
err = ast.Runtime.Validate()
vs := scope.NewScope(scope.GlobalScope)
res, err := ast.Runtime.Eval(vs, make(map[string]interface{}), threadId)
事件处理
如果要使用事件,需要先启动运行时提供者的处理器:
rtp.Processor.Start()
然后可以注入事件到解释器中:
monitor, err := rtp.Processor.AddEventAndWait(engine.NewEvent("MyEvent", []string{"foo", "bar", "myevent"}, map[interface{}]interface{}{
"data1": 123,
"data2": "123",
}), nil)
收集错误:
monitor.RootMonitor().AllErrors()
ECAL中处理事件的接收器示例:
sink mysink
kindmatch [ "foo.bar.myevent" ],
{
log("Got event: ", event)
}
sink mysink2
kindmatch [ "foo.*.*" ],
{
log("Got event: ", event)
}
在ECAL中使用Go插件
ECAL支持通过Go插件扩展标准库(stdlib)函数。此功能的目的是允许轻松扩展标准库,即使是平台相关的代码。
插件示例在examples/plugin
目录中。插件.so文件可以用buildplugin.sh
编译(Go编译器必须与编译解释器二进制文件的版本相同)。运行示例run.sh
将使ECAL解释器在执行ECAL代码之前加载编译的插件。
ECAL插件函数必须符合以下接口:
/*
ECALPluginFunction models a callable function in ECAL which can be imported via a plugin.
*/
type ECALPluginFunction interface {
/*
Run executes this function with a given list of arguments.
*/
Run(args []interface{}) (interface{}, error)
/*
DocString returns a descriptive text about this function.
*/
DocString() string
}
更多关于golang支持并发事件处理的嵌入式脚本语言插件ecal的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang支持并发事件处理的嵌入式脚本语言插件ecal的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 中使用 eCAL 进行并发事件处理的嵌入式脚本语言插件
eCAL (Efficient Communication Abstraction Layer) 是一个开源的中间件,专为高性能分布式系统设计。在 Golang 中使用 eCAL 可以很好地支持并发事件处理,虽然 eCAL 本身不是一个脚本语言,但我们可以结合 Golang 的插件系统和脚本引擎来实现类似功能。
eCAL 简介
eCAL 提供了发布/订阅模式的消息传递,支持多种语言包括 C++, C#, Python 和 Go。它的主要特点包括:
- 低延迟进程间通信
- 支持多播和共享内存传输
- 自动服务发现
- 支持多种序列化格式
在 Golang 中使用 eCAL
首先需要安装 eCAL 的 Go 绑定:
go get github.com/continental/ecal-go
基本使用示例
1. 发布者示例
package main
import (
"fmt"
"time"
"github.com/continental/ecal-go"
)
func main() {
// 初始化 eCAL
ecal.Initialize([]string{}, "Go Publisher")
defer ecal.Finalize()
// 设置进程状态
ecal.SetProcessState(1, 1, "I feel good")
// 创建发布者
pub, err := ecal.NewPublisher("topic1", "base", "string", 1)
if err != nil {
fmt.Println("Failed to create publisher:", err)
return
}
defer pub.Destroy()
// 发布消息
count := 0
for ecal.Ok() {
msg := fmt.Sprintf("Hello eCAL %d", count)
err := pub.Send([]byte(msg), -1)
if err != nil {
fmt.Println("Failed to send message:", err)
} else {
fmt.Println("Sent:", msg)
}
count++
time.Sleep(500 * time.Millisecond)
}
}
2. 订阅者示例
package main
import (
"fmt"
"github.com/continental/ecal-go"
)
func main() {
// 初始化 eCAL
ecal.Initialize([]string{}, "Go Subscriber")
defer ecal.Finalize()
// 创建订阅者
sub, err := ecal.NewSubscriber("topic1", "base", "string")
if err != nil {
fmt.Println("Failed to create subscriber:", err)
return
}
defer sub.Destroy()
// 设置接收回调
sub.SetCallback(func(topicName string, msg *ecal.ReceivedMessageData) {
fmt.Printf("Received on %s: %s\n", topicName, string(msg.Payload))
})
// 保持运行
for ecal.Ok() {
// 主线程可以在这里做其他工作
// 或者只是 sleep
ecal.SleepMS(100)
}
}
结合脚本引擎实现插件系统
要实现嵌入式脚本语言插件,可以结合 Go 的插件系统和脚本引擎如 goja (JavaScript) 或 gopher-lua (Lua)。以下是使用 goja 的示例:
1. 安装 goja
go get github.com/dop251/goja
2. 脚本插件系统示例
package main
import (
"fmt"
"github.com/continental/ecal-go"
"github.com/dop251/goja"
"sync"
)
// ScriptPlugin 表示一个脚本插件
type ScriptPlugin struct {
vm *goja.Runtime
cancel chan struct{}
wg sync.WaitGroup
}
// NewScriptPlugin 创建新的脚本插件
func NewScriptPlugin(script string) (*ScriptPlugin, error) {
vm := goja.New()
plugin := &ScriptPlugin{
vm: vm,
cancel: make(chan struct{}),
}
// 注册 eCAL 相关函数到 VM
vm.Set("ecalPublish", func(topic, msg string) {
pub, _ := ecal.NewPublisher(topic, "base", "string", 1)
defer pub.Destroy()
pub.Send([]byte(msg), -1)
})
// 执行脚本
_, err := vm.RunString(script)
if err != nil {
return nil, err
}
return plugin, nil
}
// Run 运行插件
func (p *ScriptPlugin) Run() {
p.wg.Add(1)
go func() {
defer p.wg.Done()
for {
select {
case <-p.cancel:
return
default:
// 调用脚本中的 main 函数
if fn, ok := goja.AssertFunction(p.vm.Get("main")); ok {
fn(goja.Undefined())
}
}
}
}()
}
// Stop 停止插件
func (p *ScriptPlugin) Stop() {
close(p.cancel)
p.wg.Wait()
}
func main() {
// 初始化 eCAL
ecal.Initialize([]string{}, "Script Plugin Host")
defer ecal.Finalize()
// 示例脚本
script := `
let count = 0;
function main() {
ecalPublish("script_topic", "Message from script " + count++);
}
`
// 创建并运行插件
plugin, err := NewScriptPlugin(script)
if err != nil {
fmt.Println("Error creating plugin:", err)
return
}
defer plugin.Stop()
plugin.Run()
// 创建订阅者来验证
sub, _ := ecal.NewSubscriber("script_topic", "base", "string")
defer sub.Destroy()
sub.SetCallback(func(topic string, msg *ecal.ReceivedMessageData) {
fmt.Println("Received:", string(msg.Payload))
})
// 主循环
for ecal.Ok() {
ecal.SleepMS(500)
}
}
并发事件处理
eCAL 本身支持并发,Go 的 goroutine 也能很好地处理并发事件。以下是并发处理多个订阅的示例:
package main
import (
"fmt"
"sync"
"github.com/continental/ecal-go"
)
func main() {
ecal.Initialize([]string{}, "Concurrent Subscriber")
defer ecal.Finalize()
var wg sync.WaitGroup
topics := []string{"topic1", "topic2", "topic3"}
for _, topic := range topics {
wg.Add(1)
go func(t string) {
defer wg.Done()
sub, err := ecal.NewSubscriber(t, "base", "string")
if err != nil {
fmt.Printf("Error creating subscriber for %s: %v\n", t, err)
return
}
defer sub.Destroy()
sub.SetCallback(func(topicName string, msg *ecal.ReceivedMessageData) {
fmt.Printf("[%s] Received: %s\n", topicName, string(msg.Payload))
})
for ecal.Ok() {
ecal.SleepMS(100)
}
}(topic)
}
wg.Wait()
}
总结
通过结合 eCAL 和 Go 的并发特性,可以构建高效的分布式事件处理系统。嵌入脚本语言插件可以提供动态扩展能力,而 Go 的 goroutine 和 channel 可以很好地管理并发事件流。
这种架构适用于需要高性能、分布式处理,同时又需要灵活性的场景,如物联网数据处理、实时分析系统等。