分布式中间件-Redis(抢红包系统)

整体业务模块:
1,发红包模块:处理发红包的逻辑业务;
2,抢红包模块:分成点红包和拆红包模块;
3,数据库模块:发红包记录,抢红包记录,红包详情;
4,redis模块:缓存红包个数和金额;

第一步先构建数据库:创建3个表分别是
发红包记录表:主键,用户id,红包总金额,人数,全局唯一标识串,是否有效(1是是,0是否,默认是1),创建时间;
抢红包记录表:主键,红包记录id,每个红包随机金额,是否有效,创建时间;
红包详情表:主键,用户id,红包全局唯一标识串,抢红包时间,抢红包的金额,是否有效;
第二步搭建开发环境,利用mybatis的逆向工程生成表对应的实体类,操作数据的mapper和配置文件mapper.xml;

逆向工程:由于插件问题,idea一直报错,查看pom文件发现插件mybatis-generator插件出问题。找不到这个插件。换了版本依然如此,以前写的好好的逆向工程项目突然也运行不了了,可能是网络原因。使用阿里镜像依然如此。。最后使用eclipse 安装插件mybatis-generator插件 ,help–Eclipse Marketplace–搜索mybatis generator 安装插件,安装成功重启后,可以在new -others可以看到mybatis generator configuration file,这样说明插件安装成功(然后配置generatorConfig.xml文件,右键运行);

注意:WARNING: Project src does not exist Mybatis逆向工程;
与idea不同 generatorConfig.xml文件中对应的targetProject填写项目名称,不要添加任何路径信息

改写为

表中自增ID,这个时候领域模型如果想要获取该ID的值,就需要在相应的mapper文件中添加
<insert id=“insertSelective” parameterType=“com.guo.entity.RedRecord” useGeneratedKeys=“true” keyProperty=“id”

这时候如果想继续使用idea 就可以把生成的文件copy到idea项目中即可;
使用lombok:方便编辑模板代码 @Data 使用注解,省了Getter和Setter代码的编写。。(idea首先需要安装支持lombok的插件file–settings–plugins 找到lombok安装 ,pom文件导入依赖就可以使用了;注意枚举类不能使用@Data注解 使用@Getter注解就好;

红包生成使用二倍均值算法:红包的总金额,发放的人数
public class RedPacketUtil {

public static List<Integer> divideRedPacket(Integer totalAmount,Integer totalPeopleNum){

    List<Integer> amountList = new ArrayList<>();
    if(totalAmount>0&&totalPeopleNum>0){
        Integer restAmount = totalAmount;
        Integer restPeopleNum = totalPeopleNum;
        Random random = new Random();
        //循环产生,假如10个人 循环9次,最后一个拿到剩余全部金额
        for(int i =0; i<totalPeopleNum-1;i++){
            //随机金额;二倍均值法 总金额/人数乘以2,下边这么写是左闭右开,表示随机数是[1,人均金额的2倍),红包的单个值不会超过人均金额的两倍;
            int amount = random.nextInt(restAmount/restPeopleNum*2-1)+1;
            restAmount-=amount;
            restPeopleNum--;
            //每次随机生成的金额
            amountList.add(amount);
        }
        //剩余的金额
        amountList.add(restAmount);
    }
    return amountList;
}

发红包与抢红包的业务逻辑
发红包:用户输入金额与个数,确定输入, 后台验证参数,如果正确生成红包标识串,使用红包的工具类生成随机金额的列表,产生的红包金额列表和红包个数放入到redis缓存系统中,发红包的明细表,红包记录传入数据库;

    @Override
    public String handOut(RedPacketForm form) throws Exception {
        if(form.getTotal()>0&&form.getAmount()>0){
            List<Integer> list = RedPacketUtil.divideRedPacket(form.getAmount(),form.getTotal());
            //使用时间戳作为红包标识字符串
            String timeStamp = String.valueOf(System.nanoTime());
            //生成一个随机金额列表的key
            String redId = new StringBuffer(keyPrefix).append(form.getUserId()).append(":").append(timeStamp).toString();
            //将随机金额列表放入缓存中
            redisTemplate.opsForList().leftPushAll(redId,list);
            String redTotalKey = redId+":total";
            redisTemplate.opsForValue().set(redTotalKey,form.getTotal());
            redService.recordRedPacket(form,redId,list);
            return redId;
        }else{
            throw new Exception("系统异常,参数不合法!");
        }
    }

@Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。如果不加这个参数那么只有运行时异常才会回滚,非运行异常就是IO,SQLException,以及自定义的异常;
  Checked exception: 继承自 Exception 类是 checked exception。代码需要处理 API 抛出的 checked exception,要么用 catch 语句,要么直接用 throws 语句抛出去。不处理编译都通过;
Unchecked exception: 也称 RuntimeException,它也是继承自 Exception。但所有 RuntimeException 的子类都有个特点,就是代码不需要处理它们的异常也能通过编译,所以它们称作 unchecked exception;
抢红包
抢红包细分两个逻辑 点红包 与 拆红包;
点红包就是查询缓存系统redis中红包的个数,有过个数大于0;说明存在红包 ,进行拆红包逻辑;否则就是红包被抢完了;
拆红包逻辑:先判断当前用户是否抢过红包,查询缓存,如果获取结果不为null说明抢过了,如果抢过了,则直接返回红包金额,没有抢过就执行点红包逻辑 ,从小红包中取出一个,然后更新缓存系统红包的个数减1,并将抢到红包的账号金额信息记录在数据库中;将当前抢到红包的用户设置到缓存系统中,表示当前用户已经抢过红包了(下次再拆就提示就不是null了,直接犯浑红包金额);

//判断缓冲中红包的个数;
  private Boolean click(String redId){
        String RedTotalKey = redId+":total";
        Object total = redisTemplate.opsForValue().get(RedTotalKey);
        if(total!=null&&Integer.valueOf(total.toString())>0){
            return true;
        }
        return false;
    }

抢红包
多线程问题:如果同一个用户多次发送请求,点红包。。有可能出现多次请求,查询红包个数>0;后续进行拆红包逻辑,没有抢过红包,缓存列别可能同时弹出多个小红包响应多个请求,造成一个人抢到多个红包;
解决: 使用redis.setIfAbsent()方法(对应redis的setNX)对拆红包添加分布式锁;根据redId和用户Id 生成锁的键;。同一用户多次请求拆红包的时候一次只能请求一个操作,只有第一次进行拆红包逻辑,再次请求缓存中已经存在数据,直接返回红包金额;这里加锁是为了防止一个一个用户抢多个红包问题;高并发会产生大量的key锁,要对key锁设置过期时间;

    public BigDecimal rob(Integer userId, String redId) {
        Object obj = redisTemplate.opsForValue().get(redId+userId+":rob");
        if(obj!=null) {
            return new BigDecimal(obj.toString());
        }
        boolean res = click(redId);
        if(res){
            final String keyLock = redId+userId+"lock";
            boolean lock = redisTemplate.opsForValue().setIfAbsent(keyLock,redId);
            redisTemplate.expire(keyLock,24L,TimeUnit.HOURS);
            if(lock){
                Object value = redisTemplate.opsForList().rightPop(redId);
                if(value!=null){
                    String redTotalKey = redId+":total";
                    Integer currTotal = redisTemplate.opsForValue().get(redTotalKey)!=null?
                            (Integer)redisTemplate.opsForValue().get(redTotalKey) :0;
                    redisTemplate.opsForValue().set(redTotalKey,currTotal-1);

                    BigDecimal result = new BigDecimal(value.toString()).divide(new BigDecimal(100));

                    redService.recordRobRedPocket(userId,redId,new BigDecimal(value.toString()));

                    redisTemplate.opsForValue().set(redId+userId+":rob",result,24, TimeUnit.HOURS);
                    log.info("当前用户抢到红包了:userId={} key={} 金额={}",userId,redId,result);
                    return result;
                }
            }
        }
        return null;
    }

写入数据库操作可以设置为异步的:
@EnableAysnc注解在类上@Async注解在异步执行的方法上;

要使用分布式scrapy-redis进行爬取豆瓣电影top250,需要进行以下步骤: 1. 安装Scrapy和Scrapy-Redis 首先需要安装Scrapy和Scrapy-Redis,可以使用以下命令进行安装: ``` pip install scrapy pip install scrapy-redis ``` 2. 配置Redis数据库 配置Redis数据库,用于存储爬虫的请求队列和爬取结果。可以在本地搭建一个Redis服务器,也可以使用云服务提供商提供的Redis服务。 3. 创建Scrapy项目 使用命令行工具创建Scrapy项目: ``` scrapy startproject douban_top250 ``` 4. 配置Scrapy-Redis 在settings.py文件中添加以下配置信息: ``` # 开启Redis调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 去重过滤器,使用Redis数据库进行去重 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 将爬取到的数据存储到Redis数据库中 ITEM_PIPELINES = { "scrapy_redis.pipelines.RedisPipeline": 300 } # Redis数据库连接配置 REDIS_HOST = 'localhost' REDIS_PORT = 6379 ``` 5. 创建Spider 在spiders目录下创建一个名为douban.py的Spider文件,编写爬取代码,例如: ``` from scrapy_redis.spiders import RedisSpider class DoubanSpider(RedisSpider): name = 'douban' allowed_domains = ['movie.douban.com'] redis_key = 'douban:start_urls' def parse(self, response): # 爬取代码 ``` 6. 启动爬虫 在命令行中启动爬虫: ``` scrapy crawl douban ``` 爬虫会自动将请求放入Redis数据库中,多个爬虫节点可以同时从Redis数据库中获取请求进行爬取,实现分布式爬取。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值