golang的rpc使用/ go實現簡單的基礎的微服務

在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
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章