http流量镜像

“流量镜像”是指将网络中的数据流量复制一份,并将这份复制流量发送到另一个目的地(如监控、分析或安全检测系统)。这项技术常用于网络安全、故障排查、业务灰度发布等场景。

主要应用场景

  • 安全监控与威胁检测

    将生产环境的流量镜像到安全分析设备(如IDS/IPS),用于实时监控和威胁检测。

  • 性能分析与故障排查

    将流量镜像到分析平台,对网络异常、延迟、丢包等问题进行实时排查和定位。

  • 灰度发布和A/B测试

    将真实用户流量镜像到新版本服务,进行灰度环境验证和兼容性测试,不影响真实用户体验。

  • 合规与审计

    对重要业务流量进行行为记录,以满足合规和审计要求。

VKProxy 目前只支持 http流量镜像, 需注意由于会再一次发送http 请求,请求body会临时暂存内存,所以无论内存还是请求延迟都会受到影响,特别body很大的请求

设置

大家可以在Metadata中设置缓存, 具体设置项如下

  • MirrorCluster

    镜像流量发送到的集群id

配置示例:

{
"ReverseProxy": {
"Routes": {
"a": {
"Order": 0,
"Match": {
"Hosts": [ "api.com" ],
"Paths": [ "*" ]
},
"ClusterId": "apidemo",
"Metadata": {
"MirrorCluster": "apidemoMirror"
}
}
},
"Clusters": {
"apidemo": {
"LoadBalancingPolicy": "Hash",
"Metadata": {
"HashBy": "header",
"Key": "X-forwarded-For"
},
"Destinations": [
{
"Address": "https://xxx.lt"
}
]
},
"apidemoMirror": {
"LoadBalancingPolicy": "Hash",
"Metadata": {
"HashBy": "header",
"Key": "X-forwarded-For"
},
"Destinations": [
{
"Address": "http://xxx.org/"
}
]
}
}
}
}

具体实现

首先需要缓存body 内容,这里实现一个简单的 ReadBufferingStream


public class ReadBufferingStream : Stream, IDisposable
{
private readonly SparseBufferWriter<byte> bufferWriter;
protected Stream innerStream; public ReadBufferingStream(Stream innerStream)
{
this.innerStream = innerStream;
bufferWriter = new SparseBufferWriter<byte>();
} public override bool CanRead => innerStream.CanRead; public override bool CanSeek => innerStream.CanSeek; public override bool CanWrite => innerStream.CanWrite; public override long Length => innerStream.Length; public override long Position
{
get => innerStream.Position;
set => innerStream.Position = value;
} public override int WriteTimeout
{
get => innerStream.WriteTimeout;
set => innerStream.WriteTimeout = value;
} public Stream BufferingStream => bufferWriter.AsStream(true); public override void Flush()
{
innerStream.Flush();
} public override Task FlushAsync(CancellationToken cancellationToken)
{
return innerStream.FlushAsync(cancellationToken);
} public override int Read(byte[] buffer, int offset, int count)
{
var res = innerStream.Read(buffer, offset, count); // Zero-byte reads (where the passed in buffer has 0 length) can occur when using PipeReader, we don't want to accidentally complete the RequestBody logging in this case
if (count == 0)
{
return res;
} bufferWriter.Write(buffer.AsSpan(offset, res)); return res;
} public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var res = await innerStream.ReadAsync(buffer.AsMemory(offset, count), cancellationToken); if (count == 0)
{
return res;
} bufferWriter.Write(buffer.AsSpan(offset, res)); return res;
} public override long Seek(long offset, SeekOrigin origin)
{
return innerStream.Seek(offset, origin);
} public override void SetLength(long value)
{
innerStream.SetLength(value);
} public override void Write(byte[] buffer, int offset, int count)
{
innerStream.Write(buffer, offset, count);
} public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return innerStream.WriteAsync(buffer, offset, count, cancellationToken);
} public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
return innerStream.WriteAsync(buffer, cancellationToken);
} public override void Write(ReadOnlySpan<byte> buffer)
{
innerStream.Write(buffer);
} public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return innerStream.BeginRead(buffer, offset, count, callback, state);
} public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return innerStream.BeginWrite(buffer, offset, count, callback, state);
} public override int EndRead(IAsyncResult asyncResult)
{
return innerStream.EndRead(asyncResult);
} public override void EndWrite(IAsyncResult asyncResult)
{
innerStream.EndWrite(asyncResult);
} public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
var res = await innerStream.ReadAsync(buffer, cancellationToken);
if (buffer.IsEmpty)
{
return res;
} bufferWriter.Write(buffer.Slice(0, res).Span); return res;
} public override ValueTask DisposeAsync()
{
return innerStream.DisposeAsync();
} protected override void Dispose(bool disposing)
{
if (disposing)
{
bufferWriter.Dispose();
}
}
}

然后利用中间件进行镜像处理

public class MirrorFunc : IHttpFunc
{
private readonly IServiceProvider serviceProvider;
private readonly IHttpForwarder forwarder;
private readonly ILoadBalancingPolicyFactory loadBalancing;
private readonly IForwarderHttpClientFactory forwarderHttpClientFactory;
private readonly ProxyLogger logger; public int Order => int.MinValue; public MirrorFunc(IServiceProvider serviceProvider, IHttpForwarder forwarder, ILoadBalancingPolicyFactory loadBalancing, IForwarderHttpClientFactory forwarderHttpClientFactory, ProxyLogger logger)
{
this.serviceProvider = serviceProvider;
this.forwarder = forwarder;
this.loadBalancing = loadBalancing;
this.forwarderHttpClientFactory = forwarderHttpClientFactory;
this.logger = logger;
} public RequestDelegate Create(RouteConfig config, RequestDelegate next)
{
if (config.Metadata == null || !config.Metadata.TryGetValue("MirrorCluster", out var mirrorCluster) || string.IsNullOrWhiteSpace(mirrorCluster)) return next; return c => Mirror(c, mirrorCluster, next);
} private async Task Mirror(HttpContext c, string mirrorCluster, RequestDelegate next)
{
var config = serviceProvider.GetRequiredService<IConfigSource<IProxyConfig>>();
if (config.CurrentSnapshot == null || config.CurrentSnapshot.Clusters == null || !config.CurrentSnapshot.Clusters.TryGetValue(mirrorCluster, out var cluster) || cluster == null)
{
await next(c);
return;
} var originBody = c.Request.Body;
using var buffer = new ReadBufferingStream(originBody);
c.Request.Body = buffer; try
{
await next(c);
}
finally
{
c.Request.Body = buffer.BufferingStream;
try
{
var proxyFeature = c.Features.GetRequiredFeature<IReverseProxyFeature>();
var origin = proxyFeature.SelectedDestination;
var selectedDestination = loadBalancing.PickDestination(proxyFeature, cluster);
proxyFeature.SelectedDestination = origin;
if (selectedDestination != null)
{
cluster.InitHttp(forwarderHttpClientFactory);
await forwarder.SendAsync(c, proxyFeature, selectedDestination, cluster, new NonHttpTransformer(proxyFeature.Route.Transformer));
}
}
catch (Exception ex)
{
logger.LogWarning(ex, "Mirror failed");
}
finally
{
c.Request.Body = originBody;
}
}
}
}

所以说会再一次发送http 请求,请求body会临时暂存内存,所以无论内存还是请求延迟都会受到影响,特别body很大的请求

VKProxy 是使用c#开发的基于 Kestrel 实现 L4/L7的代理(感兴趣的同学烦请点个github小赞赞呢)

http流量镜像的更多相关文章

  1. H3C交换机流量镜像

    今天需要对交换机进行本地流量镜像,在此记录: 交换机:H3C S5120 配置本地端口镜像时,用户首先要创建一个本地镜像组,然后为本地镜像组配置源端口和目的端口. 表1-1 配置本地端口镜像 操作 命 ...

  2. idou老师教你学Istio12 : Istio 实现流量镜像

    微服务为我们带来了快速开发部署的优秀特性,而如何降低开发和变更的风险成为了一个问题.Istio的流量镜像,也称为影子流量,是将生产流量镜像拷贝到测试集群或者新的版本中,在引导实时流量之前进行测试,可以 ...

  3. 在 Istio 中实现 Redis 集群的数据分片、读写分离和流量镜像

    Redis 是一个高性能的 key-value 存储系统,被广泛用于微服务架构中.如果我们想要使用 Redis 集群模式提供的高级特性,则需要对客户端代码进行改动,这带来了应用升级和维护的一些困难.利 ...

  4. Kube-OVN 0.6.0 发布,支持 IPv6、流量镜像及更多功能

    Kube-OVN 是一个基于 OVN 的 Kubernetes 开源网络系统. 本次更新主要包含了以下内容: 1. 支持流量镜像 在安装 Kube-OVN 时可以开启 mirror 选项,会自动在每个 ...

  5. linux 双网卡桥接,实现网卡流量镜像与转发

    确认本地是否存在brctl,如果不存在请先安装: 1.确定你的镜像端口,比如eth1: 2.将实际数据通过的端口,比如eth0和镜像端口绑成一个bridge: brctl addbr br0 brct ...

  6. 虚机抓取Hyper-V宿主的镜像流量(Windows Server 2012R2)

    1.将交换机流量镜像到Hyper-V宿主的一块网卡(eth4) 2.在Hyper-V宿主上新建虚拟交换机(Network_Mirror),选择外部网络,扩展属性中启用“Microsoft NDIS捕获 ...

  7. 如何用路由器改成WiFi Pineapple系统镜像网络流量

    本文主要介绍利用自己现有的设备,如何制作和使用WiFi Pineapple镜像网络流量,利用DWall模块分析用户数据,然后根据自己的需求,给DWall加入了日志记录功能.最后介绍了如何防范wifi ...

  8. 运用Ntop监控网络流量(视频Demo)

    运用Ntop监控网络流量 ____网络流量反映了网络的运行状态,是判别网络运行是否正常的关键数据,在实际的网络中,如果对网络流量控制得不好或发生网络拥塞,将会导致网络吞吐量下降.网络性能降低.通过流量 ...

  9. Ntop监控网络流量

    运用Ntop监控网络流量 ____ 网络流量反映了网络的运行状态,是判别网络运行是否正常的关键数据,在实际的网络中,如果对网络流量控制得不好或发生网络拥塞,将会导致网络吞吐量下降. 网络性能降低.通过 ...

  10. H3c交换机配置端口镜像详情

    端口镜像 需要将G0/0/1口的全部流量镜像到G0/0/2口,即G0/0/1为源端口,G0/0/2为目的端口. 配置步骤 1.进入配置模式:system-view: 2.创建本地镜像组:mirrori ...

随机推荐

  1. 【Docker】常用服务镜像安装

    Docker常用安装 总体步骤 搜索镜像:docker search xxx 拉取镜像:docker pull xxx 查看镜像:docker images 启动镜像:docker run xxx 停 ...

  2. 【Maven】POM基本概念

    目前的技术在开发中存在的问题: 一个项目就是一个工程 如果项目非常庞大,就不适合继续使用 package 来划分模块.最好是每一个模块对应一个工程,利于分工协作. 借助于 Maven 就可以将一个项目 ...

  3. k8s集群创建之后coredns一直处于pending状态

    按照官网教程 master节点kubectl init, 每个从节点kubectl join之后, 在master节点执行 kubectl get pods -n kube-system,发现core ...

  4. exim4

    exim4 一台 debian 机器日常执行 apt update 后发现需要更新如下软件包, 之前没见过, 特此记录下. root@idebian:~# apt list --upgradable ...

  5. 『Plotly实战指南』--饼图绘制高级篇

    在数据可视化的世界里,饼图是最直观的展示比例关系的工具之一. 然而,传统的静态饼图已经无法满足现代数据分析的需求.Plotly作为一款强大的可视化库,不仅提供了饼图丰富的基础功能,还支持交互效果和动态 ...

  6. 康谋方案 | ADAS时空融合数据采集方案

    自动驾驶技术的飞速发展,正在重新定义未来出行的边界.从感知到决策,从规划到控制,每一个环节都离不开海量.精准的高质量数据支撑.然而,随着传感器数量的增加和数据规模的指数级增长,行业正面临一系列挑战:多 ...

  7. 基于Java Swing开发好看的皮肤

    先介绍几款开源及商业的皮肤. Weblaf:非常赞的套件,界面现代.简约.依赖包较少. 有开源也有商业协议,个人最喜欢的皮肤.https://github.com/mgarin/weblaf PgsL ...

  8. 【HUST】网安|计算机网络|计算机网络自顶向下方法(原书第7版)第三章部分习题答案

    参考:英文版的原答案. 答案放gitee了,自取. 3-P18. 3.4.4 节我们学习的一般性 SR 协议中,只要报文可用(如果报文在窗口中) ,发送方就会不等待确认而传输报文.假设现在我们要求一个 ...

  9. 关于Linux内核自带GPIO LED控制(正点原子开发板呼吸灯的一些小问题)

    正点原子Linux开发板IMX6ULL上的呼吸灯如何停止? 学习到驱动开发Linux系统自带的LED驱动控制的时候,才知道,原来该呼吸灯经过设备树配置好之后,直接由Linux内核程序配置为呼吸灯(前提 ...

  10. Python 常用魔法方法(下)

    Python 常用魔法方法(下) 回顾 魔法方法是 Python 内置方法, 不需要我们手动调用, 它存在的目的是给 解释器 调用的. 比如我们在写 "1 + 1 " 的时候, 这 ...