go的rpc可以使用http的方式,也可以使用tcp的,这里两种一起使用,如果服务挂了,我们可以通过http来测试服务是否正常。
server.go
package main import ( "net/rpc" "net/http" "log" "net" // "time" ) type Args struct { A, B int } type Arith int func (t *Arith) Multiply(args *Args, reply *([]string)) error { *reply = append(*reply, "test") return nil } func main() { newServer := rpc.NewServer() newServer.Register(new(Arith)) l, e := net.Listen("tcp", "127.0.0.1:1234") // any available address if e != nil { log.Fatalf("net.Listen tcp :0: %v", e) } go newServer.Accept(l) newServer.HandleHTTP("/foo", "/bar") http.ListenAndServe(":8080",newServer) }
go newServer.Accept(l)的内部:go server.ServeConn(conn)func (server *Server) Accept(lis net.Listener) {
for { conn, err := lis.Accept() if err != nil { log.Print("rpc.Serve: accept:", err.Error()) return } go server.ServeConn(conn) }
}
因为rpc的Server类实现了ServHTTP方法,所以可以当作net handle来使用。并且限制了请求的http的method为connect,后面会使用curl来测试,两个相同的地方就是调用了server.ServeConn(conn)
代码如下:
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.Method != "CONNECT" { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusMethodNotAllowed) io.WriteString(w, "405 must CONNECT\n") return } conn, _, err := w.(http.Hijacker).Hijack() if err != nil { log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error()) return } io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n") server.ServeConn(conn) }
client.go
package main import( "net/rpc" "net/http" "log" "net" "fmt" // "io/ioutil" ) type Args struct { A, B int } func main(){ // 使用tcp的方式来调用rpc address, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234") if err != nil { panic(err) } conn, _ := net.DialTCP("tcp", nil, address) defer conn.Close() client := rpc.NewClient(conn) defer client.Close() args := &Args{7,8} reply := make([]string, 10) err = client.Call("Arith.Multiply", args, &reply) if err != nil { log.Fatal("arith error:", err) } log.Println("rpc:",reply) // 使用http的方式来调用rpc cli,err:=rpc.DialHTTP("tcp","localhost:8080/bar") if err!=nil{ fmt.Println(err) } err = cli.Call("Arith.Multiply", args, &reply) if err != nil { log.Fatal("arith error:", err) } log.Println("http:",reply) //使用http request请求,看服务是否异常,rpc服务会通过response的status返回 req,err:=http.NewRequest("CONNECT","http://127.0.0.1:8080/bar",nil) if err!=nil{ fmt.Println(err) } httpclient := &http.Client{} rspo,err:=httpclient.Do(req) if err!=nil{ fmt.Println(err) } var by []byte // by=make([]byte,1024)加上这句程序会阻塞 fmt.Println("status:",rspo.Status) n,_:=rspo.Body.Read(by) fmt.Println("http:",string(by),"n:",n) fmt.Println("end") }
输出结果:两种调用rpc都输出了结果,使用http request只返回了response的status,body中是没有数据的。
2016/10/28 14:16:05 rpc: [test] 2016/10/28 14:16:05 http: [test] status: 200 Connected to Go RPC http: n: 0 end
使用curl命令测试:
[baizhi@localhost ex]$ curl localhost:8080/bar 405 must CONNECT [baizhi@localhost ex]$ curl -I -X CONNECT localhost:8080/bar HTTP/1.0 200 Connected to Go RPC