2024.07.07 day8

情景

对于大营销项目,目前已经处理好开发环境和持久化数据的问题,接下来要根据梳理出的业务流程一步步的实现,首先要实现的是抽奖策略的装配
![[Pasted image 20240707211309.png]]

任务

实现抽奖策略的装配,这个过程会需要用到数据库查询,策略值计算,redis map数据存储。

通过策略ID检索数据库策略配置,根据策略配置的概率计算出占比值,初始化到redis服务中,完成简单的随机抽奖

策略概率装配处理

Target: 将抽奖的概率初始化到应用中
原始思路:

两种抽奖算法

空间换时间

将所有可能的数据(0-10000)都存放在redis里作为key,对应的valueawardId,直接在redis中通过key就可以获得对应的奖品id

提前计算好抽奖概率分布,用本地内存guava或者redis存储,抽奖的时候通过生成的随机值在空间内定位,时间复杂度为O(1)

本地内存比redis快,但是redis可以直接解决分布式存储问题,本地内存需要让多台分布式机器都保持数据的同步更新,需要引入配置中心和定时检测的功能。来处理应用启动前/运行中,对活动新增/变更做本地内存做数据加载处理

时间换空间
抽奖的时候生成一个随机值,用随机值和概率范围for循环对比

行动

  1. 了解两种不同的抽奖算法知识
    [[抽奖算法]]

  2. 安装portainer. 密码:fcjvUR2RfcjvUR2R

  3. 使用docker-compose安装redis。一个很傻逼的问题是,redis默认用户名是没有的,也没有默认密码,我一直登陆不上redis-admin,是因为设置了redis-admin的用户名和密码而不是redis的

  4. 在工程中新加上redission包,数据持久层中加上IRedisService接口和RedissionService实现类。

  5. 在application层的config重新配置了RedisClientConfig和RedisClientConfigProperties。

  6. 测试了一下redis连接是否正常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.priska.test;  

    import com.priska.infrastructure.persistent.redis.IRedisService;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.redisson.api.RMap;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;

    import javax.annotation.Resource;

    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ApiTest {

    @Resource
    private IRedisService redisService;

    @Test
    public void test() {
    RMap<Object, Object> map = redisService.getMap("strategy_id_100001");
    map.put(1,101);
    map.put(2,101);
    map.put(3,101);
    map.put(4,102);
    map.put(5,102);
    map.put(6,102);
    map.put(7,103);
    map.put(8,103);
    map.put(9,104);
    map.put(10,105);
    log.info("测试结果: {}", redisService.getFromMap("strategy_id_100001",10),toString());
    }

    }
  7. 这一节实现策略概率装配的主要业务逻辑是在domain层新建strategy业务,在service.armory中实现,一个IStrategyArmory接口,一个实现类。

  8. 在实现业务逻辑之前,发现需要实现策略奖品实体(放在domain.strategy.model.entity中),和一个queryStrategyAwardList(策略服务仓储功能),这个步骤主要是能够从仓库中获取到策略奖品实体/数据(仓库可以是redis,也可以是mysql)

  9. 所以为了实现queryStrategyAwardList函数在基础仓库中实现(infrastructure.repository)

  10. 开始实现业务逻辑

  11. 测试

实现1:策略装配

  1. 根据strategyId查找StrategyAwardList
    • 仓储层实现对数据库的增删查改操作,领域层定义数据库增删查改接口
    • 定义接口:domain--strategy-repository--IStrategyRepository
    • 实现操作: infrastructure--persistent--repository Redis查缓存后查mysql
  2. 根据strategyAwadList中的award_rate,计算最小概率值和总概率的商,用这个结果作为查找表的度量值(千分位或者万分位)。度量值✖概率就是对应奖品在查找表中的元素个数
  3. 生成策略奖品概率查找表
    • 在list中,存放对应奖品的数量,占位越多代表概率越高
    • 乱序查找表并转化为Map
  4. 将Map存放到redis
    • storeStrategyAwardSearchRateTable也是在domain中定义接口,在infrasturcture实现

实现2: 简单抽奖

根据策略id进行抽奖

  1. 先从redis中获取rateRange
  2. 根据rateRange生成一个随机值
  3. 根据该随机值在查找表中查找对应的奖品

知识
根据策略id查值。在DDD架构中,需要用什么值,不用自己实现,在基础层中调dao

若要在Redis可视化界面中只管看到数据,而非乱码,可以打开Redisson配置中注释掉的config.setCodec(new RedisCodec()); ,但是这样会有一个问题,用Redis的String类型存储的Java实例对象无法正确被反序列化,查了网上资料,直把上述代码改成config.setCodec(new JsonJacksonCodec());也就是用Jackson做序列化和反序列化,原生代码有点问题的,这样就可以同时实现解决可视化和Redis中的String值被成功反序列化成实体类这俩问题