首页 > 其他分享 >ZCube:在我的优惠券中的落地实践 | 京东云技术团队

ZCube:在我的优惠券中的落地实践 | 京东云技术团队

时间:2024-01-02 14:04:09浏览次数:31  
标签:优惠券 ZCube tranName result jd import 京东 com logger

前言

我的优惠券作为营销玩法的一种运营工具,在营销活跃场中起到很至关重要的作用。如何更加高效的赋能业务,助理业务发展,灵活扩展业务,是我们一直追求和思考的方向

一、背景

1.1 现状

营销中台作为券的“供应链端”,控制券的所有类型。
我的优惠券作为工具,提供用户已有优惠券的展示列表,不同类型的券利益点不同,运营会提供各自展示规则。
谋略作为用户触达方,为了提高券的核销率,会对用户做过期提醒push,同时触达文案要求跟券的营销文案一致。

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_压测

1.2 挑战点

  • 1、营销中台每次新增券类型,都需要运营指定营销文案后,由研发硬编码实现。能不能支持业务运营人员根据需求灵活扩展,动态配置营销文案,并且能够及时生效呢?
  • 2、消息中心的push提醒文案需要跟营销展示文案一致,那就由业务侧研发硬编码实现一套,消息中心侧实现一套,并且还得保证两处的规则逻辑一致才可以。
    能不能将这种相同的规则抽取出来,以订阅的方式下发到订阅者上去,既保证规则的唯一性,也能够做到规则共享?

二、解决方案

鉴于上述场景的痛点,我们接入ZCube平台来解决

2.1 接入ZCube

官网地址:https://zcube.jr.jd.com文章介绍:《ZCube:会员权益体系规则引擎原理介绍 【一】

首先,在ZCube平台接入我的优惠券应用

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_压测_02

搭建优惠券运营玩法规则

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_压测_03

展示逻辑映射规则

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_json_04

发布知识包

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_ci_05

2.2 应用系统接入SDK

maven坐标依赖

<!-- 规则引擎客户端SDK -->
<dependency>
    <groupId>com.jd.jdt.rule.core</groupId>
    <artifactId>rule-core-client-spring</artifactId>
    <version>1.0.1-SNAPSHOT</version>
</dependency>

properties参数配置

rule.env=prod
rule.appName=jrm_member_center
rule.secret=xxx
rule.packages=xxx

spring xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
       default-lazy-init="false">

    <bean id="ruleExecuteService" class="com.jd.jdt.rule.core.client.service.impl.RuleExecuteServiceImpl">
        <property name="knowledgeCacheService" ref="knowledgeCachePushService"/>
    </bean>
    <bean id="knowledgeCachePushService" class="com.jd.jdt.rule.core.client.service.impl.KnowledgeCachePushService">
        <property name="cloudFileInnerService" ref="cloudFileInnerService"/>
    </bean>
    <bean id="cloudFileInnerService" class="com.jd.jdt.rule.core.client.service.impl.CloudFileInnerService">
        <constructor-arg index="0" value="#{frozenParamBean['rule.env']}"/>
    </bean>
    <bean id="ruleClient" class="com.jd.jdt.rule.core.client.etcd.RuleClient">
        <constructor-arg index="0" value="#{frozenParamBean['rule.appName']}"/>
        <constructor-arg index="1" value="#{frozenParamBean['rule.env']}"/>
        <constructor-arg index="2" value="#{frozenParamBean['rule.packages']}"/>
        <constructor-arg index="3" ref="knowledgeCachePushService"/>
    </bean>
</beans>

api调用

RuleExecutionResult ruleExecutionResult = ruleExecuteService.fireRules(knowPackageName, param);

2.3 AB方式灰度上线

为了保证现有功能的稳定,我们采用AB方式灰度上线,配置在白名单内的用户走规则引擎执行的逻辑,否则走原硬编码逻辑。

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_压测_06

三、结果展示

C端页面展示

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_json_07

SGM执行方法监控,tp99基本在1ms,cpu及内存较稳定,对系统原业务逻辑基本无影响

ZCube:在我的优惠券中的落地实践 | 京东云技术团队_json_08


ZCube:在我的优惠券中的落地实践 | 京东云技术团队_ci_09

四、改造后的优势

1、营销中台新增品后,只需要在策略中心可视化配置,0代码
2、策略规则支持热部署,发布审批即生效
3、业务规则以知识库形式存储,所见即所得,便于业务侧优惠券资源治理

五、压测

5.1 压测对象


--

描述

备注

1

压测案例项目

SDK客户端示例项目

fin-rule-client-example

2

压测机器

用户持有优惠券接口

接口:com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade 别名:fin-rule-client-example

3

执行知识包01 exePackage01 执行知识包03 exePackage03 执行aviator01 exeRule03

SDK客户端示例项目

fin-rule-client-example

4

压测机器

指定预发机器

11.248.242.133 100.99.122.24

5.2 压测平台

forcebot 压测平台

压测脚本:


压测方法

压测脚本

1

exePackage01

exePackage01.groovy

2

exePackage03

exePackage03.groovy

3

exeRule03

exeRule03.groovy

exePackage01

package jsf
import com.jd.fastjson.JSON
import com.jd.fastjson.JSONObject
import com.jd.forcebot.engine.TestUtils
import com.jd.forcebot.engine.groovy.Lifecycle
import com.jd.forcebot.engine.groovy.RatePolicy
import com.jd.forcebot.engine.groovy.TestCase
import com.jd.forcebot.engine.groovy.TestSuite
import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily
import com.jdd.test.performance.common.GenericServiceInvoker
import org.slf4j.Logger

@TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD)
class TestQueryRightsCardDetail {
    public final Logger logger = TestUtils.LOGGER;
    public static GenericServiceInvoker genericServiceInvoker;
    public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt");
    static {
        genericServiceInvoker = new GenericServiceInvoker();
        genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example");
    }

    String getPin() {
        return realPinFile.readLine().trim();
    }

    @TestCase( record = false)
    void queryRightsCardDetail() {
        // 真实pin
        String pin = getPin();
        logger.info("pin: {}", pin);

        String tranName = "exePackage01";

        JSONObject param = new JSONObject();
        param.put("pin", pin);
        param.put("clientIP", "127.0.0.1");

        String strParam = JSON.toJSONString(param);




        try {
            TestUtils.transactionBegin(tranName);
            Object result = genericServiceInvoker.invoke("exePackage01",new String[]{"java.lang.Integer","java.lang.Integer"},new Object[]{45,45});
            logger.info("{} result: {}", tranName, result);
            boolean ret = resultCheck(result, tranName);
        } catch (Exception ex) {
            TestUtils.transactionFailure(tranName);
            logger.error(ex.getMessage(), ex);
        }
    }

    boolean resultCheck(Object result, String tranName) {
        if (result == null) {
            logger.error("{} exec error, result is null.", tranName);
            return false;
        }

        if (!(result instanceof Map)) {
            logger.error("{} exec error, result is {}.", tranName, result);
            return false;
        }

        Map<String, Object> resultMap = result;
        logger.info(tranName + " result: {}.", resultMap);

        if (resultMap == null || resultMap.size() == 0) {
            logger.warn("{} exec error, result map size is zero.", tranName);
            TestUtils.transactionFailure(tranName);
            return false;
        }
        TestUtils.transactionSuccess(tranName);
        // logger.info("{} exec succeed, result: {}.", tranName, resultMap);
        return true;
    }


}

exePackage03

package jsf
import com.jd.fastjson.JSON
import com.jd.fastjson.JSONObject
import com.jd.forcebot.engine.TestUtils
import com.jd.forcebot.engine.groovy.Lifecycle
import com.jd.forcebot.engine.groovy.RatePolicy
import com.jd.forcebot.engine.groovy.TestCase
import com.jd.forcebot.engine.groovy.TestSuite
import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily
import com.jdd.test.performance.common.GenericServiceInvoker
import org.slf4j.Logger

@TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD)
class TestQueryRightsCardDetail {
    public final Logger logger = TestUtils.LOGGER;
    public static GenericServiceInvoker genericServiceInvoker;
    public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt");
    static {
        genericServiceInvoker = new GenericServiceInvoker();
        genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example");
    }

    String getPin() {
        return realPinFile.readLine().trim();
    }

    @TestCase( record = false)
    void queryRightsCardDetail() {
        // 真实pin
        String pin = getPin();
        logger.info("pin: {}", pin);

        String tranName = "exePackage03";

        JSONObject param = new JSONObject();
        param.put("pin", pin);
        param.put("clientIP", "127.0.0.1");
        param.put("orderType", "ssahhh");
        param.put("bizCode", "qwerrtta");

        String strParam = JSON.toJSONString(param);


        try {
            TestUtils.transactionBegin(tranName);
            Object result = genericServiceInvoker.invoke("exePackage03","com.jd.jdt.rule.engine.core.example.domain.MqVar",strParam);
            logger.info("{} result: {}", tranName, result);
            boolean ret = resultCheck(result, tranName);
        } catch (Exception ex) {
            TestUtils.transactionFailure(tranName);
            logger.error(ex.getMessage(), ex);
        }
    }

    boolean resultCheck(Object result, String tranName) {
        if (result == null) {
            logger.error("{} exec error, result is null.", tranName);
            return false;
        }

        if (!(result instanceof Map)) {
            logger.error("{} exec error, result is {}.", tranName, result);
            return false;
        }

        Map<String, Object> resultMap = result;
        logger.info(tranName + " result: {}.", resultMap);

        if (resultMap == null || resultMap.size() == 0) {
            logger.warn("{} exec error, result map size is zero.", tranName);
            TestUtils.transactionFailure(tranName);
            return false;
        }
        TestUtils.transactionSuccess(tranName);
        // logger.info("{} exec succeed, result: {}.", tranName, resultMap);
        return true;
    }


}

execRule03

package jsf
import com.jd.fastjson.JSON
import com.jd.fastjson.JSONObject
import com.jd.forcebot.engine.TestUtils
import com.jd.forcebot.engine.groovy.Lifecycle
import com.jd.forcebot.engine.groovy.RatePolicy
import com.jd.forcebot.engine.groovy.TestCase
import com.jd.forcebot.engine.groovy.TestSuite
import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily
import com.jdd.test.performance.common.GenericServiceInvoker
import org.slf4j.Logger

@TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD)
class TestQueryRightsCardDetail {
    public final Logger logger = TestUtils.LOGGER;
    public static GenericServiceInvoker genericServiceInvoker;
    public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt");
    static {
        genericServiceInvoker = new GenericServiceInvoker();
        genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example");
    }

    String getPin() {
        return realPinFile.readLine().trim();
    }

    @TestCase( record = false)
    void queryRightsCardDetail() {
        // 真实pin
        String pin = getPin();
        logger.info("pin: {}", pin);

        String tranName = "execRule03";

        JSONObject params = new JSONObject();
        params.put("orderType","1");
        params.put("orderType","1");
        params.put("bizCode",1);
        params.put("subBizCode",0);
        params.put("merchantCode","200022");
        params.put("subMerchantCode","110597078001");

        JSONObject param = new JSONObject();
        param.put("ruleId", "bt");
        param.put("params", params);

        String strParam = JSON.toJSONString(param);


        try {
            TestUtils.transactionBegin(tranName);
            Object result = genericServiceInvoker.invoke("execRule03","com.jd.jdt.rule.engine.core.example.domain.RuleRequest",strParam);
            logger.info("{} result: {}", tranName, result);
            boolean ret = resultCheck(result, tranName);
        } catch (Exception ex) {
            TestUtils.transactionFailure(tranName);
            logger.error(ex.getMessage(), ex);
        }
    }

    boolean resultCheck(Object result, String tranName) {
        if (result == null) {
            logger.error("{} exec error, result is null.", tranName);
            return false;
        }

        if (!(result instanceof Map)) {
            logger.error("{} exec error, result is {}.", tranName, result);
            return false;
        }

        Map<String, Object> resultMap = result;
        logger.info(tranName + " result: {}.", resultMap);

        if (resultMap == null || resultMap.size() == 0) {
            logger.warn("{} exec error, result map size is zero.", tranName);
            TestUtils.transactionFailure(tranName);
            return false;
        }
        TestUtils.transactionSuccess(tranName);
        // logger.info("{} exec succeed, result: {}.", tranName, resultMap);
        return true;
    }


}

模拟调用:easyone接口调用

5\. 3 压测指标监控

SGM监控:
sgm方法监控

5\. 4 结论

demo


tps峰值

tp99

tp999

成功率

内存使用率

cpu使用率

1

10000

1

1

100%

12.6%

31.6%

2

19500

1

1

100%

12.6%

53.7%

白条规则


tps峰值

tp99

tp999

成功率

内存使用率

cpu使用率

1

2500

1

1

100%

12.5%

9.88%

2

3000

1

1

100%

12.5%

11.1%

3

3500

1

1

100%

12.6%

13.0%

4

4000

1

1

100%

12.5%

13.9%

5

5000

1

1

100%

12.5%

17.9%

6

6300

1

1

100%

12.5%

21.6%

7

8400

1

1

100%

12.5%

26.9%

8

10500

1

1

100%

12.5%

33.7%

9

14500

1

1

100%

12.5%

46.1%

10

19200

1

1

100%

12.5%

59.2%

白条规则 aviator执行


tps峰值

tp99

tp999

成功率

内存使用率

cpu使用率

1

6100

1

1

100%

12.4%

18.4%

2

10500

1

1

100%

12.4%

29.6%

3

20000

1

1

100%

12.5%

51.9%


压测tps指标

执行方式

SGM监控

JDOS实例监控

1

6000

aviator执行

2

6000

package执行

3

10000

aviator执行

4

10000

package执行

5

20000

aviator执行

6

20000

package执行

作者:京东科技 王芳

来源:京东云开发者社区 转载请注明来源

标签:优惠券,ZCube,tranName,result,jd,import,京东,com,logger
From: https://blog.51cto.com/u_15714439/9068947

相关文章

  • 【稳定性】浅谈11.11大促之预案演练 | 京东物流技术团队
    一、预案演练预案演练主要解决的问题是:根据单个系统的应急预案,模拟应用系统的一种或多种故障场景,验证系统的可靠性。1.1、预案演练形式预案演练根据应急预案组织相关的应急组织机构和人员,针对事先假设的异常应急场景,通过模拟实际决策、指挥和技术操作,完成应急响应及处置的过程,从而......
  • 【积微成著】性能测试调优实战与探索(存储模型优化+调用链路分析)| 京东物流技术团队
    一、前言性能测试之于软件系统,是保障其业务承载能力及稳定性的关键措施。以软件系统的能力建设为主线,系统能力设计工作与性能测试工作,既有先后之顺序,亦有相互之影响。以上,在性能测试的场景决策,架构分析、流量分析、压测实施和剖解调优等主要环节中,引发对于系统能力底盘夯实和测试策......
  • 【Python数据爬取课程设计】数据爬取—京东手机品牌信息数据爬取和数据分析与可视化
    一、选题的背景随着互联网的快速发展,大数据已经成为各行各业决策的重要依据。在电商领域,京东作为国内领先的电商平台,积累了大量的用户购买数据。这些数据中蕴含着丰富的品牌信息,对于手机品牌来说,分析这些数据可以帮助他们更好地了解市场趋势、消费者需求以及竞品情况。然而,目......
  • 浅析RobotFramework工具的使用 | 京东物流技术团队
    1简介最近几年越来越多的公司都开始进行自动化测试的设计和布局了,自动化,顾名思义就是把以人为驱动的测试行为转化为机器执行的一种过程,并经常用于回归测试中,市面上也存在很多开源的自动化测试的工具和理论知识,今天我要说的是RobotFramework这个工具;我也是在偶然的机会中接触到......
  • 日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队
    前言随着互联网和大数据的迅猛发展,分布式日志系统和日志分析系统已广泛应用,几乎所有应用程序都使用各种日志框架记录程序运行信息。因此,作为工程师,了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影响,但没有日志的应用程序是不完整的,甚至可以说是有缺陷的......
  • java浅拷贝BeanUtils.copyProperties引发的RPC异常 | 京东物流技术团队
    背景近期参与了一个攻坚项目,前期因为其他流程原因,测试时间已经耽搁了好几天了,本以为已经解决了卡点,后续流程应该顺顺利利的,没想到人在地铁上,bug从咚咚来~没有任何修改的服务接口,抛出异常:java.lang.ClassCastException:java.util.HashMapcannotbecasttocn.xxx.xxx.xxx.xxx.Ba......
  • java浅拷贝BeanUtils.copyProperties引发的RPC异常 | 京东物流技术团队
    背景近期参与了一个攻坚项目,前期因为其他流程原因,测试时间已经耽搁了好几天了,本以为已经解决了卡点,后续流程应该顺顺利利的,没想到人在地铁上,bug从咚咚来~没有任何修改的服务接口,抛出异常:java.lang.ClassCastException:java.util.HashMapcannotbecasttocn.xxx.xxx.xxx.xx......
  • 性能翻倍!京东亿级体量小程序优化实践
    一、前言小程序性能是指小程序在微信APP或者其他宿主APP中加载和呈现的速度,以及小程序对用户交互的响应程度。性能欠缺的小程序渲染和响应速度较慢,甚至会出现无法正常打开小程序的情况,在不同程度上极大地影响了用户体验,从而导致用户流失。京东购物小程序随着更多业务不断的更新......
  • 决定了,今日起开始准备弃用京东JD
    估计京东是为了节约开支,然后开始大比例的把快递物流业务进行外包了,这直接导致服务质量的直线下滑,10多年前我选择弃用当当网而选择京东JD就是因为当时当地的当当网快递是用沈阳晚报的快递上门的,快递员连POS机都不会用,场面十分的尴尬,谁又能想到10多年后的今天,京东也开始用外包快递了......
  • HBase深度历险 | 京东物流技术团队
    简介HBase的全称是HadoopDatabase,是一个分布式的,可扩展,面向列簇的数据库,是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。本文会像剥洋葱一样,层层剥开她的心。特点首先我们看一下hbase有哪些特点:•高性能基于LSM树的数据结构设计,保证了顺序写,并且......