更新時間:2018-06-07 來源:黑馬程序員 瀏覽量:
開發(fā)單體式應(yīng)用
假設(shè)你正準(zhǔn)備開發(fā)一款與Uber和Hailo競爭的出租車調(diào)度軟件,經(jīng)過初步會議和需求分析,你可能使用傳統(tǒng)的程序框架來生成你的項目,最終的程序架構(gòu)如下圖所示:
盡管也是模塊化邏輯,但是最終它還是會打包并部署為單體式應(yīng)用。具體的格式依賴于應(yīng)用語言和框架。最終程序發(fā)布的時候也會被打包成單一的程序發(fā)布出來。
單體式應(yīng)用的不足
不幸的是,這種簡單方法卻有很大的局限性。一個簡單的應(yīng)用會隨著時間推移逐漸變大。在每次的sprint中,開發(fā)團(tuán)隊都會面對新“故事”,然后開發(fā)許多新代碼。幾年后,這個小而簡單的應(yīng)用會變成了一個巨大的怪物。這兒有一個例子,我最近和一個開發(fā)者討論,他正在寫一個工具,用來分析他們一個擁有數(shù)百萬行代碼的應(yīng)用中JAR文件之間的依賴關(guān)系。我很確信這個代碼正是很多開發(fā)者經(jīng)過多年努力開發(fā)出來的一個怪物。
一旦你的應(yīng)用變成一個又大又復(fù)雜的怪物,那開發(fā)團(tuán)隊肯定很痛苦。敏捷開發(fā)和部署舉步維艱,其中最主要問題就是這個應(yīng)用太復(fù)雜,以至于任何單個開發(fā)者都不可能搞懂它。因此,修正bug和正確的添加新功能變的非常困難,并且很耗時。另外,團(tuán)隊士氣也會走下坡路。如果代碼難于理解,就不可能被正確的修改。最終會走向巨大的、不可理解的泥潭。
另外,復(fù)雜而巨大的單體式應(yīng)用也不利于持續(xù)性開發(fā)。今天,SaaS應(yīng)用常態(tài)就是每天會改變很多次,而這對于單體式應(yīng)用模式非常困難。另外,這種變化帶來的影響并沒有很好的被理解,所以不得不做很多手工測試。那么接下來,持續(xù)部署也會很艱難。
單體式應(yīng)用另外一個問題是可靠性。因為所有模塊都運(yùn)行在一個進(jìn)程中,任何一個模塊中的一個bug,比如內(nèi)存泄露,將會有可能弄垮整個進(jìn)程。除此之外,因為所有應(yīng)用實例都是唯一的,這個bug將會影響到整個應(yīng)用的可靠性。
最后,單體式應(yīng)用使得采用新架構(gòu)和語言非常困難。比如,設(shè)想你有兩百萬行采用XYZ框架寫的代碼。如果想改成ABC框架,無論是時間還是成本都是非常昂貴的,即使ABC框架更好。因此,這是一個無法逾越的鴻溝。你不得不在最初選擇面前低頭。
那么如何應(yīng)對呢?
微處理架構(gòu)——處理復(fù)雜事物
許多公司,比如Amazon、eBay和NetFlix,通過采用微處理結(jié)構(gòu)模式解決了上述問題。其思路不是開發(fā)一個巨大的單體式的應(yīng)用,而是將應(yīng)用分解為小的、互相連接的微服務(wù)。
一個微服務(wù)一般完成某個特定的功能,比如下單管理、客戶管理等等。每一個微服務(wù)都是微型六角形應(yīng)用,都有自己的業(yè)務(wù)邏輯和適配器。一些微服務(wù)還會發(fā)布API給其它微服務(wù)和應(yīng)用客戶端使用。其它微服務(wù)完成一個Web UI,運(yùn)行時,每一個實例可能是一個云VM或者是Docker容器。
比如,一個前面描述系統(tǒng)可能的分解如下:
每一個應(yīng)用功能區(qū)都使用微服務(wù)完成,另外,Web應(yīng)用會被拆分成一系列簡單的Web應(yīng)用(比如一個對乘客,一個對出租車駕駛員)。這樣的拆分對于不同用戶、設(shè)備和特殊應(yīng)用場景部署都更容易。
每一個后臺服務(wù)開放一個REST API,許多服務(wù)本身也采用了其它服務(wù)提供的API。比如,駕駛員管理使用了告知駕駛員一個潛在需求的通知服務(wù)。UI服務(wù)激活其它服務(wù)來更新Web頁面。所有服務(wù)都是采用異步的,基于消息的通訊。
微服務(wù)架構(gòu)的好處
微服務(wù)架構(gòu)模式有很多好處。首先,通過分解巨大單體式應(yīng)用為多個服務(wù)方法解決了復(fù)雜性問題。在功能不變的情況下,應(yīng)用被分解為多個可管理的分支或服務(wù)。每個服務(wù)都有一個用RPC-或者消息驅(qū)動API定義清楚的邊界。微服務(wù)架構(gòu)模式給采用單體式編碼方式很難實現(xiàn)的功能提供了模塊化的解決方案,由此,單個服務(wù)很容易開發(fā)、理解和維護(hù)。
第二,這種架構(gòu)使得每個服務(wù)都可以有專門開發(fā)團(tuán)隊來開發(fā)。開發(fā)者可以自由選擇開發(fā)技術(shù),提供API服務(wù)。當(dāng)然,許多公司試圖避免混亂,只提供某些技術(shù)選擇。然后,這種自由意味著開發(fā)者不需要被迫使用某項目開始時采用的過時技術(shù),他們可以選擇現(xiàn)在的技術(shù)。甚至于,因為服務(wù)都是相對簡單,即使用現(xiàn)在技術(shù)重寫以前代碼也不是很困難的事情。
第三,微服務(wù)架構(gòu)模式是每個微服務(wù)獨(dú)立的部署。開發(fā)者不再需要協(xié)調(diào)其它服務(wù)部署對本服務(wù)的影響。這種改變可以加快部署速度。UI團(tuán)隊可以采用AB測試,快速的部署變化。微服務(wù)架構(gòu)模式使得持續(xù)化部署成為可能。
最后,微服務(wù)架構(gòu)模式使得每個服務(wù)獨(dú)立擴(kuò)展。你可以根據(jù)每個服務(wù)的規(guī)模來部署滿足需求的規(guī)模。甚至于,你可以使用更適合于服務(wù)資源需求的硬件。
微服務(wù)架構(gòu)的特性
1. 單一職責(zé)
微服務(wù)架構(gòu)中的每個服務(wù),都是具有業(yè)務(wù)邏輯的,符合高內(nèi)聚、低耦合原則以及單一職責(zé)原則的單元,不同的服務(wù)通過“管道”的方式靈活組合,從而構(gòu)建出龐大的系統(tǒng)。
2. 輕量級通信
服務(wù)之間通過輕量級的通信機(jī)制實現(xiàn)互通互聯(lián),而所謂的輕量級,通常指語言無關(guān)、平臺無關(guān)的交互方式。
對于輕量級通信的格式而言,我們熟悉的 XML 和 JSON,它們是語言無關(guān)、平臺無關(guān)的;對于通信的協(xié)議而言,通?;? HTTP,能讓服務(wù)間的通信變得標(biāo)準(zhǔn)化、無狀態(tài)化。目前大家熟悉的 REST(Representational State Transfer)是實現(xiàn)服務(wù)間互相協(xié)作的輕量級通信機(jī)制之一。使用輕量級通信機(jī)制,可以讓團(tuán)隊選擇更適合的語言、工具或者平臺來開發(fā)服務(wù)本身。
3. 獨(dú)立性
每個服務(wù)在應(yīng)用交付過程中,獨(dú)立地開發(fā)、測試和部署。
在單塊架構(gòu)中所有功能都在同一個代碼庫,功能的開發(fā)不具有獨(dú)立性;當(dāng)不同小組完成多個功能后,需要經(jīng)過集成和回歸測試,測試過程也不具有獨(dú)立性;當(dāng)測試完成后,應(yīng)用被構(gòu)建成一個包,如果某個功能存在
bug,將導(dǎo)致整個部署失敗或者回滾。
在微服務(wù)架構(gòu)中,每個服務(wù)都是獨(dú)立的業(yè)務(wù)單元,與其他服務(wù)高度解耦,只需要改變當(dāng)前服務(wù)本身,就可以完成獨(dú)立的開發(fā)、測試和部署。
4. 進(jìn)程隔離
在微服務(wù)架構(gòu)中,每個服務(wù)都是獨(dú)立的業(yè)務(wù)單元,與其他服務(wù)高度解耦,只需要改變當(dāng)前服務(wù)本身,就可以完成獨(dú)立的開發(fā)、測試和部署。有時候我們會將重復(fù)的代碼抽取出來封裝成組件,在單塊架構(gòu)中,組件通常的形態(tài)叫做共享庫(如 jar 包或者 DLL),但是當(dāng)程序運(yùn)行時,所有組件最終也會被加載到同一進(jìn)程中運(yùn)行。
在微服務(wù)架構(gòu)中,應(yīng)用程序由多個服務(wù)組成,每個服務(wù)都是高度自治的獨(dú)立業(yè)務(wù)實體,可以運(yùn)行在獨(dú)立的進(jìn)程中,不同的服務(wù)能非常容易地部署到不同的主機(jī)上。
既然要介紹微服務(wù),就不得不介紹一下與微服務(wù)相關(guān)的技術(shù)。那么,接下來,我們一一做一下詳細(xì)講解。
protoBuf(Google旗下平臺語言無關(guān)序列化數(shù)據(jù)協(xié)議)
protobuf是google旗下的一款平臺無關(guān),語言無關(guān),可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式。所以很適合用做數(shù)據(jù)存儲和作為不同應(yīng)用,不同語言之間相互通信的數(shù)據(jù)交換格式,只要實現(xiàn)相同的協(xié)議格式即同一 proto文件被編譯成不同的語言版本,加入到各自的工程中去。這樣不同語言就可以解析其他語言通過 protobuf序列化的數(shù)據(jù)。目前官網(wǎng)提供了 C++,Python,JAVA,GO等語言的支持。
protobuf語法定義
要想使用 protobuf必須得先定義 proto文件。所以得先熟悉 protobuf的消息定義的相關(guān)語法。下面就來介紹。
首先我們先定義一個 proto文件,結(jié)構(gòu)如下:
message Article {
required int32 article_id=1;
optional string article_excerpt=2;
repeated string article_picture=3;
}
上面我們主要定義了一個消息,這個消息包括文章 ID,文章摘要,文章圖片。下面給出消息定義的相關(guān)說明 :
n message是消息定義的關(guān)鍵字。
a) required表示是一個必須字段,必須相對于發(fā)送方,在發(fā)送消息之前必須設(shè)置該字段的值,對于接收方,必須能夠識別該字段的意思。發(fā)送之前沒有設(shè)置required字段或者無法識別required字段都會引發(fā)編解碼異常,導(dǎo)致消息被丟棄。
b) Optional:表示是一個可選字段,可選對于發(fā)送方,在發(fā)送消息時,可以有選擇性的設(shè)置或者不設(shè)置該字段的值。對于接收方,如果能夠識別可選字段就進(jìn)行相應(yīng)的處理,如果無法識別,則忽略該字段,消息中的其它字段正常處理。---因為optional字段的特性,很多接口在升級版本中都把后來添加的字段都統(tǒng)一的設(shè)置為optional字段,這樣老的版本無需升級程序也可以正常的與新的軟件進(jìn)行通信,只不過新的字段無法識別而已,因為并不是每個節(jié)點(diǎn)都需要新的功能,因此可以做到按需升級和平滑過渡。
c) Repeated:表示該字段可以包含0~N個元素。其特性和optional一樣,但是每一次可以包含多個值??梢钥醋魇窃趥鬟f一個數(shù)組的值
d) int32和string是字段的類型。后面是我們定義的字段名。最后的 1,2,3則是代表每個字段的一個唯一的編號標(biāo)簽,在同一個消息里不可以重復(fù)。這些編號標(biāo)簽用與在消息二進(jìn)制格式中標(biāo)識你的字段,并且消息一旦定義就不能更改。需要說明的是標(biāo)簽在 1到15范圍的采用一個字節(jié)進(jìn)行編碼。所以通常將標(biāo)簽 1到15用于頻繁發(fā)生的消息字段。編號標(biāo)簽大小的范圍是1到229 – 1。此外不能使用protobuf系統(tǒng)預(yù)留的編號標(biāo)簽(19000 -19999)
當(dāng)然 protobuf支持更多的類型,比如 bool,double,float,枚舉,也可以是其他定義過的消息類型譬如前面的消息 Article。支持的基本類型如下:
一般在我們的項目中肯定會有很多消息類型。我們總不能都定義在一個文件中。當(dāng)一個 proto文件需要另一個 proto文件的時候,我們可以通過 import導(dǎo)入,就像下面這樣:
import "article.proto";
message Book {
//定義消息體
}
protoBuf使用
protobuf的使用方法是將數(shù)據(jù)結(jié)構(gòu)寫入到 .proto文件中,使用 protoc編譯器編譯(間接使用了插件)得到一個新的 go包,里面包含 go中可以使用的數(shù)據(jù)結(jié)構(gòu)和一些輔助方法。
Golang & protoBuf
1. $GOPATH/src/創(chuàng)建 myproto文件夾
2. myproto文件夾中創(chuàng)建 test.proto文件 (protobuf協(xié)議文件 )
syntax = “proto2”;
package myproto;
enum FOO {X = 17;};
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredFiled = 5;
}
}
3. 編譯 :執(zhí)行
protoc --go_out=. *.proto
生成 test.pb.go文件
4. 使用 protobuf做數(shù)據(jù)格式轉(zhuǎn)換
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"myproto"
)
func main() {
test := &myproto.Test{
Label: proto.String("hello"),
Type: proto.Int32(17),
Reps: []int64{1, 2, 3},
Optionalgroup: &myproto.Test_OptionalGroup{
RequiredFiled: proto.String("good bye"),
},
}
//將Struct test 轉(zhuǎn)換成 protobuf
data, err := proto.Marshal(test)
if err != nil {
fmt.Println("marshaling error: ", err)
}
//得到一個新的Test結(jié)構(gòu)體 newTest
newTest := &myproto.Test{}
//將 data 轉(zhuǎn)換成 Test結(jié)構(gòu)體
err = proto.Unmarshal(data, newTest)
if err != nil {
fmt.Println("unmarshaling error: ", err)
}
//將newTest的字符串序列化打出
fmt.Println(newTest.String())
//得到type字段
if test.GetType() != newTest.GetType() {
fmt.Println("type is not equal")
}
//...
}
gRPC(Google定義的PRC協(xié)議標(biāo)準(zhǔn))
gRPC是什么?
在 gRPC里客戶端應(yīng)用可以像調(diào)用本地對象一樣直接調(diào)用另一臺不同的機(jī)器上服務(wù)端應(yīng)用的方法,使得您能夠更容易地創(chuàng)建分布式應(yīng)用和服務(wù)。與許多 RPC系統(tǒng)類似, gRPC也是基于以下理念:定義一個服務(wù),指定其能夠被遠(yuǎn)程調(diào)用的方法(包含參數(shù)和返回類型)。在服務(wù)端實現(xiàn)這個接口,并運(yùn)行一個 gRPC服務(wù)器來處理客戶端調(diào)用。在客戶端擁有一個存根能夠像服務(wù)端一樣的方法。 gRPC客戶端和服務(wù)端可以在多種環(huán)境中運(yùn)行和交互 -從 google內(nèi)部的服務(wù)器到你自己的筆記本,并且可以用任何 gRPC支持的語言 來編寫。所以,你可以很容易地用 Java創(chuàng)建一個 gRPC服務(wù)端,用 Go、 Python、Ruby來創(chuàng)建客戶端。此外, Google最新 API將有 gRPC版本的接口,使你很容易地將 Google的功能集成到你的應(yīng)用里。
使用 protocol buffers
gRPC默認(rèn)使用protoBuf,這是 Google開源的一套成熟的結(jié)構(gòu)數(shù)據(jù)序列化機(jī)制(當(dāng)然也可以使用其他數(shù)據(jù)格式如 JSON)。正如你將在下方例子里所看到的,你用 proto files創(chuàng)建 gRPC服務(wù),用 protoBuf消息類型來定義方法參數(shù)和返回類型。你可以在 Protocol Buffers文檔找到更多關(guān)于 protoBuf的資料。
雖然你可以使用 proto2 (當(dāng)前默認(rèn)的 protocol buffers版本 ),我們通常建議你在 gRPC里使用 proto3,因為這樣你可以使用 gRPC支持全部范圍的的語言,并且能避免 proto2客戶端與 proto3服務(wù)端交互時出現(xiàn)的兼容性問題,反之亦然。
你好 gRPC
現(xiàn)在你已經(jīng)對 gRPC有所了解,了解其工作機(jī)制最簡單的方法是看一個簡單的例子。 Hello World將帶領(lǐng)你創(chuàng)建一個簡單的客戶端 —— 服務(wù)端應(yīng)用,向你展示:
n 通過一個protoBuf模式,定義一個簡單的帶有 Hello World方法的 RPC服務(wù)。
n 用你最喜歡的語言 (如果可用的話 )來創(chuàng)建一個實現(xiàn)了這個接口的服務(wù)端。
n 用你最喜歡的 (或者其他你愿意的 )語言來訪問你的服務(wù)端。
go語言實現(xiàn) gRPC遠(yuǎn)程調(diào)用
創(chuàng)建一個 protobuf package,如: my_rpc_proto; 在$GOPATH/src/下創(chuàng)建 go_lession/gRPC_test/my_rpc_proto/文件夾里面創(chuàng)建 protobuf協(xié)議文件 helloServer.proto
syntax = "proto3";
package my_rpc_proto;
// The HelloServer service definition.
service HelloServer {
// 第一個遠(yuǎn)程調(diào)用接口
rpc SayHello (HelloRequest) returns (HelloReply) {}
// 第二個遠(yuǎn)程調(diào)用接口
rpc GetHelloMsg (HelloRequest) returns (HelloMessage) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
message HelloMessage {
string msg = 1;
}
在當(dāng)前文件下,編譯 helloServer.proto文件
protoc –go_out=plugins=grpc:./ *.proto
得到 helloServer.pb.go文件
1. gRPC-Server編寫
package main
import (
"fmt"
"net"
pb "go_lession/gRPC_test/my_rpc_proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
port = ":18881"
)
type server struct{}
//實現(xiàn)RPC SayHello 接口
func (this *server) SayHello(ctx context.Context, in *pb.HelloRe
quest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "hello" + in.Name}, nil
}
//實現(xiàn)RPC GetHelloMsg 接口
func (this *server) GetHelloMsg(ctx context.Context, in *pb.Hell
oRequest) (*pb.HelloMessage, error) {
return &pb.HelloMessage{Msg: "this is from server HAHA!"}, nil
}
func main() {
listen, err := net.Listen("tcp", port)
if err != nil {
fmt.Println("failed to listen : ", err)
return
}
//得到一個gRPC 服務(wù)句柄
srv := grpc.NewServer()
//將 server 結(jié)構(gòu)體注冊到 gRPC 服務(wù)
pb.RegisterHelloServerServer(srv, &server{})
//啟動監(jiān)聽gRPC服務(wù)
if err := srv.Serve(listen); err != nil {
fmt.Println("failed to serve, ", err)
return
}
}
2. gRPC-Client編寫
package main
import (
"fmt"
pb "go_lession/gRPC_test/my_rpc_proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
address = "localhost:18881"
clientName = "GreenHat"
)
func main() {
//了客戶端連接服務(wù)器
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
fmt.Println("did not connetc : ", err)
return
}
defer conn.Close()
//獲取一個 gRPC 句柄
c := pb.NewHelloServerClient(conn)
//遠(yuǎn)程調(diào)用 SayHello接口
r1, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: clientName})
if err != nil {
fmt.Println("cloud not get Hello server ..", err)
return
}
fmt.Println("HelloServer resp: ", r1.Message)
//遠(yuǎn)程調(diào)用 GetHelloMsg接口
r2, err := c.GetHelloMsg(context.Background(), &pb.HelloRequest{Name: clientName})
if err != nil {
fmt.Println("cloud not get hello msg ..", err)
return
}
fmt.Println("HelloServer resp: ", r2.Msg)
}
運(yùn)行 server,在運(yùn)行 client
得到以下輸出結(jié)果:
HelloServer resp: helloGreenHat
HelloServer resp: this is from server HAHA!
Consul(基于Go的服務(wù)發(fā)現(xiàn)工具)
Consul簡介
Consul是什么
Consul是HashiCorp公司推出的開源工具,用于實現(xiàn)分布式系統(tǒng)的服務(wù)發(fā)現(xiàn)與配置。 Consul是分布式的、高可用的、可橫向擴(kuò)展的。它具備以下特性 :
l service discovery:consul通過DNS或者HTTP接口使服務(wù)注冊和服務(wù)發(fā)現(xiàn)變的很容易,一些外部服務(wù),例如saas提供的也可以一樣注冊。
l health checking:健康檢測使consul可以快速的告警在集群中的操作。和服務(wù)發(fā)現(xiàn)的集成,可以防止服務(wù)轉(zhuǎn)發(fā)到故障的服務(wù)上面。
l key/value storage:一個用來存儲動態(tài)配置的系統(tǒng)。提供簡單的HTTP接口,可以在任何地方操作。
l multi-datacenter:無需復(fù)雜的配置,即可支持任意數(shù)量的區(qū)域。
什么是服務(wù)發(fā)現(xiàn)
微服務(wù)的框架體系中,服務(wù)發(fā)現(xiàn)是不能不提的一個模塊。我相信了解或者熟悉微服務(wù)的童鞋應(yīng)該都知道它的重要性。這里我只是簡單的提一下,畢竟這不是我們的重點(diǎn)。我們看下面的一幅圖片:
圖中,客戶端的一個接口,需要調(diào)用服務(wù)A-N??蛻舳吮仨氁浪蟹?wù)的網(wǎng)絡(luò)位置的,以往的做法是配置是配置文件中,或者有些配置在數(shù)據(jù)庫中。這里就帶出幾個問題:
· 需要配置N個服務(wù)的網(wǎng)絡(luò)位置,加大配置的復(fù)雜性
· 服務(wù)的網(wǎng)絡(luò)位置變化,都需要改變每個調(diào)用者的配置
· 集群的情況下,難以做負(fù)載(反向代理的方式除外)
與之前一張不同的是,加了個服務(wù)發(fā)現(xiàn)模塊。圖比較簡單,這邊文字描述下。服務(wù)A-N把當(dāng)前自己的網(wǎng)絡(luò)位置注冊到服務(wù)發(fā)現(xiàn)模塊(這里注冊的意思就是告訴),服務(wù)發(fā)現(xiàn)就以K-V的方式記錄下,K一般是服務(wù)名,V就是IP:PORT。服務(wù)發(fā)現(xiàn)模塊定時的輪詢查看這些服務(wù)能不能訪問的了(這就是健康檢查)??蛻舳嗽谡{(diào)用服務(wù)A-N的時候,就跑去服務(wù)發(fā)現(xiàn)模塊問下它們的網(wǎng)絡(luò)位置,然后再調(diào)用它們的服務(wù)。這樣的方式是不是就可以解決上面的問題了呢?客戶端完全不需要記錄這些服務(wù)網(wǎng)絡(luò)位置,客戶端和服務(wù)端完全解耦!
下面的例子有可能更有助于我們理解服務(wù)發(fā)現(xiàn)的形式:
例如郵遞員去某公司一棟大樓投遞快件,向門衛(wèi)詢問員工甲在哪一個房間,門衛(wèi)拿起桌上的通訊錄查詢,告知郵遞員員工甲在具體什么位置。假如公司來了一個員工乙,他想讓郵遞員送過來,就要先讓門衛(wèi)知道自己在哪一個房間,需要去門衛(wèi)那邊登記,員工乙登記后,當(dāng)郵遞員向門衛(wèi)詢問時,門衛(wèi)就可以告訴郵遞員員工乙的具體位置。門衛(wèi)知道員工乙的具體位置的過程就是服務(wù)發(fā)現(xiàn),員工乙的位置信息可以被看作服務(wù)信息,門衛(wèi)的通訊錄就是上文中提到的數(shù)據(jù)交換格式,此例中員工乙就是上文的已方,門衛(wèi)就是服務(wù)發(fā)現(xiàn)的提供者。
以調(diào)試模式啟動consul:
$ conusl agent –dev –bind=0.0.0.0
go_micro(基于Go的微服務(wù)框架)
依賴
我們需要一個發(fā)現(xiàn)服務(wù)器,這里 micro默認(rèn)使用的 Consul,我們這里用之前安裝部署好的 consul,用來做 go的micro服務(wù)發(fā)現(xiàn)
protoBuf作為Server端和Client端的數(shù)據(jù)交換格式。
下載 micro
go get github.com/micro/micro
hello micro
趕緊完成一個go_micro的微服務(wù)吧 ~
micro為我們提供的微服務(wù)框架如下
這里面有很多角色, Micro API, Customer API, Customer Service等等 ...
其中 Micro API是micro給我們提供的一個工具,是通過 RPC調(diào)用我們模塊的 API和做一些負(fù)載均衡的作用,實際上 Customer API, Customer Service是一組微服務(wù), Customer API收到 Micro API轉(zhuǎn)發(fā)的請求后,將 RESTful轉(zhuǎn)換成 protobuf通過 gRPC調(diào)用發(fā)送給 Customer Service做服務(wù)處理,然后又將返回 protobuf數(shù)據(jù)轉(zhuǎn)換成 RESTful返回給用戶。
在微服務(wù)架構(gòu)中一般稱 API為RPC GateWay
GRPC Gateway
此指南幫助我們使用 go-micro微服務(wù)中的 grpc gateway。
grpc-gateway是一個 protoc的一個插件。它基于 gRPC服務(wù)定義,并且提供一
個將 RESTful JSON API轉(zhuǎn)換成 gRPC協(xié)議的反響代理服務(wù)。
我們使用 go-grpc去完成后臺服務(wù), go-grpc是一個為 client/server將go-micro
和gRPC結(jié)合的一個包裹。
代碼案例
examples/grpc.
Create service proto-創(chuàng)建服務(wù)端protobuf
這里,我們的proto文件定義如下:
syntax = "proto3";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 2;
}
Write the service
1、 實現(xiàn)自定義接口
2、 初始化一個微服務(wù)
3、 注冊Greeter 句柄
4、 啟動服務(wù)
package main
import (
"context"
"fmt"
micro "github.com/micro/go-micro"
proto "github.com/micro/examples/service/proto"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(
micro.Name("greeter"),
)
// Init will parse the command line flags.
service.Init()
// Register handler
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
// Run the server
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
Run service
go run examples/service/main.go
Output
2016/03/14 10:59:14 Listening on [::]:50137
2016/03/14 10:59:14 Broker Listening on [::]:50138
2016/03/14 10:59:14 Registering node: greeter-ca62b017-e9d3-11e5-9bbb-68a86d0d36b6
Define a client-編寫客戶端
下面是客戶端訪問微服務(wù)的代碼:
package main
import (
"context"
"fmt"
micro "github.com/micro/go-micro"
proto "github.com/micro/examples/service/proto"
)
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(micro.Name("greeter.client"))
service.Init()
// Create new greeter client
greeter := proto.NewGreeterClient("greeter", service.Client())
// Call the greeter
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.Greeting)
}
Run the client
go run client.go
Output
Hello John
QA
1 Go語言除了能開發(fā)區(qū)塊鏈還能開發(fā)哪些領(lǐng)域?
Go語言作為一個開發(fā)效率高,天生支持高并發(fā),同時又具備媲美C語言性能的語言,在未來一定是后端開發(fā)語言的最具有潛力的編程語言。目前很多企業(yè)的服務(wù)器架構(gòu)也逐步在用Go語言重構(gòu)。
Go語言目前主要涉及的領(lǐng)域有:高并發(fā)服務(wù)器開發(fā)、分布式開發(fā)、微服務(wù)開發(fā)、Web框架及應(yīng)用開發(fā)、和區(qū)塊鏈開發(fā)。
高并發(fā)服務(wù)器開發(fā):
不用解釋了,Go天生語法的并發(fā)支持和Goroutine協(xié)程的輕量級與調(diào)度器的優(yōu)化,目前很多游戲公司主要服務(wù)開發(fā)語言最優(yōu)選擇一定是Golang.
分布式開發(fā):
我們知道的兩個分布式虛擬化明星:Docker、Kubernetes他們的開發(fā)實現(xiàn)語言都是Go語言。有人說是Docker捧紅了分布式,實際上很多人并不知道,是Go捧紅了Docker生態(tài)。
微服務(wù)開發(fā):
Go的微服務(wù)框架居多,加上Docker對go的支持最好,所以go也是微服務(wù)開發(fā)的首選語言。
go的微服務(wù)框架有g(shù)o-micro,go-kit。服務(wù)發(fā)現(xiàn)有g(shù)o實現(xiàn)的Consul。微服務(wù)通信的RPC機(jī)制有g(shù)oogle實現(xiàn)的gRPC,其中通信協(xié)議protobuf也是對go無縫銜接的。
Web框架及應(yīng)用開發(fā):
對于web,大家可能會想到j(luò)ava的Spring、python的Django。但是可能并不知道Go的Beego和Gin、Echo等web框架正在逐步侵蝕大型互聯(lián)網(wǎng)公司。很多公司已經(jīng)通過Beego來搭建web后臺服務(wù),因為Go的天生網(wǎng)絡(luò)處理的流暢,讓開發(fā)者在構(gòu)建大型web的時候,更加喜歡了Go語言。
區(qū)塊鏈開發(fā):
我們所遇見的區(qū)塊鏈應(yīng)用項目或者相關(guān)框架幾乎都是Go語言實現(xiàn),或者對Go的支持最好。主流的Hyperledger Fabric 和以太坊也是目前企業(yè)正在大規(guī)模使用的開發(fā)框架。go的這種開發(fā)效率高和高性能,對于區(qū)塊鏈這種注重網(wǎng)絡(luò)通信和信息安全的技術(shù),更是不可或缺的。
2 Go語言難易程度如何?
Go語言學(xué)習(xí)起來完全可以零基礎(chǔ)入門。Google在創(chuàng)建Go語言的最初定義為:簡單快樂的開發(fā)高性能系統(tǒng)語言??梢奼o語言并不是很難。
本文版權(quán)歸黑馬程序員C/C++與網(wǎng)絡(luò)攻防學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!
作者:黑馬程序員C/C++與網(wǎng)絡(luò)攻防培訓(xùn)學(xué)院
首發(fā):http://c.itheima.com/