micro微服務 基礎元件的組織方式
micro微服務 基礎元件的組織方式
簡介
micro是go語言實現的一個微服務框架,該框架自身實現了為服務常見的幾大要素,閘道器,代理,註冊中心,訊息傳遞,也支援可插拔擴充套件。本本通過micro中的一個核心物件展開去探討這個專案是如何實現這些元件並將其組織在一起工作的。
Notice: go程式碼有時候比較繁瑣,擷取原始碼的時候會刪除部分不影響思想的程式碼會標記為...
核心服務
micro通過micro.NewService建立一個服務例項,所有的微服務例項(包括閘道器,代理等等)需要通過這個例項來與其他服務打交道。
type service struct { opts Options once sync.Once }
程式碼很少,但是micro中所有微服務要素最後都會匯聚在這個型別上了,因為option包羅永珍(^^).
type Options struct { Broker broker.Broker Cmd cmd.Cmd Client client.Client Server server.Server Registry registry.Registry Transport transport.Transport // Before and After funcs BeforeStart []func() error BeforeStop []func() error AfterStart []func() error AfterStop []func() error // Other options for implementations of the interface // can be stored in a context Context context.Context }
這裡可以看到微服務的常見元件了,當micro.Server初始化的時候會給這個物件設定上對應的元件,元件的設定方式包括預設值,cli指定,env讀取。
func (s *service) Init(opts ...Option) { // process options for _, o := range opts { o(&s.opts) } s.once.Do(func() { // Initialise the command flags, overriding new service _ = s.opts.Cmd.Init( cmd.Broker(&s.opts.Broker), cmd.Registry(&s.opts.Registry), cmd.Transport(&s.opts.Transport), cmd.Client(&s.opts.Client), cmd.Server(&s.opts.Server), ) }) }
Server是最終的行為實體,監聽埠並提供業務服務,註冊本地服務到註冊中心。 Broker非同步訊息,mq等方法可以替換這個型別 Client服務呼叫客戶端 Registry 註冊中心 Cmd cli客戶端 Transport 類似與socket,訊息同步通訊,服務監聽等
服務型別
目前支援的服務型別有rpc,grpc。服務中如果存在兩種不同的rpc協議,訊息投遞的時候會進行協議轉換。這裡詳細說下預設的rpc服務。 rpc服務基於HTTP POST協議,服務啟動的時候會嘗試連線Broker,然後註冊本服務到註冊中心,最後監聽服務埠.簡單提一句這裡是如何做到協議轉換,如果http過來的訊息要投遞到一個grpc協議服務上,需要在Content-Type設定對應目的服務協議型別application/grpc,SerConn這裡讀取在Content-Type在獲取對應的Codec進行協議轉換,最後投遞到對應服務,服務的返回內容也同樣要處理下再返回。
type Service interface {
Init(...Option)
Options() Options
Client() client.Client
Server() server.Server
Run() error
String() string
}
rpc server
func (s *rpcServer) Start() error {
...
ts, err := config.Transport.Listen(config.Address)
// swap address
...
// connect to the broker
if err := config.Broker.Connect(); err != nil {
return err
}
...
// use RegisterCheck func before register
if err = s.opts.RegisterCheck(s.opts.Context); err != nil {
log.Logf("Server %s-%s register check error: %s", config.Name, config.Id, err)
} else {
// announce self to the world
if err = s.Register(); err != nil {
log.Logf("Server %s-%s register error: %s", config.Name, config.Id, err)
}
}
...
go func() {
for {
// listen for connections
err := ts.Accept(s.ServeConn)
...
}
}()
..
return nil
}
協議轉換
func (s *rpcServer) ServeConn(sock transport.Socket) {
...
for {
var msg transport.Message
if err := sock.Recv(&msg); err != nil {
return
}
...
// we use this Content-Type header to identify the codec needed
ct := msg.Header["Content-Type"]
// strip our headers
hdr := make(map[string]string)
for k, v := range msg.Header {
hdr[k] = v
}
// set local/remote ips
hdr["Local"] = sock.Local()
hdr["Remote"] = sock.Remote()
...
// TODO: needs better error handling
var err error
if cf, err = s.newCodec(ct); err != nil { //請求協議轉換器
...返回錯誤
return
}
...
rcodec := newRpcCodec(&msg, sock, cf) //返回轉換器
// internal request
request := &rpcRequest{
service: getHeader("Micro-Service", msg.Header),
method: getHeader("Micro-Method", msg.Header),
endpoint: getHeader("Micro-Endpoint", msg.Header),
contentType: ct,
codec: rcodec,
header: msg.Header,
body: msg.Body,
socket: sock,
stream: true,
}
// internal response
response := &rpcResponse{
header: make(map[string]string),
socket: sock,
codec: rcodec,
}
// set router
r := Router(s.router)
...
// serve the actual request using the request router
if err := r.ServeRequest(ctx, request, response); err != nil {
// write an error response
err = rcodec.Write(&codec.Message{
Header: msg.Header,
Error: err.Error(),
Type: codec.Error,
}, nil)
// could not write the error response
if err != nil {
log.Logf("rpc: unable to write error response: %v", err)
}
if s.wg != nil {
s.wg.Done()
}
return
}
...
}
}
註冊中心
註冊中心包括服務發現和服務註冊。micro中每個註冊中心型別都要實現註冊中心介面
type Registry interface {
Init(...Option) error
Options() Options
Register(*Service, ...RegisterOption) error
Deregister(*Service) error
GetService(string) ([]*Service, error)
ListServices() ([]*Service, error)
Watch(...WatchOption) (Watcher, error)
String() string
}
預設的註冊中心是mdns,該程式會在本地監聽一個組播地址接收網路中所有及其廣播的資訊,同時傳送的資訊也能被所有其他機器發現。每當程式啟動時都會廣播自己的服務資訊,其他節點收到該資訊後新增到自己的服務列表裡面,服務關閉時會發出關閉資訊。mdns自身不具備健康檢查,熔斷等功能,其出發點也僅是便於測試使用,因而不推薦在生產環境中使用。
Resolve
查詢服務需要根據url或者content資訊來獲取服務名稱,在通過服務名稱到註冊中心查詢,獲取到服務後,隨機一個節點投遞
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
String() string
}
//預設的api resolve例項,除此之外還有host,path,grpc 三種resolve,可以根據需求在啟動程式的時候指定或者設定在環境變數裡面
//
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
var name, method string
switch r.Options.Handler {
// internal handlers
case "meta", "api", "rpc", "micro":
///foo/bar/zool => go.micro.api.foo 方法:Bar.Zool
///foo/bar => go.micro.api.foo 方法:Foo.Bar
name, method = apiRoute(req.URL.Path)
default:
//如果handler是web會走到這裡
///foo/bar/zool => go.micro.api.foo method bar/zool
// 1/foo/bar/ => go.micro.api.1.foo method bar
method = req.Method
name = proxyRoute(req.URL.Path)
}
return &resolver.Endpoint{
Name: name,
Method: method,
}, nil
}
外掛
程式碼中定義了一個plugin,然而這個plugin並非是引入元件的作用,其作用在於在請求的管道中加一層,這樣比較容易新增新的邏輯進去。然而真正需要著重提到的是micro引入新元件的方式,micro service的幾個重要成員都有其對應的介面規約,只要正確實現了介面就可以輕鬆的接入新的元件。這裡用一個引入kubernetes作為註冊中心的例子。
kubernetes元件位於github.com\micro\go-plugins中
go get -u github.com\micro\go-plugins\kubernetes
由於在go中無法實現java/c#中包動態載入功能,語言本身不提供全域性型別,函式掃描。往往只能通過一些曲折的辦法來實現。而micro是通過在入口檔案中匯入包,利用init函式在啟動時將需要的功能元件寫入到一個map裡面。 在import中加入外掛
_ "github.com/micro/go-plugins/registry/kubernetes"
micro api --registry=kubernetes --registry_address=yourAddress
工作原理: 在github.com\micro\go-micro\config\cmd\cmd.go中有一個map用於儲存所有的註冊中心建立函式
DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
"consul": consul.NewRegistry,
"gossip": gossip.NewRegistry,
"mdns": mdns.NewRegistry,
"memory": rmem.NewRegistry,
}
kubernetes會在包的init中寫入對應的建立函式
func init() {
cmd.DefaultRegistries["kubernetes"] = NewRegistry
}
生效的位置
if name := ctx.String("registry"); len(name) > 0 && (*c.opts.Registry).String() != name {
r, ok := c.opts.Registries[name]
if !ok {
return fmt.Errorf("Registry %s not found", name)
}
*c.opts.Registry = r()
serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
if err := (*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)); err != nil {
log.Fatalf("Error configuring registry: %v", err)
}
clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
if err := (*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)); err != nil {
log.Fatalf("Error configuring broker: %v", err)
}
}
最後附上一張圖說明下這個核心的物件的引用關係,元件引用那一塊只是畫了註冊中心的,Broker,Server也都是類似的原理。
相關推薦
micro微服務 基礎元件的組織方式
micro微服務 基礎元件的組織方式 簡介 micro是go語言實現的一個微服務框架,該框架自身實現了為服務常見的幾大要素,閘道器
解析微服務架構元件,看這一篇文章就夠
1. 如何釋出和引用服務 服務描述:服務呼叫首先解決的問題就是服務如何對外描述。 常用的服務描述方式包括 RESTful API、XML 配置以及 IDL 檔案三種。 RESTful API 主要被用作 HTT
SpringCloud微服務基礎
單點系統架構 傳統專案架構 傳統專案分為三層架構,將業務邏輯層、資料庫訪問層、控制層放入在一個專案中。 優點:適合於個人或者小團隊開發,不適合大團隊開發。 分散式專案架構 根據業務需求進行拆分成N個子系統,多個子系統相互協作才能完成業務流程子系統之間通訊使
SpringCloud微服務基礎5:Zuul閘道器
我們使用Spring Cloud Netflix中的Eureka實現了服務註冊中心以及服務註冊與發現;而服務間通過Ribbon或Feign實現服務的消費以及均衡負載;通過Spring Cloud Config實現了應用多環境的外部化配置以及
SpringCloud微服務基礎4:Feign
Spring Cloud Feign是一套基於Netflix Feign實現的宣告式服務呼叫客戶端。它使得編寫Web服務客戶端變得更加簡單。我們只需要通過建立介面並用註解來配置它既可完成對Web服務介面的繫結。它具備可插拔的註解支援,包括Fei
SpringCloud微服務基礎2:Robbin負載均衡
spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。它是一個基於HTTP和TCP的客戶端負載均衡器。它可以通過在客戶端中配置ribbonServerList來設定服務端列表去輪詢訪問以達到均衡負載的作用。
SpringCloud微服務基礎1:Eureka註冊中心
1.Eureka基本概念 Spring Cloud Eureka是Spring Cloud Netflix專案下的服務治理模組。而Spring Cloud Netflix專案是Spring Cloud的子專案之一,主要內容是對Netflix公司一系列開源產品
SpringCloud微服務基礎6:引數配置
1、Eureka引數配置說明 (1)eureka.client.service-url.defaultZone 這裡是我們配置的微服務名稱。(2)eureka.client.fetch-registry 設定是否從註冊中心獲取註冊資訊(預設tr
微服務學習(一)微服務基礎
單體架構主要問題 編號 問題 1 系統資源浪費 2 部署效率太低 3 技術選型單一
微服務架構元件
https://mp.weixin.qq.com/s/ImxFxnJIpBJzYuKZV4X8rA 1、 如何釋出和引用服務 服務描述:服務呼叫首先解決的問題就是服務如何對外描述。 常用的服務描述方式包括 RESTful API、XML 配置以及 IDL 檔案三種。 RESTful AP
Spring cloud微服務實戰(三)——基於OAUTH2.0統一認證授權的微服務基礎架構升級
前言 從2018年年初寫的一篇主題為 Spring cloud微服務實戰——基於OAUTH2.0統一認證授權的微服務基礎架構的文章後就很少更新了。自從小寶貝誕生和公司業務的繁忙,年初計劃每週更新一篇博文的計劃已經落空了。年底了,終於清閒了些。 升級 Spring cloud微
微服務核心元件 Zuul 閘道器原理剖析 | 併發程式設計網
一、前言 Zuul 閘道器是具體核心業務服務的看門神,相比具體實現業務的系統服務來說它是一個邊緣服務,主要提供動態路由,監控,彈性,安全性等功能。在分散式的微服務系統中,系統被拆為了多套系統,通過zuul閘道器來對使用者的請求進行路由,轉發到具體的後臺服務系統中。 本 Chat 主要內容如下:
Choerodon學習筆記1——環境搭建:微服務支撐元件部署(原始碼形式)
安裝Docker for Windows 首先確保能科學上網,且系統是win10專業版、教育版。 確保在BIOS中已經開啟了CPU虛擬化,否則安裝完成後啟動Docker會出現Hardware assisted virtualization and data executio
32.微服務之間的呼叫方式RestTemplate和FeignClient
SpringCloud服務間的呼叫有兩種方式:RestTemplate和FeignClient。不管是什麼方式,他都是通過REST介面呼叫服務的http介面,引數和結果預設都是通過jackson序列化和
微服務基礎設施之服務註冊中心: Spring Cloud Eureka
對服務註冊中心的需求 在微服務架構中,由於每一個服務的粒度相對傳統SOA來說要小的多,所以服務的數量會成倍增加。這時如果有效管理服務的註冊資訊就尤為重要。我們對服務註冊中心的期望主要有以下幾條: 簡單易用:最好對開發者透明 高可用:幾臺註冊中心壞掉不會導致
微服務時代元件化和服務化的抉擇
隨著業務系統的複雜性越來越高,系統之間的呼叫也越來越多,在微服務拆分和迭代過程中,是不斷的拆分出新的獨立的服務還是封裝獨立的元件以jar包依賴的方式提供服務是我們經常需要面對的問題,本文將詳細探討這兩種不同的方式區別、各自的優劣勢及適用的場景,希望能夠對大家有所啟發。 # **一、元件化&服務化定義
微服務架構案例(05):SpringCloud 基礎元件應用設計
本文原始碼:GitHub·點這裡 || GitEE·點這裡 更新進度(共6節): 01:專案技術選型簡介,架構圖解說明 02:業務架構設計,系統分層管理 03:資料庫選型,業務資料設計規劃 04:中介軟體整合,公共服務管理 05:SpringCloud 基礎元件應用設計 一、元件應用規劃 1、註冊中心
[轉]微服務架構的理論基礎 - 康威定律
搭建 基礎 維系 接口 api pro 1.8 project 個人 轉自:https://yq.aliyun.com/articles/8611 概述 關於微服務的介紹,可以參考微服務那點事。 微服務是最近非常火熱的新概念,大家都在追,也都覺得很對,但是似乎沒有很充足的
使用nhmicro提供的micro-datasource嵌入式的解決微服務架構中分布式事務問題
微服務 nhmicro 分布式 應用原理:使用micro-datasource數據源使事務與線程解耦,通過groupid在其他線程進行事務提交或回滾。多個系統需要統一提交時,通過activemq發送提交消息(含有groupid),各系統收到消息後進行統一提交或回滾。micro-datasource
微服務架構的基礎框架選擇:Spring Cloud還是Dubbo?
還需要 storm 選擇框架 依賴 通過 service 完整 國內開發 模塊化 最近一段時間不論互聯網還是傳統行業,凡是涉及信息技術範疇的圈子幾乎都在討論 微服務架構 。近期也看到各大技術社區開始組織一些沙龍和論壇來分享spring Cloud的相關實施經驗,這對於最