生命不止,继续 go go go
继续Gorilla web toolkit,今天介绍rpc.
关于rpc,诸位也不会很陌生,之前也有介绍过:
Go实战–go中使用rpc(The way to go)
Go实战–golang中使用gRPC和Protobuf实现高性能api(golang/protobuf、google.golang.org/grpc)
gorilla/rpc
官网:
http://www.gorillatoolkit.org/pkg/rpc
简介:
Package gorilla/rpc is a foundation for RPC over HTTP services, providing access to the exported methods of an object through HTTP requests.
This package derives from the standard net/rpc package but uses a single HTTP request per call instead of persistent connections.
获取:
go get github.com/gorilla/rpc
API:
func NewServer
func NewServer() *Server
NewServer returns a new RPC server.
type Codec
type Codec interface {
NewRequest(*http.Request) CodecRequest
}
Codec creates a CodecRequest to process each request.
func (*Server) RegisterCodec
func (s *Server) RegisterCodec(codec Codec, contentType string)
RegisterCodec adds a new codec to the server.
Codecs are defined to process a given serialization scheme, e.g., JSON or XML. A codec is chosen based on the “Content-Type” header from the request, excluding the charset definition.
func (*Server) RegisterService
func (s *Server) RegisterService(receiver interface{}, name string) error
RegisterService adds a new service to the server.
The name parameter is optional: if empty it will be inferred from the receiver type name
gorilla/rpc/json
官网:
http://www.gorillatoolkit.org/pkg/rpc/json
获取:
go get github.com/gorilla/rpc/json
API:
func DecodeClientResponse
func DecodeClientResponse(r io.Reader, reply interface{}) error
DecodeClientResponse decodes the response body of a client request into the interface reply.
func EncodeClientRequest
func EncodeClientRequest(method string, args interface{}) ([]byte, error)
EncodeClientRequest encodes parameters for a JSON-RPC client request.
net/rpc
先看看golang官方为我们提供的标准库net/rpc:
- 方法的类型是可输出的 (the method’s type is exported)
- 方法本身也是可输出的 (the method is exported)
- 方法必须由两个参数,必须是输出类型或者是内建类型 (the method has two arguments, both exported or builtin types)
- 方法的第二个参数是指针类型 (the method’s second argument is a pointer)
- 方法返回类型为 error (the method has return type error)
例子:
生成了一个Arith对象,并使用rpc.Register注册这个服务,然后通过HTTP暴露出来。
客户端可以看到服务Arith以及它的两个方法Arith.Multiply和Arith.Divide
model.go
package rpcexample
import (
"log"
)
// Holds arguments to be passed to service Arith in RPC call
type Args struct {
A, B int
}
// Represents service Arith with method Multiply
type Arith int
// Result of RPC call is of this type
type Result int
// This procedure is invoked by rpc and calls rpcexample.Multiply
func (t *Arith) Multiply(args Args, result *Result) error {
return Multiply(args, result)
}
// stores product of args.A and args.B in result pointer
func Multiply(args Args, result *Result) error {
log.Printf("Multiplying %d with %d\n", args.A, args.B)
*result = Result(args.A * args.B)
return nil
}
server/main.go
package main
import (
"log"
"net"
"net/http"
"net/rpc"
"gorilla_toolkit/gorilla_rpc/model"
)
func main() {
//register Arith object as a service
arith := new(rpcexample.Arith)
err := rpc.Register(arith)
if err != nil {
log.Fatalf("Format of service Arith isn't correct. %s", err)
}
rpc.HandleHTTP()
//start listening for messages on port 1234
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatalf("Couldn't start listening on port 1234. Error %s", e)
}
log.Println("Serving RPC handler")
err = http.Serve(l, nil)
if err != nil {
log.Fatalf("Error serving: %s", err)
}
}
client/main.go
package main
import (
"log"
"net/rpc"
"gorilla_toolkit/gorilla_rpc/model"
)
func main() {
//make connection to rpc server
client, err := rpc.DialHTTP("tcp", ":1234")
if err != nil {
log.Fatalf("Error in dialing. %s", err)
}
//make arguments object
args := &rpcexample.Args{
A: 2,
B: 3,
}
//this will store returned result
var result rpcexample.Result
//call remote procedure with args
err = client.Call("Arith.Multiply", args, &result)
if err != nil {
log.Fatalf("error in Arith", err)
}
//we got our result in result
log.Printf("%d*%d=%d\n", args.A, args.B, result)
}
gorilla/rpc应用
server/main.go
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/rpc"
"github.com/gorilla/rpc/json"
)
type Args struct {
A, B int
}
type Arith int
type Result int
func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error {
log.Printf("Multiplying %d with %d\n", args.A, args.B)
*result = Result(args.A * args.B)
return nil
}
func main() {
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
arith := new(Arith)
s.RegisterService(arith, "")
r := mux.NewRouter()
r.Handle("/rpc", s)
http.ListenAndServe(":1234", r)
}
client/main.go
package main
import (
"bytes"
"log"
"net/http"
"gorilla_toolkit/gorilla_rpc/model"
"github.com/gorilla/rpc/json"
)
func main() {
url := "http://localhost:1234/rpc"
args := &rpcexample.Args{
A: 2,
B: 3,
}
message, err := json.EncodeClientRequest("Arith.Multiply", args)
if err != nil {
log.Fatalf("%s", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(message))
if err != nil {
log.Fatalf("%s", err)
}
req.Header.Set("Content-Type", "application/json")
client := new(http.Client)
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Error in sending request to %s. %s", url, err)
}
defer resp.Body.Close()
var result rpcexample.Result
err = json.DecodeClientResponse(resp.Body, &result)
if err != nil {
log.Fatalf("Couldn't decode response. %s", err)
}
log.Printf("%d*%d=%d\n", args.A, args.B, result)
}
运行结果
客户端:
2018/01/05 11:50:47 2*3=6
服务端:
2018/01/05 11:50:47 Multiplying 2 with 3