EnvoyProxy 中 UDP 协议概览

EnvoyProxy 中 UDP 协议概览

Table of Contents

本文结合一些基础的网络知识,介绍了 EnvoyProxy 中 UDP 协议的介绍与使用。

背景知识

TCP 与 UDP 对比

UDP(用户数据报协议)和TCP(传输控制协议)都是网络通信中常用的传输层协议,它们之间的主要区别如下:

  1. 连接模式:

TCP 是面向连接的协议,它需要在通信前先进行三次握手建立连接,然后才能进行数据传输。而 UDP 是面向无连接的协议,它不需要建立连接,每个数据报都是相互独立的。

  1. 可靠性:

TCP 提供可靠的数据传输,它保证数据的传输顺序和完整性,如果发生丢包或错误,TCP 会进行重传和错误检测等机制来保证数据的正确性。而 UDP 不提供可靠的数据传输保证,如果发生丢包或错误,UDP 不会进行重传和错误检测,因此有可能导致数据的丢失或不完整。

  1. 传输效率:

由于TCP需要进行连接建立、错误检测、重传等操作,因此在传输效率方面不如 UDP。而 UDP 具有较高的传输效率,因为它不需要进行连接建立和错误检测等操作,同时也没有 TCP 的拥塞控制机制,因此可以实现更快的数据传输。

  1. 应用场景:

TCP 通常用于对数据传输可靠性要求较高的场景,如文件传输、网页浏览、邮件传输等,因为 TCP 可以保证数据的正确性和完整性。而 UDP 通常用于对数据传输效率要求较高的场景,如音视频传输、游戏数据传输等,因为 UDP 具有较高的传输效率和低延迟优势。

综上,TCP 和 UDP 各有优势,具体选择哪种协议要根据应用场景和需求来决定。如果要求数据传输的可靠性和完整性,则选择 TCP;如果要求数据传输效率和低延迟,则选择 UDP。

不过 Google 开源了 QUIC 协议,它是一套基于 UDP 的开源协议,它汇集了 TCPUDP 的优点,传输高效并且可靠。

TCP 与 Session

在 TCP 协议中,Session 通常是指一对主机之间建立的一种持续的、可靠的通信连接。TCP协议采用三次握手来建立连接,通过四次挥手来终止连接,从而确保了连接的可靠性和数据的完整性。

在建立连接时,TCP 协议使用源IP地址、目标IP地址、源端口号、目标端口号这四个元素来标识一条连接。这个标识可以看作是 TCP Session 的一个唯一标识符,也被称为 Session ID。

通过这个四元组,TCP 协议可以识别出每个连接的唯一标识符,从而实现对连接的管理和控制。 TCP 协议使用 Session ID 来区分不同的连接,确保每个连接都是独立的、互不干扰的。

Session ID 在 TCP 协议中起着非常重要的作用,它可以用于连接的管理、控制、跟踪和调试等。

总之,在 TCP 协议中,Session 是指一对主机之间建立的一种持续的、可靠的通信连接,Session ID 则是用于标识和管理这种连接的唯一标识符,它通常由源IP地址、目标IP地址、源端口号和目标端口号这四个元素组成。

UDP 与 Session

在 UDP(用户数据报协议)中,不存在像 TCP(传输控制协议)中那样的会话(Session)概念。UDP 是一种面向无连接的协议,每个 UDP 数据报都是一个独立的包,它们之间没有先后顺序或关联性,也没有对数据报是否到达或重复的确认机制。

UDP 通信是 Stateless 的,每次数据包都是相互独立的,没有上下文关系,因此 UDP 的数据包是没有 Session ID 的。每次通信都是从头开始,通信结束后就断开连接,不会保持长久的连接状态。

因此,使用 UDP 协议时,需要在应用层自行实现数据报的编号、确认、重传等功能,以保证数据的可靠传输。

当然,在实际应用中,为了更好的管理和控制 UDP 的通信,可以通过一些手段来模拟"会话"的概念,比如在应用层协议中定义一些约定好的标识符来标识通信的双方,或者通过网络地址和端口号来识别通信的进程等。但这些方法并不是 UDP 协议本身提供的功能。

Envoy 中 UDP 的支持

Envoy 是一种开源的代理服务器,它支持多种协议和网络模型,包括 UDP 协议。在 Envoy 中,对于 UDP 协议的通信,可以使用 UDP listener 和 UDP proxy 来进行管理和控制。

UDP 代理监听过滤器允许 Envoy 在 UDP 客户端和服务器之间作为非透明代理运行。非透明性意味着上游服务器将看到 Envoy 实例的源 IP 和端口,而不是客户端的 IP 和端口。所有数据报从客户端流向 Envoy,再流向上游服务器,然后再返回 Envoy 和客户端。

由于 UDP 不是面向连接的协议,Envoy 必须跟踪客户端的会话,以便将来自上游服务器的响应数据报路由回正确的客户端。

在 Envoy 中,每个 UDP 会话都会被赋予一个唯一的 Session ID,它由 Envoy 根据 UDP 数据包的源地址、目标地址、源端口号和目标端口号等信息自动生成,并在整个会话中保持不变。这个 Session ID 可以用于在 Envoy 中跟踪和管理 UDP 通信的状态,包括限流、监控、安全策略等方面,会话持续到达到空闲超时时间(idle_timeout)为止。

可以通过将 use_per_packet_load_balancing 设置为 true 来禁用上述会话粘滞性。在这种情况下,将启用每个数据块的负载均衡。这意味着使用当前使用的负载均衡策略在 UDP 代理接收到每个数据块时选择上游主机。

如果将 use_original_src_ip 字段设置为 true,UDP 代理监听器过滤器还可以作为透明代理运行。但请注意,它不会将端口转发给上游,只会将 IP地址 转发给上游。

UDP 负载均衡策略与健康检查

Envoy 在负载均衡 UDP 数据报时,将充分利用配置的负载均衡器来配置上游集群。

默认情况下,当创建新会话时,Envoy 将使用配置的负载均衡器选择一个上游主机并将该会话与之关联。将来属于该会话的所有数据报将路由到同一上游主机

但是,如果将 use_per_packet_load_balancing 字段设置为 true,则 Envoy 使用配置的负载均衡器会在下一个数据报上选择另一个上游主机,并在不存在此类主机时创建一个新会话。因此,如果有多个上游主机可用于负载均衡器,则每个数据块都将转发到不同的主机。

当上游主机变得不健康(由于主动健康检查),当收到下一个数据报时,Envoy 将尝试创建到健康主机的新会话。每个上游集群可以创建的会话数受到集群的最大连接熔断器的限制,默认情况下,此值为 1024

UDP 路由策略

在 UDP 路由中,可以根据 UDP network input

对 UDP 流量进行路由分发,并且可以基于强大的 Matching API ,对 UDP 路由进行描述。而 UDP 路由(extensions.filters.udp.udp_proxy.v3.Route) 配置很简单,只有目标 Cluster:

{
  "cluster": ...
}

通过以下例子来做一个示例,以下 matcher 配置将 Envoy 根据源 IP 路由 UDP 数据报,忽略源 IP127.0.0.1 以外的数据报,并且根据不同的源端口将其余数据报过滤到不同的 Cluster:

        matcher:
          # The outer matcher list matches source IP.
          matcher_list:
            matchers:
            - predicate:
                single_predicate:
                  input:
                    name: envoy.matching.inputs.source_ip
                    typed_config:
                      '@type': type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.SourceIPInput
                  value_match:
                    exact: 127.0.0.1
              on_match:
                matcher:
                  # The inner matcher tree matches source port.
                  matcher_tree:
                    input:
                      name: envoy.matching.inputs.source_port
                      typed_config:
                        '@type': type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.SourcePortInput
                    exact_match_map:
                      map:
                        "80":
                          action:
                            name: route
                            typed_config:
                              '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route
                              cluster: udp_service
                        "443":
                          action:
                            name: route
                            typed_config:
                              '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route
                              cluster: udp_service2
                  on_no_match:
                    action:
                      name: route
                      typed_config:
                        '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route
                        cluster: udp_service3

再举个完整的例子,以下示例配置将使 Envoy 监听 UDP 端口 1234 ,并配置 UDP Listener 配置(udp_listener_config) ,并代理到监听 1235 端口的 UDP 上游服务器,允许双向 9000 字节的数据包:

admin:
  address:
    socket_address:
      protocol: TCP
      address: 127.0.0.1
      port_value: 9901
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: UDP
        address: 127.0.0.1
        port_value: 1234
    udp_listener_config:
      downstream_socket_config:
        max_rx_datagram_size: 9000
    listener_filters:
    - name: envoy.filters.udp_listener.udp_proxy
      typed_config:
        '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.UdpProxyConfig
        stat_prefix: service
        matcher:
          on_no_match:
            action:
              name: route
              typed_config:
                '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route
                cluster: service_udp
        upstream_socket_config:
          max_rx_datagram_size: 9000
  clusters:
  - name: service_udp
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_udp
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 1235

Envoy UDP 相关配置

Envoy 中 UDP 代理的监听器配置在 config.listener.v3.UdpListenerConfig :

{
  "downstream_socket_config": {...},
  "quic_options": {...},
  "udp_packet_packet_writer_config": {...}
}

downstream_socket_config:下游监听器 UDP socket 配置,可设置最大接受 UDP 数据报的大小等。

quic_options:QUIC 协议的配置。如果为空,则不会在此侦听器上启用 QUIC。

udp_packet_packet_writer_config:UDP 数据包 Write 配置。

Envoy 中 UDP 代理的核心配置在 extensions.filters.udp.udp_proxy.v3.UdpProxyConfig

{
  "stat_prefix": ...,
  "cluster": ...,
  "matcher": {...},
  "idle_timeout": {...},
  "use_original_src_ip": ...,
  "hash_policies": [],
  "upstream_socket_config": {...},
  "use_per_packet_load_balancing": ...,
  "access_log": [],
  "proxy_access_log": []
}

stat_prefix:UDP Proxy 的生成的 stats 前缀。

cluster:转发给的 Upstream Cluster 名称,已经 Deprecated,使用 matcher 替代。

matcher:matching API,用与描述 UDP 路由规则。

idle_timeout:session 保持时间,默认为一分钟。

use_original_src_ip:向上游发送数据包时,是否使用下游 IP 地址作为发送方 IP 地址。此选项开启后不会传递源端口信息,并且需要 Linux 内核必须开启 IPv6。

hash_policies:指定 UDP 哈希策略,数据包可以通过哈希策略进行路由。如果没有设置 hash_policies,基于哈希的负载均衡算法将随机选择一个主机。目前哈希策略的数量限制为 1。

use_per_packet_load_balancing:对每个收到的数据块进行每个数据包的负载均衡。如果没有指定,默认为 false,这意味着每个数据块被转发到该 “会话”(由源IP/端口和本地IP/端口识别)第一次接收数据块时选择的上游主机。

access_logproxy_access_log: access log 相关配置。

comments powered by Disqus

Related Posts

Envoy Gateway 架构设计与开源贡献

Envoy Gateway 架构设计与开源贡献

本文通过介绍 Envoy Gateway 设计与架构,提供开源贡献的指南

Read More
Envoy Gateway v0.6 发布

Envoy Gateway v0.6 发布

本文介绍 Envoy Gateway v0.6.0 版本发布有哪些变化

Read More
Envoy Gateway 快速开始

Envoy Gateway 快速开始

本文介绍如何快速部署 Envoy Gateway,以及通过一个简单的例子,来展示如何通过 Envoy Gateway 访问 kubernetes 集群中的服务。

Read More