线索系统性能优化实践

这篇具有很好参考价值的文章主要介绍了线索系统性能优化实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在京东家居事业部,线索CRM系统扮演着至关重要的角色,它作为构建家居场景核心解决方案集的首要环节,肩负着获客和拓展业务的重要使命。然而,随着业务的不断扩张和市场需求的日益增长,系统原有的架构开始显露出诸多不适应之处,如架构设计不再清晰,代码存在过量冗余,核心的读写接口响应时间长等问题,这些问题严重制约了业务的敏捷性和快速发展。鉴于这一状况,系统的性能优化和调整势在必行,以确保其能够更好地支撑业务的快速发展需求。

系统优化概述

一. 线索提交接口的统一与性能优化

系统优化前存在的问题

1. 新渠道接入周期长,代码冗余,

系统中存在五个主要的线索创建渠道,它们的处理流程高度相似,但是代码却是分散冗余的。每当有新渠道需要接入时,之前的做法都是从已有代码中复制粘贴并做小幅调整,缺乏抽象和封装,导致了代码的高度重复,增加了维护的难度和出错的风险。

比如当时我们为了支持多供应商这个需求,需要对线索分派商家的逻辑进行更改,由于这段逻辑分散在多处,同时由于测试对底层实现的不了解,可能会误认为只需要测试一个渠道就能覆盖基本场景,就有可能导致非必要的线上问题的产生。

2. 性能瓶颈

在线索创建过程中,由于业务的复杂性需要执行10来个子流程以及开发过程中的不规范导致的对线索主数据的不必要的重复更新、重复同步ES等问题,接口性能较慢,tp99将近3000ms。

3. 数据一致性问题

线索创建主数据,分配商家以及匹配到重复规则时需要新增运营回访记录,这些流程都涉及到对数据库的写操作,但是这些写入没有放在同一事务中,导致了某个子流程写入失败时存在数据一致性问题。

优化目标

我们的优化目标是对线索提交流程统一封装和抽象,提高系统的可维护性和扩展性。同时降低新渠道接入的时间成本,提高接口性能和响应,保证接口在复杂情况下的正确性。

优化策略与实施

流程梳理与抽象

我们首先对当前所有渠道的线索创建流程进行了全面的梳理,将线索的创建流程抽象化,并定义出一套标准化的流程模板。具体来说一个线索的创建包括以下流程:

(1) 入参校验

(2) 查询三级渠道

(3) 验证三级渠道开关

(4) 根据入参封装要创建的线索实体

(5) 线索是否重复的规则校验

(6) 数据库保存线索和异构到ES

(7) 读取配置规则以及后续的同步京音系统

(8) 将新线索分配到对应的商家

(9) 短信消息和京麦消息通知商家

(10) 根据线索和分配的商家信息为用户创建装修档案

创建流程拆分

通过分析发现,对于不同渠道的线索创建过程来说,最大的差异点在于流程(1) 和 (2), 对于流程(3)-(10)基本相似。

这些流程虽然在逻辑上紧密相连,但是对于线索创建这一业务来说最核心的流程是流程(6)及之前的流程,至于流程(7)-(10)则是线索创建后的附属操作,这些附属操作涉及到和外部门系统间复杂的交互,占用了大量资源并影响到核心流程的响应速度。

因此我们聚焦于线索创建这一核心流程,和从职责单一的角度考虑,我们将整个线索的常见进行拆分:

第一 核心流程-线索的创建。

第二 线索分配商家以及之后的通知操作

第三 为分配商家后为用户创建对应的装修档案

这三个创建流程通过京东自研消息JMQ进行串联,解耦了线索创建和附属操作的执行。通过异步处理附属操作,附属操作的耗时不会阻塞核心流程的执行,减少了对核心流程的干扰,从而大大提升了系统的响应性和吞吐量。

模板方法设计模式的应用

定义: 模板方法设计模式是一种行为设计模式,它在一个方法中定义了一个算法的骨架,将一些步骤的执行延迟到子类中。这样,子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。

通用类图:

线索系统性能优化实践,性能优化

示例代码:

public abstract class Game {
    // 模板方法,定义算法骨架
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }

    // 需要子类实现的方法
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();
}

public class Cricket extends Game {
    @Override
    void initialize() {
        System.out.println("Cricket Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Cricket Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Cricket Game Finished!");
    }
}

public class Football extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        Game game = new Cricket();
        game.play();

        System.out.println();

        game = new Football();
        game.play();
    }
}


在这个例子中,Game是一个抽象类,定义了游戏的模板方法play()CricketFootball是具体的游戏,它们实现了Game类的抽象方法,以提供各自的游戏初始化、开始和结束的具体实现。

具体到我们系统, 流程1到10是创建线索的骨架抽象和定义。对于骨架中的子流程,我们识别出易变部分(步骤1和2)和 不易变的部分(步骤3至6)。易变部分需要交给子类去实现,不易变部分则需要统一实现。

易变部分抽象

对于入参校验和查询三级渠道这两个流程来说,每个渠道都存在独有的逻辑,比如,心愿单渠道需要校验心愿单类型和来源ID必传,而投放助手渠道则需校验投放单号必传;多阶段订单渠道是通过SKU来查询三级渠道,而市场部渠道则是通过媒体账号ID来查询。

因此我们对于这两个流程定义了抽象方法,并将实现细节交个具体渠道的负责。

不变部分统一处理

对于线索创建流程中的不易变部分,我们实现了统一的处理逻辑,如三级渠道开关验证、线索归集信息封装、重复规则校验、数据库保存以及异构到ES等流程。

同时对于所有需要数据库变更的操作放到一个事务中,保证了写入的同时成功或失败。

工程实践

通过上文介绍, 编码大体实现如下:

//获取三级渠道
protected abstract ChannelThreeDto getChannel(ClueDTO clueDTO);

//前置状态校验
protected abstract boolean preConditionCheck(ClueDTO clueDTO);

public ResultDto<Boolean> submit(ClueDTO clueDTO) {

        //1.前置状态校验
        if (!preConditionCheck(clueDTO)) {
            return ResultDto.getFailedResult(ResultCodeEnum.SERVICE_ERROR.getMsg(), ResultCodeEnum.SERVICE_ERROR.getCode());
        }

        //2.获取三级渠道
        ChannelThreeDto channelThreeDto = getChannel(clueDTO);

        //3.确认渠道开关是否开启
        if (!checkChannel(channelThreeDto)) {
            return ResultDto.getFailedResult(ResultCodeEnum.SERVICE_ERROR.getMsg(), ResultCodeEnum.SERVICE_ERROR.getCode());
        }

        //4.线索重复校验
        Boolean isRepeat = checkClueRepeat(clueDTO, channelThreeDto);
        if (isRepeat) {
            return ResultDto.getFailedResult(ResultCodeEnum.SERVICE_ERROR.getMsg(), ResultCodeEnum.SERVICE_ERROR.getCode());
        }

        //5.封装线索实体对象
        ClueManageDto clueManageDto = buildClueManage(clueDTO, channelThreeDto);

        //6.数据清洗规则检查
        ClueVisitDto clueVisitDto = clueDataWash(clueManageDto, channelThreeDto);
        if (!ObjectUtils.isEmpty(clueVisitDto)) {
            clueManageDto.setClueStatus(ClueStatusEnum.INVALID.getCode());
        }

        //7.数据库保存
        boolean result = saveClueManage(clueManageDto, clueVisitDto);

        //8.发送线索创建通知,执行之后的线索分配商家等操作
        sendClueMessage(clueDistributionDTO);

        return ResultDto.getSuccessResult(result, ResultCodeEnum.SUCCESS.getCode());
    }


优化成果

通过引入模板方法的设计模式、异步拆分以及优化事务管理策略,创建线索的系统架构得到了根本性的改进。 我们不仅提高了代码的复用率,降低了新渠道接入的成本,也极大地提升了系统的可维护性和扩展性。

现在,新渠道的接入变得更加快捷和灵活,从之前新渠道接入耗时6人/天降低到2人/天左右;同时线索创建的响应时间也从之前的3000ms降到现在的250ms左右。

二. 线索核心写接口性能优化实践

背景

在竞争激烈的市场环境中,CRM系统不仅需要准确无误地收集用户的客资信息,更重要的是要实现对这些宝贵信息的快速响应和有效跟进。用户留下联系方式的瞬间,往往是他们对产品或服务兴趣最浓厚的时刻,我们需要快速响应,抢占先机,才有可能增加用户转化为客户的可能性,因此对于核心接口的性能有较高的要求。

但是当前系统在处理线索创建、分配商家,状态变更以及商家反馈等核心流程上存在接口性能不理想的问题,比如商家反馈线索tp99耗时2000ms, 分配商家耗时1500ms。

问题分析

在每个核心流程中,系统会进行两项重要的操作:

1.更新数据库:将业务操作的结果持久化到数据库中。

2.数据同步到ES:将变更的数据同步到两个ES集群中(一个供运营端查询,另一个供商家端查询适用)

传统同步机制是在业务逻辑操作完成后立即进行数据同步。这种同步方式虽然简单直接,但存在几个缺点:

性能瓶颈:同步操作耗时,导致接口响应时间增长,影响用户体验。

复杂度增加:业务逻辑与数据同步逻辑耦合,增加了代码的复杂度和维护难度。

扩展性受限:随着业务增长,同步操作成为系统扩展的瓶颈。

优化方案

针对上述问题,我们采取了一系列措施来优化系统性能,核心策略是将数据同步到ES的过程异步化。

1. 订阅Binlake变更

我们将业务逻辑操作和数据同步到ES的过程分离。业务接口只负责业务逻辑的变更和数据库的更新,而数据同步到ES的操作,通过订阅Binlake变更事件来异步执行。

2. 处理变更消息

通过订阅线索主数据和线索分配商家数据的变更消息,封装接口将线索主数据和分配商家信息同步到ES。值得注意的是,为了避免数据库变更在JMQ中的乱序性以及可能带来的数据被错误覆盖的问题,我们只关注消息中的哪个线索单号发生了变化,而不关注具体的变更细节,通过线索单号反查数据库的方式,将最新的数据同步到ES。

3. 合并更新和统一事务

在原来的线索分配商家以及商家反馈线索接口中,存在对同一个表反复更新并且多次同步ES的问题,通过底层重构,我们把所有的DML操作合并到一个事务中,减少更新次数的同时保证了数据的正确性。

4. 非核心流程异步化

把原来线索反馈商家接口中的非核心流程异步化。在商家反馈线索状态后需要触发回流操作,回流操作本身就是一个非常耗时的操作,经常导致用户反馈接口超时,但是回流本身是用户不关注的,用户只关注他反馈的动作是否完成。因此我们对回流进行异步化,反馈线索接口现在只处理线索状态的更新,回流则是通过发送JMQ消息的方式异步处理来减少用户等待时间。

优化成效

经过优化,线索系统的性能得到了显著提升:

1. 接口响应时间明显缩短

(1) 线索提交 (投放助手渠道):

优化前:(2000-4000ms)

线索系统性能优化实践,性能优化

优化后:(100-300ms

线索系统性能优化实践,性能优化

(2) 线索分配商家接口:

优化前:(1000-2000ms)

线索系统性能优化实践,性能优化

优化后:(100-400ms

线索系统性能优化实践,性能优化

(3) 商家反馈线索接口:

优化前:(1000-3000ms)

线索系统性能优化实践,性能优化

优化后:(30-60ms

线索系统性能优化实践,性能优化

2. 用户体验改善:商家在反馈线索状态时不再遇到超时问题。

3. 架构清晰:业务逻辑与数据同步逻辑解耦,代码更加清晰和容易维护。

4. 扩展性提升:异步化后的数据同步流程为未来的系统扩展提供了更大的空间。

三. 线索列表读接口性能优化实践

优化前的挑战

在运营管理CRM系统的实践中,线索列表的查询功能是不可或缺的一环,它支持基于复杂组合条件对线索数据进行精细筛选。然而,在当前的系统实现中,线索列表页面需要展示每页50条或100条线索数据时,接口性能表现并不理想:响应时间普遍超过2000毫秒,有时甚至延迟至6000毫秒。这一性能瓶颈已经引起了用户的广泛关注和较为严重的负面反馈。

优化策略

通过对接口的分析,接口性能瓶颈主要来源于以下几个方面:

1.多次ES查询: 先根据搜索条件查询一次ES获取基础数据后,再循环遍历列表,对每个线索再查询两次ES来获取线索的手动及自动分配商家数量。

2.频繁的RPC调用:循环遍历线索列表为每个用户进行RPC调用以获取用户昵称。

3.过多的远程调用:ES查询和获取用户昵称都是调用服务端服务。

针对这些拖慢接口性能的瓶颈点,我们采取下列优化措施:

1.减少远程调用: 我们将线索运营端多次请求服务端的过程调整成单次调用。查询逻辑都下沉到服务端,由服务端查询所有字段,运营端只需要调用一次,从而显著减少了网络延迟。

2.聚合查询优化:我们利用ES的Aggregation聚合API,一次查询获取当前分页内所有线索的手动分配和自动分配商家数量,减少了多次查询的性能损耗。

代码部分实现:

BoolQueryBuilder query = QueryBuilders.boolQuery();
//线索单号列表过滤
query.filter(QueryBuilders.termsQuery("clueNo", clueIds));

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.trackTotalHits(true);
searchSourceBuilder.query(query);

IncludeExclude includeExclude = new IncludeExclude(new String[]{"1"}, null);
//按照分配类型聚合1
AggregationBuilder aggregation2 = AggregationBuilders.terms("distributionType").field("distributionType").includeExclude(includeExclude);
//按照线索单号聚合2
AggregationBuilder aggregation1 = AggregationBuilders.terms("clueNo").size(100).field("clueNo").subAggregation(aggregation2);
searchSourceBuilder.aggregation(aggregation1);

SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(vendorClueESIndexName);
searchRequest.source(searchSourceBuilder);

SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


1.合理使用缓存:针对用户昵称变动频率低的特点,我们引入了缓存机制, 首次RPC查询用户的昵称成功后对结果进行缓存,再次请求时直接从缓存获取昵称,减少RPC次数。

2.并行: 在处理线索列表填充手动分配和自动分配商家数量以及用户昵称的过程中,我们使用parallelStream()并行流技术,从而加快数据处理速度。

通过以上优化方案, 对于查询100条线索需要的查询次数:

优化前: 1次ES查询列表 + 200次ES查询分配商家数量 + 100次RPC

优化后: 1次ES查询列表 + 1次ES查询商家分配数量 + 100次RPC(有缓存下会减少次数)

优化成果

响应时间缩短

优化前:

线索系统性能优化实践,性能优化

优化后(250ms以下):

线索系统性能优化实践,性能优化

总结与展望

通过对线索系统的深度优化,我们不仅解决了线索系统在核心流程中的性能瓶颈,也为系统的长期健康发展奠定了基础。这一实践表明,适时地对系统架构进行优化,能够有效提升系统的性能和可维护性,进而支持业务的快速增长和变化。在未来,我们将继续追踪新的技术趋势和业务需求,不断优化我们的系统,确保它们能够支撑起日益增长的业务挑战。

作者:京东零售 贾攀

来源:京东云开发者社区 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-824260.html

到了这里,关于线索系统性能优化实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React组件性能优化实践

    React组件性能优化的核心是减少渲染真实DOM节点的频率,减少 Virtual DOM比对的频率。 在组件中为 window注册的全局事件,以及定时器,在组件卸载前要清理掉,防止组件卸载后继续执行影响应用性能。 需求:开启定时器,然后卸载组件,查看组件中的定时器是否还在运行。 什

    2024年02月14日
    浏览(38)
  • XxlJob深度性能优化实践

    天画项目的数据工厂目前在与xxl-job对接自动化数据生成任务,另外我司也在使用该组件做业务,所以想深入了解下XxlJob。在跟进了社区的github等仓库issue发现开发迭代停滞了一段时间,思来想去准备开个下游分支做一些性能优化和特性开发等,于是fork了下源码,将其作为天画

    2024年02月21日
    浏览(43)
  • Andriod开发性能优化实践

    内存优化 在Android开发中,有一些实践可以帮助进行内存优化,以减少应用程序的内存占用和提高性能。以下是一些常见的内存优化实践: 使用合适的数据结构和集合:选择合适的数据结构和集合来存储和操作数据,以减少内存占用。例如,使用SparseArray代替HashMap,使用Arr

    2024年02月15日
    浏览(43)
  • ES性能优化最佳实践- 检索性能提升30倍!

            Elasticsearch是被广泛使用的搜索引擎技术,它的应用领域远不止搜索引擎,还包括日志分析、实时数据监控、内容推荐、电子商务平台、企业级搜索解决方案以及许多其他领域。其强大的全文搜索、实时索引、分布式性能和丰富的插件生态系统使其成为了许多不同

    2024年02月08日
    浏览(52)
  • 【万字长文】前端性能优化实践

    从一个假死页面引发的思考: 作为前端开发,除了要攻克页面难点,也要有更深的自我目标,性能优化是自我提升中很重要的一环; 在前端开发中,会偶遇到页面假死的现象, 是因为当js有大量计算时,会造成 UI 阻塞,出现界面卡顿、掉帧等情况,严重时会出现页面卡死的

    2024年02月05日
    浏览(55)
  • Vue 项目性能优化 — 实践指南

    Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题,所以我们仍然需要去关注 Vue 项目性能方面的优化,

    2024年02月11日
    浏览(50)
  • Flutter性能监控与优化实践

    Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的,可以用一套代码同时构建Android和iOS应用,性能可以达到原生应用一样的

    2024年02月07日
    浏览(40)
  • Flutter性能优化实践 —— UI篇

    _clickable = false; } if (vCode.isEmpty || vCode.length 6) { _clickable = false; } if (password.isEmpty || password.length 6) { _clickable = false; } setState(() { }); } MyButton( onPressed: _clickable ? _register : null, text: ‘注册’, ) 其实这里可以优化一下。因为现在的每次输入都必定刷新,我们可以在 _clickable 参数有变化

    2024年04月27日
    浏览(31)
  • 开源 Serverless 框架 Laf 性能优化实践

    Laf 是一个完全开源的 Serverless 框架,Laf 的 Node.js 运行时容器 (以下简称为 Runtime ) 是 Laf 的 函数执行环境 ,依托于 Express.js 框架。采用容器进程常驻的方式,每一个应用对应于一个或多个容器 (弹性伸缩下),底层使用了 Node.js 的 vm 模块,使用 MongoDB 的 watch() 方法来监听函数

    2024年02月04日
    浏览(41)
  • Elasticsearch性能优化:实战策略与最佳实践

    -在数据密集型的应用场景中,Elasticsearch作为一个强大的搜索和分析引擎,能够提供快速的搜索能力和处理大规模数据的能力。然而,随着数据量的增长和查询需求的复杂化,对Elasticsearch的性能优化成为了维护高效、稳定服务的重要任务。本文将深入探讨Elasticsearch的优化策略

    2024年04月23日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包