在搭建微服務框架的時候,離不開一個關鍵的微服務組件,應用程序網關,這個組件在微服務框架體系內至關重要。通過應用程序網關,可以將微服務框架內的服務進行重定向、限流、監控、故障轉移等整操作后,對外提供應用程序池中的服務,應用程序服務池是對外部不透明的,唯一的數據交換點就是微服務的應用程序網關。
應用程序網的成熟開源產品比較多,Java、Go、.Net Core 等開發語言都提供了優秀的開源產品,由於目前專攻.Net Core 微服務架構,本文將只談.Net Core 語言開發的應用程序網關。其中最為出名的就是Ocelot 開源組件了,它提供了極其豐富的應用程序網關功能,並且Ocelot衍生的其它開源產品也是很多,Ocelot的生態非常不錯。
但是今天,給大家推薦一款微軟開源的反向代理組件Yarp.ReverseProxy(Yet Another Robot Platform),這個開源產品知名度目前並不高,提供的應用程序網關功能也不是很強大,通過深入的研究,我相信它是一個很有潛力的開源產品。首先相信微軟推出的開源產品一定是精品,其次,YARP基於管道機制提供了豐富的二次開發接口和方法,這樣可以根據自己項目或自己產品個性化的需求進行深度的定制開發。還有,Yarp 本身也提供了軟負載均衡、健康監控、分布式追蹤、報文和路由Transforms等強大功能、對gRPC、WebSock、SPDY 等網絡協議原生支持,新版本又推出了對后端服務提供基於HTTP/3連接的支持。微軟提供的性能測試,新版本(.NET 6 +)的性能高於Go 開發的ReverseProxy很高。

通過簡單的描述,大家對YARP有了一個簡單了解,現在開始說一下它具體怎么使用,YARP 的使用非常簡單,可以通過JSON配置文件就可以使用其很多功能,滿是大部分的反向代理功能。官網提供了詳細的使用說明文檔https://microsoft.github.io/reverse-proxy/index.html。並且這個YARP針對ServiceFabric 微服務運行平台提供了擴展包,可以滿足基於ServiceFabric 開發的微服務平台。
YARP 的使用非常簡單,主要包含兩部分:Routes 和 Clusters 兩部分配置,這兩部分通過ClusterId 進行關聯。具體的配置請參考官網文檔或下面提供的示例,非常容易理解每一個配置選項和配置方法,同時需要提醒的是,對路由的匹配和路由的轉變部分,還是需要用心研究一下。
{
// Base URLs the server listens on, must be configured independently of the routes below
"Urls": "http://localhost:5000;https://localhost:5001",
"Logging": {
"LogLevel": {
"Default": "Information",
// Uncomment to hide diagnostic messages from runtime and proxy
// "Microsoft": "Warning",
// "Yarp" : "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ReverseProxy": {
// Routes tell the proxy which requests to forward
"Routes": {
"minimumroute" : {
// Matches anything and routes it to www.example.com
"ClusterId": "minimumcluster",
"Match": {
"Path": "{**catch-all}"
}
},
"allrouteprops" : {
// matches /something/* and routes to "allclusterprops"
"ClusterId": "allclusterprops", // Name of one of the clusters
"Order" : 100, // Lower numbers have higher precedence
"Authorization Policy" : "Anonymous", // Name of the policy or "Default", "Anonymous"
"CorsPolicy" : "Default", // Name of the CorsPolicy to apply to this route or "Default", "Disable"
"Match": {
"Path": "/something/{**remainder}", // The path to match using ASP.NET syntax.
"Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], // The host names to match, unspecified is any
"Methods" : [ "GET", "PUT" ], // The HTTP methods that match, uspecified is all
"Headers": [ // The headers to match, unspecified is any
{
"Name": "MyCustomHeader", // Name of the header
"Values": [ "value1", "value2", "another value" ], // Matches are against any of these values
"Mode": "ExactHeader", // or "HeaderPrefix", "Exists" , "Contains", "NotContains"
"IsCaseSensitive": true
}
],
"QueryParameters": [ // The query parameters to match, unspecified is any
{
"Name": "MyQueryParameter", // Name of the query parameter
"Values": [ "value1", "value2", "another value" ], // Matches are against any of these values
"Mode": "Exact", // or "Prefix", "Exists" , "Contains", "NotContains"
"IsCaseSensitive": true
}
]
},
"MetaData" : { // List of key value pairs that can be used by custom extensions
"MyName" : "MyValue"
},
"Transforms" : [ // List of transforms. See the Transforms article for more details
{
"RequestHeader": "MyHeader",
"Set": "MyValue",
}
]
}
},
// Clusters tell the proxy where and how to forward requests
"Clusters": {
"minimumcluster": {
"Destinations": {
"example.com": {
"Address": "http://www.example.com/"
}
}
},
"allclusterprops": {
"Destinations": {
"first_destination": {
"Address": "https://contoso.com"
},
"another_destination": {
"Address": "https://10.20.30.40",
"Health" : "https://10.20.30.40:12345/test" // override for active health checks
}
},
"LoadBalancingPolicy" : "PowerOfTwoChoices", // Alternatively "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests"
"SessionAffinity": {
"Enabled": true, // Defaults to 'false'
"Policy": "Cookie", // Default, alternatively "CustomHeader"
"FailurePolicy": "Redistribute", // default, Alternatively "Return503Error"
"Settings" : {
"CustomHeaderName": "MySessionHeaderName" // Defaults to 'X-Yarp-Proxy-Affinity`
}
},
"HealthCheck": {
"Active": { // Makes API calls to validate the health.
"Enabled": "true",
"Interval": "00:00:10",
"Timeout": "00:00:10",
"Policy": "ConsecutiveFailures",
"Path": "/api/health" // API endpoint to query for health state
},
"Passive": { // Disables destinations based on HTTP response codes
"Enabled": true, // Defaults to false
"Policy" : "TransportFailureRateHealthPolicy", // Required
"ReactivationPeriod" : "00:00:10" // 10s
}
},
"HttpClient" : { // Configuration of HttpClient instance used to contact destinations
"SSLProtocols" : "Tls13",
"DangerousAcceptAnyServerCertificate" : false,
"MaxConnectionsPerServer" : 1024,
"EnableMultipleHttp2Connections" : true,
"RequestHeaderEncoding" : "Latin1" // How to interpret non ASCII characters in header values
},
"HttpRequest" : { // Options for sending request to destination
"ActivityTimeout" : "00:02:00",
"Version" : "2",
"VersionPolicy" : "RequestVersionOrLower",
"AllowResponseBuffering" : "false"
},
"MetaData" : { // Custom Key value pairs
"TransportFailureRateHealthPolicy.RateLimit": "0.5", // Used by Passive health policy
"MyKey" : "MyValue"
}
}
}
}
}
通過代碼也是可以進行配置的,使用代碼配置其實更符合真正的生產環境場景,生產環境,配置往往需要動態的進行配置調整,同時也可能會增加一些額外的運行參數到網關中等等,如增加網關的身份認證,通過身份認證后,拿到一些額外參數加入到請求報文中傳遞給后台服務等。下面是通過代碼進行的配置,如下圖。

現在我們就可以進行測試我們的YARP 網關了,我們通過將后端服務部署到Docker 中,通過 YARP 作為代理網關對外提供服務,如下圖,這樣達到了應用程序網關的效果,也實現了軟負載功能。

后續將繼續YARP 的高級功能研究。
