自定义服务网关设计方案调研

一、需求

流量入口代理作为互联网系统的门户组件,具备众多选型:从老牌代理 HAProxy、Nginx,到微服务 API 网关 Kong、Zuul,再到容器化 Ingress 规范与实现,不同选型间功能、性能、可扩展性、适用场景参差不齐。当云原生时代大浪袭来,Envoy 这一 CNCF 毕业数据面组件为更多人所知。那么,优秀“毕业生”Envoy 能否成为云原生时代下流量入口标准组件?

1 背景:流量入口的众多选型与场景

在互联网体系下,凡是需要对外暴露的系统几乎都需要网络代理:较早出现的 HAProxy、Nginx 至今仍在流行;进入微服务时代后,功能更丰富、管控能力更强的 API 网关又成为流量入口必备组件;在进入容器时代后,Kubernetes Ingress 作为容器集群的入口,是容器时代微服务的流量入口代理标准。关于这三类典型的七层代理,核心能力对比如下:

file

总体来说,Envoy 是一个功能与性能都非常优秀的“双优生”。在实际业务流量入口代理场景下,Envoy 具备先天优势,可以作为云原生技术趋势流量入口的标准技术方案:

  1. 较 HAProxy、Nginx 更丰富的功能

相较于 HAProxy、Nginx 提供流量代理所需的基本功能(更多高级功能通常需要通过扩展插件方式实现),Envoy 本身基于 C++ 已经实现了相当多代理所需高级功能,如高级负载均衡、熔断、限流、故障注入、流量复制、可观测性等。更为丰富的功能不仅让 Envoy 天生就可以用于多种场景,原生 C++ 的实现相较经过扩展的实现方式性能优势更为明显。

  1. 与 Nginx 相当,远高于传统 API 网关的性能

在性能方面,Envoy 与 Nginx 在常用协议代理(如 HTTP)上性能相当。与传统 API 网关相比,性能优势明显。如下为 Envoy 与几种业务常用的 API 网关选型在 8 核物理机容器运行环境下,简单路由代理性能对比数据:(网易内部环境实测数据,仅供参考)

二、说明

要实现基于请求参数、流量和头部的分流或灰度发布网关,可以使用 Java 编写一个自定义的服务网关。下面是一个大致的思路:

  1. 构建服务网关:使用 Java 编写一个独立的服务网关,它可以接收来自客户端的请求,并根据逻辑进行转发。

  2. 接收请求:服务网关需要能够接收来自客户端的请求。这可以通过使用 Spring Boot 或其他 Java Web 框架来实现。

  3. 请求分析:在服务网关中,解析客户端请求,包括请求参数、头部和流量信息。你可以使用 Java Servlet API 或框架提供的工具来获取请求参数和头部信息,并记录请求流量。

  4. 数据配置:定义分流规则或灰度发布策略的配置数据。这些数据可以以配置文件、数据库或其他形式存储。配置数据应该包括目标服务的 URL、分流规则的条件等。

  5. 分流逻辑:根据客户端请求的参数、流量和头部信息,使用预先定义的规则对请求进行分流或灰度发布。可以使用条件语句、规则引擎或其他方式来实现分流逻辑。

  6. 目标服务转发:根据分流规则确定请求应该转发到哪个目标服务。使用 Java 的 HTTP 客户端库(如 Apache HttpClient、OkHttp 或 Spring RestTemplate 等)将请求转发到目标服务。注意,这里可能涉及负载均衡和故障恢复等功能,你可以使用现成的负载均衡库(如 Ribbon、Nginx 等)来实现。

  7. 返回响应:将目标服务的响应返回给客户端。在返回过程中,你可以对响应进行一些处理,如修改头部、添加额外的信息等。

  8. 监控和日志:实现监控和日志记录,可以帮助你了解服务网关的性能、请求分流情况和问题诊断。你可以使用开源工具、日志框架或自定义的监控模块来完成这些任务。

以上是一个大致的思路,你可以根据具体需求和技术栈选择合适的框架和工具来实现服务网关。此外,要注意安全性和性能问题,比如对请求进行合法性验证、缓存优化和并发处理等。

相比手动实现一个完整的服务网关,使用 Istio 或类似的服务网格解决方案可以更轻松地管理和控制流量分发、灰度发布等功能。它们提供了可视化界面、自动化配置和更高级的特性。因此,在选择方案时,请考虑你的具体需求、团队技术水平和维护成本。

假如你作为一个架构师,整个系统是部署在k8s上的,要实现服务的流量分发、灰度发布等功能,但是不能用istio来实现,请结合现有条件来设计一个可以落地的方案,开发语言用java;

在条件限制下,结合 Kubernetes (K8s) 环境和 Java 开发语言,以下是一个可以落地的方案来实现服务的流量分发和灰度发布功能:

  1. 使用 K8s Ingress:K8s Ingress 是一个用于管理外部流量访问集群内服务的 API 对象,它可以通过定义规则来实现流量分发。你可以使用 Nginx Ingress Controller、Traefik 或其他支持 Ingress 的控制器来部署和配置 Ingress。

  2. 实现自定义负载均衡:如果 K8s Ingress 的默认负载均衡算法无法满足需求,你可以编写一个自定义的负载均衡器。这个负载均衡器可以根据请求头、参数或其他标识来确定目标服务的选择,并将请求转发到相应的服务实例。你可以使用 Java 编写一个负载均衡器应用程序,将其部署为一个独立的容器或作为 Sidecar 容器与应用程序一起运行。

  3. 实现分流策略:根据流量分发需求,你可以定义不同的分流策略来控制请求的路由。例如,可以基于请求头、IP 地址、用户标识等条件来决定请求应该路由到哪个版本或灰度环境。在负载均衡器中实现这些分流策略,并通过配置文件或环境变量动态调整策略。这样,你可以在不重启负载均衡器的情况下更新分流规则。

  4. 实现灰度发布:为了实现灰度发布,你可以使用 K8s 的 Deployment 对象来创建多个副本集(ReplicaSet)。这些副本集可以分别对应不同版本或灰度环境的服务。然后,在负载均衡器中设置权重和路由规则,控制请求分发给不同版本或灰度副本集。你可以逐步调整权重或修改路由规则,实现渐进式灰度发布。

  5. 实时监控和日志记录:为了确保系统的稳定性和性能,你可以实现实时监控和日志记录功能。使用 Prometheus 和 Grafana 来收集和可视化指标数据,帮助你监控流量、响应时间、错误率等关键指标。另外,使用 ELK(Elasticsearch、Logstash 和 Kibana)堆栈或类似工具来收集和分析日志数据。

综上所述,你可以结合 K8s Ingress、自定义负载均衡器和分流策略来实现服务的流量分发和灰度发布功能。这种方案可以在 K8s 环境下部署,同时使用 Java 作为开发语言来开发自定义的负载均衡器。请根据实际需求和团队技术水平选择适当的工具和库来实施方案。

如何实现自定义负载均衡器?有没有开源的代码或产品推荐?

要实现自定义负载均衡器,你可以编写一个独立的应用程序或使用现有的开源项目。以下是一些开源的负载均衡器项目和库,供你选择:

  1. HAProxy:HAProxy 是一个功能强大且广泛使用的开源负载均衡器,支持 TCP 和 HTTP 协议,并提供灵活的请求路由和负载均衡算法。你可以使用 HAProxy 的配置文件来定义后端服务的地址和权重,以及路由规则。官方网站:http://www.haproxy.org/

  2. NGINX:NGINX 是一个高性能的开源 Web 服务器,同时也可以作为负载均衡器使用。NGINX 提供了简单而强大的负载均衡功能,支持 HTTP、TCP 和 UDP 协议,并可根据不同的规则进行请求路由。官方网站:https://nginx.org/

  3. Envoy:Envoy 是一个现代化的开源边缘和服务代理,具有强大的负载均衡、流量管理和故障恢复功能。Envoy 基于可扩展性和性能而设计,支持多种协议,并提供灵活的配置选项。官方网站:https://www.envoyproxy.io/

  4. Netflix Ribbon:Netflix Ribbon 是一个开源的客户端负载均衡库,特别适用于基于 Java 的微服务架构。它具有灵活的负载均衡策略和可插拔的扩展机制,可以与多种服务发现组件集成。GitHub 地址:https://github.com/Netflix/ribbon

  5. Spring Cloud Load Balancer:Spring Cloud Load Balancer 是 Spring Cloud 生态系统中的一个组件,提供了基于 Spring Cloud 的负载均衡能力。它具有简单易用的编程接口,支持自定义的负载均衡策略,并与 Spring Cloud 服务发现组件无缝集成。GitHub 地址:https://github.com/spring-cloud/spring-cloud-loadbalancer

这些开源项目都有广泛的社区支持和活跃的开发者社区,你可以根据自己的需求和偏好选择适合的负载均衡器。请注意,使用任何开源项目或库时,务必仔细阅读文档并遵循最佳实践来确保安全性和稳定性。

问:上边推荐的集中自定义负载均衡器,哪种更能满足提的需求,并且能灵活扩展,支持 基于流量、Header、灰度发布、请求参数来做流量分流?

对于你提到的需求,Envoy 是一个更适合的选择。Envoy 是一个功能强大且高度可定制的边缘和服务代理,它具有以下特点:

  1. 灵活的负载均衡和流量管理:Envoy 提供了丰富的负载均衡算法、健康检查、超时控制和熔断机制,可以帮助你实现灵活的流量管理和负载均衡策略。

  2. 多种路由规则支持:Envoy 支持基于请求头、路径、查询参数等多种条件进行请求路由,可以实现基于流量、Header 和请求参数的流量分流。

  3. 动态配置和扩展性:Envoy 的配置使用标准的 xDS API,支持动态更新负载均衡器的路由规则和集群成员。你可以集成 Envoy with EDS(Endpoint Discovery Service)和 CDS(Cluster Discovery Service)来实现动态的服务发现和扩展性。

  4. 灰度发布和流量切换:Envoy 提供了流量切换和灰度发布的功能。你可以使用 Envoy 的权重路由、请求头匹配和虚拟主机路由功能,将请求流量分发给不同版本或灰度环境的服务。

总而言之,Envoy 可以满足自定义负载均衡器的需求,并提供了灵活的路由规则、可扩展的配置和灰度发布功能。它还得到了广泛的社区支持和活跃的开发者社区,有很多企业在生产环境中使用 Envoy 来实现流量管理和负载均衡。因此,我推荐你考虑使用 Envoy 来实现你的需求。

问:Spring Cloud Load Balancer 能否实现?

是的,Spring Cloud Load Balancer 也可以实现你提到的需求。Spring Cloud Load Balancer 是 Spring Cloud 生态系统中的一个组件,专门用于提供负载均衡的能力。

Spring Cloud Load Balancer 提供了简单易用的编程接口,可以与 Spring Cloud 的服务发现组件(如 Eureka、Consul)无缝集成。通过使用 Spring Cloud Load Balancer,你可以实现以下功能:

  1. 负载均衡策略:Spring Cloud Load Balancer 支持多种负载均衡算法,如轮询、随机、权重等。你可以根据自己的需求选择合适的算法。

  2. 动态配置和扩展性:Spring Cloud Load Balancer 使用 Reactive 应用程序接口,并且支持动态更新负载均衡器的路由规则和服务实例。你可以利用 Spring Cloud Config 或其他配置中心来实现动态配置,并根据需要扩展负载均衡器的功能。

  3. 请求参数和Header路由:Spring Cloud Load Balancer 支持在请求中使用参数和Header进行路由。你可以根据请求中的参数或Header值来决定请求应该转发到哪个服务实例。

  4. 灰度发布和流量切换:Spring Cloud Load Balancer 可以和 Spring Cloud Gateway 或 Zuul 网关配合使用,来实现灰度发布和流量切换的功能。你可以在网关层面定义路由规则,将请求流量分发给不同版本或灰度环境的服务。

总结而言,Spring Cloud Load Balancer 可以满足你提到的基于流量、Header、灰度发布和请求参数的流量分流需求。它与 Spring Cloud 生态系统紧密集成,并且易于使用和扩展。因此,如果你正在使用 Spring Cloud 或计划使用 Spring Cloud,那么使用 Spring Cloud Load Balancer 是一个很好的选择。

问:Envoy 和 Spring Cloud Load Balancer 这两种方式哪种更好?从架构到部署、使用等方面考虑

Envoy 和 Spring Cloud Load Balancer 都是很优秀的负载均衡器,它们各有优缺点,具体选择需要根据你的业务场景和需求来决定。我这里给你分别介绍一下它们的特点和适用场景。

  1. Envoy

Envoy 是一个功能强大且高度可定制的边缘和服务代理,它可以解决多种复杂的流量管理问题,包括负载均衡、路由、流控、故障恢复、安全等。它的优点在于:

  • 完整的控制平面和数据平面架构,支持动态配置和扩展性;
  • 丰富的路由规则和负载均衡策略,可以灵活满足不同的业务需求;
  • 支持灰度发布和流量切换等高级流量管理功能;
  • 有一个成熟的社区和广泛的应用场景。

不过,在使用 Envoy 负载均衡器时,也需要考虑以下缺点:

  • 由于 Envoy 的配置非常灵活和复杂,所以对使用者的学习和使用成本较高;
  • Envoy 作为独立的代理服务存在部署和维护的成本;
  • 当服务数量较少时,Envoy 的资源占用可能会过于庞大。

因此,Envoy 适用于需要灵活配置和高级流量管理的复杂业务场景,需要考虑 Envoy 的学习和部署成本。

  1. Spring Cloud Load Balancer

Spring Cloud Load Balancer 是 Spring Cloud 生态系统中的一个组件,它专门用于提供负载均衡的能力。与 Envoy 相比,Spring Cloud Load Balancer 更加轻量级和易于使用,特点在于:

  • 与 Spring Cloud 微服务框架紧密结合,无缝与 Eureka、Consul 等服务发现组件集成;
  • 简单易用的编程接口,支持多种负载均衡算法、动态配置和参数路由;
  • 部署和维护成本低,可以直接嵌入到 Spring Boot 应用程序中。

当然,Spring Cloud Load Balancer 也有一些限制和缺陷:

  • 功能相对简单,只适用于基本的负载均衡需求;
  • 对于某些复杂的路由规则(如 Header 路由),需要较多的编码工作。

因此,如果你正在使用 Spring Cloud 微服务架构,那么 Spring Cloud Load Balancer 是一个很不错的选择。它具有轻量、易用、低成本的优势,但是对于一些复杂的应用场景,可能需要考虑使用 Envoy 等更加灵活和强大的负载均衡器。

总而言之,Envoy 和 Spring Cloud Load Balancer 都具有其独特的优势,具体选择需要结合自己的业务场景和需求来决定,从架构到部署、使用等方面考虑。

问:Haproxy 可以做到根据请求体body 的参数来做流量转发吗?

是的,HAProxy 可以根据请求体(Request Body)的参数来进行流量转发。HAProxy 是一款功能强大的开源负载均衡器和代理服务器,它支持多种流量调度算法和高级路由配置,包括基于请求体的参数。

要实现根据请求体参数的流量转发,你可以使用 HAProxy 的 HTTP 请求匹配和转发规则。以下是一种可能的配置示例:

frontend http-in
    bind *:80
    mode http
    default_backend servers

    acl param_value_1 req.body_param(param_name) -m str value_1
    acl param_value_2 req.body_param(param_name) -m str value_2

    use_backend backend_value_1 if param_value_1
    use_backend backend_value_2 if param_value_2

backend backend_value_1
    mode http
    server server1 192.168.0.1:8080

backend backend_value_2
    mode http
    server server2 192.168.0.2:8080

在这个示例中,frontend 配置项定义了监听的端口和协议。通过 acl 指令,我们在请求体中匹配指定的参数值,并使用 use_backend 指令将请求转发到相应的后端服务器。

请注意,上述示例仅作为一个概念演示,并不涵盖所有细节和配置选项。根据你的具体需求,你可能需要进一步调整和扩展配置。

总结而言,通过适当的配置,HAProxy 可以根据请求体的参数来实现流量转发,使你能够根据请求体的不同参数值将请求发送到不同的后端服务器。

问:HAProxy 和 Envoy 哪种方案更好?
HAProxy 和 Envoy 是两个功能强大的负载均衡器和代理服务器,它们都有自己的优点和适用场景。以下是对比它们的一些方面,以帮助你做出选择:

  1. 功能和灵活性:

    • Envoy 提供了更多的功能和高级特性,如动态配置、流量管理、路由规则等,并且可扩展性较强。如果你需要处理复杂的流量管理和负载均衡需求,Envoy 可能更适合。
    • HAProxy 功能相对较简单,主要专注于负载均衡和代理功能。它的配置相对直观和简单,并且易于上手。如果你只需要基本的负载均衡功能,HAProxy 可能更适合。
  2. 性能:

    • Envoy 在性能方面表现出色,尤其适用于处理高并发和大规模流量的场景。它使用异步事件处理和多线程模型,具有出色的性能和低延迟。
    • HAProxy 同样具有出色的性能,尤其在处理小规模流量时表现良好。它采用高效的事件驱动模型,通常被认为是一个轻量级的解决方案。
  3. 配置和部署:

    • HAProxy 的配置相对简单,可以直接在单个配置文件中完成。它的部署和维护相对容易,对于小型项目或简单的负载均衡需求来说是一个很好的选择。
    • Envoy 的配置更加复杂,需要使用 YAML 或 JSON 格式的配置文件,并具有更多的选项和灵活性。它通常作为一个独立的代理服务部署,需要一些额外的工作来配置和管理。

综上所述,如果你需要更多高级特性、动态配置和对复杂流量管理的需求,Envoy 可能更适合。而如果你只需要基本的负载均衡功能,且希望配置简单、性能良好,那么 HAProxy 可能更适合。最佳选择将取决于你的具体业务需求、技术栈和团队经验。

问:哪个更适合通过代码操作 路由规则配置文件呢?

如果要通过代码操作路由规则配置文件,Envoy 可能更适合。Envoy 提供了丰富的 API 和动态配置功能,可以通过控制平面 API 来进行运行时的路由规则修改和配置更改。

Envoy 的 API 采用了 gRPC 和 protobuf 协议,提供了诸如服务发现、负载均衡、健康检查等操作的接口。使用这些接口,你可以根据自己的需求,实时更新 Envoy 的路由规则配置,而无需重新启动或停止 Envoy 实例。

另外,Envoy 还提供了一个灵活的插件系统,可以让你通过编写自定义插件来扩展 Envoy 的特性,包括路由规则配置方面。插件可以以官方支持的 C++、Protobuf 或 Lua 脚本方式实现,灵活性很高。

相比之下,HAProxy 缺少类似的 API 和插件扩展机制。虽然 HAProxy 也提供了运行时的配置功能,但是它的配置文件是静态的文本格式,需要手动编辑和重载。因此,在需要频繁修改路由规则并希望通过代码操作的场景下,Envoy 是更加适合的选择。

请举例通过 Envoy 的 api来操作配置根据请求的参数来进行分配流量到不同的服务

当使用 Envoy 的 API 来根据请求参数进行流量分配时,你可以使用 Envoy 的动态路由功能。以下是一个使用 Envoy API 进行配置的示例:

首先,你需要在 Envoy 的配置文件中启用动态路由功能,并配置相关的过滤器和路由规则。然后,通过调用 Envoy 的 API 来更新这些配置。

  1. 定义静态部分的配置(例如监听地址、集群等)。
  2. 定义动态路由配置和过滤器配置。
  3. 使用 API 调用来将配置传递给 Envoy。

下面是一个通过 Envoy 的 API 设置路由规则,根据请求参数进行流量分配的示例:

// 导入 Envoy 的 protobuf 文件
import envoy.api.v2.RouteConfiguration;
import envoy.api.v2.VirtualHost;
import envoy.api.v2.route.RouteAction;
import envoy.api.v2.route.RouteMatch;
import envoy.api.v2.route.HeaderMatcher;
import envoy.api.v2.ClusterWeight;
import google.protobuf.UInt32Value;

// 创建 Route Configuration
RouteConfiguration routeConfig = RouteConfiguration.newBuilder()
    .setName("my_route_config")
    .addVirtualHosts(
        VirtualHost.newBuilder()
            .setName("my_virtual_host")
            .addRoutes(
                Route.newBuilder()
                    .setMatch(RouteMatch.newBuilder()
                        .setPrefix("/api")
                        .addHeaders(HeaderMatcher.newBuilder()
                            .setName(":path")
                            .setExactMatch("/api/my-service")
                            .build())
                        .build())
                    .setRoute(RouteAction.newBuilder()
                        .setCluster("my-service-cluster")
                        .build())
                    .build())
            .build())
    .build();

// 创建 Cluster 权重配置
ClusterWeight clusterWeight = ClusterWeight.newBuilder()
    .setName("my-service-cluster")
    .setWeight(UInt32Value.newBuilder().setValue(100))
    .build();

// 使用 API 将路由规则和集群权重配置传递给 Envoy
// 此处需要根据具体的编程语言和 Envoy API 的实现方式进行调用,以下为伪代码示例
envoyApi.updateRouteConfig(routeConfig);
envoyApi.updateClusterWeight(clusterWeight);

上述示例中,我们定义了一个名为 my_route_config 的路由配置,它会匹配以 /api/my-service 开头的请求路径,并将其转发到 my-service-cluster 集群。你可以根据实际需求修改这些配置。

请注意,以上示例仅为演示如何通过 Envoy 的 API 进行配置,并使用请求参数来进行流量分配。具体的 API 调用方式和参数可能会因不同的编程语言和 Envoy API 的实现方式而有所不同,请根据实际情况进行调整。

问:java api对接 envoy
在 Java 中使用 Envoy API,你可以使用 Envoy 的 Java 客户端库来进行对接。Envoy 提供了一个名为 envoy-control-plane-java 的官方 Java 控制平面客户端库,该库可用于与 Envoy 的管理 API 进行通信。

以下是使用 envoy-control-plane-java 客户端库对接 Envoy API 的一般步骤:

  1. 添加依赖:首先,在你的 Java 项目中添加 envoy-control-plane-java 客户端库的依赖。你可以通过 Maven、Gradle 等构建工具来管理依赖关系。

  2. 创建服务端监听:使用 envoy-control-plane-java 客户端库,创建一个 HTTP/REST 或 gRPC 服务端监听,用于接收 Envoy 的请求并提供相应的配置信息。

  3. 实现服务端逻辑:在服务端监听中,实现具体的逻辑以响应 Envoy 的请求。这可能包括处理发现请求、将路由规则转换为 Envoy 兼容的配置对象等。

  4. 注册服务端与 Envoy:将服务端监听注册到 Envoy 的管理服务器中。这样,Envoy 将能够通过 API 调用你的服务端来获取配置信息。

  5. 启动服务端:启动服务端监听,保持其处于运行状态,以便能够响应 Envoy 的请求。

使用 envoy-control-plane-java 还可以实现自定义的功能,如动态配置更新、流量拦截等。你可以在服务端逻辑中处理这些功能,并通过服务端监听向 Envoy 提供更新后的配置。

请注意,以上步骤是一个概述,并且具体实现方式可能会因使用的环境、版本和需求而有所不同。你可以参考 envoy-control-plane-java 的官方文档和示例代码,以便更详细地了解如何在 Java 中对接 Envoy API。


相关文章:
envoy代理官方文档

为者常成,行者常至