P2P平台标的融资金额的超标控制

运营得不错的P2P平台经常遇到的一个问题是,在标的开始投标的时候出现抢标的情况,有点像电商平台的秒杀,并发量很大,标的总额经常会超标,比如借款人只要100W,但是会员投标金额累加起来会超过100W,这时候就很尴尬了。解决这个问题有很多种方法,今天我们来探讨一下基于Redis的乐观锁的概念下的解决方案。
关于Redis事务的详细介绍,这里就不再赘述了,网上有很多。Redis的事务处理机制中,有一个命令叫“watch”,该命令用于对一个key的监视,在事务处理之前,先把key值监视起来,在事务提交的时候,如果这个key的值被修改过,则事务执行失败。利用该特性,可以实现乐观锁的业务。unwatch命令取消对key的监视,multi命令开始一个事务,exec提交事务,事务提交后,将会关闭所有key的监视。

在投标业务中,我们先检查一下标的剩余可投金额,如果足够,那就继续投资,如果不够,就投资失败。我们用100个线程来模拟投标动作,代码如下:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 40, 3,  
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(40),  
                new ThreadPoolExecutor.DiscardOldestPolicy());  

for (int i = 1; i < 50; i++) {
threadPool.execute(new Thread1(i));
}

具体的投标动作在线程中进行,在线程里,我们用线程序号来初始化一个可投金额,后面按投资2元起,逐个扣减,直到扣完为止。

package com.fire8.redis.test;
import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
 * 这个thread 用来并发往redis里增加数据
 * @author lixg
 */
public class Thread1 implements Runnable {
	private int index=1;
	public Thread1(int i)
	{
		index = i;
	}
	@Override
	public void run() {
		Jedis jedis = new Jedis("127.0.0.1");
		String value = String.valueOf(index);//获取线程序号,用来初始化标的可投余额
		System.out.println("thread ["+index+"]init "+value);
		if(!jedis.exists("item.1") && index >9)//查看缓存里有没有
		{
			long v = jedis.hsetnx("item.1", "amount", value);//没有则放进去
			System.err.println("thread ["+index+"]set "+v);//如果1,表示放成功了,0表示没放成功; 检查看是哪个线程放进去的
		}
		String string = jedis.hget("item.1", "amount");
		System.out.println("thread ["+index+"]get:"+string);//其他线程取出来看看是多少
		
		while (true) {
			jedis.watch("item.1");//监视这个缓存
			string = jedis.hget("item.1", "amount");
			System.out.println("thread ["+index+"]watch:"+string);//取出值来计算
			long amount = Long.valueOf(string);
			if(amount-2<0)//如果可投金额不足,就返回
			{
				jedis.unwatch();//取消监视
				System. err.println( "thread ["+index+"] 标的可投金额不足");
				//jedis.lpush("fail", String.format("%02d", index));//记录到失败列表
				jedis.zadd("fail", index, String.format("%02d", index));
				break;
			}
			Transaction transaction = jedis.multi();//如果可投金额足够,则开启事务
			transaction.hincrBy("item.1", "amount", -2);//扣减剩余金额
			
			List<Object> result = transaction.exec();//执行事务
			if (result == null || result.isEmpty()) {
			     System. err.println( "thread ["+index+"]事务执行失败,说明剩余可投金额被别人修改了");//事务执行失败,说明剩余可投金额被别人修改了
			     //如果没成功,则再来一次
			     continue;
			}
			 //能跑到这里,说明事务执行成功,剩余金额扣减了
			for (Object rt : result) {
				long aa = (Long)rt;
			     System.err.println("-----------------------------thread ["+index+"]update success ,amount:"+rt.toString());//打印剩作数量 
			     jedis.zadd("success", aa, aa+":"+String.format("%02d", index));//将成功的,记录到一个有序列表里
				//jedis.lpush("success", String.format("%02d", index) +":"+rt.toString());
			}
			break;
		}
		jedis.disconnect();
		jedis.close();
	}
}


执行的结果大致如下:

thread [47]init 47
thread [48]init 48
thread [47]get:43
thread [47]watch:43
thread [48]get:43
thread [49]init 49
thread [48]watch:43
thread [49]get:43
thread [49]watch:43
thread [43]事务执行失败,说明剩余可投金额被别人修改了
thread [49]事务执行失败,说明剩余可投金额被别人修改了
thread [47]事务执行失败,说明剩余可投金额被别人修改了
thread [1]事务执行失败,说明剩余可投金额被别人修改了
thread [44]事务执行失败,说明剩余可投金额被别人修改了
thread [46]事务执行失败,说明剩余可投金额被别人修改了
thread [48]事务执行失败,说明剩余可投金额被别人修改了
-----------------------------thread [42]update success ,amount:41
thread [43]watch:41
thread [45]事务执行失败,说明剩余可投金额被别人修改了
thread [47]watch:41
thread [1]watch:41
thread [44]watch:41
thread [49]watch:41
thread [46]watch:41
-----------------------------thread [43]update success ,amount:39
thread [47]事务执行失败,说明剩余可投金额被别人修改了
success count:21----fails count:28

代码的注释已经详细说明了整个业务逻辑,我在这里抛个砖,欢迎大家拍砖

关于P2P平台设计相关的总结,后续我会再发表一些上来讨论


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值