日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

go-micro開發RPC服務以及運行原理介紹_Golang

作者:波斯馬 ? 更新時間: 2022-08-26 編程語言

go-micro是一個知名的golang微服務框架,最新版本是v4,這篇文章將介紹go-micro v4開發RPC服務的方法及其運作原理。

基本概念

go-micro有幾個重要的概念,后邊開發RPC服務和介紹其運行原理的時候會用到,這里先熟悉下:

  • Service:代表一個go-micro應用程序,Service中包括:Server、Client、Broker、Transport、Registry、Config、Store、Cache等程序運行所需的各個模塊。
  • Server:代表一個go-micro服務器,主要函數包括:Start、Stop、Handle、Subscribe。默認創建的Server是 rpcServer。
  • Broker:用于處理異步消息,主要的函數包括:Connect、Publish、Subscribe。默認的Broker是httpBroker。
  • Router:用于消息處理的路由,內部包括兩種路由方式:RPC服務映射serviceMap和消息訂閱器subscribers。
  • Codec:用于消息的編解碼,主要函數包括:Marshal、Unmarshal默認的Codec是json.Marshaler,是基于jsonpb的。RPC服務是根據請求頭中的Content-Type自動創建的。
  • Registry:用于服務發現,主要函數包括:Register、Deregister、GetService、ListServices、Watch。默認的Registry是mdns。
  • Selector:?用于從同一個服務的多個實例之中選擇一個,支持緩存,有隨機和輪詢兩種策略。
  • Transport:用于同步通信,主要函數包括:Dial、Listen。它的底層基于Socket的send、recv語義,有多種實現,包括http、grpc、quic等。默認的Transport是httpTransport。

開發RPC服務

RPC全稱是Remote Procedure Call,翻譯過來是就是:遠程過程調用,中心思想是:像調用本地函數一樣調用遠程函數。常見的Dubbo、Spring Cloud都可以稱為RPC框架,還有最近很流行的gRPC。

使用go-micro創建一個RPC服務很簡單,共分三步走:

1、編寫proto協議文件

這個服務提供的功能很簡單,名字為Hello,提供一個方法名字為Say,需要傳入一個字符串Name,然后返回一個字符串Message。這個文件我命名為 hello.proto,放到了項目中的 proto 文件夾中。

syntax = "proto3";

option go_package="/proto";

package Business;

service Hello {
  rpc Say (SayRequest) returns (SayResponse);
}

message SayResponse {
  string Message = 1;
}

message SayRequest {
  string Name = 1;
}

2、生成go-micro服務端代理

需要首先安裝protoc和兩個代碼生成插件。

protoc下載地址:https://github.com/protocolbuffers/protobuf/releases,保存到GO?PATH/bin目錄中。同時建議將?GOPATH/bin?添加到環境變量?PATH?中,方便直接執行相關命令。

兩個插件直接通過命令即可安裝:

go install google.golang.org/protobuf/cmd/protoc-gen-go
go install go-micro.dev/v4/cmd/protoc-gen-micro@v4

然后在項目的目錄下執行命令:

protoc --go_out=. --go_opt=paths=source_relative --micro_out=. --micro_opt=paths=source_relative proto/hello.proto

然后會在proto文件夾中生成兩個文件:hello.pb.go 和 hello.pb.micro.go 。

下個步驟中就要使用它們來創建RPC服務。

3、編寫go-micro服務

這里先把代碼貼出來,然后再做一個簡要說明:

package main

import (
	"context"
	"fmt"
	"log"
	"rpchello/proto"

	"go-micro.dev/v4"
	"go-micro.dev/v4/server"
)

type Hello struct{}

func (s *Hello) Say(ctx context.Context, req *proto.SayRequest, rsp *proto.SayResponse) error {
	fmt.Println("request:", req.Name)
	rsp.Message = "Hello " + req.Name
	return nil
}

func main() {
	rpcServer := server.NewServer(
		server.Name("rpchello.service"),
		server.Address("0.0.0.0:8001"),
	)

	proto.RegisterHelloHandler(rpcServer, &Hello{})

	service := micro.NewService(
		micro.Server(rpcServer),
	)

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}

上邊我們創建了一個 Hello 類型,然后給它綁定了一個名為Say的函數。這個是和proto協議對應的,其實是實現了生成代碼 hello.pb.micro.go 中的HelloHandler接口:

type HelloHandler interface {
	Say(context.Context, *SayRequest, *SayResponse) error
}

然后main函數中是我們的重頭戲:先創建一個Server,默認情況下就是rpc Server,設置它的名字、監聽地址等參數;然后創建一個Service,并綁定剛剛創建的Server;然后使用生成的服務端代理函數將我們編寫的Hello服務注冊到Server中;最后開啟運行Service。

當然只有一個服務端沒有什么意義,還得有客戶端來訪問它。這里也給一個例子:

package main

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"rpchello/proto"

	"go-micro.dev/v4"
	"go-micro.dev/v4/client"
)

func main() {

	service := micro.NewService(
		micro.Client(client.NewClient()),
	)

	service.Init()
	client := proto.NewHelloService("rpchello.service", service.Client())

	rsp, err := client.Say(context.TODO(), &proto.SayRequest{Name: "BOSSMA"})
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(rsp)

	fmt.Println("Press Enter key to exit the program...")
	in := bufio.NewReader(os.Stdin)
	_, _, _ = in.ReadLine()
}

這里調用服務的時候沒有指定服務的地址和端口,因為內部走了服務發現,服務端會自動注冊服務,客戶端會根據服務名稱查找到對應的地址和端口。默認的服務發現機制使用的是mdns。

RPC服務的運行原理

這里從服務端的角度進行介紹,先來看一張圖:

請大家參考代碼從上往下看。

NewServer 時創建一個rpcServer,這個rpcServer還會創建一個httpTransport用于程序間網絡通信,并綁定到當前rpcServer。

RegisterXXXHandler 時使用我們編寫的Handler創建一個內部的service實例,然后注冊這個service實例到rpcServer內部的router中,客戶端請求時會用到它。這里其實可以注冊任意一個帶方法的類型,并不一定要定義proto協議,定義它只是為了協作更方便。

Service.Run 時會調用rpcServer的Start方法,這個方法內部會調用其綁定的httpTransport的Listen方法,然后在其創建的Listener上接收客戶端連接,接收方法Accept傳入了當前rpcServer的連接處理方法:rpcServer.ServeConn,有連接到來時會調用它。

當客戶端請求來臨時,客戶端連接被交給rpcServer的ServeConn方法,然后又調用到HandleEvent方法。

然后進入rpcServer內部的router的函數ServeRequest中,通過分析請求消息,找到請求的服務名字和方法名字,在router中找到前面注冊過的service,通過servcie.call,再進入function.call,最終通過反射調用到我們編寫的Handler的業務方法。

有的同學可能會想,反射不是性能很低嗎?!反射性能低主要是查找方法和字段的時候,調用方法的性能并不低,而查找方法和字段等的操作已經在RegisterXXXHandler的步驟中做了,并且緩存到了router中,所以性能并不受影響。

原文鏈接:https://www.cnblogs.com/bossma/p/16184306.html

欄目分類
最近更新