This is a creation in Article, where the information may have evolved or changed.
The go language version of the PROTOBUF-RPC is basically complete. Now let's simply say how to use it.
Install the test environment
Download the code first (not supported go get
):
hg clone https://bitbucket.org/chai2010/gopath
Then the downloaded directory is set to GOPATH
, and added $GOPATH/bin
to the PATH
environment variable.
The $GOPATH/bin
version under Windows is already included in the 2.4.1
protoc.exe
. If you are Linux
waiting for the system, please download and install the protoc
program yourself.
Install protoc.exe
The Go language plugin:
go install encoding/protobuf/protoc-gen-go
The plugin is based on code.google.com/p/goprotobuf/protoc-gen-go
implementation, which mainly adds encoding/protobuf/protoc-gen-go/generator/service.go
files for RPC
the code generation. The generated RPC
code relies on the net/rpc/protorpc
underlying implementation of this package Protobuf-RPC
, which can be used alone.
You can now run the test program:
C:\>go test net/rpc/protorpc/service.pbok net/rpc/protorpc/service.pb 2.123s
Test pass, continue.
Compiling the Proto file
Create a pbrpc
subdirectory that is named working directory and then create pbrpc/arith.pb
.
net/rpc/protorpc/service.pb/service.proto
pbrpc/arith.pb
the subdirectory to which the files are copied.
The package name is changed arith
to, arith.proto
the contents of the file are as follows:
package arith;option cc_generic_services = true;option java_generic_services = true;option py_generic_services = true;message ArithRequest { optional int32 a = 1; optional int32 b = 2;}message ArithResponse { optional int32 c = 1;}service ArithService { rpc add (ArithRequest) returns (ArithResponse); rpc mul (ArithRequest) returns (ArithResponse); rpc div (ArithRequest) returns (ArithResponse); rpc error (ArithRequest) returns (ArithResponse);}
The main definition is an ArithService
interface. Be aware of cc_generic_services
/ java_generic_services
, a py_generic_services
few options.
I mentioned in the premise that protoc-gen-go
when generating code, these 3 options must have at least one true
code that will be generated RPC
.
Of course, if you do not generate RPC
code, you can use net/rpc/protorpc
the package separately. However, protoc-gen-go
the generated code is much simpler.
pbrpc/arith.pb
to enter a subdirectory, compile the arith.proto
file:
protoc --go_out=. arith.proto
Generate arith.pb.go
the file, where RPC
the code is mainly the following:
Type Arithservice Interface {ADD (in *arithrequest, out *arithresponse) error Mul (in *arithrequest, out *arithrespon SE) error Div (in *arithrequest, out *arithresponse) Error error (in *arithrequest, out *arithresponse) error}//Regis Terarithservice publish the given Arithservice implementation on the Server.func registerarithservice (SRV *rpc. Server, x arithservice) error {if err: = srv. Registername ("Arithservice", X); Err! = Nil {return err} return nil}//Servearithservice serves the given arithservice implementation on con N.func servearithservice (conn io. Readwritecloser, x Arithservice) error {srv: = RPC. NewServer () If err: = srv. Registername ("Arithservice", X); Err! = Nil {return err} srv. Servecodec (PROTORPC. NEWSERVERCODEC (conn)) return nil}//Listenandservearithservice Listen announces on the local network address laddr//an D serves the given Arithservice Implementation.func listenandservearithservice (Network, addr string, x AriThservice) Error {clients, err: = Net. Listen (Network, addr) if err! = Nil {return err} SRV: = RPC. NewServer () If err: = srv. Registername ("Arithservice", X); Err! = Nil {return err} for {conn, err: = clients. Accept () if err! = Nil {return err} go srv. Servecodec (PROTORPC. NEWSERVERCODEC (conn)} Panic ("unreachable")}type rpcarithservicestub struct {*rpc. Client}func (c *rpcarithservicestub) Add (in *arithrequest, out *arithresponse) error {return C.call ("Arithservice.add" }func (c *rpcarithservicestub) Mul (in *arithrequest, out *arithresponse) error {return C.call ("Arithservice"). Mul ", in, out)}func (c *rpcarithservicestub) Div (in *arithrequest, out *arithresponse) error {return C.call (" Arithserv Ice. Div ", in, out)}func (c *rpcarithservicestub) error (in *arithrequest, out *arithresponse) error {return C.call (" Arithse Rvice. Error ", in, out)}//Dialarithservice connects to an ArithService at the specified network Address.func dialarithservice (network, addr string) (*rpc. Client, Arithservice, error) {conn, err: = Net. Dial (Network, addr) if err! = Nil {return nil, nil, err} c, srv: = newarithserviceclient (conn) return C, SRV, nil}//newarithserviceclient returns a Arithservice RPC. Client and stub to handle//requests to the set of Arithservice at the other end of the Connection.func NEWARITHSERVICECLI ENT (conn io. Readwritecloser) (*rpc. Client, Arithservice) {c: = RPC. Newclientwithcodec (PROTORPC. NEWCLIENTCODEC (conn)) return C, &rpcarithservicestub{c}}//Newarithservicestub returns a arithservice stub to Handl E RPC. Client.func newarithservicestub (c *rpc. Client) Arithservice {return &rpcarithservicestub{c}}
The generated server-side code is: ListenAndServeArithService
, ServeArithService
, RegisterArithService
.
The interfaces of the generated client are:, DialArithService
NewArithServiceClient
NewArithServiceStub
.
where RPC
interfaces correspond to ArithService
interfaces.
Writing test Code
Create the file in the pbrpc
directory rpc_server.go
, with the following code:
Package Mainimport ("Encoding/protobuf/proto" "Errors" "./ARITH.PB") type Arith IntFunc (t *arith) Add (Args *arith. Arithrequest, reply *arith. Arithresponse) Error {reply. C = Proto. Int32 (args. Geta () + args. GETB ()) return Nil}func (t *arith) Mul (args *arith. Arithrequest, reply *arith. Arithresponse) Error {reply. C = Proto. Int32 (args. Geta () * args. GETB ()) return Nil}func (t *arith) Div (args *arith. Arithrequest, reply *arith. Arithresponse) Error {if args. GETB () = = 0 {return errors. New ("Divide by Zero")} reply. C = Proto. Int32 (args. Geta ()/args. GETB ()) return Nil}func (t *arith) Error (args *arith. Arithrequest, reply *arith. Arithresponse) error {return errors. New ("Aritherror")}func Main () {Arith. Listenandservearithservice ("TCP", ": 1234", New (Arith))}
The key thing is arith.ListenAndServeArithService("tcp", ":1234", new(Arith))
. Of course, you can also use RegisterArithService
or wait for the ServeArithService
interface to be customized.
Then pbrpc
create the rpc_client.go
corresponding client with the following code:
Package Mainimport ("Encoding/protobuf/proto" "Log" "./ARITH.PB") func main () {//Client client, stub, err : = Arith. Dialarithservice ("TCP", "127.0.0.1:1234") if err! = Nil {log. Fatalf (' Arith. Dialarithservice ("TCP", "127.0.0.1:1234"):%v ', err)} defer client. Close () var args arith. arithrequest var reply arith. Arithresponse//Add args. A = Proto. Int32 (1) args. B = Proto. Int32 (2) If Err = stub. ADD (&args, &reply); Err! = Nil {log. Fatalf (' Arith. ADD:%v ', err)} if reply. GetC ()! = 3 {log. Fatalf (' Arith. Add:expected =%d, got =%d ', 3, reply. GetC ())}//Mul args. A = Proto. Int32 (2) args. B = Proto. Int32 (3) If Err = stub. Mul (&args, &reply); Err! = Nil {log. Fatalf (' Arith. Mul:%v ', err)} if reply. GetC ()! = 6 {log. Fatalf (' Arith. Mul:expected =%d, got =%d ', 6, reply. GetC ())}//Div args. A = Proto. Int32 (+) args. B = Proto. Int32 (5) If Err = stub. Div (&args, &reply); Err! = Nil {log. Fatalf (' Arith. Div:%v ', err)} if reply. GetC ()! = 2 {log. Fatalf (' Arith. Div:expected =%d, got =%d ', 2, reply. GetC ())}//Div zero args. A = Proto. Int32 (1) args. B = Proto. Int32 (0) If Err = stub. Div (&args, &reply); Err. Error ()! = "Divide by zero" {log. Fatalf (' Arith. error:expected =%s, got =%s ', ' divide by zero ', err. Error ())}//Error args. A = Proto. Int32 (1) args. B = Proto. Int32 (2) If Err = stub. Error (&args, &reply); Err. Error ()! = "Aritherror" {log. Fatalf (' Arith. error:expected =%s, got =%s ', "Aritherror", err. Error ())} log. Printf ("Done")}
You can then start the service and test the client.