个人网站用什么服务器,做货代哪个网站上好找客户,wordpress 满屏主题,公司logo设计费用一般多少钱先看基本的业务流程 那么我们可以看到整个流程都是一个线程来完成的#xff0c;这样的话耗时还是很长的#xff0c;那么可不可以采用多线程去实现呢#xff1f;
首先我们要思考怎么对业务进行拆分#xff0c;可以想象一个我们去饭店点餐#xff0c;会有前台接待#xff…先看基本的业务流程 那么我们可以看到整个流程都是一个线程来完成的这样的话耗时还是很长的那么可不可以采用多线程去实现呢
首先我们要思考怎么对业务进行拆分可以想象一个我们去饭店点餐会有前台接待询问订单之后将小票传给后厨去做饭这样就会快很多也可以接待更多的客人
也就是说 一个线程负责去读数据库做准备工作另一个线程去实现写操作如下图中所示 确定了我们可以将判断库存和检验一人一单业务抽取出来之后我们在想一下 还能不能优化这个时候我们会发现这两个操作还是在数据库进行的那么mysql的并发本身也是不高的现在我们就要通过另一个性能更好的数据库进行实现就是redis 这样只需要业务进行到校验完成就可以给用户返回下单完成的信息之后在通过另一个线程异步进行扣减库存操作
redis中实现上面两个操作的业务流程如下 由于操作流程较长应该使用lua脚本来保证原子性
将上面的逻辑采用lua脚本进行编写之后程序运行首先判断返回值如果是0就说明用户有下单资格如果是1或者2就说明用户没有资格下单
如果有下单资格就可以将用户id优惠券id和订单id存入一个阻塞队列里面之后异步进行写入数据库操作
整体流程 提供lua脚本代码
-- 1.参数列表
-- 1.1.优惠券id
local voucherId ARGV[1]
-- 1.2.用户id
local userId ARGV[2]
-- 1.3.订单id
local orderId ARGV[3]-- 2.数据key
-- 2.1.库存key
local stockKey seckill:stock: .. voucherId
-- 2.2.订单key
local orderKey seckill:order: .. voucherId-- 3.脚本业务
-- 3.1.判断库存是否充足 get stockKey
if(tonumber(redis.call(get, stockKey)) 0) then-- 3.2.库存不足返回1return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call(sismember, orderKey, userId) 1) then-- 3.3.存在说明是重复下单返回2return 2
end
-- 3.4.扣库存 incrby stockKey -1
redis.call(incrby, stockKey, -1)
-- 3.5.下单保存用户sadd orderKey userId
redis.call(sadd, orderKey, userId)
-- 3.6.发送消息到队列中 XADD stream.orders * k1 v1 k2 v2 ...
redis.call(xadd, stream.orders, *, userId, userId, voucherId, voucherId, id, orderId)
return 0
使用方式 private static final DefaultRedisScriptLong SECKILL_SCRIPT;static {SECKILL_SCRIPT new DefaultRedisScript();SECKILL_SCRIPT.setLocation(new ClassPathResource(seckill.lua));SECKILL_SCRIPT.setResultType(Long.class);}Overridepublic Result seckillVoucher(Long voucherId) {Long userId UserHolder.getUser().getId();long orderId redisIdWorker.nextId(order);// 1.执行lua脚本Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString(), String.valueOf(orderId));int r result.intValue();// 2.判断结果是否为0if (r ! 0) {// 2.1.不为0 代表没有购买资格return Result.fail(r 1 ? 库存不足 : 不能重复下单);}// 3.返回订单idreturn Result.ok(orderId);} redis的流程到此就完结了接下来就是使用阻塞队列存储要进行写操作的信息
阻塞队列的实现方式通常是使用一个先进先出的队列来存储元素同时使用锁来实现线程安全。当队列为空时put()方法会被阻塞直到有元素被添加到队列中当队列满时put()方法同样会被阻塞直到队列中有元素被移除。
阻塞队列通常用于生产者-消费者模型中生产者将元素添加到队列中消费者从队列中取出元素进行处理。通过使用阻塞队列可以避免生产者和消费者之间的直接交互从而简化了代码的设计和维护。
首先我们可以可以使用java自带的阻塞队列实现提供一个样例
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueueInteger queue new ArrayBlockingQueue(10); // 创建一个容量为10的阻塞队列Thread producer new Thread(new Producer(queue));Thread consumer new Thread(new Consumer(queue));producer.start();consumer.start();}
}class Producer implements Runnable {private BlockingQueueInteger queue;public Producer(BlockingQueueInteger queue) {this.queue queue;}Overridepublic void run() {for (int i 0; i 100; i) {try {System.out.println(Producing i);queue.put(i); // 将元素添加到队列中} catch (InterruptedException e) {e.printStackTrace();}}}
}class Consumer implements Runnable {private BlockingQueueInteger queue;public Consumer(BlockingQueueInteger queue) {this.queue queue;}Overridepublic void run() {while (true) {try {Integer item queue.take(); // 从队列中取出元素进行处理System.out.println(Consuming item);} catch (InterruptedException e) {e.printStackTrace();}}}
}当然如果想要性能更好的话我们可以采用消息队列来做