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