用go实现erlang模型
?
以前说过Go的goroutine+channel模式比erlang的process+message模式更灵活和通用,可以说erlang的模式是go模式的子集subset,那么用golang可以实现erlang那样的process模式吗?那样go就看起来像erlang了
?
erlang的process有着built-in的可靠性,通过link和monitor使得每个process的运行可靠性加强,golang中的goroutine可没有可靠性一说,不过go中还是可以通过defer实现erlang的可靠性的。
花了几个小时的时间写了一个go package,暂命名为erlang.go,功能是在go中实现erlang process模型,下面看代码:
package erlangtype Pid chan interface {}type link struct { next *link pid Pid}type Monitor struct { next *Monitor pid Pid}type Process struct { name string fun func(Pid) status int linklist *link exit_reason interface {}}const MsgCap = 1000var process_reg = make(map[string]Pid)var link_map = make(map[Pid]*link)var monitor_map = make(map[Pid]*link)var processes_map = make(map[Pid]*Process)func Get_process_name(process *Process) (string){ return process.name}func Get_process_exitreason(process *Process) (interface {}){ return process.exit_reason}func SpawnProcess(process *Process) (Pid) { return Spawn(process.fun)}func IsAlive(pid Pid) (bool) { return (processes_map[pid] != nil)}func CountProcesses() int { i := 0 for _, v := range processes_map { if v != nil { i++ } } return i}func Register(name string, pid Pid) { if process_reg[name] != nil { return } process_reg[name] = pid processes_map[pid].name = name}func Clear_name(name string) { process_reg[name] = nil}func Getpid(name string) (Pid) { return process_reg[name]}func Spawn(fun func(Pid)) (Pid) { pid := make(Pid, MsgCap) process := new(Process) process.fun = fun processes_map[pid] = process go func() { defer func() { if x := recover(); x != nil { process.exit_reason = x } for this := process.linklist; this != nil; this = this.next { this.pid<- process } processes_map[pid] = nil close(pid) }() fun(pid) }() return pid}func Addlink(supervisor, pid Pid) { newlink := new(link) newlink.pid = supervisor tmp := processes_map[pid].linklist processes_map[pid].linklist = newlink newlink.next = tmp}func Spawn_link(supervisor Pid, fun func(Pid)) (Pid){ return Spawn(func(self Pid) { Addlink(supervisor, self) fun(self) })}func Spawn_name(name string, fun func(Pid)) (Pid) { return Spawn(func(self Pid) { Register(name, self) defer Clear_name(name) fun(self) })}func Spawn_link_name(name string, supervisor Pid, fun func(Pid)) (Pid) { return Spawn_link(supervisor, func(self Pid) { Register(name, self) defer Clear_name(name) fun(self) })}
?
再看看测试代码:
package mainimport ("runtime" "time" "fmt" "erlang")////////////////////////////////////// testing code //////////////////////////////////////////func server(self erlang.Pid) { for { select { case msg := <-self: fmt.Println("server receive msg: ", msg) if msg == "panic" { panic("some one kill me") } } }}func client(self, peer erlang.Pid) { peer<- 1 peer<- "one" peer<- "two" time.Sleep(time.Second) //wait name service registe success erlang.Getpid("server")<- 2 erlang.Getpid("server")<- "panic" // kill server time.Sleep(time.Second) // wait server restart erlang.Getpid("server")<- "three" peer<- "three" // send on closed channel}func supervisor(self erlang.Pid) { for { msg := <-self process, _ := msg.(*erlang.Process) fmt.Println("supervisor: process terminated:", erlang.Get_process_name(process), "\treason:", erlang.Get_process_exitreason(process)) switch erlang.Get_process_name(process) { case "server": erlang.SpawnProcess(process) } }}func main() { runtime.GOMAXPROCS(3) supervisor := erlang.Spawn(func(self erlang.Pid) { supervisor(self) }) pid1 := erlang.Spawn_name("server", func(self erlang.Pid) { server(self) }); erlang.Addlink(supervisor, pid1) pid2 := erlang.Spawn_name("client", func(self erlang.Pid) { client(self, pid1) }); erlang.Addlink(supervisor, pid2) fmt.Println("server alived:", erlang.IsAlive(pid1)) fmt.Println("client alived:", erlang.IsAlive(pid2)) fmt.Println("CountProcesses:", erlang.CountProcesses()) time.Sleep(10*time.Second) fmt.Println("server alived:", erlang.IsAlive(pid1)) // should be false fmt.Println("server Getpid alived:", erlang.IsAlive(erlang.Getpid("server"))) // should be true fmt.Println("client alived:", erlang.IsAlive(pid2)) // should be true fmt.Println("CountProcesses:", erlang.CountProcesses())}
?
示例很清楚,实现功能如下:
1) spawn
像erlang那样调用erlang.Spwan生成一个process,返回一个pid,
2)message
可以向任何pid发送任何message,可以发数字,也可以发字符串。
3)supervision
erlang.go中也实现了supervision功能,调用erlang.Addlink指定supervisor和需监控的pid,当process退出时(无论异常退出还是正常退出),supervisor将会收到消息。这里每个process就像erlang中那样相互隔离。可以指定某些process的行为,如server类的process退出后可以re-spawn
4)name service
可以给每个process注册name,就像erlang的register,可以向指定name的process发送消息
?
有了erlang.go的帮助后,erlang丰富的otp library很多可以架构在go之上了,这样就可以弥补了go在library上的欠缺
?
下面是示例程序的运行结果:
[root@localhost test]# go run test.go server alived: trueserver receive msg: 1server receive msg: oneserver receive msg: twoclient alived: trueCountProcesses: 3server receive msg: 2server receive msg: panicsupervisor: process terminated: server reason: some one kill meserver receive msg: threesupervisor: process terminated: client reason: runtime error: send on closed channelserver alived: falseserver Getpid alived: trueclient alived: falseCountProcesses: 2?
本人一直比较喜欢erlang的genfsm,所以想在go中实现像genfsm那样的gofsm,可惜go中没有atom,go中没有apply,还是很难实现genfsm那样的简洁。
?