JSON RPC using Gorilla RPC

We saw that the Gorilla toolkit helps us by providing many useful libraries. Then we explored Mux, Handlers, and now, the Gorilla RPC library. Using this, we can create RPC servers and clients that talk using a JSON instead of a custom reply pointer. Let us convert the preceding example into a much more useful one.

Consider this scenario. We have a JSON file on the server that has details of books (name, ID, author). The client requests book information by making an HTTP request. When the RPC server receives the request, it reads the file from the filesystem and parses it. If the given ID matches any book, then the server sends the information back to the client in the JSON format. We can install Gorilla RPC with the following command:

go get github.com/gorilla/rpc

This package derives from the standard net/rpc package but uses a single HTTP request per call instead of persistent connections. Other differences compared to net/rpc: are explained in the following sections.

Multiple codecs can be registered in the same server. A codec is chosen based on the Content-Type header from the request. Service methods also receive the http.Request as a parameter. This package can be used on Google App Engine. Now, let us write an RPC JSON server. Here we are implementing the JSON1.0 specification. For 2.0, you should use Gorilla JSON2:

package main
import (
jsonparse "encoding/json"
"io/ioutil"
"log"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/gorilla/rpc"
"github.com/gorilla/rpc/json"
)
// Args holds arguments passed to JSON RPC service
type Args struct {
Id string
}
// Book struct holds Book JSON structure
type Book struct {
Id string `"json:string,omitempty"`
Name string `"json:name,omitempty"`
Author string `"json:author,omitempty"`
}
type JSONServer struct{}
// GiveBookDetail
func (t *JSONServer) GiveBookDetail(r *http.Request, args *Args, reply *Book) error {
var books []Book
// Read JSON file and load data
raw, readerr := ioutil.ReadFile("./books.json")
if readerr != nil {
log.Println("error:", readerr)
os.Exit(1)
}
// Unmarshal JSON raw data into books array
marshalerr := jsonparse.Unmarshal(raw, &books)
if marshalerr != nil {
log.Println("error:", marshalerr)
os.Exit(1)
}
// Iterate over each book to find the given book
for _, book := range books {
if book.Id == args.Id {
// If book found, fill reply with it
*reply = book
break
}
}
return nil
}
func main() {
// Create a new RPC server
s := rpc.NewServer() // Register the type of data requested as JSON
s.RegisterCodec(json.NewCodec(), "application/json")
// Register the service by creating a new JSON server
s.RegisterService(new(JSONServer), "")
r := mux.NewRouter()
r.Handle("/rpc", s)
http.ListenAndServe(":1234", r)
}

This program might look different from the preceding RPC server implementation. It is because of the inclusion of the Gorilla MuxGorilla rpc, and jsonrpc packages. Let us run the preceding program before explaining what is happening. Run the server with the following command:

go run jsonRPCServer.go

Now where it the client? Here the client can be a CURL command since the RPC server is serving requests over HTTP. We need to post JSON with a book ID to get the details. So fire up another shell and execute this CURL request:

curl -X POST 
http://localhost:1234/rpc
-H 'cache-control: no-cache'
-H 'content-type: application/json'
-d '{
"method": "JSONServer.GiveBookDetail",
"params": [{
"Id": "1234"
}],
"id": "1"
}'

The output will be nice JSON, that served directly from the JSON RPC server:

{"result":{"Id":"1234","Name":"In the sunburned country","Author":"Bill Bryson"},"error":null,"id":"1"}

Now, coming to the program, we have a lot to understand. The documentation for creating RPC services is very limited. So the technique we used in the program can be applied to a wide variety of use cases. First, we are creating the Args and Book structs to hold the information about the JSON arguments passed and the book structure, respectively. We are defining a remote function called GiveBookDetail on a resource called JSONServer. This struct is a service created to register with the RegisterService function of the RPC server. If you notice, we are also registering the codec as JSON.

Whenever we get a request from the client, we load the JSON file called books.json into memory and then into the Book struct using JSON's Unmarshal method. jsonparse is the alias given for the Go package encoding/json because the JSON package from the Gorilla import has the same name. In order to remove conflict, we used an alias. 

The reply reference is passed to the remote function. In the remote function, we are setting the value of the reply with the matched book. If the ID sent by the client matches with any of the books in JSON, then the data is filled. If there is no match, then empty data will be sent back by the RPC server. In this way, one can create a JSON RPC to allow clients to be universal. Here, we didn't write a Go client. Any client can access data from the service.

Prefer JSON RPC when multiple client technologies need to connect to your RPC service.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.247.68