Envoy学习和实战

可能一些图片不见请见原文章:https://www.yuque.com/simonalong/ruanjian/zk5nr4


早先公司提出OS中枢概念,希望能有一个系统进行对整个OS进行感知并加以控制,后来由另外一个同事率先落地实施,规划其实挺好,只是一期之后就暂停直到现在,诸多功能以及定位其实都没有实现,其原因有多种,但是站在现在来看,该项目对OS中枢系统的实现其实偏应用层而非系统层,对多语言、多底层系统而言适配性并没有考虑到,而多语言和底层系统这种在我们公司却是很重要的考虑项。鉴于中枢系统的重要性,以及对新技术的初始调研方面,考虑对中枢系统进行重新调研和设计,也对新技术做了一些探索。其中就是Envoy。
在介绍Envoy之前,更应该对Envoy出现的历史说明一下。

Sidecar:边车模式

在这里插入图片描述

边车模式是一种分布式架构的设计模式。
分布式架构越来越复杂和微服务越拆越细,就越来越迫切的希望有一个统一的控制面来管理所有的微服务,来帮助维护和管理所有微服务(其实就是OS中枢的定位)。
那传统的实现是怎么做的,服务注册、服务发现、监控、流量控制、日志记录、服务限流和服务服务熔断等功能全部都是在应用层上面,通过SDK或者lib的方式,在开发时引入软件包依赖,使其与应用包结合起来,这种方式对代码有侵入,而且对整个公司层面的技术规范的统一化要求很高,比如需统一的语言、统一的框架、统一的规范,此外当依赖有bug需要修复和升级时候,业务代码需要重新编译和发布,还有依赖的包停止维护等等,都会给系统维护带来很大的成本。
边车模式是什么,就是给每一个服务都配置一个“代理”,这个代理其实就是一个“边车(Sidecar)”,该服务所有的通信都是通过这个“代理”来完成的,这个“代理”同服务一起创建,一起销毁。由该代理完成服务的各种功能:服务注册、服务发现、监控、流量控制、日志记录、服务限流和服务服务熔断。这样可以真正实现业务的逻辑和控制的分离。

ServiceMesh:服务网格

Sidecar对业务的逻辑与控制进行了解耦,其实该模式很早就已经实践,只是对其标准化并未真正的落地,随着分布式和微服务架构越来越多的接受,对该需求就更多,这就诞生了ServiceMesh(服务网格),服务网格是CNCF(云原生基金会)目前主推的新一代为服务架构。
ServiceMesh将底层的网络通信进行统一管理,比如:流控、访问控制等。
非网格服务

数据面控制面模式

我们从整个集群上面来看,就是如下这种最典型的服务网格架构,数据面使用边车模式,控制面板单独部署

将业务层的数据剥离后,控制层和数据层的部分:实现是相互调用,虚线是双方通信,控制层管控数据层

这种ServiceMesh思想在业内也经过了一些时间的沉淀,目前业内有多种实现方式,这里做了汇总和整理

功能LinkerdEnvoyIstioConduit
代理1.x:Fingagle + Jetty2.x:新的ProxyC++实现的EnvoyC++实现的EnvoyRust实现的Conduit
语言数据层:1.X:jvm语言Scala;2.X:Rust控制层:Go数据层:C++数据层:C++控制层:Go数据层:Rust;控制层:Go
熔断支持支持支持不支持
动态路由支持支持支持不支持
流量分流支持支持支持不支持
服务发现支持,支持扩展支持,支持扩展支持,支持扩展只支持k8s
负载均衡支持支持支持只支持http的
安全通信支持TLS支持TLS支持TLS支持TLS
访问控制不支持不支持只是,基于RBAC不支持
链路追踪支持,Zipkin支持,Zipkin支持,Zipkin不支持
数据指标支持,InfluxDb、Prometheus、statsd支持,statsd支持,Prometheus、statsd,支持监控:NewRepic、Stackdriver支持,Prometheus
部署模式sidecar或者pre-hostsidecarsidecarsidecar
控制平面包含,Namerd包含,Pilot,Mixer,CitadelConduit
协议支持http1、http2、gRpchttp1、http2、gRpc、tcphttp1、http2、gRpc、tcphttp1、http2、gRpc、tcp
运行平台1.X:平台无关;2.X:支持k8s较好平台无关支持k8s,未来会平台无关只支持k8s

在寻找适合我们操作系统的控制面和数据面的探索中,有这么几点原则我觉得我们系统长期来说是需要的

  • 轻量化:加入后对现有资源的占用,不能太多
  • 影响小:数据面本身是会对系统有影响,要保证非常小的影响,资源占用、耗时影响、性能损耗
  • 少侵入:这里侵入两层意思,引入数据面和控制面后,第一:我们系统后续的改造;第二:业务改造
  • 平台无关化
  • 灵活化:方便定制,我们自己的系统并不是主流业务,为了方便我们后续做更多定制化,建议只包含基本数据面和控制面即可

基于以上我们对ServiceMesh系统的进行考量,在考虑到功能的灵活化以及平台无关化这两个方面时候,建议使用Envoy作为我们自己系统的服务网格基础,因为它足够简单,基本功能足够,边界很清晰,如果使用envoy来作为OS的中枢的话,长期来说可以逐渐打磨中枢系统,最后可以形成公司的中枢控制系统的核心基座。基于以上考量下面就对Envoy进行了各种探索

envoy介绍

前面表格中也说明了Envoy是数据面的实现部分,数据控制面是用来控制当前节点中的所有网络出入请求的,那这是怎么实现的,我们能想到用来控制网络出入请求的只有防火墙,而防火墙是怎么实现的,采用的是内核的netfilter,envoy也是采用netfilter实现的,只是netfilter是内核的,要调用该软件采用的是用户软件iptables,该软件来调用netfilter,那netfilter是啥,以及怎么控制的,以前写过一点文章,这里重新介绍下。

envoy网络拦截原理

iptables 是一个工具软件,一个操作linux防火墙软件netfilter的命令软件,netfilter位于内核中,而iptables位于用户控制层,我们用iptables来操作防火墙软件netfilter
Netfilter是Linux操作系统核心层内部的一个数据包处理模块,它具有如下功能:

  • 网络地址转换(Network Address Translate)
  • 数据包内容修改
  • 以及数据包过滤的防火墙功能

Netfilter主要是根据规则办事的,而iptable就是给这些规则进行增删改查,规则就是对如下的规则进行判断,判断OK,然后就放行(accept)、拒绝(reject)和丢弃(drop)

  • 源地址
  • 目的地址
  • 传输协议
  • 服务类型
  • …等

根据规则的分类,这里又分了几个类,叫做表,每个表都有很多规则配置,这里采用的是链式模式,顺序执行链中的规则,进而判断处理,将整个流程全部细化,为如下部分

iptables的工作流程是如下的

将iptable与envoy结合,整体的流程是如下的

借鉴一下,istio的部分,后续我们如果要做,也是要借鉴这个,目前iptables的一些相关处理还没有做,需要补充
image.png

envoy拦截流程

上面介绍了网络拦截的原理,知道envoy能够对网络拦截了,那么envoy怎么进行对数据管控的,为了说明整个envoy的构成与原理,这里画了一个详细的图。
envoy对数据包的流程,划分了基本的四步

  • 监听器:端口等监听的匹配
  • 路由:请求url的路径的匹配和映射
  • 集群:集群的负载均衡等配置
  • 端点:待请求的服务的ip和端口

以上的步骤,最后在配置上是如下的配置
image.png
在真实的流程上,详细的为如下配置。

在业务A这边请求B,A服务所在节点经过一层envoy的配置转换到,外部网络到达B服务所在节点,B节点的envoy节点接收后转发到服务B。

这里面有一些概念,列举下,方面后续理解

  • Downstream(下游):下游主机连接到 Envoy,发送请求并接收响应,即发送请求的主机。
  • Upstream(上游):上游主机接收来自Envoy的链接和请求,并返回响应,即接收请求的主机。
  • LDS:监听器的发现服务
    • Listener(监听器):监听器是对外暴露的地址,包括端口、网址等,调用方(下游)可以连接这些监听器。
  • RDS:路由的发现服务;路由是由各种路由规则组成,这个规则包括http头部修改,虚拟主机以及每个虚拟主机的映射规则,这些都可以通过RDS进行动态修改
  • CDS:集群发现服务;集群指Envoy连接的一组逻辑相同的上游主机,可通过CDS发现
  • EDS:端点发现服务;其中端点即上游主机,是一个或多个集群的成员,可通过EDS发现
  • ADS:聚合的发现服务,是对CDS、RDS、LDS、EDS服务的统一封装,为了解决CDS、RDS、LDS、EDS信息更新顺序依赖的问题,从而保证以一定的顺序同步各类配置信息
  • xDS:所有发现服务的总称,xDS中的x是一个代词,指代LDS、RDS、CDS等

envoy动态化配置

envoy的很多配置都是直接位于数据层的,如果需要变化肯定不能让业务感知到,envoy是支持动态的变更这些xDS的配置的,就是“数据层”直接通过gRpc或者http2与“数据层”通信,在配置变化时候,修改“数据层”的envoy的一些配置,就可以实现控制。如下是控制层的一些核心代码部分;提示:自己使用的envoy的V3版本的api

控制层代码

控制层envoy本身没有提供,但是提供了grpc的接口,也提供了对应的go和java的客户端,我们这里基于go的客户端进行了调研和部分开发,将其中的核心代码列出如下

func runGrpcServer(ctx context.Context, snapshotCacheData cache.SnapshotCache, port int) {
	var grpcOptions []grpc.ServerOption
	grpcOptions = append(grpcOptions,
		grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams),
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time:    grpcKeepaliveTime,
			Timeout: grpcKeepaliveTimeout,
		}),
		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
			MinTime:             grpcKeepaliveMinTime,
			PermitWithoutStream: true,
		}),
	)
	grpcServer := grpc.NewServer(grpcOptions...)

	cb := &test.Callbacks{Debug: true}
    
    // 创建grpc
	srv := server.NewServer(ctx, snapshotCacheData, cb)

    // 注册LDS
	listenerservice.RegisterListenerDiscoveryServiceServer(grpcServer, srv)
    // 注册RDS
	routeservice.RegisterRouteDiscoveryServiceServer(grpcServer, srv)
    // 注册CDS
	clusterservice.RegisterClusterDiscoveryServiceServer(grpcServer, srv)
    // 注册EDS
	endpointservice.RegisterEndpointDiscoveryServiceServer(grpcServer, srv)

    // 注册ADS
	discoverygrpc.RegisterAggregatedDiscoveryServiceServer(grpcServer, srv)
    // 注册SDS
	secretservice.RegisterSecretDiscoveryServiceServer(grpcServer, srv)
    // 注册RTDS
	runtimeservice.RegisterRuntimeDiscoveryServiceServer(grpcServer, srv)

	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		logger.Error("启动端口失败: %d", port, err)
		return
	}

	go func() {
		logger.Info("grpc服务启动监听端口:%v", port)
		if err = grpcServer.Serve(lis); err != nil {
			logger.Error("启动grpc异常", err)
		}
	}()
}
func SendEnvoyData(envoyDataInsert *dto.EnvoyDataInsert) {
    // 配置节点名,这样可以发送到指定的“数据层”
	node := corev3.Node{Id: envoyDataInsert.Id, Cluster: envoyDataInsert.ClusterName}

	ctx := context.Background()
	resourcesMap := map[resource.Type][]types.Resource{}

	var listeners []types.Resource
	if len(envoyDataInsert.Listeners) != 0 {
		for _, info := range envoyDataInsert.Listeners {
			listeners = append(listeners, AddListener(info, envoyDataInsert.ClusterName))
		}
	}
    // LDS的配置
	resourcesMap[resource.ListenerType] = listeners

	var routers []types.Resource
	if len(envoyDataInsert.Routers) != 0 {
		for _, info := range envoyDataInsert.Routers {
			routers = append(routers, AddRouter(info))
		}
	}
    // RDS的配置
	resourcesMap[resource.RouteType] = routers

	var clusters []types.Resource
	if len(envoyDataInsert.Clusters) != 0 {
		for _, info := range envoyDataInsert.Clusters {
			clusters = append(clusters, AddCluster(info))
		}
	}
    // CDS的配置
	resourcesMap[resource.ClusterType] = clusters

	var endpoints []types.Resource
	if len(envoyDataInsert.Endpoints) != 0 {
		for _, info := range envoyDataInsert.Endpoints {
			endpoints = append(endpoints, addEndpoint(info))
		}
	}
    // EDS的配置
	resourcesMap[resource.EndpointType] = endpoints

	snap, _ := cache.NewSnapshot(isc.ToString(envoyDataInsert.Version), resourcesMap)
	if err := snap.Consistent(); err != nil {
		logger.Error("参数检查异常:%v", err)
		return
	}

    // 发送到数据层
	if err := CacheData.SetSnapshot(ctx, node.GetId(), snap); err != nil {
		logger.Error("数据发送异常:%v", err)
	}
}
func GetListener(listenerName string, route string) *listener.Listener {
	return &listener.Listener{
		// 监听器名称
		Name: "",

		// 监听器地址,必须唯一
		Address: GetListenerAddress("0.0.0.0", ListenerPort),

		// stat_prefix:用于侦听器统计信息的可选前缀
		StatPrefix: "",

		// 访问日志配置
		AccessLog: nil,

		// -------------------------------- 过滤器 --------------------------------
		// 过滤器链子
		FilterChains: Filter(route, ""),
		// 默认过滤器链
		DefaultFilterChain: nil,
		// 监听器过滤器
		ListenerFilters: nil,
		// 监听器过滤器超时时间
		ListenerFiltersTimeout: nil,
		// 当侦听器过滤器超时时是否应该创建连接。默认false
		ContinueOnListenerFiltersTimeout: false,

		// -------------------------------- tcp配置 --------------------------------
		// 侦听器是否应接受 TCP 快速打开 (TFO) 连接,这里是对应的长度
		TcpFastOpenQueueLength: nil,
		// tcp 侦听器的挂起连接队列可以增长到的最大长度,linux上默认为128
		TcpBacklogSize: nil,
		// 是否启动Mptcp,这个
		EnableMptcp: true,

		// -------------------------------- udp配置 --------------------------------
		// udp监听器配置
		UdpListenerConfig: nil,

		// -------------------------------- 套接字配置 --------------------------------
		// 套接字配置
		SocketOptions: nil,
		// 侦听器是否应设置为透明套接字
		Transparent: nil,
		// 侦听器是否应设置 *IP_FREEBIND* 套接字选项
		Freebind: nil,
		// 套接字重用配置,默认为true,连接重用
		EnableReusePort: nil,
		// 侦听器是否应绑定到端口。未绑定的侦听器只能接收从将 use_original_dst 设置为 true 的其他侦听器重定向的连接。默认为true
		BindToPort: &wrapperspb.BoolValue{Value: true},

		// -------------------------------- 连接配置 --------------------------------
		// 每个连接缓存限值,默认1Mb
		PerConnectionBufferLimitBytes: nil,
		// 连接平衡配置,不太懂????
		ConnectionBalanceConfig: nil,
		// 如果使用 iptables 重定向连接,则代理接收它的端口可能与原始目标地址不同
		UseOriginalDst: nil,

		// -------------------------------- 其他 --------------------------------

		// 请求的拉取类型,没太懂??后续测试下
		DrainType: listener.Listener_DEFAULT,
		// 指定流量相对于本地 Envoy 的预期方向,对于使用原始目标过滤器的侦听器,Windows 上需要此属性
		TrafficDirection: 0,
		// 用于表示一个 API 监听器,用于非代理客户端。向非代理应用程序公开的 API 类型取决于 API 侦听器的类型。设置此字段时,不应设置除名称以外的其他字段。
		ApiListener: nil,
	}
}
func GetRouter(routeName string, clusterName string) *route.RouteConfiguration {
	return &route.RouteConfiguration{
		// 路由名称
		Name: routeName,

		// ----------------------------------- 虚拟主机 -----------------------------------
		// 组成路由表的虚拟主机数组
		VirtualHosts: getHost(clusterName),
		// 一组虚拟主机将通过 VHDS API 动态加载
		Vhds: nil,

		// ----------------------------------- 请求和响应 -----------------------------------
		// 指定应该添加到由 HTTP 连接管理器路由的每个请求的 HTTP 标头列表。
		RequestHeadersToAdd: nil,
		// 指定应从 HTTP 连接管理器路由的每个请求中删除的 HTTP 标头列表。
		RequestHeadersToRemove: nil,
		// 指定应添加到连接管理器编码的每个响应的 HTTP 标头列表。
		ResponseHeadersToAdd: nil,
		// 指定应该从连接管理器编码的每个响应中删除的 HTTP 标头列表。
		ResponseHeadersToRemove: nil,
		// 响应的最大尺寸
		MaxDirectResponseBodySizeBytes: &wrappers.UInt32Value{Value: 4096},
		// 要允许在路由或虚拟主机级别设置覆盖为true
		MostSpecificHeaderMutationsWins: false,
		//(可选)指定连接管理器将仅视为内部的 HTTP 标头列表。
		InternalOnlyHeaders: nil,

		// ----------------------------------- 其他 -----------------------------------
		// 一个可选的布尔值,指定路由表引用的集群是否将由集群管理器验证
		ValidateClusters: &wrappers.BoolValue{Value: true},

		// 插件列表
		ClusterSpecifierPlugins: nil,
	}
}
func GetCluster(clusterName string) *cluster.Cluster {
	return &cluster.Cluster{
		// 集群名称
		Name: clusterName,
		// 控制层的连接超时时间
		ConnectTimeout: durationpb.New(5 * time.Second),

		// 集群类型,这里使用集群名字解析出来的第一个ip,算是逻辑ip
		ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS},

		// -------------------------- dns解析 ---------------------------------------
		// dns的域名解析策略
		DnsLookupFamily: cluster.Cluster_AUTO,

		// dns刷新率, 如果指定了DNS刷新率且群集类型为STRICT_DNS或LOGICAL_DNS,则此值将用作群集的DNS刷新率。 如果未指定此设置,则默认值为5000毫秒。 对于除STRICT_DNS和LOGICAL_DNS之外的集群类型,将忽略此设置。
		DnsRefreshRate: nil,

		// dns失败刷新率,只有type为strict或者logic时候且指定了该失败率才会生效,否则,则使用dns刷新率
		DnsFailureRefreshRate: nil,

		// 相当于dns,设置此选项是指定STATIC,STRICT_DNS或LOGICAL_DNS集群的成员所必需的
		LoadAssignment: makeEndpoint(clusterName),

		// eds的一些配置,只有上面的cluster_type为eds的时候才有效
		EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{},

		// 用于设置集群的 DNS 刷新率的可选配置。如果该值设置为 true,则集群的 DNS 刷新率将设置为来自 DNS 解析的资源记录的 TTL
		RespectDnsTtl: false,

		// dns解析配置
		TypedDnsResolverConfig: nil,

		// -------------------------- 负载均衡---------------------------------------
		// 选择主机时的负载均衡策略
		LbPolicy: cluster.Cluster_ROUND_ROBIN,
		// LbPolicy进行选择指定的配置类型,比如上面的,Round,则该配置需要为 Cluster_RoundRobinLbConfig_
		LbConfig: nil,
		// 负载均衡子集配置
		LbSubsetConfig: nil,
		// 负载均衡配置,如果这个值配置了,则会替换LbPolicy
		LoadBalancingPolicy: nil,

		// -------------------------- 上游配置 ---------------------------------------
		// 清理上游的host的时间
		CleanupInterval: nil,

		// 上游配置
		UpstreamConfig: nil,

		// 上游绑定的配置
		UpstreamBindConfig: nil,

		// 用于上游连接的自定义传输套接字实现,可选
		TransportSocket: nil,

		// 上游游链接配置
		UpstreamConnectionOptions: nil,

		// 扩展协议,该字段用于为上游连接提供特定于扩展的协议选项。密钥应与扩展过滤器名称匹配,例如“envoy.filters.network.thrift_proxy”。有关特定选项的详细信息,请参阅扩展的文档。
		TypedExtensionProtocolOptions: nil,

		// -------------- 上游健康检查配置 --------------
		// 集群的可选活动健康检查配置。如果未指定配置,则不会进行健康检查,并且所有集群成员将始终被认为是健康的。
		HealthChecks: getHealthCheck(),
		// 上游不健康时候是否关闭所有链接
		CloseConnectionsOnHostHealthFailure: true,
		// 上游移除的时候,将不再考虑他的健康值
		IgnoreHealthOnHostRemoval: true,

		// -------------- 上游链接配置 --------------

		// 集群连接读写缓冲区大小的软限制。如果未指定,则应用实现定义的默认值 (1MiB),单位应该是Byte
		PerConnectionBufferLimitBytes: &wrappers.UInt32Value{Value: 32768},

		// 集群的预链接配置
		PreconnectPolicy: nil,

		// 断路器,其实也是连接池的一些配置
		CircuitBreakers: nil,

		// -------------------------- 下游配置 ---------------------------------------
		// 为true,则集群将为每个下游连接使用单独的连接池
		ConnectionPoolPerDownstreamConnection: true,

		// -------------------------- 信息追踪配置 ---------------------------------------
		// 用于跟踪可选集群统计信息的配置
		TrackClusterStats: nil,

		// -------------------------- 其他配置 ---------------------------------------
		// 用于在预热时阻止集群就绪的可选配置
		WaitForWarmOnInit: nil,

		// 元数据字段:元数据字段可用于提供有关群集的其他信息
		Metadata: nil,

		// 一个(可选的)网络过滤器链,按应用过滤器的顺序列出。该链将应用于 Envoy 与该集群的上游服务器建立的所有传出连接。
		Filters: nil,
	}
}
func GetEndpoint() *endpoint.Endpoint {
	return &endpoint.Endpoint{

		// 上游主机地址
		Address: nil,
		// 选的健康检查配置用作健康检查器与健康检查主机联系的配置
		HealthCheckConfig: nil,
		// 此端点关联的主机名
		Hostname: "",
	}
}

数据层配置

数据层其实就是envoy进程启动时候配置的,envoy的配置有静态配置和动态配置,静态配置是envoy启动时候直接写死的,动态配置是运行中动态修改的,是看不到的,其中的静态配置如下,以及

# node是对集群进行区分的,动态配置也是根据这个来进行改配置
node:
  cluster: default
  id: biz-d-service
# 查看相关的envoy的配置信息
admin:
  access_log_path: /var/log/envoy/admin_access.log
  profile_path: /var/log/envoy/envoy.prof
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9901

dynamic_resources:
  ads_config:
    api_type: GRPC
    transport_api_version: V3
    grpc_services:
      - envoy_grpc:
          cluster_name: cluster_xds
  cds_config:
    resource_api_version: V3
    ads: {}
  lds_config:
    resource_api_version: V3
    ads: {}

static_resources:
  clusters:
    - 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:
            http2_protocol_options: {}
      # envoy控制层的集群
      name: cluster_xds
      load_assignment:
        cluster_name: cluster_xds
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      # 控制层的服务
                      address: isc-envoy-controller-service
                      # 控制层服务的端口
                      port_value: 11000
      # 本机的服务某个服务,目前只有一个,因此配置的就该服务
    - name: cluster_local
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: cluster_local
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 127.0.0.1
                      # 本机某个服务的端口,比如服务B
                      port_value: 18003
    # 数据搜集服务,包括链路数据等
    - name: data-collector
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: zipkin
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: isc-envoy-data-collector
                      port_value: 18080
  listeners:
    - name: listener_ingress
      address:
        socket_address:
          address: 0.0.0.0
          # 所有服务对外的端口,这里采用10000
          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
                tracing:
                  provider:
                    name: envoy.tracers.zipkin
                    typed_config:
                      "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
                      collector_cluster: data-collector
                      collector_endpoint: "/api/v2/spans"
                      collector_endpoint_version: HTTP_JSON
                stat_prefix: http
                route_config:
                  # 配置流量的输入监听
                  name: route_local
                  virtual_hosts:
                    - name: route_local
                      domains:
                        - "*"
                      routes:
                        - match:
                            prefix: "/api/d/"
                          route:
                            cluster: cluster_local
                          decorator:
                            operation: checkAvailability
                http_filters:
                  - name: envoy.filters.http.router
                generate_request_id: true

在调研中我们使用了go和java两种业务来进行开发,这里做一个示例,使用的是docker进行部署,就是在docker里面运行envoy和业务两个进程

.
├── Dockerfile
├── envoy
│   ├── envoy-config.yaml
│   └── start_service.sh
├── go.mod
├── go.sum
├── ...
└── ...

#!/usr/bin/env bash
/app/server &
envoy -c /etc/envoy/envoy.yaml --service-cluster biz-b-service
#build stage
FROM golang:1.18 as builder


RUN go version

ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn

WORKDIR /app

COPY . /app/

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app
EXPOSE 18001

#image stage
FROM envoyproxy/envoy:v1.21.2

WORKDIR /app

ENV TZ=Asia/Shanghai
ENV ZONEINFO=/app/zoneinfo.zip

COPY --from=builder /app/application.yaml /app/application.yaml
COPY --from=builder /app/biz-b-service /app/server
COPY --from=builder /usr/local/go/lib/time/zoneinfo.zip /app

COPY ./envoy/start_service.sh /usr/local/bin/start_service.sh
COPY ./envoy/envoy-config.yaml /etc/envoy/envoy.yaml
ADD ./envoy/log /var/log/envoy

RUN chmod u+x /usr/local/bin/start_service.sh
ENTRYPOINT /usr/local/bin/start_service.sh

#CMD ["./server"]


.
├── Dockerfile
├── envoy
│   ├── envoy-config.yaml
│   └── start_service.sh
├── pom.xml
├── ...
└── ...

#!/usr/bin/env bash
java -jar /usr/local/opt/server.jar & envoy -c /etc/envoy/envoy.yaml --service-cluster biz-a-service

#image stage
FROM envoyproxy/envoy:v1.21.2

WORKDIR /app

ENV TZ=Asia/Shanghai
ENV ZONEINFO=/app/zoneinfo.zip

RUN mkdir /usr/local/java8

COPY ./jdk/* /usr/local/java8/

# 配置JAVA的环境变量
ENV JAVA_HOME=/usr/local/java8
# 配置到PAHT中
ENV PATH=$PATH:$JAVA_HOME/bin

COPY ./jar/biz-a-service.jar /usr/local/opt/server.jar

COPY ./envoy/start_service.sh /usr/local/bin/start_service.sh
COPY ./envoy/envoy-config.yaml /etc/envoy/envoy.yaml
ADD ./envoy/log /var/log/envoy

RUN chmod u+x /usr/local/bin/start_service.sh
RUN chmod u+x /usr/local/java8/bin/*
ENTRYPOINT /usr/local/bin/start_service.sh

运行中的envoy配置

使用envoy的admin端口,查看config_dump

http:xxx:9901/config_dump

{
 "configs": [
  {
   "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
   "bootstrap": {
    "node": {
     "id": "biz-a-service",
     "cluster": "default",
     "user_agent_name": "envoy",
     "user_agent_build_version": {
      "version": {
       "major_number": 1,
       "minor_number": 21,
       "patch": 2
      },
      "metadata": {
       "revision.sha": "dc7f46eb44e54d5646301aa5ab4ba01f662fdf75",
       "build.type": "RELEASE",
       "revision.status": "Clean",
       "ssl.version": "BoringSSL"
      }
     },
     "extensions": [
      {
       "name": "envoy.grpc_credentials.aws_iam",
       "category": "envoy.grpc_credentials"
      },
      {
       "name": "envoy.grpc_credentials.default",
       "category": "envoy.grpc_credentials"
      },
      {
       "name": "envoy.grpc_credentials.file_based_metadata",
       "category": "envoy.grpc_credentials"
      },
      {
       "name": "preserve_case",
       "category": "envoy.http.stateful_header_formatters"
      },
      {
       "name": "envoy.http.original_ip_detection.custom_header",
       "category": "envoy.http.original_ip_detection"
      },
      {
       "name": "envoy.http.original_ip_detection.xff",
       "category": "envoy.http.original_ip_detection"
      },
      {
       "name": "envoy.http.stateful_session.cookie",
       "category": "envoy.http.stateful_session"
      },
      {
       "name": "envoy.tls.cert_validator.default",
       "category": "envoy.tls.cert_validator"
      },
      {
       "name": "envoy.tls.cert_validator.spiffe",
       "category": "envoy.tls.cert_validator"
      },
      {
       "name": "envoy.cluster.eds",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.cluster.logical_dns",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.cluster.original_dst",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.cluster.static",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.cluster.strict_dns",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.clusters.aggregate",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.clusters.dynamic_forward_proxy",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.clusters.redis",
       "category": "envoy.clusters"
      },
      {
       "name": "envoy.key_value.file_based",
       "category": "envoy.common.key_value"
      },
      {
       "name": "envoy.ip",
       "category": "envoy.resolvers"
      },
      {
       "name": "default",
       "category": "envoy.dubbo_proxy.route_matchers"
      },
      {
       "name": "envoy.transport_sockets.alts",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.quic",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.raw_buffer",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.starttls",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.tap",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.tcp_stats",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.tls",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.transport_sockets.upstream_proxy_protocol",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "raw_buffer",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "starttls",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "tls",
       "category": "envoy.transport_sockets.upstream"
      },
      {
       "name": "envoy.rbac.matchers.upstream_ip_port",
       "category": "envoy.rbac.matchers"
      },
      {
       "name": "envoy.quic.crypto_stream.server.quiche",
       "category": "envoy.quic.server.crypto_stream"
      },
      {
       "name": "composite-action",
       "category": "envoy.matching.action"
      },
      {
       "name": "skip",
       "category": "envoy.matching.action"
      },
      {
       "name": "envoy.client_ssl_auth",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.echo",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.ext_authz",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.client_ssl_auth",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.connection_limit",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.direct_response",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.dubbo_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.echo",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.ext_authz",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.http_connection_manager",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.local_ratelimit",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.mongo_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.ratelimit",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.rbac",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.redis_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.sni_cluster",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.sni_dynamic_forward_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.tcp_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.thrift_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.wasm",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.filters.network.zookeeper_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.http_connection_manager",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.mongo_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.ratelimit",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.redis_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.tcp_proxy",
       "category": "envoy.filters.network"
      },
      {
       "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
       "category": "envoy.upstream_options"
      },
      {
       "name": "envoy.upstreams.http.http_protocol_options",
       "category": "envoy.upstream_options"
      },
      {
       "name": "request-headers",
       "category": "envoy.matching.http.input"
      },
      {
       "name": "request-trailers",
       "category": "envoy.matching.http.input"
      },
      {
       "name": "response-headers",
       "category": "envoy.matching.http.input"
      },
      {
       "name": "response-trailers",
       "category": "envoy.matching.http.input"
      },
      {
       "name": "envoy.resource_monitors.fixed_heap",
       "category": "envoy.resource_monitors"
      },
      {
       "name": "envoy.resource_monitors.injected_resource",
       "category": "envoy.resource_monitors"
      },
      {
       "name": "envoy.retry_host_predicates.omit_canary_hosts",
       "category": "envoy.retry_host_predicates"
      },
      {
       "name": "envoy.retry_host_predicates.omit_host_metadata",
       "category": "envoy.retry_host_predicates"
      },
      {
       "name": "envoy.retry_host_predicates.previous_hosts",
       "category": "envoy.retry_host_predicates"
      },
      {
       "name": "dubbo.hessian2",
       "category": "envoy.dubbo_proxy.serializers"
      },
      {
       "name": "envoy.transport_sockets.alts",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.quic",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.raw_buffer",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.starttls",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.tap",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.tcp_stats",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.transport_sockets.tls",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "raw_buffer",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "starttls",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "tls",
       "category": "envoy.transport_sockets.downstream"
      },
      {
       "name": "envoy.filters.udp.dns_filter",
       "category": "envoy.filters.udp_listener"
      },
      {
       "name": "envoy.filters.udp_listener.udp_proxy",
       "category": "envoy.filters.udp_listener"
      },
      {
       "name": "envoy.internal_redirect_predicates.allow_listed_routes",
       "category": "envoy.internal_redirect_predicates"
      },
      {
       "name": "envoy.internal_redirect_predicates.previous_routes",
       "category": "envoy.internal_redirect_predicates"
      },
      {
       "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
       "category": "envoy.internal_redirect_predicates"
      },
      {
       "name": "envoy.filters.connection_pools.tcp.generic",
       "category": "envoy.upstreams"
      },
      {
       "name": "envoy.filters.listener.http_inspector",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.filters.listener.original_dst",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.filters.listener.original_src",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.filters.listener.proxy_protocol",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.filters.listener.tls_inspector",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.listener.http_inspector",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.listener.original_dst",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.listener.original_src",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.listener.proxy_protocol",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.listener.tls_inspector",
       "category": "envoy.filters.listener"
      },
      {
       "name": "envoy.matching.common_inputs.environment_variable",
       "category": "envoy.matching.common_inputs"
      },
      {
       "name": "envoy.dog_statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.graphite_statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.metrics_service",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.dog_statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.graphite_statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.hystrix",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.metrics_service",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.stat_sinks.wasm",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.statsd",
       "category": "envoy.stats_sinks"
      },
      {
       "name": "envoy.access_loggers.file",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.http_grpc",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.open_telemetry",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.stderr",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.stdout",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.tcp_grpc",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.wasm",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.file_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.http_grpc_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.open_telemetry_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.stderr_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.stdout_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.tcp_grpc_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.wasm_access_log",
       "category": "envoy.access_loggers"
      },
      {
       "name": "envoy.access_loggers.extension_filters.cel",
       "category": "envoy.access_logger.extension_filters"
      },
      {
       "name": "envoy.compression.brotli.decompressor",
       "category": "envoy.compression.decompressor"
      },
      {
       "name": "envoy.compression.gzip.decompressor",
       "category": "envoy.compression.decompressor"
      },
      {
       "name": "envoy.filters.dubbo.router",
       "category": "envoy.dubbo_proxy.filters"
      },
      {
       "name": "auto",
       "category": "envoy.thrift_proxy.protocols"
      },
      {
       "name": "binary",
       "category": "envoy.thrift_proxy.protocols"
      },
      {
       "name": "binary/non-strict",
       "category": "envoy.thrift_proxy.protocols"
      },
      {
       "name": "compact",
       "category": "envoy.thrift_proxy.protocols"
      },
      {
       "name": "twitter",
       "category": "envoy.thrift_proxy.protocols"
      },
      {
       "name": "envoy.dynamic.ot",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.lightstep",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.datadog",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.dynamic_ot",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.lightstep",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.opencensus",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.skywalking",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.xray",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.tracers.zipkin",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.zipkin",
       "category": "envoy.tracers"
      },
      {
       "name": "envoy.watchdog.abort_action",
       "category": "envoy.guarddog_actions"
      },
      {
       "name": "envoy.watchdog.profile_action",
       "category": "envoy.guarddog_actions"
      },
      {
       "name": "envoy.request_id.uuid",
       "category": "envoy.request_id"
      },
      {
       "name": "envoy.quic.proof_source.filter_chain",
       "category": "envoy.quic.proof_source"
      },
      {
       "name": "envoy.network.dns_resolver.cares",
       "category": "envoy.network.dns_resolver"
      },
      {
       "name": "envoy.retry_priorities.previous_priorities",
       "category": "envoy.retry_priorities"
      },
      {
       "name": "dubbo",
       "category": "envoy.dubbo_proxy.protocols"
      },
      {
       "name": "envoy.bandwidth_limit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.buffer",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.cors",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.csrf",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.ext_authz",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.ext_proc",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.fault",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.adaptive_concurrency",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.admission_control",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.alternate_protocols_cache",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.aws_lambda",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.aws_request_signing",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.bandwidth_limit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.buffer",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.cache",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.cdn_loop",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.composite",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.compressor",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.cors",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.csrf",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.decompressor",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.dynamic_forward_proxy",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.dynamo",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.ext_authz",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.ext_proc",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.fault",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.grpc_http1_bridge",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.grpc_http1_reverse_bridge",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.grpc_json_transcoder",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.grpc_stats",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.grpc_web",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.header_to_metadata",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.health_check",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.ip_tagging",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.jwt_authn",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.local_ratelimit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.lua",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.oauth2",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.on_demand",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.original_src",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.ratelimit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.rbac",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.router",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.set_metadata",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.stateful_session",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.tap",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.filters.http.wasm",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.grpc_http1_bridge",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.grpc_json_transcoder",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.grpc_web",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.health_check",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.http_dynamo_filter",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.ip_tagging",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.local_rate_limit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.lua",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.rate_limit",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.router",
       "category": "envoy.filters.http"
      },
      {
       "name": "match-wrapper",
       "category": "envoy.filters.http"
      },
      {
       "name": "envoy.formatter.metadata",
       "category": "envoy.formatter"
      },
      {
       "name": "envoy.formatter.req_without_query",
       "category": "envoy.formatter"
      },
      {
       "name": "envoy.filters.thrift.header_to_metadata",
       "category": "envoy.thrift_proxy.filters"
      },
      {
       "name": "envoy.filters.thrift.rate_limit",
       "category": "envoy.thrift_proxy.filters"
      },
      {
       "name": "envoy.filters.thrift.router",
       "category": "envoy.thrift_proxy.filters"
      },
      {
       "name": "envoy.rate_limit_descriptors.expr",
       "category": "envoy.rate_limit_descriptors"
      },
      {
       "name": "envoy.compression.brotli.compressor",
       "category": "envoy.compression.compressor"
      },
      {
       "name": "envoy.compression.gzip.compressor",
       "category": "envoy.compression.compressor"
      },
      {
       "name": "envoy.wasm.runtime.null",
       "category": "envoy.wasm.runtime"
      },
      {
       "name": "envoy.wasm.runtime.v8",
       "category": "envoy.wasm.runtime"
      },
      {
       "name": "envoy.matching.matchers.consistent_hashing",
       "category": "envoy.matching.input_matchers"
      },
      {
       "name": "envoy.matching.matchers.ip",
       "category": "envoy.matching.input_matchers"
      },
      {
       "name": "envoy.bootstrap.wasm",
       "category": "envoy.bootstrap"
      },
      {
       "name": "envoy.extensions.network.socket_interface.default_socket_interface",
       "category": "envoy.bootstrap"
      },
      {
       "name": "envoy.health_checkers.redis",
       "category": "envoy.health_checkers"
      },
      {
       "name": "envoy.extensions.http.cache.simple",
       "category": "envoy.http.cache"
      },
      {
       "name": "auto",
       "category": "envoy.thrift_proxy.transports"
      },
      {
       "name": "framed",
       "category": "envoy.thrift_proxy.transports"
      },
      {
       "name": "header",
       "category": "envoy.thrift_proxy.transports"
      },
      {
       "name": "unframed",
       "category": "envoy.thrift_proxy.transports"
      }
     ]
    },
    "static_resources": {
     "listeners": [
      {
       "name": "listener_ingress",
       "address": {
        "socket_address": {
         "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",
            "stat_prefix": "http",
            "route_config": {
             "name": "route_local",
             "virtual_hosts": [
              {
               "name": "route_local",
               "domains": [
                "*"
               ],
               "routes": [
                {
                 "match": {
                  "prefix": "/api/a/"
                 },
                 "route": {
                  "cluster": "cluster_local"
                 },
                 "decorator": {
                  "operation": "checkAvailability"
                 }
                }
               ]
              }
             ]
            },
            "http_filters": [
             {
              "name": "envoy.filters.http.router"
             }
            ],
            "tracing": {
             "provider": {
              "name": "envoy.tracers.zipkin",
              "typed_config": {
               "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
               "collector_cluster": "zipkin",
               "collector_endpoint": "/api/v2/spans",
               "collector_endpoint_version": "HTTP_JSON"
              }
             }
            },
            "generate_request_id": true
           }
          }
         ]
        }
       ]
      }
     ],
     "clusters": [
      {
       "name": "cluster_xds",
       "type": "STRICT_DNS",
       "load_assignment": {
        "cluster_name": "cluster_xds",
        "endpoints": [
         {
          "lb_endpoints": [
           {
            "endpoint": {
             "address": {
              "socket_address": {
               "address": "192.168.8.66",
               "port_value": 11000
              }
             }
            }
           }
          ]
         }
        ]
       },
       "typed_extension_protocol_options": {
        "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
         "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
         "explicit_http_config": {
          "http2_protocol_options": {}
         }
        }
       }
      },
      {
       "name": "cluster_local",
       "type": "STRICT_DNS",
       "load_assignment": {
        "cluster_name": "cluster_local",
        "endpoints": [
         {
          "lb_endpoints": [
           {
            "endpoint": {
             "address": {
              "socket_address": {
               "address": "127.0.0.1",
               "port_value": 18000
              }
             }
            }
           }
          ]
         }
        ]
       }
      },
      {
       "name": "jaeger",
       "type": "STRICT_DNS",
       "load_assignment": {
        "cluster_name": "jaeger",
        "endpoints": [
         {
          "lb_endpoints": [
           {
            "endpoint": {
             "address": {
              "socket_address": {
               "address": "jaeger",
               "port_value": 9411
              }
             }
            }
           }
          ]
         }
        ]
       }
      },
      {
       "name": "zipkin",
       "type": "STRICT_DNS",
       "load_assignment": {
        "cluster_name": "zipkin",
        "endpoints": [
         {
          "lb_endpoints": [
           {
            "endpoint": {
             "address": {
              "socket_address": {
               "address": "zipkin",
               "port_value": 9411
              }
             }
            }
           }
          ]
         }
        ]
       }
      }
     ]
    },
    "dynamic_resources": {
     "lds_config": {
      "ads": {},
      "resource_api_version": "V3"
     },
     "cds_config": {
      "ads": {},
      "resource_api_version": "V3"
     },
     "ads_config": {
      "api_type": "GRPC",
      "grpc_services": [
       {
        "envoy_grpc": {
         "cluster_name": "cluster_xds"
        }
       }
      ],
      "transport_api_version": "V3"
     }
    },
    "admin": {
     "access_log_path": "/var/log/envoy/admin_access.log",
     "profile_path": "/var/log/envoy/envoy.prof",
     "address": {
      "socket_address": {
       "address": "0.0.0.0",
       "port_value": 9901
      }
     }
    }
   },
   "last_updated": "2022-08-01T13:43:52.585Z"
  },
  {
   "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
   "version_info": "0",
   "static_clusters": [
    {
     "cluster": {
      "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
      "name": "cluster_local",
      "type": "STRICT_DNS",
      "load_assignment": {
       "cluster_name": "cluster_local",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "127.0.0.1",
              "port_value": 18000
             }
            }
           }
          }
         ]
        }
       ]
      }
     },
     "last_updated": "2022-08-01T13:43:52.607Z"
    },
    {
     "cluster": {
      "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
      "name": "cluster_xds",
      "type": "STRICT_DNS",
      "load_assignment": {
       "cluster_name": "cluster_xds",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "192.168.8.66",
              "port_value": 11000
             }
            }
           }
          }
         ]
        }
       ]
      },
      "typed_extension_protocol_options": {
       "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
        "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
        "explicit_http_config": {
         "http2_protocol_options": {}
        }
       }
      }
     },
     "last_updated": "2022-08-01T13:43:52.606Z"
    },
    {
     "cluster": {
      "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
      "name": "jaeger",
      "type": "STRICT_DNS",
      "load_assignment": {
       "cluster_name": "jaeger",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "jaeger",
              "port_value": 9411
             }
            }
           }
          }
         ]
        }
       ]
      }
     },
     "last_updated": "2022-08-01T13:43:52.612Z"
    },
    {
     "cluster": {
      "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
      "name": "zipkin",
      "type": "STRICT_DNS",
      "load_assignment": {
       "cluster_name": "zipkin",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "zipkin",
              "port_value": 9411
             }
            }
           }
          }
         ]
        }
       ]
      }
     },
     "last_updated": "2022-08-01T13:43:52.614Z"
    }
   ],
   "dynamic_active_clusters": [
    {
     "version_info": "0",
     "cluster": {
      "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
      "name": "cluster_d",
      "type": "LOGICAL_DNS",
      "connect_timeout": "5s",
      "load_assignment": {
       "cluster_name": "cluster_d",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "biz-d-service",
              "port_value": 10000
             }
            }
           }
          }
         ]
        }
       ]
      }
     },
     "last_updated": "2022-08-01T13:43:53.117Z"
    }
   ]
  },
  {
   "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
   "version_info": "0",
   "static_listeners": [
    {
     "listener": {
      "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
      "name": "listener_ingress",
      "address": {
       "socket_address": {
        "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",
           "stat_prefix": "http",
           "route_config": {
            "name": "route_local",
            "virtual_hosts": [
             {
              "name": "route_local",
              "domains": [
               "*"
              ],
              "routes": [
               {
                "match": {
                 "prefix": "/api/a/"
                },
                "route": {
                 "cluster": "cluster_local"
                },
                "decorator": {
                 "operation": "checkAvailability"
                }
               }
              ]
             }
            ]
           },
           "http_filters": [
            {
             "name": "envoy.filters.http.router"
            }
           ],
           "tracing": {
            "provider": {
             "name": "envoy.tracers.zipkin",
             "typed_config": {
              "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
              "collector_cluster": "zipkin",
              "collector_endpoint": "/api/v2/spans",
              "collector_endpoint_version": "HTTP_JSON"
             }
            }
           },
           "generate_request_id": true
          }
         }
        ]
       }
      ]
     },
     "last_updated": "2022-08-01T13:43:52.635Z"
    }
   ],
   "dynamic_listeners": [
    {
     "name": "listener_egress_d",
     "active_state": {
      "version_info": "0",
      "listener": {
       "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
       "name": "listener_egress_d",
       "address": {
        "socket_address": {
         "address": "0.0.0.0",
         "port_value": 18003
        }
       },
       "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": "http",
            "rds": {
             "config_source": {
              "api_config_source": {
               "api_type": "GRPC",
               "grpc_services": [
                {
                 "envoy_grpc": {
                  "cluster_name": "cluster_xds"
                 }
                }
               ],
               "set_node_on_first_message_only": true,
               "transport_api_version": "V3"
              },
              "resource_api_version": "V3"
             },
             "route_config_name": "route_d"
            },
            "http_filters": [
             {
              "name": "envoy.filters.http.router"
             }
            ],
            "tracing": {
             "provider": {
              "name": "envoy.tracers.zipkin",
              "typed_config": {
               "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
               "collector_cluster": "zipkin",
               "collector_endpoint": "/api/v2/spans",
               "shared_span_context": false,
               "collector_endpoint_version": "HTTP_JSON"
              }
             }
            },
            "generate_request_id": true
           }
          }
         ]
        }
       ]
      },
      "last_updated": "2022-08-01T13:43:53.127Z"
     }
    }
   ]
  },
  {
   "@type": "type.googleapis.com/envoy.admin.v3.ScopedRoutesConfigDump"
  },
  {
   "@type": "type.googleapis.com/envoy.admin.v3.RoutesConfigDump",
   "static_route_configs": [
    {
     "route_config": {
      "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
      "name": "route_local",
      "virtual_hosts": [
       {
        "name": "route_local",
        "domains": [
         "*"
        ],
        "routes": [
         {
          "match": {
           "prefix": "/api/a/"
          },
          "route": {
           "cluster": "cluster_local"
          },
          "decorator": {
           "operation": "checkAvailability"
          }
         }
        ]
       }
      ]
     },
     "last_updated": "2022-08-01T13:43:52.634Z"
    }
   ],
   "dynamic_route_configs": [
    {
     "version_info": "0",
     "route_config": {
      "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
      "name": "route_d",
      "virtual_hosts": [
       {
        "name": "route_d",
        "domains": [
         "*"
        ],
        "routes": [
         {
          "match": {
           "prefix": "/api/d/"
          },
          "route": {
           "cluster": "cluster_d"
          }
         }
        ]
       }
      ]
     },
     "last_updated": "2022-08-01T13:43:53.132Z"
    }
   ]
  },
  {
   "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump"
  }
 ]
}

envoy对系统影响如何

为了对envoy在性能还有资源占用方面进行基准测试
系统方面有

  • 两个业务组成的小型服务
  • 6个java/go组成的业务中型服务

包含envoy方面

  • 没有envoy
  • 有envoy

观察指标

  • cpu影响
  • 内存影响
  • 耗时影响

小型服务

简单版部署图
在这里插入图片描述

小型服务:有一些功能性的尝试,更多也是非功能性,针对单个接口的多次平均测试,最后得到如下

  • 耗时(统计后计算):平均每次增加3.02066675ms
  • cpu:这个是大批量的变化统计:是增加6.60
  • 内存:这个跟两个因素有关,内存平均增加:13.41mb
    • 默认内存分配:内存分配方式采用TCMalloc,实际占用内存会比分配的更多,建议设置内存上限
    • 内存占用与Listener和Cluster个数呈线性关系,需要在业务上对这两个个数管控,还是要在大集群规模中进行测试,本次简化版是每个服务只有一个Listener和一个Cluster

具体衡量部分
非功能调研的数据:两个服务

中型服务

这里java和go的服务都有
全量版部署图
在这里插入图片描述

中型服务:一些功能性的尝试,也有非功能性的尝试,更多的是不同链路接口长度,对整体耗时的影响。(对于这种长链的中型服务,内存和cpu的统计会更加精确)

  • 耗时:平均增加2.10ms
  • cpu:这里有java和go的服务,两者对cpu的使用差距较大,因此分开进行了统计。总体来说cpu变化并不大
    • java的cpu变化:-2.07
    • go的cpu变化:1.31
    • 总的变化:-0.38
  • 内存:平均增加12.89mb

具体衡量部分
非功能调研:多个服务

项目代码:
控制层代码

  • https://github.com/simonalong/envoy-control-service

业务测试代码

  • https://github.com/simonalong/biz_envoy_a
  • https://github.com/simonalong/biz_envoy_b
  • https://github.com/simonalong/biz_envoy_c
  • https://github.com/simonalong/biz_envoy_d
  • https://github.com/simonalong/biz_envoy_e
  • https://github.com/simonalong/biz_envoy_f

整体上来说

  • 耗时:少量服务单接口多次大批量更为精准,平均增加:3.02066675ms
  • 内存:大量服务多接口一次大批量更为精准,平均增加:12.89mb
    • 即使是长连接和文件上传,也是增加12.8mb(一个12.83mb,一个是12.81mb)
  • cpu:两者统计来看差距都不大,暂时忽略

envoy功能有哪些

先看下envoy本身说的提供的功能

  • 安全:服务之间如何授权
  • 链路:如何轻量化接入全公司的链路
  • 数据采集:能够采集哪些数据供展示
  • 服务发现与负载均衡:是否符合公司本身的技术方向
  • 熔断、限流:
  • 更多功能:…

根据以上的功能我们主要探索以上几个重要功能

安全

安全意思就是我可以在一定情况下,让某个服务不让其他服务访问。这里面有两类,一个粒度粗的服务级别,一个粒度细的接口级别
粗粒度的,比如服务B,我不让服务A访问,那么这种在envoy这边就可以通过动态的RDS,来动态修改路由规则,让服务A的envoy数据层没有向服务B的路由集群配置
细粒度的,比如服务B,我的接口/api/app/b/test/no/a,不让服务A访问。这种可以动态修改路由让服务A的envoy里面没有接口匹配的/api/app/b/test/no/a这个接口,不过这样的话,目前来实现只能通过穷举,目前暂时没有看到有exclude字段

  listeners:
    - name: listener_ingress
      address:
        socket_address:
          address: 0.0.0.0
          # 所有服务对外的端口,这里采用10000
          port_value: 10000
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                stat_prefix: http
                route_config:
                  # 配置流量的输入监听
                  name: route_local
                  virtual_hosts:
                    - name: route_local
                      domains:
                        - "*"
                      routes:
                        - match:
                            # 这里就是前缀的配置
                            prefix: "/api/d/"
                          route:
                            cluster: cluster_local
                          decorator:
                            operation: checkAvailability
                http_filters:
                  - name: envoy.filters.http.router
                generate_request_id: true

对应代码方面就是上面的api

func getHost(clusterName string) []*route.VirtualHost {
	return []*route.VirtualHost{{
		Name:    "local_service",
		Domains: []string{"*"},
		Routes: []*route.Route{{
			Match: &route.RouteMatch{
				PathSpecifier: &route.RouteMatch_Prefix{
					Prefix: "/",
				},
			},
			Action: &route.Route_Route{
				Route: &route.RouteAction{
					ClusterSpecifier: &route.RouteAction_Cluster{
						Cluster: clusterName,
					},
					HostRewriteSpecifier: &route.RouteAction_HostRewriteLiteral{
						HostRewriteLiteral: UpstreamHost,
					},
				},
			},
		}},
	}}
}

链路

1. envoy配置链路

目前上面envoy的默认的静态配置中就添加了zipkin的,zipkin也是可以使用jeager接口

    - name: data-collector
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: zipkin
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      # 搜集埋点数据的服务
                      address: isc-data-collector-service
                      port_value: 18080
  listeners:
    - name: listener_ingress
      address:
        socket_address:
          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
                tracing:
                  provider:
                    name: envoy.tracers.zipkin
                    typed_config:
                      "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
                      # 配置搜集的
                      collector_cluster: data-collector
                      collector_endpoint: "/api/v2/spans"
                      collector_endpoint_version: HTTP_JSON
                stat_prefix: http
                # 配置自动生成request_id
                generate_request_id: true
2. 服务埋点

我们遵守jaeger的规范,需要将head中的数据传递下去,那么每个服务向下发送需要带上如下的head

● x-request-id:其实就是traceId
● x-b3-traceid:一个工作单元(rpc 调用)的唯一标识。
● x-b3-spanid:一个工作单元(rpc 调用)的唯一标识。
● x-b3-parentspanid:一个工作单元(rpc 调用)的唯一标识。
● x-b3-sampled:是否被抽样为输出的标志,1 为需要被输出,0 为不需要被输出。
● x-b3-flags:
● x-ot-span-context:
示例:
// 一定要判断值是否为空,不空再做处理,否则会造成链断开
headers := netHttp.Header{}
if c.GetHeader("x-request-id") != "" {
    headers["x-request-id"] = []string{c.GetHeader("x-request-id")}
    headers["x-b3-traceid"] = []string{c.GetHeader("x-b3-traceid")}
    headers["x-b3-spanid"] = []string{c.GetHeader("x-b3-spanid")}
    headers["x-b3-parentspanid"] = []string{c.GetHeader("x-b3-parentspanid")}
    headers["x-b3-sampled"] = []string{c.GetHeader("x-b3-sampled")}
    headers["x-b3-flags"] = []string{c.GetHeader("x-b3-flags")}
    headers["x-ot-span-context"] = []string{c.GetHeader("x-ot-span-context")}
}

// .....
// 发送示例:data, err := http.GetOfStandard(config.GetValueString("biz.url.f") + "/api/f/cf/" + haveMysql, headers, map[string]string{})
// ......
@Slf4j
@Component
public class DataFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        
        // 请务必判断为空
        String reqId = httpServletRequest.getHeader("x-request-id");
        if (null == reqId || "".equals(reqId)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }
        Map<String, Object> headMap = new HashMap<>();
        headMap.put("x-request-id", httpServletRequest.getHeader("x-request-id"));
        headMap.put("x-b3-traceid", httpServletRequest.getHeader("x-b3-traceid"));
        headMap.put("x-b3-spanid", httpServletRequest.getHeader("x-b3-spanid"));
        headMap.put("x-b3-parentspanid", httpServletRequest.getHeader("x-b3-parentspanid"));
        headMap.put("x-b3-sampled", httpServletRequest.getHeader("x-b3-sampled"));
        headMap.put("x-b3-flags", httpServletRequest.getHeader("x-b3-flags"));
        headMap.put("x-ot-span-context", httpServletRequest.getHeader("x-ot-span-context"));
        
        // 保存到阿里的替换ThreadLocal的Ttl中(TransmittableThread)
        // ContextHelper.setData(headMap);
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
3. 链路搜集服务

我们这里使用的是我们自己的埋点数据搜集服务,链路使用jaeger的标准接口,只需实现如下接口即可

post api/v2/spans

比如,我们这里使用数据搜集服务“isc-data-collector-service”实现了该接口

func main() {
	server.Post("api/v2/spans", spansData)
	server.Run()
}

func spansData(c *gin.Context) {
	var spans2 []map[string]interface{}
	data, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		logger.Error("数据异常, %v", err.Error())
		rsp.FailedOfStandard(c, 500, "解析异常:"+err.Error())
		return
	}

	err = json.Unmarshal(data, &spans2)
	if err != nil {
		logger.Error("数据异常, %v", err.Error())
		rsp.FailedOfStandard(c, 53, "解析异常:"+err.Error())
		return
	}
    // str就是我们要的数据
	str, err := json.Marshal(spans2)
	rsp.SuccessOfStandard(c, "value")
}

在多服务的复杂调用中,测试接口的调用示例:

a -> d -> e -> f

服务语言类型:

  • a:java
  • d:go
  • e:java
  • f:go

得到如下数据:一共7条数据,接收的顺序无所谓,根据其中的parentId和id就可以建立树形结构

  • a:2条
  • d:2条
  • e:2条
  • f:1条
[{"annotations":[{"timestamp":1659353374125652,"value":"ss"}],"duration":59895,"id":"5211e6310573d8f2","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.103","port":0,"serviceName":"biz-d-service"},"name":"checkAvailability","parentId":"0e2327a579634223","shared":true,"tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18003/api/d/ad/false","node_id":"biz-d-service","peer.address":"172.25.0.100","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_local","upstream_cluster.name":"cluster_local","user_agent":"okhttp/3.14.7"},"timestamp":1659353374064811,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374131938,"value":"ss"}],"duration":126252,"id":"0e2327a579634223","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.100","port":0,"serviceName":"biz-a-service"},"name":"checkAvailability","shared":true,"tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:10000/api/a/front/adef/false/false","node_id":"biz-a-service","peer.address":"172.25.0.1","request_size":"0","response_flags":"-","response_size":"22","upstream_cluster":"cluster_local","upstream_cluster.name":"cluster_local","user_agent":"apifox/1.0.0 (https://www.apifox.cn)"},"timestamp":1659353374000938,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374112755,"value":"ss"}],"duration":7005,"id":"af68aeb64876ac17","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.105","port":0,"serviceName":"biz-f-service"},"name":"checkAvailability","parentId":"391882d9b20b1c1b","shared":true,"tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18005/api/f/ef/false","node_id":"biz-f-service","peer.address":"172.25.0.104","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_local","upstream_cluster.name":"cluster_local","user_agent":"okhttp/3.14.7"},"timestamp":1659353374105164,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374118792,"value":"ss"}],"duration":44736,"id":"391882d9b20b1c1b","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.104","port":0,"serviceName":"biz-e-service"},"name":"checkAvailability","parentId":"5211e6310573d8f2","shared":true,"tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18004/api/e/de/false","node_id":"biz-e-service","peer.address":"172.25.0.103","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_local","upstream_cluster.name":"cluster_local","user_agent":"Go-http-client/1.1"},"timestamp":1659353374073455,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374126043,"value":"ss"}],"duration":64958,"id":"5211e6310573d8f2","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.100","port":0,"serviceName":"biz-a-service"},"name":"localhost:18003","parentId":"0e2327a579634223","tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18003/api/d/ad/false","node_id":"biz-a-service","peer.address":"127.0.0.1","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_d","upstream_cluster.name":"cluster_d","user_agent":"okhttp/3.14.7"},"timestamp":1659353374060944,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374113019,"value":"ss"}],"duration":8149,"id":"af68aeb64876ac17","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.104","port":0,"serviceName":"biz-e-service"},"name":"localhost:18005","parentId":"391882d9b20b1c1b","tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18005/api/f/ef/false","node_id":"biz-e-service","peer.address":"127.0.0.1","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_f","upstream_cluster.name":"cluster_f","user_agent":"okhttp/3.14.7"},"timestamp":1659353374104764,"traceId":"0e2327a579634223"}]
[{"annotations":[{"timestamp":1659353374119114,"value":"ss"}],"duration":46159,"id":"391882d9b20b1c1b","kind":"SERVER","localEndpoint":{"ipv4":"172.25.0.103","port":0,"serviceName":"biz-d-service"},"name":"localhost:18004","parentId":"5211e6310573d8f2","tags":{"component":"proxy","downstream_cluster":"-","guid:x-request-id":"3296709e-de77-9931-8b45-fb777c01773c","http.method":"GET","http.protocol":"HTTP/1.1","http.status_code":"200","http.url":"http://localhost:18004/api/e/de/false","node_id":"biz-d-service","peer.address":"127.0.0.1","request_size":"0","response_flags":"-","response_size":"42","upstream_cluster":"cluster_e","upstream_cluster.name":"cluster_e","user_agent":"Go-http-client/1.1"},"timestamp":1659353374072874,"traceId":"0e2327a579634223"}]

根据上面数据,我们就可以建立服务之间的链路关系,并呈现链路数据,我们可以根据以上数据展示我们想要的画面即可。
也可以参考zipkin拿上面这些数据展示的形式
image.png
整条链路数据
image.png
查看服务依赖关系
image.png
某个服务的调用次数和异常
image.png

数据指标采集

每一个envoy进程,都有一个admin端口,直接访问该端口,如下
image.png
我们这里关注stats/prometheus即可,然后在prometheus配置下即可
envoy定义了很多metric,可以直接与prometheus将端口配置下即可,如下

注意:
prometheus的默认接口是:/metrics,envoy这里是:/stats/prometheus,因此在配置prometheus的配置时候,要注意配置如下

global:
  scrape_interval:     60s
  evaluation_interval: 60s
 
scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']
        labels:
          instance: prometheus
 
  - job_name: biz-c-service
    # 这个很重要,看了官网才找到如下字段,否则就是默认路径: /metrics
    metrics_path: /stats/prometheus
    static_configs:
      - targets: ['192.168.8.66:9902']
        labels:
          instance: localhost

配置好后就可以使用了
image.png

服务发现

服务发现:属于数据动态变更和EDS部分,其中当某个服务需要指向某个服务时候,就可以配置EDS。

func GetEndpoint() *endpoint.Endpoint {
	return &endpoint.Endpoint{

		// 上游主机地址
		Address: nil,
		// 选的健康检查配置用作健康检查器与健康检查主机联系的配置
		HealthCheckConfig: nil,
		// 此端点关联的主机名
		Hostname: "",
	}
}

负载均衡

负载均衡,属于CDS的范围,其中负载均衡如下

  • Cluster_ROUND_ROBIN:轮询
  • Cluster_LEAST_REQUEST:最小请求
  • Cluster_RING_HASH:经典一致性哈希的换均衡
  • Cluster_RANDOM:随机均衡
  • Cluster_MAGLEV:Maglev算法的一致性均衡
  • Cluster_CLUSTER_PROVIDED:配置集群情况下的集群负载均衡
  • Cluster_LOAD_BALANCING_POLICY_CONFIG:弃用

比如如下配置cluster地方就可以配置负载均衡策略,这里只截取一部分

func GetCluster(clusterName string) *cluster.Cluster {
	return &cluster.Cluster{
		// 集群名称
		Name: clusterName,
		// 控制层的连接超时时间
		ConnectTimeout: durationpb.New(5 * time.Second),

		// ...

		// -------------------------- 负载均衡---------------------------------------
		// 选择主机时的负载均衡策略
		LbPolicy: cluster.Cluster_ROUND_ROBIN,
		// LbPolicy进行选择指定的配置类型,比如上面的,Round,则该配置需要为 Cluster_RoundRobinLbConfig_
		LbConfig: nil,
		// 负载均衡子集配置
		LbSubsetConfig: nil,
		// 负载均衡配置,如果这个值配置了,则会替换LbPolicy
		LoadBalancingPolicy: nil,

		// ...
	}
}

熔断

熔断是分布式系统的重要组成部分。快速失败并尽快给下游施加压力,该配置也是在CDS集群中,主要是对上游链接的一些配置

func GetCluster(clusterName string) *cluster.Cluster {
	return &cluster.Cluster{
		// 集群名称
		Name: clusterName,

		// ...

		// -------------- 上游链接配置 --------------

		// ...

		// 熔断: 断路器,其实也是连接池的一些配置
		CircuitBreakers: nil,

		// ...
	}
}

其中circuit_breakers就是熔断器的配置,官方示例:

circuit_breakers:
  thresholds:
  - priority: "DEFAULT"
    max_requests: 75
    max_pending_requests: 35
    retry_budget:
      budget_percent:
        value: 25.0
      min_retry_concurrency: 10

其中

  • Priority:指定的断路器设置适用于,目前有两个:0:DEFAULT,1:HIGH
  • MaxConnections:Envoy 将与上游集群建立的最大连接数。默认 1024
  • MaxPendingRequests:Envoy 对上游集群的待处理请求的最大数量。默认1024
  • MaxRequests:Envoy 将向上游集群发出的最大并行请求数。默认1024
  • MaxRetries:Envoy 允许上游集群的最大并行重试次数。默认为 3
  • RetryBudget:(可选)指定与活动请求数相关的并发重试限制
  • TrackRemaining:为true:则将发布统计信息,显示在断路器打开之前剩余的资源数量。默认false
  • MaxConnectionPools:Envoy 将同时支持的每个集群的最大连接池数。默认无限制

限流

限流是RDS的部分,也就是路由配置的部分。限流在envoy这里支持两种限流

  • 普通限流:就是在路由规则里面配置
  • 全局限流:就是提供额外的一个限流服务,每次请求进行判断
普通限流
func GetRouter(routeName string, clusterName string) *route.RouteConfiguration {
	return &route.RouteConfiguration{
		// 路由名称
		Name: routeName,

		// ----------------------------------- 虚拟主机 -----------------------------------
		// 组成路由表的虚拟主机数组
		VirtualHosts: getHost(clusterName),
		// ...
	}
}

func getHost(clusterName string) []*route.VirtualHost {
	return []*route.VirtualHost{{
		Name:    "local_service",
		Domains: []string{"*"},
        // ...
		// 限速配置
		RateLimits: nil,
	}}
}
全局限流

全局限速,这里没有做尝试,摘录了一张图,如下
image.png

基于envoy要做的事情

如果公司要真的基于envoy来做中枢控制系统,那么长期来说,可以做如下规划

从以上规划来说,对人力物力和资源投入还是很大的,尤其是第一期,不过目前已经部分做了技术尝试,一期的话,更多的是作为一个尝试,在三期后,我们希望能达到这样一个成果,并能够在各种细节上面做更多控制

参考:

envoy中文文档:https://cloudnative.to/envoy/about_docs.html
https://zhuanlan.zhihu.com/p/153105848?from_voters_page=true

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值