在golang中實現RPC非常簡單,有封裝好的官方庫和一些第三方庫提供支持。
golang官方的net/rpc庫使用encoding/gob進行編解碼,支持tcp或http數據傳輸方式,由於其他語言不支持gob編解碼方式,所以使用net/rpc庫實現的RPC方法沒辦法進行跨語言調用。
golang官方還提供了net/rpc/jsonrpc庫實現RPC方法,JSON RPC採用JSON進行數據編解碼,因而支持跨語言調用。但目前的jsonrpc庫是基於tcp協議實現的,暫時不支持使用http進行數據傳輸。
除了golang官方提供的rpc庫,還有許多第三方庫爲在golang中實現RPC提供支持,大部分第三方rpc庫的實現都是使用protobuf進行數據編解碼,根據protobuf聲明文件自動生成rpc方法定義與服務註冊代碼,在golang中可以很方便的進行rpc服務調用。
例子一: 使用net/rpc
目錄如下:
rpc_server.go 代碼如下:
package main
import (
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"os"
)
type Greeter struct {
}
type Request struct{
Name string
}
type Response struct{
Msg string
}
func (s *Greeter) Hello(req Request, rsp *Response) error{
rsp.Msg = "hello " + req.Name
return nil
}
func main() {
//註冊rpc 服務
rpc.Register(new(Greeter))
//採用http 協議作爲 rpc 載體
rpc.HandleHTTP()
lis ,err := net.Listen("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("fatal error:", err)
}
fmt.Fprintf(os.Stdout, "%s", "start connection")
http.Serve(lis , nil)
}
rpc_client.go 代碼如下:
package main
import (
"fmt"
"log"
"net/rpc"
)
type Request struct{
Name string //注意首字母大寫,不然gob 時會出現問題: error: gob: type main.Request has no exported fields
}
type Response struct{
Msg string
}
func main() {
conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("dial error:", err)
}
req := Request{"liang"}
var rsq = Response{}
err = conn.Call("Greeter.Hello", req, &rsq)
if err != nil {
log.Fatalln("Greeter error:", err)
}
fmt.Printf("%s",rsq.Msg)
}
注意事項:
1. Request, Response struct 的屬性可見性,一定要爲可見(public) 如下:
type Request struct{
Name string //注意首字母大寫,不然gob 時會出現問題: error: gob: type main.Request has no exported fields
}
type Response struct{
Msg string
}
例子二: 使用 net/rpc; net/rpc/jsonrpc;
目錄如下:
jsonrpc_server.go 代碼如下:
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
)
type Greeter struct {
}
type Request struct{
Name string
}
type Response struct{
Msg string
}
func (s *Greeter) Hello(req Request, rsp *Response) error{
rsp.Msg = "hello " + req.Name
return nil
}
func main() {
//註冊rpc 服務
rpc.Register(new(Greeter))
//採用http 協議作爲 rpc 載體
rpc.HandleHTTP()
lis ,err := net.Listen("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("fatal error:", err)
}
fmt.Fprintf(os.Stdout, "%s", "start connection")
//http.Serve(lis , nil)
for {
//接收客戶端連接請求
conn, err := lis.Accept()
if err != nil {
continue
}
go func(conn net.Conn) {
fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
jsonrpc.ServeConn(conn)
}(conn)
}
}
jsonrpc_client.go 代碼如下:
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
type Request struct{
Name string //注意首字母大寫,不然gob 時會出現問題: error: gob: type main.Request has no exported fields
}
type Response struct{
Msg string
}
func main() {
//conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("dial error:", err)
}
req := Request{"liang"}
var rsq = Response{}
err = conn.Call("Greeter.Hello", req, &rsq)
if err != nil {
log.Fatalln("Greeter error:", err)
}
fmt.Printf("%s",rsq.Msg)
}
總結:
我們分別使用net/rpc、net/rpc/jsonrpc, 實現了golang中的RPC服務端,
並給出了對應的golang客戶端RPC調用示例,因爲JSON和protobuf是支持多語言的,所以使用jsonrpc和protorpc實現的RPC方法我們是可以在其他語言中進行調用的。下面給出一個php客戶端程序,通過socket連接調用jsonrpc實現的服務端RPC方法。
php客戶端程序 調查jsonrpc微服務:
<?php
/**
* Created by PhpStorm.
* Date: 2020-01-02
* Time: 15:56
*/
class JsonRPC
{
private $conn;
function __construct($host, $port) {
$this->conn = fsockopen($host, $port, $errno, $errstr, 3);
if (!$this->conn) {
return false;
}
}
public function Call($method, $params) {
if (!$this->conn) {
return false;
}
$err = fwrite($this->conn, json_encode(array(
'method' => $method,
'params' => array($params),
'id' => "999xxx888",
))."\n");
if ($err === false) {
return false;
}
stream_set_timeout($this->conn, 0, 3000);
$line = fgets($this->conn);
if ($line === false) {
return NULL;
}
return json_decode($line,true);
}
}
$client = new JsonRPC("127.0.0.1", 8096);
$args = array("name" => "xiaoli");
$result = $client->Call("Greeter.Hello", $args);
var_dump($result);
//輸出如下:
/Users/zhaozhiliang/Documents/work_www/lbm/platform/trade_service/JsonRPC.php:46:
array(3) {
'id' =>
string(9) "999xxx888"
'result' =>
array(1) {
'Msg' =>
string(12) "hello xiaoli"
}
'error' =>
NULL
}