读书人

用go兑现erlang模型

发布时间: 2012-12-22 12:05:06 作者: rapoo

用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那样的简洁。

?

读书人网 >编程

热点推荐