作者:京东物流 林群

一、背景

在常规的运维及线上故障响应实践中,我们观察到系统监控指标(System-Level Metrics)的异常波动往往与业务监控指标(Business-Level Metrics)的异常呈现高度相关性。具体而言,当系统级监控指标出现异常时,业务级监控指标在绝大多数情况下亦会表现出异常状态。然而,反之则不然,即业务级监控指标的异常并不总是伴随着系统级监控指标的同步异常。

此种现象导致相关职能团队(包括研发、测试、运营等)在业务异常的感知上存在显著滞后性。具体表现为,业务异常的发现往往依赖于终端用户的事后反馈,而非通过实时监控体系的前置预警。这种滞后性可能意味着在问题被识别之前,业务已遭受大范围的负面影响,或已逼近其服务能力的临界阈值,从而对物流的运营稳定性和用户体验造成严重损害。

二、业务监控方案

通用数据监控流程如下图,从埋点输出数据,再到采集数据通过计算聚合得到各项指标,最后根据指标配置阈值进行告警规则设置和通过各类面板进行指标展示。

在这里插入图片描述

截止到目前集团内部DevOps平台建设过3个通用的业务监控应用,分别是UMP业务监控、PFinder业务监控和泰山业务监控,KA商家服务在这三个平台都有过一些案例实践,下面分别进行介绍。

2.1、UMP业务监控

UMP的业务监控建设的时间最早,现在也已下线。

在这里插入图片描述



但原有已接入的业务监控还在继续运营,比如KA商家服务的一个应用

在这里插入图片描述

关于这个应用基于UMP业务监控的实践场景可以参考《记一次大库大表的治理过程》的4.3章节。

2.2、PFinder业务监控



在快运导单业务流程中,一旦包裹数量超出设定阈值,相关订单便会自动迁移至单独的导单分组,由独立机器集群通过限流机制进行处理。在单子转移过程,基于PFinder的业务监控能力进行了埋点追踪,对事业部包裹数量进行统计,实时监控单位时间内的包裹总数,对触发阈值进行告警机制,提前感知系统压力,及时监控系统指标压力。

监控展示如下:

在这里插入图片描述



告警规则配置:

在这里插入图片描述



告警效果:

在这里插入图片描述

泰山业务监控是当前KA商家服务使用场景和业务覆盖最广的,也是本篇要重点介绍的业务监控平台,下面从统一日志格式、编码实践、数据可视化、业务监控告警和最佳实践分别进行介绍。

二、统一日志

2.1、日志格式

针对KA商家的业务特点和应用场景对日志的输出格式进行了统一规范;

•业务域和业务子域:这两个字段按照集团业务域对每个应用进行定义;

•业务场景:指单据的类型,比如下单、取消、修改和回传等;

•渠道来源:指请求的渠道来源,比如JOS、EDI、物流网关、WMS等;

•商家编码和青龙业主号或事业部编码:这是重点,因为KA商家需要按照事业部或商家维度进行监控

•密度:指一次请求的单量;

•结果(Y/N):Y表示成功、N表示失败(业务层面);

•结果码和结果码描述:指一级结果码,大的分类;

•结果子码和结果子码描述:指二级结果码,细的分类;

•商家单号:指商家下传的唯一值;

•订单号和运单号:指业务成功后京东内部生成的各类单号,非固定字段,由各应用各接口自定义;

|业务域|业务子域|业务场景|渠道来源|商家编码|青龙业主号或事业部编码|密度|结果(Y/N)|结果码|结果码描述|结果子码|结果子码描述|商家单号|订单号|运单号

2.2、举个例子

下面分别举一个业务成功和失败的例子,重点在于ECP、EBU、Y/N。

成功:
|订单域|销售出|下单|50|ECP|EBU|1|Y|200|success|200|success|T20240704000086|ESL1230989|JDV002323
失败:
|订单域|退供出|下单|70|ECP|EBU|1|N|4007|fail|3-01-11020|可销售库存不足|T20240704000086||

三、编码实践

3.1、log4j文件配置

在log4j的xml配置文件里,<Properties>通常都有patternLayout格式化输出的前缀,复用就可以

<property name="patternLayout">%d{yyyy-MM-dd HH:mm:ss.SSS}-%X{PFTID}-%-5p - [%t] %c -%m%n</property>

<Appenders>里添加RollingRandomAccessFile

<RollingRandomAccessFile name="businessFile" fileName="${log_path}/eclp-biz-eclp-isv-business.log"
                         filePattern="${log_path}/eclp-biz-eclp-isv-business-%i.log">
    <PatternLayout charset="UTF-8" pattern="${patternLayout}"/>
    <Policies>
        <SizeBasedTriggeringPolicy size="1GB"/>
    </Policies>
    <DefaultRolloverStrategy max="5"/>
</RollingRandomAccessFile>

<Loggers>里设置AsyncLogger的AppenderRef

<AsyncLogger name="BusinessLogger" level="INFO" additivity="false" includeLocation="false">
    <AppenderRef ref="businessFile"/>
</AsyncLogger>

3.2、业务日志打印

在代码需要业务监控日志埋点的类文件里定义业务日志Logger

/**
 * 业务日志
*/
private static final Logger blogger = LoggerFactory.getLogger("BusinessLogger");

Logger打印日志

blogger.info("|订单域|销售出|下单|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
        order.getSourceChannel(), order.getShopNo(), order.getDepartmentNo(), 1,
        result, code, message, subCode, subMessage,
        order.getIsvUUID(), context.getPin(), soNo);

业务逻辑埋点打印业务日志

protected void doProcess(ProcessorContext processorContext) throws Exception {

    OrderContext context=buildOrderContext(processorContext);

    try {
        String soNo = isvSoReceiveService.transportOrder(context);
        processorContext.setAttachment(IsvProcessorConstant.PRCS_RTN_ORD_TRANSPORT_SO_NO, soNo);
        processorContext.setAttachment(IsvProcessorConstant.PRCS_RTN_SO_IS_REPEAT_ORDER, context.isRepeatOrder());
        processorContext.setAttachment(IsvProcessorConstant.PRCS_RTN_ORD_CREATE_SUCC_MSG, context.getCache(CachedKeyConstants.ORDER_CREATE_SUCCESS_ATTACH_MSG, String.class));
        processorContext.setBizNo1(soNo);
    } catch (Exception e) {
        printBusinessLog(context, e);
        throw e;
    } finally {
        fillLogContext(context, processorContext);
    }

    printBusinessLog(context, null);

}

业务日志打印方法

protected void printBusinessLog(OrderContext context, Exception e) {
    String result = "Y";
    String code = "200";
    String message = "success";
    String subCode = "200";
    String subMessage = "success";
    String soNo = "";

    Order order = context.getOrder();

    if (e == null) {
        soNo = order.getSoNo();
    } else {
        result = "N";
        if (e instanceof JdlOpenException) {
            JdlOpenException jdlOpenException = (JdlOpenException) e;
            code = jdlOpenException.getCode();
            message = jdlOpenException.getMessage();
            subCode = jdlOpenException.getSubCode();
            subMessage = jdlOpenException.getSubMsg();
        } else if (e instanceof SafJosException) {
            SafJosException safJosException = (SafJosException) e;
            code = "4001";
            message = safJosException.getMessage();
            subCode = safJosException.getCode();
            subMessage = safJosException.getZhMsg();
        } else {
            code = "4002";
            message = e.getMessage();
            subCode = "";
            subMessage = "";
        }
    }

    blogger.info("|订单域|销售出|下单|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
            order.getSourceChannel(), order.getShopNo(), order.getDepartmentNo(), 1,
            result, code, message, subCode, subMessage,
            order.getIsvUUID(), context.getPin(), soNo);
}

3.3、日志文件

输出的日志文件

在这里插入图片描述

四、数据可视化

4.1、监控项

按照泰山业务监控配置日志文件各字段取值,对下单的业务监控配置后效果如下:

在这里插入图片描述

4.2、监控大盘

在泰山监控大盘里配置KA商家重点事业部的分钟级监控,每个事业部一个表格,可以直观实时了解各事业部下单情况。

在这里插入图片描述

在这里插入图片描述

下图是针对某个事业部的单独监控大盘配置,成功率和失败数以图形的方式放在左边,可以直观感受到业务变动情况;每一分钟的下单量以表格的形式放在中间,便于准确了解分钟级的单量;最后观测周期内的下单结果放在右边,有助于知悉周期内的异常情况。

在这里插入图片描述



五、业务监控告警

5.1、成功率告警

根据每个事业部的下单规律合理配置告警阈值,比如某个事业部剔除库存不足的业务失败场景后还有其他业务失败,无法一一剔除,故配置成功率连续2次低于50%触发告警。

在这里插入图片描述

5.2、单量突增/突降

由于单个事业部流量不均,在初期单量突增或突降的规则阈值调整过程中频繁触发告警


在这里插入图片描述

在这里插入图片描述



在对某个事业部不断调整后,下单量突降配置如下,通过同比昨日和同比上周来避免单日的流量不均来减少告警触发次数,再根据总量超过一定单量来避免单量偏小情况下的阈值波动过大的场景;

单量突降告警规则配置:

在这里插入图片描述

单量突增告警规则配置:

在这里插入图片描述

六、最佳实践

6.1、商家搬仓

在业务监控初期,GOC大盘的巡检过程中发现A商家的推单成功率极低,平均只有1%是成功的,通过联系前端业务跟商家反馈,商家表示商品正在切仓,待切换完成后会停用旧SKU的下单。

在这里插入图片描述

在这里插入图片描述

下午再次巡检的时候该商家的推单已恢复到正常状态。

在这里插入图片描述



6.2、重复下单

B商家不定时会有大量的下单失败,报错原因都是:重复提交,通过反馈业务咨询商家的下单场景,了解到的该商家是业务从商家侧拿到文件上传至FTP某个目录下,EDI定时拉单,如果其中有个别单子因为某些原因下单失败的话,业务暂无法从单子列表中筛选出来,只能将原文件重复上传让异常单重试下单,这时候已下单成功的单子就会出现“重复提交”的报错信息。

在这里插入图片描述



6.3、库存不足

C商家的下单成功率一直不高,基本都在70%左右,大量的失败原因都是:库存不足,经过抽查部分单子,发现商家会不停的重试,在最后有库存的情况下都会下单成功,同样通过业务了解到商家的下单逻辑是:商家有2B和2C两个仓,商品通过采购入库单进入到2B仓,再通过调拨入到2C仓,所以存在一定的时间差会出现库存不足,待商品调拨到2C仓,经过不断地重试就能下单成功和正常出库。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



在这里插入图片描述



6.4、事业部切换

在大促开门前的某一天,D商家突发严重告警,下单成功率跌至0%且有很大的单量,紧急排查日志发现失败原因是基础数据归属事业部错误,通过业务联系商家,反馈是商家在做事业部切换,当前推单失败的单子待切换后会进行再次推送。

在这里插入图片描述

6.5、商品等级调整

上面的C商家的2B仓,成功率一直在100%,2B业务单量少,因此经过优化后配置的告警阈值是连续2次小于50%,在某一天突发严重告警,同一个单子连续请求了2次,因为库存不足触发了告警规则,业务反馈商家在操作某个商品的等级调整导致原需要出库的商品等级库存不足。


在这里插入图片描述

6.6、外部接口超时

E商家的O2O下运单业务,因为商家用的是腾讯地图GIS信息,需要咱们去转换成内部的GIS信息,在调用腾讯地图的API时会偶发超时,如果多次失败需要研发介入排查是否有系统异常还是日常的超时原因导致的。

在这里插入图片描述

在这里插入图片描述

6.7、OAID校验失败

F商家突发成功率严重告警,经过排查是因为OAID校验失败导致的,OAID是用来校验收货人信息的,推测是C端用户修改了收货人信息但商家系统未及时更新导致推送给物流的OAID校验失败,通过业务联系商家,反馈确实是收货人信息有更新,商家调整系统后重推单子成功下单。


在这里插入图片描述

6.8、揽收时间校验失败

G商家将老单子重推送给物流,所以揽收时间都是在当前时间之前,推单失败。


在这里插入图片描述

在这里插入图片描述

6.9、入参错误

常见的商家入参错误,反馈商家确认商家系统是否有异常情况。

在这里插入图片描述



6.10、上游流量异常

1.6号突发单量下降严重告警,通过总量查看确实在18点\~18点45分期间上游单量下降很多,待触发告警后去排查的时候单量已恢复正常值,联系上游反馈系统正常,总单量无变化,其实这里是留有疑问的,如果系统正常的话是不会出现单量突增和突降的情况。
在这里插入图片描述

在这里插入图片描述

在第二天更晚些时间,此监控告警再一次触发告警阈值,而且在排查和反馈的时候未恢复至正常值,此时上游反馈系统有异常,存在上线情况,正在回滚中。



在这里插入图片描述

可以看到在回滚后上游下发的单量有一个突增,然后恢复正常。

在这里插入图片描述



七、写在最后

在过去的这段日子里,我们全力投入到业务监控能力的构建和实践中。如今,我们欣喜地看到,物流大部分的KA商家都已成功搭建起了独立的业务监控面板。

随着不断优化的告警规则,我们可以第一时间感知并解决商家问题,确保技术和业务的同步。

然而,业务监控只是手段,而非最终目标。我们的真正使命是提升系统的可用率,进而保障商家的使用体验。

作为服务于京东物流KA商家的技术BP,KA商家研发组将继续坚定地走在这条大道上,不断探索与前行,以确保每一位KA商家都能享受到最佳的服务体验。


京东云开发者
3.4k 声望5.4k 粉丝

京东云开发者(Developer of JD Technology)是京东云旗下为AI、云计算、IoT等相关领域开发者提供技术分享交流的平台。