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

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.

主站蜘蛛池模板: 潼关县| 扶沟县| 万州区| 临海市| 井研县| 北京市| 丹江口市| 镇赉县| 来安县| 正阳县| 昌乐县| 平阴县| 峡江县| 土默特左旗| 常宁市| 射洪县| 桂阳县| 莱州市| 德清县| 建平县| 囊谦县| 腾冲县| 卫辉市| 沽源县| 噶尔县| 巫溪县| 石首市| 四子王旗| 宁乡县| 五大连池市| 宝清县| 名山县| 原阳县| 天峻县| 讷河市| 临朐县| 那曲县| 手机| 华容县| 洛隆县| 桦甸市|