SpringCloud系列:spring cloud gateway之filter篇">SpringCloud系列:spring cloud gateway之filter篇
831
2025-04-02
服務模型
首先,Istio作為一個(微)服務治理的平臺,和其他的微服務模型一樣也提供了Service,ServiceInstance這樣抽象服務模型。如Service的定義中所表達的,一個服務有一個全域名,可以有一個或多個偵聽端口。
type?Service?struct?{
// Hostname of the service, e.g. "catalog.mystore.com"
Hostname Hostname?`json:"hostname"`
Address?string?`json:"address,omitempty"`
Addresses?map[string]string?`json:"addresses,omitempty"`
// Ports is the set of Network ports where the service is listening for connections
Ports PortList?`json:"ports,omitempty"`
ExternalName Hostname?`json:"External"`
...
}
當然這里的Service不只是mesh里定義的service,還可以是通過serviceEntry接入的外部服務。
每個port的定義在這里:
type?Port?struct?{
Name?string?`json:"name,omitempty"`
Port?int?`json:"port"`
Protocol Protocol?`json:"protocol,omitempty"`
}
除了port號外,還有?一個name和protocol。可以看到支持這么幾個Protocol
const?(
ProtocolGRPC?Protocol =?"GRPC"
ProtocolHTTPS?Protocol =?"HTTPS"
ProtocolHTTP2?Protocol =?"HTTP2"
ProtocolHTTP?Protocol =?"HTTP"
ProtocolTCP?Protocol =?"TCP"
ProtocolUDP?Protocol =?"UDP"
ProtocolMongo?Protocol =?"Mongo"
ProtocolRedis?Protocol =?"Redis"
ProtocolUnsupported?Protocol =?"UnsupportedProtocol"
)
而每個服務實例ServiceInstance的定義如下:
type?ServiceInstance?struct?{
Endpoint???????? NetworkEndpoint?`json:"endpoint,omitempty"`
Service?? ???????*Service????????`json:"service,omitempty"`
Labels?????????? Labels??????????`json:"labels,omitempty"`
AvailabilityZone?string??????????`json:"az,omitempty"`
ServiceAccount???string??????????`json:"serviceaccount,omitempty"`
}
熟悉SpringCloud的朋友對比下SpringCloud中對應interface,可以看到主要字段基本完全一樣。
public interface ServiceInstance {
String getServiceId();
String getHost();
int getPort();
boolean isSecure();
URI getUri();
Map
}
以上的服務定義的代碼分析,結合官方spec可以非常清楚的定義了服務發現的數據模型。但是,Istio本身沒有提供服務發現注冊和服務發現的能力,翻遍代碼目錄也找不到一個存儲服務注冊表的服務。Discovery部分的文檔是這樣來描述的:
對于服務注冊,Istio認為已經存在一個服務注冊表來維護應用程序的服務實例(Pod、VM),包括服務實例會自動注冊這個服務注冊表上;不健康的實例從目錄中刪除。而服務發現的功能是Pilot提供了通用的服務發現接口,供數據面調用動態更新實例。
即:Istio本身不提供服務發現能力,而是提供了一種adapter的機制來適配各種不同的平臺。
多平臺支持的Adpater機制
具體講,Istio的服務發現在Pilot中完成,通過以下框圖可以看到,Pilot提供了一種平臺Adapter,可以對接多種不同的平臺獲取服務注冊信息,并轉換成Istio通用的抽象模型。
從pilot的代碼目錄也可以清楚看到,至少支持consul、k8s、eureka、cloudfoundry等平臺。
服務發現的主要行為定義
服務發現的幾重要方法方法和前面看到的Service的抽象模型一起定義在service中。可以認為是Istio服務發現的幾個主要行為。
// ServiceDiscovery enumerates Istio service instances.
type ServiceDiscovery interface {
// 服務列表
Services() ([]*Service, error)
// 根據域名的得到服務
GetService(hostname Hostname) (*Service, error)
// 被InstancesByPort代替
Instances(hostname Hostname, ports []string, labels LabelsCollection) ([]*ServiceInstance, error)
//根據端口和標簽檢索服務實例,最重要的以方法。
InstancesByPort(hostname Hostname, servicePort int, labels LabelsCollection) ([]*ServiceInstance, error)
//根據proxy查詢服務實例,如果是sidecar和pod裝在一起,則返回該服務實例,如果只是裝了sidecar,類似gateway,則返回空
GetProxyServiceInstances(*Proxy) ([]*ServiceInstance, error)
ManagementPorts(addr string) PortList
}
下面選擇其中最簡單也可能是大家最熟悉的
主要流程分析
1.服務發現服務入口
Pilot有三個獨立的服務分別是agent,discovery和sidecar-injector。分別提供sidecar的管理,服務發現和策略管理,sidecar自動注入的功能。Discovery的入口都是pilot的pilot-discovery。
在service初始化時候,初始化ServiceController?和?DiscoveryService。
if err := s.initServiceControllers(&args); err != nil {
returnnil, err
}
if err := s.initDiscoveryService(&args); err != nil {
returnnil, err
}
前者是構造一個controller來構造服務發現數據,后者是提供一個DiscoveryService,發布服務發現數據,后面的分析可以看到這個DiscoveryService向Envoy提供的服務發現數據正是來自Controller構造的數據。我們分開來看。
2.Controller對接不同平臺維護服務發現數據
首先看Controller。在initServiceControllers根據不同的registry類型構造不同的conteroller實現。如對于Eureka的注冊類型,構造了一個Eurkea的controller。
case serviceregistry.EurekaRegistry:
eurekaClient := eureka.NewClient(args.Service.Eureka.ServerURL)
serviceControllers.AddRegistry(
aggregate.Registry{
Name:???????????? serviceregistry.ServiceRegistry(r),
ClusterID:??????? string(serviceregistry.EurekaRegistry),
Controller:?????? eureka.NewController(eurekaClient, args.Service.Eureka.Interval),
ServiceDiscovery: eureka.NewServiceDiscovery(eurekaClient),
ServiceAccounts:? eureka.NewServiceAccounts(),
})
可以看到controller里包裝了Eureka的client作為句柄,不難猜到服務發現的邏輯正式這個client連Eureka的名字服務的server獲取到。
func NewController(client Client, interval time.Duration) model.Controller {
return &controller{
interval:???????? interval,
serviceHandlers:? make([]serviceHandler, 0),
instanceHandlers: make([]instanceHandler, 0),
client:?????????? client,
}
}
ServiceDiscovery中定義的幾個重要方法,我們拿最重要的InstancesByPort來看下在Eureka下是怎么支持,其他的幾個都類似。可以看到就是使用Eureka client去連Eureka server去獲取服務發現數據,然后轉換成istio通用的Service和ServiceInstance的數據結構。分別要轉換convertServices convertServiceInstances convertPorts convertProtocol等。
// InstancesByPort implements a service catalog operation
func?(sd *serviceDiscovery)?InstancesByPort(hostname model.Hostname,?port?int,
tagsList model.LabelsCollection) ([]*model.ServiceInstance,?error) {
apps,?err := sd.client.Applications()
services := convertServices(apps,?map[model.Hostname]bool{hostname:?true})
out :=?make([]*model.ServiceInstance,?0)
for?_,?instance :=?range?convertServiceInstances(services,?apps) {
out = append(out,?instance)
}
return?out,?nil
}
Eureka client或服務發現數據看一眼,其實就是通過Rest方式訪問/eureka/v2/apps連Eureka集群來獲取服務實例的列表。
func?(c *client)?Applications() ([]*application,?error) {
req,?err := http.NewRequest("GET",?c.url+appsPath,?nil)
req.Header.Set("Accept",?"application/json")
resp,?err := c.client.Do(req)
data,?err := ioutil.ReadAll(resp.Body)
var?apps getApplications
if?err = json.Unmarshal(data,?&apps);?err != nil {
return?nil,?err
}
return?apps.Applications.Applications,?nil
}
Application是本地對Instinstance對象的包裝。
type?application?struct?{
Name??????string??????`json:"name"`
Instances []*instance?`json:"instance"`
}
又看到了eureka熟悉的ServiceInstance的定義。當年有個同志提到一個方案是往metadata這個map里塞租戶信息,在eureka上做多租。
type?instance?struct?{?// nolint: maligned
Hostname???string?`json:"hostName"`
IPAddress??string?`json:"ipAddr"`
Status?????string?`json:"status"`
Port?????? port???`json:"port"`
SecurePort port???`json:"securePort"`
Metadata metadata?`json:"metadata,omitempty"`
}
以上我們就看完了服務發現數據生成的過程。對接名字服務的服務發現接口,獲取數據,轉換成Istio抽象模型中定義的標準格式。下面看下這些服務發現數據怎么提供出去被Envoy使用的。
3.DiscoveryService?發布服務發現數據
在pilot server初始化的時候,除了前面初始化了一個controller外,還有一個重要的initDiscoveryService初始化Discoveryservice。
environment := model.Environment{
Mesh:???????????? s.mesh,
IstioConfigStore: model.MakeIstioStore(s.configController),
ServiceDiscovery: s.ServiceController,
..
}
…
s.EnvoyXdsServer = envoyv2.NewDiscoveryServer(environment,?v1alpha3.NewConfigGenerator(registry.NewPlugins()))
s.EnvoyXdsServer.Register(s.GRPCServer)
..
即構造gRPC server提供了對外的服務發現接口。DiscoveryServer定義如下
//Pilot支持Evnoy V2的xds的API
type?DiscoveryServer?struct?{
// env is the model environment.
env model.Environment
ConfigGenerator *v1alpha3.ConfigGeneratorImpl
modelMutex????? sync.RWMutex
services??????? []*model.Service
virtualServices []*networking.VirtualService
virtualServiceConfigs []model.Config
}
即提供了這個grpc的服務發現Server,sidecar通過這個server獲取服務發現的數據,而server使用到的各個服務發現的功能通過Environment中的ServiceDiscovery句柄來完成。從前面environment的構造可以看到這個ServiceDiscovery正是上一個init構造的controller。
// Environment provides an aggregate environmental API for Pilot
type?Environment?struct?{
// Discovery inte**ce for listing services and instances.
ServiceDiscovery
這樣幾個功能組件的交互會是這個樣子。
Controller使用EurekaClient來獲取服務列表,提供轉換后的標準的服務發現接口和數據結構。
Discoveryserver基于Controller上維護的服務發現數據,發布成gRPC協議的服務供Envoy使用。
非常不幸的是,碼完這篇文字碼完的時候,收到社區里merge了這個PR?:因為Eureka v2.0?has been discontinued,Istio服務發現里removed eureka adapter?。即1.0版本后再也看不到Istio對Eureka的支持了。這里描述的例子真的就成為一個例子了。
總結
我們以官方文檔上這張經典的圖來端到端的串下整個服務發現的邏輯:
Pilot中定義了Istio通用的服務發現模型,即開始分析到的幾個數據結構;
Pilot使用adapter方式對接不同的(云平臺的)的服務目錄,提取服務注冊信息;
Pilot使用將2中服務注冊信息轉換成1中定義的自定義的數據結構。
Pilot提供標準的服務發現接口供數據面調用。
數據面獲取服務服務發現數據,并基于這些數據更新sidecar后端的LB實例列表,進而根據相應的負載均衡策略將請求轉發到對應的目標實例上。
API Istio
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。