官术网_书友最值得收藏!

JSON-RPC over HTTP

In this last example, we will look at the net/rpc/jsonrpc package that provides a built-in codec for serializing and deserializing to the JSON-RPC standard. We will also look at how we can send these responses over HTTP, whilst you may ask why not just use REST, and to some extent I will agree with you, it is an interesting example to be able to see how we can extend the standard framework.

The StartServer method contains nothing we have not seen before it is the standard rpc server setup, the main difference is line 42 where instead of starting the RPC server we are starting an http server and passing the listener to it along with a handler:

rpc_http_json/server/server.go

33 func StartServer() { 
34 helloWorld := new(HelloWorldHandler)
35 rpc.Register(helloWorld)
36
37 l, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
38 if err != nil {
39 log.Fatal(fmt.Sprintf("Unable to listen on given port: %s", err))
40 }
41
42 http.Serve(l, http.HandlerFunc(httpHandler))
43 }

The handler we are passing to the server is where the magic happens:

45 func httpHandler(w http.ResponseWriter, r *http.Request) { 
46 serverCodec := jsonrpc.NewServerCodec(&HttpConn{in: r.Body, out: w})
47 err := rpc.ServeRequest(serverCodec)
48 if err != nil {
49 log.Printf("Error while serving JSON request: %v", err)
50 http.Error(w, "Error while serving JSON request, details have been logged.", 500)
51 return
52 }
53 }

In line 46, we are calling the jsonrpc.NewServerCodec function and passing to it a type that implements io.ReadWriteCloser. The NewServerCodec method returns a type that implements rpc.ClientCodec, which has the following methods:

type ClientCodec interface { 
// WriteRequest must be safe for concurrent use by multiple goroutines.
WriteRequest(*Request, interface{}) error
ReadResponseHeader(*Response) error
ReadResponseBody(interface{}) error

Close() error
}

A ClientCodec type implements the writing of RPC request and reading RPC responses. To write a request to the connection a client calls the WriteRequest method. To read the response, the client must call ReadResponseHeader and ReadResponseBody as a pair. Once the body has been read, it is the client's responsibility to call the Close method to close the connection. If a nil interface is passed to ReadResponseBody then the body of the response should be read and then discarded:

17 type HttpConn struct { 
18 in io.Reader
19 out io.Writer
20 }
21
22 func (c *HttpConn) Read(p []byte) (n int, err error) { return c.in.Read(p) }
23 func (c *HttpConn) Write(d []byte) (n int, err error) { return c.out.Write(d) }
24 func (c *HttpConn) Close() error { return nil }

The NewServerCodec method requires that we pass it a type that implements the ReadWriteCloser interface. As we do not have such a type passed to us as parameters in the httpHandler method we have defined our own type, HttpConn, which encapsulates the http.Request body, which implements io.Reader, and the ResponseWriter method, that implements io.Writer. We can then write our own methods that proxy the calls to the reader and writer creating a type that has the correct interface.

And that is it for our short intro to RPC with the standard libraries; we will see when we look at some frameworks more in depth in Chapter 3, Introducing Docker, how these can be used to build a production microservice.

主站蜘蛛池模板: 拉萨市| 济南市| 陆良县| 咸丰县| 岑巩县| 古丈县| 望奎县| 深圳市| 辰溪县| 岳阳县| 镇原县| 会昌县| 昌宁县| 来宾市| 化德县| 清原| 垫江县| 赤峰市| 开封县| 禹城市| 大埔县| 阳曲县| 扎鲁特旗| 瑞金市| 军事| 伊吾县| 北安市| 邯郸县| 准格尔旗| 本溪市| 崇义县| 石台县| 靖宇县| 泰和县| 喀喇沁旗| 特克斯县| 西城区| 乌拉特中旗| 乡宁县| 海安县| 天峻县|