一:微服務架構的優勢
一旦應用程序成爲一個大型、複雜的整體,我們開發組織可能就會陷入痛苦之中。任何敏捷開發和交付的嘗試都將舉步維艱。一個主要問題是應用程序極其複雜。它太大了,任何一個開發人員都無法完全理解。因此,修復bug和正確實現新特性變得困難和費時。
應用程序的龐大規模也會降低開發速度。應用程序越大,啓動時間越長。
大型、複雜的單片應用程序的另一個問題是,它是持續部署的障礙。今天,SaaS應用程序的最新技術是每天多次將更改推入生產環境。對於複雜的整體來說,這是非常困難的,因爲必須重新部署整個應用程序才能更新其中的任何一部分。
總之,隨着時間的推移, 這個打車應用程序,將成長爲一個只有極少數(如果有的話)開發人員能夠理解的龐然大物。它是使用過時的、低效的技術編寫的,這使得僱傭有才華的開發人員非常困難。應用程序難以擴展,而且不可靠。因此,敏捷開發和交付應用程序是不可能的。
許多公司現在都在使用微服務的架構,Amazon, Netflix, 國內的滴滴, 字節跳動,都是微服務的應用者
通過採用現在所知的微服務體系結構模式解決了應用程序難以擴展,而且不可靠。因此,敏捷開發和交付應用程序是不可能的。與其構建一個龐大的、單一的應用程序,不如將應用程序分割成一組更小的、相互連接的服
服務通常實現一組不同的特性或功能,例如訂單管理、客戶管理等。每個微服務都是一個小型應用程序,它有自己的六邊形架構,由業務邏輯和各種適配器組成。一些微服務將公開由其他微服務或應用程序客戶端使用的API。其他微服務可能實現web UI。在運行時,每個實例通常是一個雲VM或一個Docker容器。
例如,前面描述的系統可能的分解如下圖所示:
應用程序的每個功能區域現在都由自己的微服務實現。此外,web應用程序被劃分爲一組更簡單的web應用程序(例如,在我們的打車示例中,一個用於乘客,一個用於司機)。這使得爲特定用戶、設備或特定用例部署不同的體驗變得更加容易。
每個後端服務公開一個REST API,大多數服務使用其他服務提供的API。例如,驅動程序管理使用通知服務器告訴可用的驅動程序潛在的行程。UI服務調用其他服務來呈現web頁面。服務也可能使用異步的、基於消息的通信。本系列後面將更詳細地討論服務間通信。
一些REST api也暴露在司機和乘客使用的移動應用程序中。然而,這些應用程序不能直接訪問後臺服務。相反,通信是由稱爲API網關的中介進行中介的。API網關負責負載平衡、緩存、訪問控制、API計量和監視等任務,可以使用NGINX有效地實現。
在運行時,旅行管理服務由多個服務實例組成。每個服務實例都是一個Docker容器。爲了獲得高可用性,容器運行在多個雲vm上。在服務實例前面是一個負載平衡器,比如NGINX,它將請求分佈在實例之間。
討論——微服務架構的技術難題?微服務的負載均衡策略?
go-micro是go語言下的一個很好的rpc微服務框架,功能很完善,而且我關心的幾個問題也解決的很好:
一:服務間傳輸格式爲protobuf,效率上沒的說,非常的快,也很安全。
二:go-micro的服務註冊和發現是多種多樣的。我個人比較喜歡etcdv3的服務服務發現和註冊。
三:主要的功能都有相應的接口,只要實現相應的接口,就可以根據自己的需要訂製插件。
二:Micro架構
micro由以下幾個部分組成:
-
API網關(API Gateway): - API Gateway 網關。API網關是請求的入口,把請求動態路由到具體服務。網關允許我們建立可伸縮的後臺微服務架構,並且讓工作在前端的公共API更健壯。Micro API基於服務發現擁有強大的路由能力,通過我們預置的handlers插件,它可以處理http、gRPC、websocket、消息推送事件等等。
-
命令行接口(Interactive CLI): 交互式的命令行接口。CLI通過終端可以描述、查詢、直接與平臺和服務進行交互。CLI提供所有的命令讓開發者明白微服務正在處理的事情。CLI也包含了交互模式。
-
服務代理(Service Proxy): 服務代理,基於Go Micro和MUCP協議構建的透明的代理服務。它將服務發現、負載均衡、消息編碼、中間件、傳輸及代理插件轉移到某一(具體服務所在)位置,同api不同,它不暴露任何接口,只工作在內部環境,相當於橋接內部服務。
-
模板生成(Template Generation): 基於模板快速創建新的服務代碼。Micor提供預置的模板,通過模板編寫統一風格的代碼。
-
SlackOps小機器人(SlackOps Bot): Slack小機器人插件,當它運行中服務中時,這個插件允許開發者通過Slack消息來操作平臺。MicroBot插件提供聊天配置選項,這樣就可以讓團隊通過向小機器人發送聊天消息來做一些我們希望它做的事,這裏面當然也包含像動態發現服務一樣創建slack命令。
-
管理控制檯(Web Dashboard): 通過Web管理控制檯,可以直接在Web頁面上查看服務的運行情況,展示端點信息,請求與響應狀態,甚至直接向服務進行查詢。管理控制檯也有CLI交互頁面提供給開發者在線上處理,就像直接操作終端一樣。
-
Go-micro框架(Go Framework): Go Micro框架是Micro的底層、核心。GO-Micro把分佈式服務抽象,並提供簡便的方式讓大家構建具有高彈性的微服務。
-
Server監聽客戶端的調用,和Brocker推送過來的信息進行處理。並且Server端需要向Register註冊自己的存在或消亡,這樣Client才能知道自己的狀態。
Register服務的註冊的發現。
Client端從Register中得到Server的信息,然後每次調用都根據算法選擇一個的Server進行通信,當然通信是要經過編碼/解碼,選擇傳輸協議等一系列過程的。
如果有需要通知所有的Server端可以使用Brocker進行信息的推送。
Brocker 信息隊列進行信息的接收和發佈。
-
go-micro之所以可以高度訂製和他的框架結構是分不開的,go-micro由8個關鍵的interface組成,每一個interface都可以根據自己的需求重新實現,這8個主要的inteface也構成了go-micro的框架結構。
這些接口go-micir都有他自己默認的實現方式,還有一個go-plugins是對這些接口實現的可替換項。你也可以根據需求實現自己的插件。
三:go-micro服務發現的簡單例子
使用默認的consual服務註冊和發現機制。
1:啓動consual服務
# ./consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=chenxun-server -bind=192.168.145.130 -ui
2:編寫接口文件
準備proto文件: 文件保存爲chenxun.proto,名稱隨便寫,在實際項目中根據項目寫就好了。
syntax = "proto3";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 2;
}
protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. chenxun.proto
執行命令後能看到下面文件:
rw-r--r--. 1 root root 2441 Jul 7 10:38 chenxun.micro.go
-rw-r--r--. 1 root root 2914 Jul 7 10:38 chenxun.pb.go
-rw-r--r--. 1 root root 185 Jul 6 11:36 chenxun.proto
3:服務端編寫
package main
import (
"context"
"fmt"
micro "github.com/micro/go-micro"
proto "mygoproject/gomirco" //這裏寫你的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)
}
}
4:編寫客戶端
package main
import (
"context"
"fmt"
micro "github.com/micro/go-micro"
proto "mygoproject/gomirco" //這裏寫你的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.NewGreeterService("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)
}
運行service:
go run examples/service/main.go
運行client:
go run examples/client/main.go
func main() {
// 我這裏用的etcd 做爲服務發現,如果使用consul可以去掉
reg := etcdv3.NewRegistry(func(op *registry.Options){
op.Addrs = []string{
"http://192.168.3.34:2379", "http://192.168.3.18:2379", "http://192.168.3.110:2379",
}
})
// 初始化服務
service := micro.NewService(
micro.Name("lp.srv.eg1"),
micro.Registry(reg),
)
// 註冊 Handler
model.RegisterSayHandler(service.Server(), new(Say))
// run server
if err := service.Run(); err != nil {
panic(err)
}
}
四:使用k8s發佈服務
Micro在Kubernetes中是原生kubernetes的微服務。
Micro是一個微服務工具包,而Kubernetes是一個容器調度平臺,它們組合在一起便構成微服務基礎設施。
特性
- 無外部依賴
- 客戶端緩存服務發現
- 可選k8s服務負載均衡
- 使用gRPC傳輸協議
- 預置工具包
首先服務Docker話
這裏簡單運行官方給出的一個例子。github.com/micro/examples/greeter
server端
package main
import (
"log"
"time"
hello "github.com/micro/examples/greeter/srv/proto/hello"
"github.com/micro/go-micro"
"context"
)
type Say struct{}
func (s *Say) Hello(ctx context.Context, req *hello.Request, rsp *hello.Response) error {
log.Print("Received Say.Hello request")
rsp.Msg = "Hello " + req.Name
return nil
}
func main() {
service := micro.NewService(
micro.Name("go.micro.srv.greeter"),
micro.RegisterTTL(time.Second*30),
micro.RegisterInterval(time.Second*10),
)
// optionally setup command line usage
service.Init()
// Register Handlers
hello.RegisterSayHandler(service.Server(), new(Say))
// Run server
if err := service.Run(); err != nil {
log.Fatal(err)
}
go build生成服務,這裏將服務的名字定義成src
Dockerfile編寫
FROM centos
ADD srv ./
CMD ["./srv"]
客戶端的代碼如下:
package main
import (
"context"
"fmt"
hello "github.com/micro/examples/greeter/srv/proto/hello"
"github.com/micro/go-micro"
)
func main() {
// create a new service
service := micro.NewService()
// parse command line flags
service.Init()
// Use the generated client stub
cl := hello.NewSayService("go.micro.srv.greeter", service.Client())
fmt.Println(1)
// Make request
rsp, err := cl.Hello(context.Background(), &hello.Request{
Name: "John",
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rsp.Msg)
}
編寫在kubernetes運行的微服務
server端的代碼如下,可以看到完全相同。
package main
import (
"context"
"log"
hello "github.com/micro/examples/greeter/srv/proto/hello"
k8s "github.com/micro/examples/kubernetes/go/micro"
"github.com/micro/go-micro"
)
type Say struct{}
func (s *Say) Hello(ctx context.Context, req *hello.Request, rsp *hello.Response) error {
log.Print("Received Say.Hello request")
rsp.Msg = "Hello " + req.Name
return nil
}
func main() {
service := k8s.NewService(
micro.Name("greeter"),
)
service.Init()
hello.RegisterSayHandler(service.Server(), new(Say))
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
注意:這裏用的grpc應該是go-micro下的 "github.com/micro/go-micro/service/grpc",而不應該是"github.com/micro/go-grpc"否則會報錯。
只是運行起來可能會出一點小的錯誤。
[root@k8s-master greeter]# ./main
2019/08/29 20:51:53 stat /var/run/secrets/kubernetes.io/serviceaccount: no such file or directory
參考解決:
ubectl get serviceaccount
NAME SECRETS
default 0
如果沒有則需要添加
在apiserver的啓動參數中添加:
--admission_control=ServiceAccount
apiserver在啓動的時候會自己創建一個key和crt(見/var/run/kubernetes/apiserver.crt和apiserver.key)
然後在啓動./kube-controller-manager 時添加flag:
--service_account_private_key_file=/var/run/kubernetes/apiserver.key
kubectl get serviceaccount
NAME SECRETS
default 1
這裏將micro基礎後端打包成鏡像!
最後發現這個路徑需要進入容器纔行。