背景
表弟刚参加工作半年,就遇到批量调用接口返回错误的问题,刚好是周五遇到让周六加班弄完,由于周六本是休息,表弟积极性很低,于是直接选择短平快的方式先应付上去,并且运行了一下,说日志有空指针报错,但核对了一下数据库数据正常,暂时没发现问题。
为了保险起见让我帮忙给review一下
表弟说用大模型分析,老是回答说存在问题,让换一种写法,但大模型的那些问题像是在专业的胡说,不太放心自己临时写的代码。说经常看很多人喜欢review代码,就跑过来让我看一下,我看完后心中已有数。但为了表弟面前表现的更专业,故将其代码去除所有业务信息,做了最简化处理让各位也来点评一下,博采众长。
表弟的代码(极简版)
表弟的环境是jdk8的单体服务,此接口调用频率很低,但一旦调用并发量很多。
java
代码解读
复制代码
import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.ReentrantLock; @Slf4j @RequestMapping("/queue") @RestController public class QueueController { //服务一旦宕机,队列消息就会全部丢失 private static final Queue<String> queue = new LinkedBlockingQueue<>(10000); private static final ReentrantLock lock = new ReentrantLock(); /** * 通过内存占用换取接口的并发能力,请求进来先进队列里排队,最后一个走的线程检查一下队列是否处理完成 * @param id 模拟任务对象 */ @RequestMapping("/offer") public void test(@RequestParam("id")String id){ if(id != null) { //模拟生产任务 boolean offered = queue.offer(id); if(!offered){ log.error("消息进入队列失败:{}",id); } } boolean isLock = lock.tryLock(); if(isLock) { try { while (!queue.isEmpty()) { String poll = queue.poll(); try { if (poll != null) { log.info("poll:{}", poll); //模拟消费任务耗时,此处可以异步再优化性能 Thread.sleep(1000); } }catch (Exception e){ log.error("消费任务出错,poll:{},错误信息{}",poll,e.getMessage(),e); } } } catch (Exception e) { log.error("消费任务出现异常,id:{},错误信息:{}",id,e.getMessage(),e); } finally { if(lock.isHeldByCurrentThread()){ lock.unlock(); //进入判断前可以加个10ms的睡眠时间,进一步降低高并发下总是此线程拿到锁的可能,从而不断递归导致栈溢出 if(!queue.isEmpty()){ log.info("清理队列剩余任务,size:{}",queue.size()); test(null); } } } } log.info("生产者执行完成,size:{}",queue.size()); } }