实现一个简易的“状态机”
server # go run server.go
2013/06/06 14:10:11 counterB timedout
//超时10秒总和未达到10
client # go run client.go counterA 1
Continue
client # go run client.go counterA 3
Continue
client # go run client.go counterA 3
Continue
client # go run client.go counterA 4 //counterA十秒内数字和>10
Done
client # go run client.go counterB 4 //超时10秒总和未达到10
Continue
用go自带的库实现http服务器端server.go:
package main import ( "fmt" "net/http" "log" "os" // "os/signal" "g10086" "bufio" "io" "strings" "time" "encoding/json" "strconv" // "syscall" ) //g10086 location 记录的配置文件 const g10086_save = "g10086.status" //查看当前g10086的全局状态信息 func rootHandler (w http.ResponseWriter, r * http.Request) { fmt.Fprintf(w, "g_status: %d\n", len(g10086.G_status)) for key,val := range g10086.G_status { fmt.Fprintf(w, "%s : %s\n", key, val) } } //将全局状态信息表存储到文件中 func saveHandler (w http.ResponseWriter, r * http.Request) { save_status() io.WriteString(w, "Save OK!") } //从文件加载信息 func load_status () { g10086.G_status = make(map[string] string) f, err := os.OpenFile(g10086_save, os.O_RDWR|os.O_CREATE, 0666) if err != nil { log.Fatal(err) } defer f.Close() r := bufio.NewReader(f) line, _, err := r.ReadLine() for err == nil { if info := strings.Split(string(line)," "); len(info) > 1 { g10086.G_status[info[0]] = info[1] } line, _, err = r.ReadLine() } } func save_status () { f, err := os.OpenFile(g10086_save, os.O_CREATE|os.O_RDWR, 0666) if err != nil { log.Fatal(err) } defer f.Close() b := bufio.NewWriter(f) for key,value := range g10086.G_status { line := key + " " + value + "\n" if n, err := b.WriteString(line); err != nil || n != len(line) { log.Fatal(err) } } if err = b.Flush(); err != nil { log.Fatal(err) } } //查看全局状态信息是否有超时的信息,如果有,删除并记录日志 func check_timeout() { cur := time.Now().Unix() for key,value := range g10086.G_status { var cur_down g10086.Dstate //不能放在for循环之前! dec := json.NewDecoder(strings.NewReader(value)) if err := dec.Decode(&cur_down); err != nil { return } for k,_ := range cur_down.Info { t, err := strconv.Atoi(k) if err != nil { return } if t < int(cur - 10) { delete(g10086.G_status, key) log.Println(key + " timedout") break } } } } //定时器,每1秒钟运行一次,检测超时 func timer () { for { timer := time.NewTicker(1 * time.Second) for { select { case <- timer.C: check_timeout() } } } } //配置服务器,监听端口和location func main () { load_status () go timer() defer save_status() http.HandleFunc("/",rootHandler) http.HandleFunc("/g10086",g10086.G10086Handler) http.HandleFunc("/save",saveHandler) log.SetOutput(os.Stdout) err := http.ListenAndServe("127.0.0.1:8088", nil) if err != nil { log.Fatal("ListenAndServe:", err) } }
g10086/g10086.go 模块代码如下
package g10086 import ( "io" "net/http" "strings" "strconv" "encoding/json" "time" ) //全局计数器信息 var G_status map[string] string // type Dstate struct { id string status string Info map[string] string } //更新计数状态,如果计数器A的数字总和超过10,则输出完毕,删除此计数器 func updateStatus (key string, down Dstate) int { total := 0 for _,v := range down.Info { n, err := strconv.Atoi(v) if err != nil { return -1 } total += n if total > 10 { down.status = "Done" break } } if (down.status == "Done") { if _, ok := G_status[key]; ok { delete(G_status,key) } return 1 } else { downinfo, err := json.Marshal(down) if err == nil { G_status[key] = string(downinfo) } return 0 } } func G10086Handler (w http.ResponseWriter, r *http.Request) { key, val := r.PostFormValue("key"), r.PostFormValue("value") if key == "" || val == "" { io.WriteString(w,"no para") return } //查询之前有无对应key的计数器,如果有,则添加记录,如果没有,新建记录 var cur_down Dstate if v, ok := G_status[key]; ok { dec := json.NewDecoder(strings.NewReader(v)) if err := dec.Decode(&cur_down); err != nil { return } cur_down.Info[strconv.Itoa(int(time.Now().Unix()))] = val } else { new_status := map[string] string{strconv.Itoa(int(time.Now().Unix())):val} cur_down = Dstate{id:key, status:"Start", Info:new_status} } res := updateStatus(key, cur_down) switch res { case 1: io.WriteString(w, "Done" + key) case 0: io.WriteString(w, "Continue " + key) default: io.WriteString(w, "Unknown") } }
client端代码如下:
package main import ( "net/http" "net/url" "log" "os" ) func main () { if len(os.Args) < 3 { log.Fatal("para error") } key, val := os.Args[1], os.Args[2] resp, err := http.PostForm("http://127.0.0.1:8088/g10086", url.Values{"key": {key}, "value": {val}}) if err != nil { log.Fatal("Post error : ", err) } if resp.StatusCode == 200 { resp.Write(os.Stdout) } } //使用示例 //go run client.go countA 3 //go run client.go countB 4
静态文件需要添加如下代码,创建template目录,将静态资源放在目录中
http.Handle("/template/", http.StripPrefix("/template/", http.FileServer(http.Dir("./template"))))
同样的静态资源小文件放在nginx和go的性能测试如下:Request Per Second(nginx为7652)(go为5321)
定时器使用如下:
import "time" func timer () { for { timer := time.NewTicker(5 * time.Second) for { select { case <- timer.C: fmt.Print("timer called\n") } } } }
JSON操作
package main import ( "encoding/json" "fmt" "io" "log" "strings" ) func main() { const jsonStream = ` {"Name": "Ed", "Text": "Knock knock."} {"Name": "Sam", "Text": "Who's there?"} {"Name": "Ed", "Text": "Go fmt."} {"Name": "Sam", "Text": "Go fmt who?"} {"Name": "Ed", "Text": "Go fmt yourself!"} ` type Message struct { Name, Text string } //json decode dec := json.NewDecoder(strings.NewReader(jsonStream)) var m Message for { if err := dec.Decode(&m); err == io.EOF { break } else if err != nil { log.Fatal(err) } fmt.Printf("%s: %s\n", m.Name, m.Text) } //json encode dataStream, err := json.Marshal(m) if err != nil { log.Fatal(err) } fmt.Print(string(dataStream)) }