Java-基于redis 分布式锁形式和队列形式实现秒杀架构(配合消息队列)

Java面试技巧:SpringBoot与Redis实战解析 Java作为企业级开发的主流语言,其技术栈的深度与广度直接影响面试表现。SpringBoot通过自动装配机制简化了配置,核心原理涉及@EnableAutoConfiguration注解和条件装配,这种设计模式显著提升了开发效率。Redis作为高性能缓存数据库,在分布式系统中解决并发与数据一致性问题,其跳跃表实现和持久化策略是面试高频考点。结合当前热词SpringBoot和Redis,本文通过典型场景拆解,展示如何从原理层理解技术价值,并应用于电商订单系统等实际业务场景,帮助开发者构建扎实的Java知识体系与 阅读详情

通过redis锁,list 以及配合消息队列实现商品普通下单流程和秒杀的架构设计
代码在这https://github.com/ItsFunny/spring-test/tree/master/spring-test-order-and-stock

测试代码都在测试文件下,若无sql则在dao中更改自己的代码即可,记得修改MQ和redis


更新日志:

  • 2019-03-28 更:

    • 不知道代码能不能运行,我也忘了…-.-,开始的时候是ok的,好像我后来改了点东西过
  • 2019-01-23 更:

    • 今天看原先的代码的时候发现还是有疏忽的地方的:
      • 关于如何避免超卖问题:
        • 原先的做法是通过在业务层的前部分先无锁通过redis判断库存(a)是否足够,如果足够之后会通过redis锁加锁再进行判断库存(a)是否足够,之后会减少,所以这里就是疏忽的地方,无锁检测并不能过滤大部分请求,压力还是会汇总于获取锁的阶段,因而更好的做法是定义一个ThreadNotSafe的变量unsafeStock代替无锁检测,在业务层的上半部就进行--操作,这步操作能过滤大部分请求,之后后端在加锁对真正的stock-- ,更改不知道会什么时候再更改,ide换成idea了,有点不习惯
        • 11:37 好像上面的说法有问题,额,有空重新再看
  • 2018-10-22 更:

    • 忙于吉林长春两地跑,基本没怎么更新,今天更新了秒杀,采用的redis分布式锁实现的
    • 不得不感叹简历10月才开始投真的是太太太晚了啊,大公司的都已经结束了…
    • strategy+handler
      前半段流程是与普通下单流程一样的:生成token->校验token,为了避免写重复的代码,将校验token抽到了aop中
  • 2018-10-23 更:

    • 添加了基于队列的方式,并且重新将order的操作给抽了出来(可以通过MQ,也可以直接DB插入),代码有点凌乱了,没好好整理过,git上已经更新
    • 下一次更新应该会加入MQ,并且尝试使得MQ顺序化
      2018-10-26 更:
      这几天放松了下,代码没怎么敲,明天会加入MQ,不过加入MQ的话需要做成SOA了好像

基本的流程图为:
在这里插入图片描述

关于可能发生的几个问题:
1.重复下单问题:
1):用户不小心点多了,点击立即购买多次
2):网络延迟,用户刷新重复提交多次
3):用户恶意请求
解决方法:
a:前端按钮当点击完毕之后立马将其按钮变灰,防止无意请求
b:在点击生成商品页面的时候生成一个token

话不多说,直接敲代码,先搭建基本的环境吧

这是包结构图:
在这里插入图片描述
再真正开始之前,先说一下大体思路:为了模拟实际,我在test包下先init数据(直接循环插入数据),然后容器启动的时候加载到缓存中,random的方式获取商品信息,至于userid也同样采取random的方式

没有service,所以参数乐观的认为是不可能出错的!!!!实际是100%需要校验的

普通商品

  • 第一个方案,按钮隐藏就不写了,直接写生成token的形式,
  • 第一步,选择某个商品,并且点击了立即购买
	@RequestMapping(value = "/buy")
	public ModelAndView buy()
	{
		Random random = new Random(47);
		int index = random.nextInt(OrderProductTestCache.PRODUCTS.size());
		Product product = OrderProductTestCache.PRODUCTS.get(index);
		ModelAndView modelAndView = new ModelAndView("buy_product");
		// 生成token
		long token = idWorkerService.nextId();
		redisTemplate.opsForValue().set("product_buy_token_" + token, 0 + "",1000);
		modelAndView.addObject("token", token);
		// 随机挑选某个商品去买
		modelAndView.addObject("buyStock", random.nextInt(10));
		modelAndView.addObject("product", product);
		return modelAndView;
	}

生成的token很简单,是通过谷歌id生成器直接生成的
第二步:点击立即购买之后,总得提交订单吧(生成订单),但是在这之前需要进行校验
1)是为了防止重复提交表单
2)提交表单是需要预扣库存的呀,重复来了,都被你买完了咋整

	//展示购买的商品信息
	//预扣库存
	@RequestMapping(value = "/doBuy")
	public ModelAndView doBuy(HttpServletRequest request, HttpServletResponse response)
	{
		Map<String, Object>params=new HashMap<String, Object>();
		ModelAndView modelAndView =null;
		//token校验应该放在这,因为需要预扣库存
		String token=request.getParameter("token");
		if(!checkToken(token))
		{
			modelAndView=new ModelAndView("error");
			modelAndView.addObject("error","无效请求,请重新购买");
			return modelAndView;
		}
		//buyNum 和productId更应该是list类型,而不是单体,因为存在购物车购买的情况
		//同样,只是为了演示的话,直接单个数据
		int buyNum=Integer.parseInt(request.getParameter("buyNumber"));
		long productId = Long.parseLong(request.getParameter("productId"));
		//因为只是个demo,所以userId直接伪造的方式
		long userId =Math.abs( random.nextLong());
		Product product = productDao.findById(productId);
		//先thread not safe 的方式校验一遍
		if(product.getProductStock()-buyNum<0) {
			params.put("error", "卖完了");
		}else {
			int validCount = productDao.decreaseStock(productId, buyNum);
			if(validCount==1) {
				//这里的步骤可以放在消息队列中
				//并且以下这块操作需要用事务包裹,演示demo,就不添加service了
				//生成订单
				Order order=new Order();
				order.setUserId(userId);
				BigDecimal bigDecimal=new BigDecimal(product.getProductPrice()+"");
				bigDecimal=bigDecimal.multiply(new BigDecimal(buyNum));
				order.setOrderPayment(bigDecimal);
				order.setOrderStatus(0);//0 新创建,未付款
				orderDao.createOrder(order);
				//生成订单详情
				OrderDetail orderDetail=new OrderDetail();
				orderDetail.setOrderId(order.getOrderId());
				orderDetail.setProductId(productId);
				orderDetail.setBuyNum(buyNum);
				orderDetailDao.createOrderDetail(orderDetail);
				params.put("orderId", order.getOrderId());
			}else {
				//失败,说明有多个买,并且卖完了了
				params.put("error", "卖完了");
			}
		}
		if(params.containsKey("error"))
		{
			modelAndView= new ModelAndView("error",params);
		}else {
			//跳转到支付页面
			modelAndView=new ModelAndView("pay",params);
		}
		return modelAndView;
	}

这是一坨代码,是的,演示demo嘛,就不拆出来了

接下来就是支付了,支付很简单,只需要修改状态即可

	如果order_detail也有状态的话,需要另外独自修改哦,想想购物车买多个不同商品,然后收货日期不同的情况
	@RequestMapping(value="/doPay")
	@ResponseBody
	public String doPay(HttpServletRequest request,HttpServletResponse response)
	{
		//调用接口
		long orderId=Long.parseLong(request.getParameter("orderId"));
		int validCount = orderDao.updateOrder(orderId, 1);
		if(validCount<1)
		{
			return "fail";
		}
		return "ok";
	}

但是我们发现万一用户提交订单但是不付钱咋办,解决方式是通过延迟的消息队列实现
所以我们还需要添加rabbitMQ的配置:

	@Bean
	public ConnectionFactory connectionFactory()
	{
		CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
		cachingConnectionFactory.setHost("localhost");
		cachingConnectionFactory.setPort(5672);
		cachingConnectionFactory.setUsername("guest");
		cachingConnectionFactory.setPassword("guest");
		return cachingConnectionFactory;
	}

	@Bean
	public RabbitAdmin rabbitAdmin()
	{
		RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory());
		rabbitAdmin.declareExchange(exchange());
		return rabbitAdmin;
	}

	@Bean
	public RabbitTemplate rabbitTemplate()
	{
		RabbitTemplate rabbitTemplate = new RabbitTemplate();
		rabbitTemplate.setConnectionFactory(connectionFactory());
		rabbitTemplate.setExchange("test_order");
		return rabbitTemplate;
	}

	@Bean
	public TopicExchange exchange()
	{
		return new TopicExchange("test_order");
	}
	@ConditionalOnBean(value=RabbitAdmin.class)
	@Bean
	public Queue nonConsumerOrderQueue()
	{
		Map<String, Object> args = new HashMap<String, Object>();
		// 超时后的转发器 过期转发到 expire_order_queue
		args.put("x-dead-letter-exchange", "test_order");
		// routingKey 转发规则
		args.put("x-dead-letter-routing-key", "order.expire");
		// 过期时间 20 秒
		// 这里设置过期时间,则内部的所有消息的过期时间是一致的,会覆盖消息的过期时间
		args.put("x-message-ttl", 5000);
		Queue queue =new Queue("nonconsumer_order_queue", false, false, false, args);
		return queue;
	}
	@ConditionalOnBean(value=RabbitAdmin.class)
	@Bean
	public Binding queueBinding()
	{
		Queue queue = nonConsumerOrderQueue();
		return BindingBuilder.bind(queue).to(exchange()).with("order.nonconsumer");
	}
	@ConditionalOnBean(value=RabbitAdmin.class)
	@Bean
	public Queue expireOrderQueue()
	{
		Queue queue = new Queue("expire_order_queue", true);
		return queue;
	}
	@ConditionalOnBean(value=RabbitAdmin.class)
	@Bean
	public Binding expireQueueBinding()
	{
		return BindingBuilder.bind(expireOrderQueue()).to(exchange()).with("order.expire");
	}
	@Bean
	public OrderTimerMessageConsumer consumer()
	{
		return new OrderTimerMessageConsumer();
	}

注意空队列那个是不能有消费者,有消费者就无法做到延时的效果了:
接下来是我们处理的消费者:

@Service
@RabbitListener(queues =
{ "expire_order_queue" })
@Slf4j
public class OrderTimerMessageConsumer implements MessageConsumer
{
	@Autowired
	private OrderDao orderDao;
	@Autowired
	private OrderDetailDao orderDetailDao;

	@RabbitHandler
	@Override
	public void process(AppEvent event)
	{
		log.info("[删除过期订单]收到消息:{}", event);
		Order order = (Order) event.getData();
		long orderId = order.getOrderId();
		int validCOunt = orderDao.deleteByIdAndStatus(orderId, 0);
		if (validCOunt >= 1)
		{
			log.info("[删除过期订单],删除了{}条数据", validCOunt);
			int detailValidCount = orderDetailDao.deleteByOrderId(orderId);
			log.info("[删除过期订单详情],删除{}条数据", detailValidCount);
		}
	}
}

其实是能改进的,这样做每次都需要到数据库中查询状态,数据库要崩溃咯,可以放到redis中,当然不是所有的属性都放到redis中,可以只判断是否为空即可(只需要在更新订单的时候记得delete key 即可)

秒杀

ok,普通商品我们是完成了,但是秒杀商品这样是不行的,秒杀的设计数据库统统不行,都需要放到缓存中来实现,并且还是差不多的流程,重复?程序猿最烦的就是重复的crud,ok我们优化:

	//展示购买的商品信息
	//预扣库存
	@RequestMapping(value = "/doBuy")
	public ModelAndView doBuy(HttpServletRequest request, HttpServletResponse response)
	{
		Map<String, Object>params=new HashMap<String, Object>();
		ModelAndView modelAndView =null;
		//token校验应该放在这,因为需要预扣库存
		String token=request.getParameter("token");
		if(!checkToken(token))
		{
			modelAndView=new ModelAndView("error");
			modelAndView.addObject("error","无效请求,请重新购买");
			return modelAndView;
		}
		//buyNum 和productId更应该是list类型,而不是单体,因为存在购物车购买的情况
		//同样,只是为了演示的话,直接单个数据
		int buyNum=Integer.parseInt(request.getParameter("buyNumber"));
		long productId = Long.parseLong(request.getParameter("productId"));
		//因为只是个demo,所以userId直接伪造的方式
		long userId =Math.abs( random.nextLong());
		ProductDTO product = productDao.findById(productId);
		product.setBuyNum(buyNum);
		//这就是优化的代码
		productBuyService.killProduct(product, userId,params);

		if(params.containsKey("error"))
		{
			modelAndView= new ModelAndView("error",params);
		}else {
			//跳转到支付页面
			modelAndView=new ModelAndView("pay",params);
		}
		return modelAndView;
	}
	//这就是优化的代码
	productBuyService.killProduct(product, userId,params); 将重复的步骤用这个代替,

相关的代码:

public interface ProductBuyService
{
	void killProduct(ProductDTO product,long userId,Map<String, Object>res);
}
public abstract class AbstractProductSecKill implements ProductBuyService
{
	private int type;
	private AbstractProductSecKill nextHandler;
	@Override
	public void killProduct(ProductDTO product, long userId,Map<String, Object>res)
	{
		if (product.getLevel() == type)
		{
			this.doKillByDifLevel(product, userId,res);
		} else if (this.nextHandler != null)
		{
			this.nextHandler.killProduct(product, userId,res);
		} else
		{
			//可以抛出异常
			return;
		}
	}
	public abstract void doKillByDifLevel(ProductDTO product, long userId,Map<String, Object>res);}
	
他有2个子类:一个是NormalProductServiceImpl 一个是SecKillProductServiceImpl 从名字就可以看出是不同product

代码就是这么的suprise,迄今为止我们的普通商品下单的流程就结束了,秒杀的代码明天上课写吧…代码传git上了,请看顶部

2018-10-22 更:
忙于吉林长春两地跑,基本没怎么更新,今天更新了秒杀,采用的redis分布式锁实现的
采用策略和责任链:
前半段流程是与普通下单流程一样的:生成token->校验token
为了避免写重复的代码,将校验token抽到了aop中:
在这里插入图片描述
aop的处理:
在这里插入图片描述
全局异常没做,这个自个儿添加吧,其中一些固定的参数写到了constants包下,图上的懒得换了,心累(求职太晚的心累,哎)
之后则是相关逻辑:
AbstractProductSecKill#killProduct在这里插入图片描述

先从redis查询,查询不到则从db中查询,这里也可以直接抽成接口,同时在实际项目中需要处理好缓存穿透(预设值,过滤)以及缓存雪崩(过期时间均匀分配,二级缓存,多机节点)问题
有2个子类:NormalProductServiceImpl 和SecKillProductServiceImpl,秒杀当然是sec的了:内部仅仅只是调用而已:
@Override
	public void doKillByDifLevel(ProductDTO product, long userId, Map<String, Object> res)
	{
		this.instrantegy.kill(product, userId, res);
	}

instrantegy是SecKillInstrantegy 的子类,做了一个,准备做第二个,第一个是分布式锁串行执行的形式:
具体逻辑:

	@Override
	public void kill(ProductDTO product, long userId, Map<String, Object> params)
	{
		long productId = product.getProductId();
		String key = ProductConstants.SEC_PRODUCT_STOCK + product.getProductId();
		long expireTime = System.currentTimeMillis() + 1000 * 60 * 3;
		// 尝试等待3秒
		if (myRedisService.tryLock(ProductConstants.SEC_PRODUCT_LOCK + productId, userId + ":" + expireTime, 3000))
		{
			try
			{
				// 占库存双重检测机制,直接用下面的取代
				// 看的出来,这步其实有重复相同的操作,所以做法是在redis中新增一块,就单独product_id 对应库存的信息
				Gson gson = new Gson();
				product = gson.fromJson(redisTemplate.opsForValue().get(key), ProductDTO.class);
				int stock = product.getProductStock();
				if (stock - 1 <0)
				{
					params.put("error", "库存不足");
					return;
				}
				product.setProductStock(product.getProductStock() - 1);
				String json = gson.toJson(product);
				redisTemplate.opsForValue().set(key, json);
				// 生成订单,如果采用串行的方式,数据库操作不可以在这里进行,应该抛到消息队列中
				log.info("userId{}生成订单", userId);
				Order order = new Order();
				order.setOrderId(idWorkerService.nextId());
				order.setOrderStatus(0);
				order.setUserId(userId);
				order.setOrderPayment(new BigDecimal(100));
				orderDao.createOrder(order);
				params.put("orderId", order.getOrderId());
			} finally
			{
				if (myRedisService.tryReleaseLock(ProductConstants.SEC_PRODUCT_LOCK + productId,
						userId + ":" + expireTime))
				{
					log.info("userId:{}[成功释放锁]", userId);
				} else
				{
					log.error("userId:{},[释放锁失败]", userId);
				}
			}
		} else
		{
			params.put("error", "当前购买人数过多,请刷新再试");
		}
	}

Redis锁的代码:

public boolean tryLock(String key, String value, int waitTimes)
	{
		long startTime = System.currentTimeMillis();
		Boolean res = false;
		while (!res && System.currentTimeMillis() - startTime <= waitTimes)
		{
			res = stringRedisTemplate.execute(new RedisCallback<Boolean>()
			{
				@Override
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException
				{
					return connection.set(key.getBytes(), value.getBytes(), Expiration.seconds(120),
							SetOption.SET_IF_ABSENT);
				}
			});
		}
		// 如果还是失败的话,则需要考虑过期时间
		// 需要考虑的原因在于:这种情况:业务执行完毕之后某种原因删除失败,导致lock还需要被占用一段时间,很显然这是不能接受的
		// 解决方法是额外在value中添加限定时间t1和超时时间t2,t1保证业务执行完毕后序线程得以继续,超时时间t2保证数据能够过期 ;
		if (!res)
		{
			String tempJson = stringRedisTemplate.opsForValue().get(key);
			if (StringUtils.isEmpty(tempJson))
			{
				res = lockAgain(key, value);
			} else
			{
				String[] arrs = tempJson.split(":");
				long expireTime = Long.parseLong(arrs[1]);
				if (expireTime < System.currentTimeMillis())
				{
					try
					{
						lock.lock();
						// double check 这里的双重检测主要是为了防止其他线程
						// 又获取到了锁,而却被这个线程给删了
						tempJson = stringRedisTemplate.opsForValue().get(key);
						if (!StringUtils.isEmpty(tempJson)
								&& (expireTime = Long.parseLong(tempJson.split(":")[1])) < System.currentTimeMillis())
						{
							Boolean delete = stringRedisTemplate.delete(key);
							if (!delete)
							{
								log.error("[删除过期key]失败,key为:{}", key);
							} else
							{
								log.error("[删除过期的key]成功");
							}
						}
					} finally
					{
						lock.unlock();
					}
				}
			}
		}
		return res;
	}

注意点:
1.双重检测,第一重检测发生在执行逻辑之前,先校验是否符合条件,第二重校验发生在获取锁之后,因为锁是随机获取的,不能保证当A获得锁的时候,pro还是那个pro
2.当获取锁失败的时候,需要判断是否超时,这种情况:业务执行完毕之后某种原因删除失败,导致lock还需要被占用一段时间,很显然这是不能接受的,解决方法是额外在value中添加限定时间t1和超时时间t2,t1保证了这种情况下锁得以释放,超时时间t2保证业务能执行完毕
3.建议:对于product,推荐新增 productId_stock的对应关系,因为从代码中可以看出有序列化和反序列化多次,所以可以考虑通过redis的increment来代替,简单又效率高
数据测试:

	@Test
	public void testManyThreads()
	{
		ExecutorService service = Executors.newFixedThreadPool(100);
		long stTime = System.currentTimeMillis();
		LinkedBlockingQueue<Integer>list=new LinkedBlockingQueue<>();
		CountDownLatch countDownLatch = new CountDownLatch(10000);
		for (int i = 0; i < 10000; i++)
		{
			final int userId = i;
			service.execute(new Runnable()
			{
				@Override
				public void run()
				{
					Map<String, Object> params = new HashMap<>();
					productBuyService.killProduct(1L, userId, 1, params);
					if (params.containsKey("error"))
					{
						System.out.println("userId:" + userId + "_error:" + params.get("error"));
					}else {
						list.add(userId);
					}
					countDownLatch.countDown();
				}
			});
		}
		try
		{
			countDownLatch.await();
			log.info("[所有任务结束]总耗时:{}",System.currentTimeMillis()-stTime);
			log.info("[总共有{}是成功购买到的]",list.size());
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

响应是正常的

分布式锁实战:从Redis到ZooKeeper,如何解决并发资源竞争难题 在分布式系统中,当多个服务实例需要访问共享资源时,如何保证数据一致性成为核心挑战。这本质上是分布式环境下对共享资源的互斥访问问题,需要一种所有节点都能认可的协调机制。分布式锁正是为解决这一问题而生的协议,它通过在外部存储系统(如Redis、ZooKeeper)中维护锁状态,确保任意时刻只有一个客户端能操作特定资源。从技术原理看,分布式锁需要满足互斥性、安全性、活性和身份标识等基本条件,同时还要权衡性能、可靠性和实现复杂度。在工程实践中,基于Redis的方案以高性能见长,但需警惕主从切换导致的数据丢失风险;而 阅读详情

相关推荐

手机摄影新宠:In-line Hardware EIS如何让你的4K视频稳如鸡头(附实测对比)

本文深入解析了手机摄影中的In-line Hardware EIS(硬件级行内电子图像稳定)技术。通过对比传统软件EIS,阐述了其通过硬件管道实时处理实现极低延迟、减少画面裁切和有效抑制果冻效应的核心优势,并结合4K视频的日夜间实测,展示了该技术如何让手持拍摄的视频“稳如鸡头”,显著提升创作体验与成片质量。

tree的博客 994

Java-redis分布式锁 抢购秒杀系统 实现

一、使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现) 共享资源(各个系统访问同一个资源,资源的载体可能是传统关系型数据库或者NoSQL) 同步访问(即有很多个进程同事访问同一个共享资源。没有同步访问,谁管你资源竞争不竞争) 二、应用的场景例子   管理后台的部署架构(多台tomc...

weixin_33796205的博客 648

具身智能与空天具身智能:从理论到实践的AIBOX技术革命

本文深入探讨了具身智能从理论到实践的发展,重点介绍了AIBOX技术如何作为边缘智能计算模块,为无人机、机器人等无人系统提供可进化的“大脑”。文章通过空天具身智能等应用场景,阐述了AIBOX在实现感知、决策、行动一体化闭环中的核心作用,并展望了其在协同智能与规模化部署中的未来挑战与机遇。

lll78的博客 765

【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)

我们在上篇文章中实现了单机模式下的秒杀业务。其中采用了synchronized加锁来解决各种线程安全问题。而synchronized关键字是依赖于单机的JVM,在集群模式下,每个服务器都有独立的JVM,如果此时还采用synchronized关键字加锁,就会导致不同服务器间出现线程安全问题。如果有什么讲的不对的地方欢迎在评论区指出,希望能够和你们一起进步✊。

acm_pn的博客 1511

大模型面试题52:LLM如果在训练过程中loss值出现spike应该怎么办?

小白急救:停更→回滚→降LR→裁剪梯度,先保住训练不崩溃;进阶排查:查数据→查优化器→查硬件,找到spike的根因并解决;深层预防:预处理保守配置+训练中实时监控,从源头规避spike。先止损,再溯源,最后预防。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 1168

TVS 和压敏电阻联合使用进行浪涌保护时,压敏电阻的压敏电压要低于TVS 的钳位电压 VC。

通过先让MOV接管,可以确保在大多数情况下,电压不会迅速上升到TVS二极管的钳位电压,从而保护TVS二极管免受持续高能量冲击的损害。总结来说,将MOV的压敏电压设置得低于TVS二极管的钳位电压,可以提供一种分级的保护机制,优化浪涌保护性能,同时保护敏感的TVS二极管不被过度的能量冲击损坏,并延长其使用寿命。MOV吸收的能量(Wmov):假设浪涌电流为10kA,MOV导通后将电压限制在300V,那么Wmov = Ipp * Vmov = 10kA * 300V = 3MW。例如,Vmov可以设定为275V。

weixin_66642980的博客 2245

RocketMQ学习笔记:秒杀+分布式锁 Redis RocketMQ SpringBoot

【代码】RocketMQ学习笔记:秒杀+分布式锁 Redis RocketMQ SpringBoot。

RuaaaHaHa的博客 520

PCL 八叉树的使用

八叉树(Octree)是一种空间划分的数据结构,广泛应用于三维点云的处理和分析。八叉树通过将空间递归地划分为八个子区域,可以有效地组织和索引三维点数据,提升点云的查询、降采样和配准等操作的效率。

纵马踏花向自由 1658

任务队列的Java实现

当前项目中遇到这样一个需求: 将需要审核的文本提交给人工智能模型接口审核,等待模型接口审核完毕以后拿到审核结果返回给前端展示给用户(另:模型处理数据所消耗的时间会随着用户提交数据的复杂度有所变化)。,如果有多个用户在同一时间提交了文本审核需求,且恰好此时此刻模型需要较长时间处理,这样的话,按照通常的代码写法,是无法满足任务需求的,那么应该如何满足这个需求呢?此时此刻,我们可以使用。上述就是任务队列的Java实现基本过程。

My52Hz 2260

YOLO26涨点改进 | 损失函数改进篇 | 引入SlideLoss,适配尺度变化+遮挡+难易样本不平衡,手把手配置+实战案例

COCO-Hard是COCO数据集的复杂场景子集,包含80个类别,重点筛选多尺度、遮挡密集样本,其中小尺度目标(<32×32像素)占比42%,大尺度目标(>128×128像素)占比28%,遮挡目标占比57%,场景涵盖人群密集、车流拥堵、复杂工业环境,完美模拟真实复杂检测场景,适合验证SlideLoss的适配性。

qq_36130719的博客 157

Qt实现思维导图功能5『纵向分布模式』

思维导图纵向分布模式:模式一

浮生流年的博客 1055

java 分布式任务队列_分布式任务队列Celery

分布式任务队列CeleryCelery (芹菜)是基于Python开发的分布式任务队列。它支持使用任务队列的方式在分布的机器/进程/线程上执行任务调度。结构核心部件broker消息队列,由第三方消息中间件完成常见有RabbitMQ, Redis, MongoDB等worker任务执行器可以有多个worker进程worker又可以起多个queue来并行消费消息backend后端存储,用于持久化任务执...

weixin_39573822的博客 637

飞腾UEFI配置GPIO—飞腾FT2000/4 (D2000/8) GPIO引脚配置及使用

讲解了在飞腾UEFI代码中如何添加GPIO配置相关代码,并以GPIO1_B0—B7为例,将这8个引脚配置为GPIO工作模式并默认输出低电平

qq_30135687的博客 5343

黑马点评项目 | Redis学习笔记「纯享版」10w字超详细总结(含资料)

这次笔记是本人二刷Redis实战篇黑马点评的8w字汇总笔记。如果需要资料,于本文末尾查找即可

2025优快云博客之星Top246,后端开发技术领域新星创作者。 3万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

电商平台高并发秒杀系统面试实录:Java全栈架构深度剖析与代码案例

本次面试围绕电商秒杀系统高并发架构展开,涵盖了Spring Boot、Vue3、Redis、RabbitMQ、Prometheus等主流技术栈,展示了高并发场景下的架构设计、前后端协作、消息队列与缓存优化、安全与监控等核心技术点。文中附带详细代码案例,适合全栈开发者系统学习与参考。

aqiHandsome的博客 310

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java学习笔记--Redis

不会,Redis 的过期删除策略是选择「惰性删除+定期删除」这两种策略配和使用。惰性删除策略的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。定期删除策略的做法是,每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。

m0_73803861的博客 758

从全栈开发视角看微服务架构下的高并发优化实践

本次面试围绕高并发系统设计、微服务架构、搜索引擎优化以及音视频传输等多个技术点展开。从基础到进阶,逐步引导应聘者展示其真实的技术能力和项目经验。通过具体代码示例,进一步验证了其对相关技术的理解和应用能力。本文以一次真实的面试对话形式,探讨了高并发系统设计与微服务架构下的关键技术点,涵盖Redis缓存、Elasticsearch搜索、WebRTC音视频传输等内容。

aqiHandsome的博客 835

Java大厂面试连环炮:Spring Boot+Kafka+Redis在电商场景下的应用与谢飞机的神回复

我最大的优势是抗压能力强——上次公司服务器崩了,我第一个拔掉电源。:订单创建后要通知库存、物流、积分服务,用Kafka如何保证最终一致性?:上RabbitMQ延迟队列,30分钟后检查订单状态,未支付就回滚库存。(挠头):穿...穿透?里的配置类,比如数据源、Redis连接池这些,不用手动new了。:同一个订单ID的消息发到同一个partition就行!(皱眉):我说的是缓存中没有的数据频繁查询数据库...那更好办了——直接把数据库关了,它就查不了了!:秒杀场景下,Redis集群预减库存,怎么防止超卖?

FLSJL的博客 931

Java全栈面试实录:从Spring Boot到AI大模型,互联网大厂求职者的技术洗礼

如果出现服务雪崩,你会如何处理?:小曾,我们公司正在开发一个高并发的电商秒杀系统,需要使用Spring Boot构建后端服务?:(微笑)不错,提到Redis和Kafka,但分布式锁的实现细节呢?比如Redis分布式锁和基于数据库的锁,你了解哪些方案?:用Spring AI?(突然兴奋)是不是要加会话记忆……:我们正在引入AI大模型提升客服效率,需要构建一个智能客服系统?如果需要扩展系统,比如接入更多工具(天气、股票),你会如何设计工具调用标准化?如果秒杀系统需要支持全球用户,你会如何设计API网关?

m0_75125940的博客 472

腾讯后端面试高频考点全解析:Java、MySQL、Redis与分布式核心原理

后端开发中,基础原理与中间件实践是衡量工程师能力的重要标尺。从 Java 并发编程的线程池与锁机制,到 MySQL 索引优化与事务隔离级别,再到 Redis 缓存穿透等分布式场景的核心问题,每一项都直接决定系统的高并发表现。理解底层原理不仅有助于面试中从容应对深度追问,更能指导实际工程中的性能调优与架构设计。在腾讯等大厂的后端面试中,这些知识点往往围绕真实业务场景展开,考察候选人是否具备完整的链路思维与解决问题的能力。本文结合近年高频真题,系统拆解 Java 基础、Spring 全家桶、MySQL、Redi

weixin_29209825的博客 281

互联网大厂Java求职面试场景演绎与技术详解——以谢飞机与面试官对话为例

通过上述三轮场景演绎,涵盖了Java面试重难点技术模块。希望每位读者都能举一反三,成为大厂面试中的强者。

2401_87752952的博客 325

《互联网大厂Java求职者面试模拟:智慧物流场景下的微服务架构与高并发订单处理实战》

【面试场景】面试官张工:资深互联网大厂架构师,严肃专业,注重技术深度与业务场景紧密结合程序员谢飞机:幽默搞笑的水货程序员,简单问题能答出,复杂问题回答含糊不清,需面试官细致引导。

weixin_46755643的博客 486

Java面试场景题解析:从八股到项目实战,覆盖并发缓存与分布式

在Java后端开发与求职面试中,单纯背诵八股文已难以应对越来越注重工程实践的场景题。面试官更关注候选人能否将并发、缓存、分布式、消息队列等基础知识,应用到真实的业务设计与故障排查中。理解缓存穿透、击穿与雪崩的差异,掌握分布式锁的实现原理,并能够通过MQTT对接车牌识别相机等物联网设备,这些能力正在成为区分普通开发者和资深工程师的关键。本内容围绕java面试题中高频出现的场景化问题,从通用解题框架出发,结合停车场项目实战,拆解接口幂等、削峰填谷、Spring事务失效、内存溢出排查等典型场景,并提供一周刷题计划

weixin_34199405的博客 483

Redis简单面试问题汇总

常见的nosql有:memcache redis mongodb。nosql以简单的kv模式存储,因此大大增加了数据库的扩展能力。

qq_45841146的博客 435

Java大厂面试实录:从电商场景到AI应用的深度技术考察

(自信)秒杀场景我会用Redis做分布式锁,订单用MySQL+MyBatis,并配合Spring Cloud实现服务拆分。:用Llama 3做Embedding,配合向量数据库检索历史话术...(结巴)但模型微调我还没实践过。:Spark处理实时数据,Hadoop存历史日志...(被追问)但Flink的窗口计算具体怎么算?你如何设计系统架构?:数据库用分库分表,服务用Kubernetes部署...(被追问)但具体副本数怎么调?:一般用Redis+本地缓存,Spring Cache也能用...(眼神飘忽)

m0_75125940的博客 489

《互联网大厂Java求职者面试模拟:电商场景微服务架构与高并发订单处理实战》

【面试场景】面试官王工:严肃专业的互联网大厂资深架构师,注重技术深度与业务背景程序员谢飞机:幽默搞笑的水货程序员,基础问题能答出,复杂问题回答含糊,需面试官引导。

weixin_46755643的博客 825
上一篇: 数据库连接池的配置
下一篇: Java转Go
Coder_Joker
博客等级 码龄8年 39粉丝 67原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值