代码改变世界

企业应用架构研究系列二十三:微服务之程序网关YARP

2022-08-17 08:43  温森特  阅读(934)  评论(0编辑  收藏  举报

  在搭建微服务框架的时候,离不开一个关键的微服务组件,应用程序网关,这个组件在微服务框架体系内至关重要。通过应用程序网关,可以将微服务框架内的服务进行重定向、限流、监控、故障转移等整操作后,对外提供应用程序池中的服务,应用程序服务池是对外部不透明的,唯一的数据交换点就是微服务的应用程序网关。

  应用程序网的成熟开源产品比较多,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 的高级功能研究。