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 新成员

Welcome Envoy Gateway new Reviewers

Read More
Envoy Gateway 治理多集群服务流量

Envoy Gateway 治理多集群服务流量

Envoy Gateway + MCS + Submariner 治理多集群服务流量

Read More