日志开源组件(六)Adaptive Sampling 自适应采样
业务背景
有时候日志的信息比较多,怎么样才可以让系统做到自适应采样呢?
拓展阅读
日志开源组件(一)java 注解结合 spring aop 实现自动输出日志
日志开源组件(二)java 注解结合 spring aop 实现日志traceId唯一标识
日志开源组件(三)java 注解结合 spring aop 自动输出日志新增拦截器与过滤器
日志开源组件(四)如何动态修改 spring aop 切面信息?让自动日志输出框架更好用
日志开源组件(五)如何将 dubbo filter 拦截器原理运用到日志拦截器中?
自适应采样
是什么?
系统生成的日志可以包含大量信息,包括错误、警告、性能指标等,但在实际应用中,处理和分析所有的日志数据可能会对系统性能和资源产生负担。
自适应采样在这种情况下发挥作用,它能够根据当前系统状态和日志信息的重要性,智能地决定哪些日志需要被采样记录,从而有效地管理和分析日志数据。
采样的必要性
日志采样系统会给业务系统额外增加消耗,很多系统在接入的时候会比较排斥。
给他们一个百分比的选择,或许是一个不错的开始,然后根据实际需要选择合适的比例。
自适应采样是一个对用户透明,同时又非常优雅的方案。

如何通过 java 实现自适应采样?
接口定义
首先我们定义一个接口,返回 boolean。
根据是否为 true 来决定是否输出日志。
/**
* 采样条件
* @author binbin.hou
* @since 0.5.0
*/
public interface IAutoLogSampleCondition {
/**
* 条件
*
* @param context 上下文
* @return 结果
* @since 0.5.0
*/
boolean sampleCondition(IAutoLogContext context);
}
百分比概率采样
我们先实现一个简单的概率采样。
0-100 的值,让用户指定,按照百分比决定是否采样。
public class InnerRandomUtil {
/**
* 1. 计算一个 1-100 的随机数 randomVal
* 2. targetRatePercent 值越大,则返回 true 的概率越高
* @param targetRatePercent 目标百分比
* @return 结果
*/
public static boolean randomRateCondition(int targetRatePercent) {
if(targetRatePercent <= 0) {
return false;
}
if(targetRatePercent >= 100) {
return true;
}
// 随机
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
int value = threadLocalRandom.nextInt(1, 100);
// 随机概率
return targetRatePercent >= value;
}
}
实现起来也非常简单,直接一个随机数,然后比较大小即可。
自适应采样
思路
我们计算一下当前日志的 QPS,让输出的概率和 QPS 称反比。
/**
* 自适应采样
*
* 1. 初始化采样率为 100%,全部采样
*
* 2. QPS 如果越来越高,那么采样率应该越来越低。这样避免 cpu 等资源的损耗。最低为 1%
* 如果 QPS 越来越低,采样率应该越来越高。增加样本,最高为 100%
*
* 3. QPS 如何计算问题
*
* 直接设置大小为 100 的队列,每一次在里面放入时间戳。
* 当大小等于 100 的时候,计算首尾的时间差,currentQps = 100 / (endTime - startTime) * 1000
*
* 触发 rate 重新计算。
*
* 3.1 rate 计算逻辑
*
* 这里我们存储一下 preRate = 100, preQPS = ?
*
* newRate = (preQps / currentQps) * rate
*
* 范围限制:
* newRate = Math.min(100, newRate);
* newRate = Math.max(1, newRate);
*
* 3.2 时间队列的清空
*
* 更新完 rate 之后,对应的队列可以清空?
*
* 如果额外使用一个 count,好像也可以。
* 可以调整为 atomicLong 的计算器,和 preTime。
*
代码实现
public class AutoLogSampleConditionAdaptive implements IAutoLogSampleCondition {
private static final AutoLogSampleConditionAdaptive INSTANCE = new AutoLogSampleConditionAdaptive();
/**
* 单例的方式获取实例
* @return 结果
*/
public static AutoLogSampleConditionAdaptive getInstance() {
return INSTANCE;
}
/**
* 次数大小限制,即接收到多少次请求更新一次 adaptive 计算
*
* TODO: 这个如何可以让用户可以自定义呢?后续考虑配置从默认的配置文件中读取。
*/
private static final int COUNT_LIMIT = 1000;
/**
* 自适应比率,初始化为 100.全部采集
*/
private volatile int adaptiveRate = 100;
/**
* 上一次的 QPS
*
* TODO: 这个如何可以让用户可以自定义呢?后续考虑配置从默认的配置文件中读取。
*/
private volatile double preQps = 100.0;
/**
* 上一次的时间
*/
private volatile long preTime;
/**
* 总数,请求计数器
*/
private final AtomicInteger counter;
public AutoLogSampleConditionAdaptive() {
preTime = System.currentTimeMillis();
counter = new AtomicInteger(0);
}
@Override
public boolean sampleCondition(IAutoLogContext context) {
int count = counter.incrementAndGet();
// 触发一次重新计算
if(count >= COUNT_LIMIT) {
updateAdaptiveRate();
}
// 直接计算是否满足
return InnerRandomUtil.randomRateCondition(adaptiveRate);
}
}
每次累加次数超过限定次数之后,我们就更新一下对应的日志概率。
最后的概率计算和上面的百分比类似,不再赘述。
/**
* 更新自适应的概率
*
* 100 计算一次,其实还好。实际应该可以适当调大这个阈值,本身不会经常变化的东西。
*/
private synchronized void updateAdaptiveRate() {
//消耗的毫秒数
long costTimeMs = System.currentTimeMillis() - preTime;
//qps 的计算,时间差是毫秒。所以次数需要乘以 1000
double currentQps = COUNT_LIMIT*1000.0 / costTimeMs;
// preRate * preQps = currentRate * currentQps; 保障采样均衡,服务器压力均衡
// currentRate = (preRate * preQps) / currentQps;
// 更新比率
int newRate = 100;
if(currentQps > 0) {
newRate = (int) ((adaptiveRate * preQps) / currentQps);
newRate = Math.min(100, newRate);
newRate = Math.max(1, newRate);
}
// 更新 rate
adaptiveRate = newRate;
// 更新 QPS
preQps = currentQps;
// 更新上一次的时间内戳
preTime = System.currentTimeMillis();
// 归零
counter.set(0);
}
自适应代码-改良
问题
上面的自适应算法一般情况下都可以运行的很好。
但是有一种情况会不太好,那就是流量从高峰期到低峰期。
比如凌晨11点是请求高峰期,我们的输出日志概率很低。深夜之后请求数会很少,想达到累计值就会很慢,这个时间段就会导致日志输出很少。
如何解决这个问题呢?
思路
我们可以通过固定时间窗口的方式,来定时调整流量概率。
java 实现
我们初始化一个定时任务,1min 定时更新一次。
public class AutoLogSampleConditionAdaptiveSchedule implements IAutoLogSampleCondition {
private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
/**
* 时间分钟间隔
*/
private static final int TIME_INTERVAL_MINUTES = 5;
/**
* 自适应比率,初始化为 100.全部采集
*/
private volatile int adaptiveRate = 100;
/**
* 上一次的总数
*
* TODO: 这个如何可以让用户可以自定义呢?后续考虑配置从默认的配置文件中读取。
*/
private volatile long preCount;
/**
* 总数,请求计数器
*/
private final AtomicLong counter;
public AutoLogSampleConditionAdaptiveSchedule() {
counter = new AtomicLong(0);
preCount = TIME_INTERVAL_MINUTES * 60 * 100;
//1. 1min 后开始执行
//2. 中间默认 5 分钟更新一次
EXECUTOR_SERVICE.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
updateAdaptiveRate();
}
}, 60, TIME_INTERVAL_MINUTES * 60, TimeUnit.SECONDS);
}
@Override
public boolean sampleCondition(IAutoLogContext context) {
counter.incrementAndGet();
// 直接计算是否满足
return InnerRandomUtil.randomRateCondition(adaptiveRate);
}
}
其中更新概率的逻辑和上面类似:
/**
* 更新自适应的概率
*
* QPS = count / time_interval
*
* 其中时间维度是固定的,所以可以不用考虑时间。
*/
private synchronized void updateAdaptiveRate() {
// preRate * preCount = currentRate * currentCount; 保障采样均衡,服务器压力均衡
// currentRate = (preRate * preCount) / currentCount;
// 更新比率
long currentCount = counter.get();
int newRate = 100;
if(currentCount != 0) {
newRate = (int) ((adaptiveRate * preCount) / currentCount);
newRate = Math.min(100, newRate);
newRate = Math.max(1, newRate);
}
// 更新自适应频率
adaptiveRate = newRate;
// 更新 QPS
preCount = currentCount;
// 归零
counter.set(0);
}
小结
让系统自动化分配资源,是一种非常好的思路,可以让资源利用最大化。
实现起来也不是很困难,实际要根据我们的业务量进行观察和调整。
开源地址
auto-log https://github.com/houbb/auto-log
日志开源组件(六)Adaptive Sampling 自适应采样的更多相关文章
- log4net--不可多得的开源日志记录组件
log4net--不可多得的开源日志记录组件 1 前奏 一直在用log4net日志工具,却没时间写个日志给大家分享一下这个工具,趁最近比较空些,好好分享一下这个工具. 2 说明 Log4net介绍就不 ...
- CVPR2020:基于自适应采样的非局部神经网络鲁棒点云处理(PointASNL)
CVPR2020:基于自适应采样的非局部神经网络鲁棒点云处理(PointASNL) PointASNL: Robust Point Clouds Processing Using Nonlocal N ...
- 日志记录组件[Log4net]详细介绍
转载:http://www.cnblogs.com/liwei6797/archive/2007/04/27/729679.html 因为工作中有要用到Log记录,找到一篇不错的文章,就转了过来. 一 ...
- C#Log4net日志记录组件的使用
一.Log4Net介绍 Log4net是基于.NET开发的一款非常著名的记录日志开源组件.它通过一套XML配置的日志引擎,将日志分不同的等级,分别是:FATAL . ERROR. WARN. INFO ...
- 【广州.NET社区推荐】.NETCore 平台上的日志集成组件 TomatoLog
TomatoLog简介 TomatoLog 是一套在 .NETCore 平台上最简单易用的日志集成组件,具有高度灵活的使用方式,完全可定义配置的可扩展性,使用异步写入,业务完全解耦,客户端的一键安装. ...
- 微信开源组件WCDB漫谈及Demo
代码地址如下:http://www.demodashi.com/demo/12422.html 前言 移动端的数据库选型一直是一个难题,直到前段时间看到了WeMobileDev(微信前端团队)放出了第 ...
- 如何利用阿里视频云开源组件,快速自定义你的H5播放器?
摘要: Aliplayer希望提供一种方便.简单.灵活的机制,让客户能够扩展播放器的功能,并且Aliplayer提供一些组件的基本实现,用户可以基于这些开源的组件实现个性化功能,比如自定义UI和自己A ...
- .NET Core开源组件:后台任务利器之Hangfire 转载 https://www.cnblogs.com/chenug/p/6655636.html
.NET Core开源组件:后台任务利器之Hangfire 一.简述 Hangfire作为一款高人气且容易上手的分布式后台执行服务,支持多种数据库.在.net core的环境中,由Core自带的D ...
- react native 的图表开源组件react-native-chart-android
react-native-chart-android是一个图表开源组件,使用方法可以去这里 由于需要在数据上加上触摸事件,而github上没有说明看源码找了半天才找到下面的解决方法,特此记录一下: 在 ...
- .net 开源组件
文章转自:http://www.cnblogs.com/asxinyu/p/dotnet_opensource_project_3.html 在前2篇文章这些.NET开源项目你知道吗?让.NET开 ...
随机推荐
- 时间不等人,但 Moment.js 可以等你解决时间问题!
前言 一直以来,处理时间和日期的JavaScript库,选用的都是Moment.js.它的API清晰简单,使用方便灵巧,功能还特别齐全. 我是Moment.js的重度使用者.凡是遇到时间和日期的操作, ...
- 让优惠再续一年!SHPC 老客专享
最近云筏君经常收到自家小伙伴发来的关于产品活动的关心慰问,掐指一算,哦,原来是一年一度大家喜闻乐见的剁手节(学名"双十一")马上要来了! 大家都知道,云筏家的产品向来主打高性价比, ...
- Conda 为什么越来越慢?
作者:生信宝典 | 生信宝典公众号原文:https://mp.weixin.qq.com/s/OkOgN4j44MHNt1_noPVpzA Conda 为什么越来越慢? Conda 中包含的软件越来越 ...
- Galaxy 平台下 LEfSe 安装与使用教程
LEfSe (Linear discriminant analysis Effect Size) 是一种用于发现和解释高维度数据生物标识(基因.通路和分类单元等)的分析工具,可以进行两个或多个分组的比 ...
- shell编程-发送消息
需求:利用 Linux 自带的 mesg 和 write 工具,编写一个向用户快速发送消息的脚本,输入用户名作为第一个参数,消息内容为第二个参数.脚本需要检测用户是否登录,是否打开消息功能,以及当前发 ...
- docker 对容器中的文件进行编辑
用途 有一些情况下,例如docker安装的redis.nacos.mysql等等,在docker容器中的安装未进行文件的映射,当需要对其进行更改配置信息时,就会遇到这种情况,需要去容器中进行编辑配置文 ...
- 01-面试必会-JAVA基础篇
1. Final 有什么用? 展开查看 被 final 修饰的类不可以被继承 被 final 修饰的方法不可以被重写 被 final 修饰的变量不可以被改变, 被 final 修饰不可变的是变量的引用 ...
- SQL Server中的NULL值处理:判断与解决方案
摘要: 在SQL Server数据库中,NULL是表示缺少数据或未知值的特殊标记.处理NULL值是SQL开发人员经常遇到的问题之一.本文将介绍SQL Server中判断和处理NULL值的不同方法,以及 ...
- 一键搞定发布自己Jar到Maven中央仓库
做java 开发那当然离不开jar包管理, 不知何时一直想想封装一个自己的jar包 然后发布到maven中央仓库给别人使用. hhh 我感觉自己写一个jar包工具然后,被很多人使用是一件很牛,很快乐事 ...
- 关于ChatGPT与机器时代的展望
关于 ChatGPT 与机器时代的展望 机器人这一概念,最初不是出自计算机科学家或工程师之手,而是来自于捷克的戏剧家卡雷尔·恰佩克(Karl Capek)在 1920 年编排的一出名为"罗森 ...