Envoy HTTP路由
HTTP 路由功能
Envoy 包含一个 HTTP路由器过滤器,可以安装它来执行高级路由任务。这对于处理边缘流量(传统的反向代理请求处理)以及构建服务 Envoy 网格(通常通过主机/授权 HTTP 标头上的路由以到达特定的上游服务集群)都很有用。Envoy 还可以配置为正向代理。在转发代理配置中,网格客户端可以通过适当地将其 http 代理配置为 Envoy 来参与。在较高级别,路由器接收传入的 HTTP 请求,将其与上游集群匹配,获取到上游集群中主机的 连接池,然后转发请求。路由器过滤器支持以下功能:
-
将域/权限映射到一组路由规则的虚拟主机。
-
前缀和精确路径匹配规则(区分大小写和不区分大小写)。
-
正则表达式路径匹配规则。
-
虚拟主机级别的TLS 重定向。
-
路由级别的路径/主机重定向。
-
路由级别的直接(非代理)HTTP 响应。
-
显式主机重写。
-
根据所选上游主机的 DNS 名称自动重写主机。
-
前缀重写。
-
使用正则表达式和捕获组进行路径重写。
-
通过 HTTP 标头或路由配置指定的请求重试。
-
通过HTTP 标头或路由配置指定的请求超时。
-
请求对冲以响应请求(每次尝试)超时而重试。
-
流量通过运行时值从一个上游集群转移到另一个集群(请参阅流量转移/拆分)。
-
使用基于权重/百分比的路由将流量拆分到多个上游集群(请参阅流量转移/拆分)。
-
任意标头匹配路由规则。
-
虚拟集群规范。虚拟集群是在虚拟主机级别指定的,Envoy 使用它在标准集群级别的基础上生成额外的统计信息。虚拟集群可以使用正则表达式匹配。
-
基于优先级的路由。
-
基于哈希策略的路由。
-
非 tls 转发代理支持绝对 url 。
将域名映射到虚拟主机
- domains:
- 将请求报文中的host标头值依次与路由表中定义的各Virtualhost的domain属性值进行比较,并于第一次匹配时终止搜索;
-
域名搜索顺序:
-
-
-
精确匹配:
www.foo.com
. -
左前缀通配符匹配:
*.foo.com
或*-bar.foo.com
. -
右后缀通配符匹配:
foo.*
或foo-*
. -
*
匹配任何域的特殊通配符。
-
-
- 通配符将不匹配空字符串。例如
*-bar.foo.com
将匹配baz-bar.foo.com
但不匹配-bar.foo.com
。最长的通配符首先匹配。整个路由配置中只有一个虚拟主机可以匹配上*
。域在所有虚拟主机中必须是唯一的,否则配置将无法加载。
路由配置
路由匹配(match)
-
基于prefix、path、safe_regex和connect_matchter 四者其中任何一个进行URL匹配
-
基础匹配:prefix、path和safe_regex
-
-
可额外根据headers和query_parameters完成报文匹配
-
高级匹配:headers和query_patameters
-
路由
-
支持cluster、weighted_clusters和cluster_header三者之一定义目标路由;
-
发期间可根据prefix_rewrite和host_rewrite完成URL重写;
-
可额外配置流量管理机制,例如
- 韧性相关:timeout、retry_policy
- 测试相关:request_mirror_policy
- 流控相关:rate_limits
- 访问控制相关: cors
路由匹配
路由匹配条件
- 匹配条件是定义的检测机制,用于过滤出符合条件的请求并对其作出所需的处理,例如路由、重定向或直接响应等;
-
必须要定义prefix、path和regex三种匹配条件中的一种形式
-
- 除了必须设置上述三者其中之一外,还可额外完成如下限定
-
区分字符大小写(case_sensitive)
- 匹配指定的运行键值表示的比例进行流量迁移(runtime_fraction);
- 不断地修改运行时键值完成流量迁移
-
基于标头的路由:匹配指定的一组标头(headers);
- 基于参数的路由:匹配指定的一组URL查询参数(query_parameters);
-
仅匹配grpc流量(grpc);
-
基于标头的路由匹配
route.HeaderMatcher
-
指定的路由需要额外匹配指定的一组标头
-
路由器将根据路由配置中的所有指定标头检查请求的标头
-
若路由中指定的所有标头都存在于请求中且具有相同值,则匹配
-
若配置中未指定标头值,则基于标头的存在性进行判断
-
- 标头及其值的上述检查机制仅能定义exact_match、safe_regex_match range_match、 present_match 、prefix_match 、suffix_match、contains_match及string_match其中之一;
-
基于标头的路由匹配格式
查看代码
--
listeners:
...
filter_chains:
filter_chain_match: {...}
filters:
name: ...
typed_config: ...
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
...
route_config:
name: ...
virtual_hosts:
name: ...
domains: []
routes:
name: ...
match:
...
headers:
name: ...
exact_match: ... # 精确值匹配
safe_regex_match: {...} # 正则表达式模式匹配
range_match: {...} # 值范围匹配,检查标头值是否在指定的范围内
present_match: ... # 标头存在性匹配,检查标头存在与否
prefix_match: ... # 值前缀匹配
suffix_match: ... # 值后缀匹配
contains_match: ... # 检测标头值是否包含此处指定的字符串
string_match: {...} # 检测标头值是否匹配该处指定的字符串
invert_match: ... # 是否将匹配的检测结果取反,即以不满足条件为”真”,默认为fase
treat_missing_header_as_empty: ... # 如果标头匹配规则指定的标头不存在,则此标头值将被视为空,默认为fase
基于查询参数的路由匹配
route.QueryParameterMatcher
-
指定的路由需要额外匹配的一组URL查询参数
-
路由器将根据路由配置中指定的所有查询参数检查路径头中的查询字符串
-
查询参数匹配将请求的URL中查询字符串视为以&符号分隔的“键”或“键=值”元素列表
-
若存在指定的查询参数,则所有参数都必须与URL中的查询字符串匹配
-
匹配条件指定为value、regex、string_match或present_match其中之一
-
-
基于查询参数的路由匹配格式
查看代码
--
listeners:
...
filter_chains:
filter_chain_match: {...}
filters:
name: ...
typed_config: ...
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
...
route_config:
name: ...
virtual_hosts:
name: ...
domains: []
routes:
name: ...
match:
...
query_parameters:
name: ...
string_match: # 参数值的字符串匹配检查,支持使用以下五种检查方式其中之一进行字符串匹配
exact: ...
prefix: ...
suffix: ...
safe_regex: {...}
contains: ...
ignore_case: ...
present_match: ...
路由目标
路由到指定集群
route
-
匹配到的流量可路由至如下三种目标之一
-
cluster:路由至指定的上游集群;
- cluster_header:路由至请求标头中由cluster_header的值指定的上游集群;
- weighted_clusters:基于权重将请求路由至多个上游集群,进行流量分割;
-
路由到指定集群格式
查看代码
--
listeners:
...
filter_chains:
filter_chain_match: {...}
filters:
name: ...
typed_config: ...
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
...
route_config:
name: ...
virtual_hosts:
name: ...
domains: []
routes:
name: ...
route:
cluster: ... # 路由到指定的目标集群
cluster_header: ...
weighted_clusters: {...} # 路由并按权重比例分配到多个上游集群
cluster_specifier_plugin: ...
inline_cluster_specifier_plugin: {...}
cluster_not_found_response_code: ...
metadata_match: {...}
prefix_rewrite: ...
regex_rewrite: {...}
host_rewrite_literal: ...
auto_host_rewrite: {...}
host_rewrite_header: ...
host_rewrite_path_regex: {...}
append_x_forwarded_host: ...
timeout: {...}
idle_timeout: {...}
early_data_policy: {...}
retry_policy: {...}
request_mirror_policies: []
priority: ...
rate_limits: []
include_vh_rate_limits: {...}
hash_policy: []
cors: {...}
max_grpc_timeout: {...}
grpc_timeout_offset: {...}
upgrade_configs: []
internal_redirect_policy: {...}
internal_redirect_action: ...
max_internal_redirects: {...}
hedge_policy: {...}
max_stream_duration: {...}
重定向
redirect
-
为请求响应一个301应答,从而将请求从一个URL永久重定向至另一个URL
-
Envoy支持如下重定向行为
-
协议重定向:https_redirect或scheme_redirect二者只能使用其一;
-
主机重定向:host_redirect
-
端口重定向:port_redirect
-
路径重定向:path_redirect
-
路径前缀重定向:prefix_redirect
-
正则表达式模式定义的重定向:regex_rewrite
-
重设响应码:response_code,默认为301
-
strip_query:是否在重定向期间删除URL中的查询参数,默认为false;
-
重定向格式
查看代码
--
listeners:
...
filter_chains:
filter_chain_match: {...}
filters:
name: ...
typed_config: ...
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
...
route_config:
name: ...
virtual_hosts:
name: ...
domains: []
routes:
name: ...
redirect:
https_redirect: ...
scheme_redirect: ...
host_redirect: ...
port_redirect: ...
path_redirect: ...
prefix_rewrite: ...
regex_rewrite: {...}
response_code: ...
strip_query: ...
直接响应请求
direct_response
-
Envoy还可以直接响应请求
-
status:指定响应码;
-
body:响应正文,可省略,默认为空;需要指定时应该由body通过如下三种方式之一给出数据源
-
filename:本地文件数据源;
-
inline_bytes:直接给出的内联字节;
- inline_string:直接给出的内联字符串;
-
-
直接响应请求格式
查看代码
--
listeners:
...
filter_chains:
filter_chain_match: {...}
filters:
name: ...
typed_config: ...
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
...
route_config:
name: ...
virtual_hosts:
name: ...
domains: []
routes:
name: ...
direct_response:
status: ...
body:
filename: ... # 本地文件数据源。必须设置filename、inline_bytes、inline_string、environment_variable之一。
inline_bytes: ... # 直接给出的内联字节
inline_string: ... # 直接给出的内联字符串
environment_variable: ... # 环境变量数据源
路由策略
基础路由配置
-
在match中简单通过prefix、path或regex指定匹配条件
-
将匹配到的请求进行重定向、直接响应或路由到指定目标集群
高级路由策略
-
在match中通过prefix、path或regex指定匹配条件,并使用高级匹配机制
-
结合runtime_fraction按比例切割流量
-
结合headers按指定的标头路由,例如基于cookie进行,将其值分组后路由到不同目标;
-
结合query_parameters按指定的参数路由,例如基于参数group进行,将其值分组后路由到不同的目标;
-
提示:可灵活组合多种条件构建复杂的匹配机制;
-
-
复杂路由目标
-
结合请求报文标头中cluster_header的值进行定向路由;
-
weighted_clusters:将请求根据目标集群权重进行流量分割;
-
配置高级路由属性,例如重试、超时、CORS、限速等;
-
路由配置示例
示例一
envoy配置文件
查看代码
admin:
profile_path: /tmp/envoy.prof
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: vh_001
domains: ["foo.com", "*.foo.com", "foo.*"]
routes:
- match:
path: "/service/blue"
route:
cluster: blue
- match:
safe_regex:
google_re2: {}
regex: "^/service/.*blue$"
redirect:
path_redirect: "/service/blue"
- match:
prefix: "/service/yellow"
direct_response:
status: 200
body:
inline_string: "This page will be provided soon later.\n"
- match:
prefix: "/"
route:
cluster: red
- name: vh_002
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: gray
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: blue
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: blue
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: blue
port_value: 80
- name: red
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: red
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: red
port_value: 80
- name: green
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: green
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: green
port_value: 80
- name: gray
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: gray
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: gray
port_value: 80
测试用例
测试domain的匹配机制
# 首先访问无法匹配到vh_001的域名
curl -H "Host: www.roo.com" http://ip/service/a
# 接着访问可以匹配vh_001的域名
curl -H "Host: www.foo.com" http://ip/service/a
测试路由匹配机制
# 首先访问“/service/blue”
curl -H "Host: www.foo.com" http://ip/service/blue
# 接着访问“/service/dark_blue”
curl -I -H "Host: www.foo.com" http://ip/service/dark_blue
HTTP/1.1 301 Moved Permanently
location: http://www.foo.com/service/blue
date: Fri, 29 Oct 2021 03:02:59 GMT
server: envoy
transfer-encoding: chunked
# 然后访问“/serevice/yellow”
curl -H "Host: www.foo.com" http://ip/service/yellow
This page will be provided soon later.
示例二
envoy配置
查看代码
admin:
profile_path: /tmp/envoy.prof
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: vh_001
domains: ["*"]
routes:
- match:
prefix: "/"
headers:
- name: X-Canary
exact_match: "true"
route:
cluster: demoappv12
- match:
prefix: "/"
query_parameters:
- name: "username"
string_match:
prefix: "vip_"
route:
cluster: demoappv11
- match:
prefix: "/"
route:
cluster: demoappv10
http_filters:
- name: envoy.filters.http.router
clusters:
- name: demoappv10
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: demoappv10
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: demoappv10
port_value: 80
- name: demoappv11
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: demoappv11
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: demoappv11
port_value: 80
- name: demoappv12
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: demoappv12
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: demoappv12
port_value: 80
测试用例
测试使用“X-Canary: true”村头的请求
# 使用特定的标头发起请求
curl -H "X-Canary: true" http://ip/hostname
ServerName: demoapp-v1.2-1
测试使用特定的查询条件
# 在请求中使用特定的查询条件
curl http://ip/hostname?username=vip_123
ServerName: demoapp-v1.1-1
curl http://ip/hostname?username=vip_456
ServerName: demoapp-v1.1-2
参考文档
https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/http_routing
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto#envoy-v3-api-msg-config-route-v3-routeconfiguration
https://www.envoyproxy.io/docs/envoy/latest/api-v3/http_routes/http_routes