遠程過程調用(Remote Procedure Call,縮寫爲 RPC)是一個計算機通訊協議。經過該協議程序員能夠實現像調取本地函數同樣,調取遠程服務的函數。這裏介紹一個高效的rpc庫(rpcx)。php
rpcx 是一個分佈式的Go語言的 RPC 框架,支持Zookepper、etcd、consul多種服務發現方式,多種服務路由方式, 是目前性能最好的 RPC 框架之一。html
官方資料:https://books.studygolang.com...java
項目:https://github.com/smallnest/...node
rpcx使用二進制協議且與平臺無關,這意味着您能夠使用其餘語言(例如Java,Python,nodejs)開發服務,還能夠使用其餘編程語言來調用Go中開發的服務。python
測試結果代表,除了標準rpc庫以外,rpcx的性能要優於其餘rpc框架。git
go get -v github.com/smallnest/rpcx/...
新建server.go程序員
package main import ( "context" "flag" "fmt" example "github.com/rpcxio/rpcx-examples" "github.com/smallnest/rpcx/server" ) var ( addr = flag.String("addr", "localhost:8972", "server address") ) type Arith struct{} // the second parameter is not a pointer func (t *Arith) Mul(ctx context.Context, args example.Args, reply *example.Reply) error { reply.C = args.A * args.B fmt.Println("C=", reply.C) return nil } func main() { flag.Parse() s := server.NewServer() //s.Register(new(Arith), "") s.RegisterName("Arith", new(Arith), "") err := s.Serve("tcp", *addr) if err != nil { panic(err) } }
client.gogithub
package main import ( "context" "flag" "fmt" "log" "github.com/smallnest/rpcx/protocol" example "github.com/rpcxio/rpcx-examples" "github.com/smallnest/rpcx/client" ) var ( addr = flag.String("addr", "localhost:8972", "server address") ) func main() { flag.Parse() d := client.NewPeer2PeerDiscovery("tcp@"+*addr, "") opt := client.DefaultOption opt.SerializeType = protocol.JSON xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, opt) defer xclient.Close() args := example.Args{ A: 10, B: 20, } reply := &example.Reply{} err := xclient.Call(context.Background(), "Mul", args, reply) if err != nil { log.Fatalf("failed to call: %v", err) } log.Printf("%d * %d = %d", args.A, args.B, reply.C) }
服務端golang
go run server.go
客戶端redis
go run client.go
這時服務端輸出:
C= 200 2020/07/21 15:19:02 server.go:358: INFO : client has closed this connection: 127.0.0.1:50186
客戶端輸出:
2020/07/21 15:19:02 10 * 20 = 200
rpcx還提供了rpcx-gateway,您能夠使用任何編程語言編寫客戶端,以經過rpcx-gateway調用rpcx服務。
安裝rpcx-gateway
go get github.com/rpcxio/rpcx-gateway
新建gateway.go文件,代碼以下:
package main import ( "errors" "flag" "fmt" "log" "strings" "time" gateway "github.com/rpcxio/rpcx-gateway" "github.com/rpcxio/rpcx-gateway/gin" "github.com/smallnest/rpcx/client" ) var ( addr = flag.String("addr", ":9981", "http server address") registry = flag.String("registry", "peer2peer://127.0.0.1:8972", "registry address") basePath = flag.String("basepath", "/rpcx", "basepath for zookeeper, etcd and consul") failmode = flag.Int("failmode", int(client.Failover), "failMode, Failover in default") selectMode = flag.Int("selectmode", int(client.RoundRobin), "selectMode, RoundRobin in default") ) func main() { flag.Parse() d, err := createServiceDiscovery(*registry) if err != nil { log.Fatal(err) } httpServer := gin.New(*addr) gw := gateway.NewGateway("/", httpServer, d, client.FailMode(*failmode), client.SelectMode(*selectMode), client.DefaultOption) gw.Serve() } func createServiceDiscovery(regAddr string) (client.ServiceDiscovery, error) { i := strings.Index(regAddr, "://") if i < 0 { return nil, errors.New("wrong format registry address. The right fotmat is [registry_type://address]") } regType := regAddr[:i] regAddr = regAddr[i+3:] switch regType { case "peer2peer": //peer2peer://127.0.0.1:8972 return client.NewPeer2PeerDiscovery("tcp@"+regAddr, ""), nil case "multiple": var pairs []*client.KVPair pp := strings.Split(regAddr, ",") for _, v := range pp { pairs = append(pairs, &client.KVPair{Key: v}) } return client.NewMultipleServersDiscovery(pairs), nil case "zookeeper": return client.NewZookeeperDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil case "etcd": return client.NewEtcdDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil case "etcdv3": return client.NewEtcdV3DiscoveryTemplate(*basePath, []string{regAddr}, nil), nil case "consul": return client.NewConsulDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil case "redis": return client.NewRedisDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil case "mdns": return client.NewMDNSDiscoveryTemplate(10*time.Second, 10*time.Second, ""), nil default: return nil, fmt.Errorf("wrong registry type %s. only support peer2peer,multiple, zookeeper, etcd, consul and mdns", regType) } }
運行 gateway.go
go run gateway.go
注意:運行網關前,要保證rpcx服務端(server.go)啓動,這裏以php客戶端爲例
代碼以下:
<?php $url = 'http://localhost:9981/'; $data = '{"A":10, "B":20}'; // use key 'http' even if you send the request to https://... $options = array( 'http' => array( 'header' => "Content-type: application/rpcx\r\n" . // "X-RPCX-MessageID: 12345678\r\n" . // "X-RPCX-MesssageType: 0\r\n" . "X-RPCX-SerializeType: 1\r\n" . "X-RPCX-ServicePath: Arith\r\n" . "X-RPCX-ServiceMethod: Mul\r\n", 'method' => 'POST', 'content' => $data ) ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context); if ($result === FALSE) { /* Handle error */ } var_dump($result); ?>
結果:
string(9) "{"C":200}"
server.go 輸出
C= 200
這樣就實現跨語言了。
其餘語言示例
https://github.com/rpcxio/rpc...
rpcx 3.0已針對如下目標進行了重構:
https://github.com/rpcxio/rpc...
https://github.com/smallnest/...