什么是Gateway
在微服务体系结构中,如果每个微服务通常都会公开一组精细终结点,这种情况可能会有以下问题
- 如果没有 API 网关模式,客户端应用将与内部微服务相耦合。
- 在客户端应用中,单个页面/屏幕可能需要多次调用多个服务。
- 如果没有网关,所有微服务必定会暴露在“外部世界”中。
- 每个公开发布的微服务都必须处理授权和 SSL 等问题。
而Gateway可以为微服务组提供单一入口点,API 网关位于客户端应用和微服务之间。 它充当反向代理,将请求从客户端路由到服务。 它还可以提供其他跨领域功能,例如身份验证、SSL 终止和缓存。
什么是Envoy
Envoy 是专为大型现代 SOA(面向服务架构)架构设计的 L7 代理和通信总线,它有以下优势
- C++11编写,原生代码高性能
- L3/L4 filter架构,例如TCP代理
- HTTP L7 filter架构,缓存,限速,路由/转发
- 顶级HTTP2与GRPC支持
- 服务发现与动态配置
- 健康检查
- 高级负载均衡
我们可以借助Envoy实现API Gateway。Envoy通过yaml配置文件来组织网关的信息。下面来说说Envoy中的核心概念
Listener
一个命名的网络地址,可以被下游客户端连接,它的配置样式如下:
static_resources: listeners: - name: listener_0 address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 10000
此配置说明Envoy监听在10000端口,下游客户端可以通过此端口与Envoy交互
L3/L4过滤器Filter
L3/L4过滤器Filter可以帮我们实现如:HTTP连接管理,限速,TCP代理等功能,它的配置样式如下:
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 scheme_header_transformation: scheme_to_overwrite: http stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: host_rewrite_literal: 192.168.43.94 cluster: service_envoyproxy_io http_filters: - name: envoy.filters.http.router
此配置说明通过HttpConnectionManager这个过滤器来接受HTTP请求,并将请求通过router过滤器的配置转发到service_envoyproxy_io这个上游集群
Upstream Cluster
Envoy 的集群管理器管理所有配置的上游集群,用来真正处理Envoy接受的请求,其配置样式如下:
clusters: - name: service_envoyproxy_io connect_timeout: 30s type: strict_dns dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_envoyproxy_io endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.43.94 port_value: 5000
此配置说明Envoy会将请求转发到192.168.43.94:5000这个地址。
调用逻辑我们总结如下,Listener接受请求,将请求交给过滤器,过滤器处理完后,根据路由规则将请求转发给上游集群,上游集群中的endpoint会真正处理请求。
运行Envoy
我们通过docker运行一个默认Envoy容器
docker run --rm -it -p 9901:9901 -p 10000:10000 envoyproxy/envoy-dev
访问http://localhost:10000/,发现其跳转到Envoy官网
我们进入容器查看其配置,发现其最终会将请求转发到www.envoyproxy.io
cat /etc/envoy/envoy.yaml
- lb_endpoints: - endpoint: address: socket_address: address: www.envoyproxy.io port_value: 443
静态文件配置
我们现在通过Envoy来实现我们自己的网关。静态文件配置是我们把配置信息提前配置好,Envoy启动后不可修改配置内容
准备服务
我们准备两个.NET WebAPI,server1与server2,其中分别创建NameController,并新建Get方法
Server1
[HttpGet] public string Get() { _logger.LogInformation("call server1"); var req = Request; return "server1"; }
Server2
[HttpGet] public string Get() { _logger.LogInformation("call server2"); var req = Request; return "server2"; }
并将server1的启动端口指定为5000,将server2的启动端口指定为5001
Server1
webBuilder.UseUrls("http://*:5555").UseStartup();
Server2
webBuilder.UseUrls("http://*:5001/").UseStartup();
我们启动Server1与Server2
准备Envoy配置
我们将上节课的默认Envoy配置文件从容器中取出,并作修改如下
admin: address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 9901 static_resources: listeners: - name: listener_0 address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 10000 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 scheme_header_transformation: scheme_to_overwrite: http stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: host_rewrite_literal: 192.168.43.94 cluster: service_envoyproxy_io http_filters: - name: envoy.filters.http.router clusters: - name: service_envoyproxy_io connect_timeout: 30s type: static # Comment out the following line to test on v6 networks dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_envoyproxy_io endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.43.94 port_value: 5000 - endpoint: address: socket_address: address: 192.168.43.94 port_value: 5001
我们启动Envoy,验证配置是否正确
docker run --rm -it -p 9901:9901 -p 10000:10000 -v D:/gateway/envoy/config/static/envoy.yaml:/etc/envoy/envoy.yaml -v D:/gateway/envoy/logs:/logs envoyproxy/envoy-dev -c /etc/envoy/envoy.yaml --log-path logs/custom.log
调用api,发现其实现了负载
http://localhost:10000/Name
动态文件配置
动态文件可以帮助我们实现当文件发生更改时,Envoy 将自动更新其配置。
修改静态文件,将其中的cluster提取到cds.yaml文件中
resources: - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: example_proxy_cluster type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http_protocol_options: {} load_assignment: cluster_name: example_proxy_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.43.94 port_value: 5000
将listener提取到lds.yaml文件中
resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http http_filters: - name: envoy.filters.http.router route_config: name: local_route virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/envoyapi/" route: prefix_rewrite: "/" host_rewrite_literal: 192.168.43.94 cluster: example_proxy_cluster
修改envoy.yaml让其引用lds.yaml与cds.yaml文件
admin: address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 9902 node: cluster: test-cluster id: test-id dynamic_resources: cds_config: path: /etc/envoy/cds.yaml lds_config: path: /etc/envoy/lds.yaml
启动Envoy
docker run --rm -it -p 9902:9902 -p 10000:10000 -v D:/gateway/envoy/config/dynamic/:/etc/envoy/ -v D:/gateway/envoy/logs:/logs envoyproxy/envoy-dev -c /etc/envoy/envoy.yaml --log-path logs/custom.log
调用api,发现调用成功
http://localhost:10000/envoyapi/Name
修改动态文件配置
修改cds.yaml,将endpoint端口设置为5001
resources: - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: example_proxy_cluster type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http_protocol_options: {} load_assignment: cluster_name: example_proxy_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.43.94 port_value: 5001
进入容器内部强制更新文件
# cd /etc/envoy # mv cds.yaml tmp # mv tmp cds.yaml
调用api,发现在不重启Envoy的情况下,实现了配置信息的动态更新
至此,我们已经通过Envoy的静态配置与文件动态配置实现了一个网关来代理我们的.NET程序