SpringBoot + Redis尝试实现外卖拼单(二)
2021/4/18 2:25:55
本文主要是介绍SpringBoot + Redis尝试实现外卖拼单(二),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 前言
- 五、实现后端websocket
- 5.1 注入所需类
- 5.2 设计内部类
- 5.2 创建连接OnOpen
- 5.3 关闭连接OnClose
- 5.4 接收和推送消息OnMessage
- 六、实现效果
前言
在上一篇文章中,我们实现了在前端创建websocket并初始化。在这篇文章中将会详细讲解后端逻辑实现步骤。
五、实现后端websocket
5.1 注入所需类
由于spring bean默认都是单例(singleton),而 websocket每次用户创建连接都会创建一个对象。这就会导致用户创建连接的websocket里需要注入的类为null,然后抛出NPE。
@Component @ServerEndpoint("/groupbuying/{orderId}/{token}") public class WebSocketServer extends BaseController { private static StringRedisTemplate redisTemplate; @Autowired public void setStringRedisTemplate(StringRedisTemplate redisTemplate) { WebSocketServer.redisTemplate = redisTemplate; } private static ProductService productService; @Autowired public void setProductService(ProductService productService) { WebSocketServer.productService = productService; } }
5.2 设计内部类
首先,我们需要在WebSocketServer里定义一个内部类UserGroup。这个的主要作用就是绑定用户ID和对应的session。再上一篇文章中的订单ID会被用于作为map的key,用户集合作为value。这样我们就可以通过订单ID找到相对应的用户来推送消息了。
@Component @ServerEndpoint("/groupbuying/{orderId}/{token}") public class WebSocketServer extends BaseController { private static class UserGroup { public Session session; public String userId; public UserGroup(Session session, String userId) { this.session = session; this.userId = userId; } public Session getSession() { return session; } } private static Map<String, Set<UserGroup>> sessionPool = new ConcurrentHashMap(); }
5.2 创建连接OnOpen
当客户端加入的时候,我们需要session和uid绑定在一起放入map中。代码如下:
@OnOpen public void onOpen(Session session, @PathParam(value = "orderId") String orderId, @PathParam(value = "token") String token) throws BusinessException { String uid = getUid(token); // 我接收的时候有空格,需要去空格 orderId = orderId.trim(); Set<UserGroup> orderGroupBuying = sessionPool.get(orderId); if (orderGroupBuying == null) { if (!sessionPool.containsKey(orderId)) { orderGroupBuying = new HashSet<>(); orderGroupBuying.add(new UserGroup(session, uid)); // 根据订单ID,更新用户集合 sessionPool.put(orderId, orderGroupBuying); } } else { orderGroupBuying.add(new UserGroup(session, uid)); } log.info(uid + "加入webSocket!订单号: " + orderId + ", 当前人数为" + orderGroupBuying.size()); }
5.3 关闭连接OnClose
当客户端离开的时候,我们需要将map里对应的UserGroup给移除掉。代码如下:
@OnClose public void onClose(@PathParam(value = "orderId") String orderId, @PathParam(value = "token") String token) throws BusinessException { String uid = getUid(token); // 我接收的时候有空格,需要去空格 orderId = orderId.trim(); Set<UserGroup> orderGroupBuying = sessionPool.get(orderId); if (orderGroupBuying != null) { for (UserGroup user : orderGroupBuying) { if (uid.equals(user.userId)) { orderGroupBuying.remove(user); log.info(uid + "离开webSocket!订单号: " + orderId + ", 当前人数为" + orderGroupBuying.size()); break; } } } }
5.4 接收和推送消息OnMessage
当接收到消息后,需要更新redis中的数据。最后返回给客户端。
@OnMessage public void onMessage(String message){ log.info("客户端:" + message + ",已收到"); if (!StringUtils.isBlank(message)) { try { // 将消息转换成list List<GroupJoinDto> gbList = JsonUtils.jsonToList(message, GroupJoinDto.class); if (gbList != null && gbList.size() > 0) { // 获得订单ID String orderId = gbList.get(0).getOrderId(); // 获得redis中拼单数据 String groupOrder = redisTemplate.opsForValue().get(orderId); LinkedList<GroupBuyingDto> redisList; if (!StringUtils.isBlank(groupOrder)) { redisList = JsonUtils.jsonToLinkedList(groupOrder, GroupBuyingDto.class); for (int i = 0; i < gbList.size(); i++) { for (int j = 0; j < redisList.size(); j++) { GroupJoinDto gb = gbList.get(i); String token = gbList.get(i).getToken(); // 验证token合法性 String uid = getUid(token); GroupBuyingDto redisDto = redisList.get(j); ProductInfoDto product = productService.findSingleProduct(gb.getProductId()); // 如redis中存在拼单数据,更新数据 // 更新商品数量 if (redisDto.getProductId().equals(gb.getProductId()) && uid.equals(redisDto.getUserId()) && !redisDto.getCount().equals(gb.getCount())) { redisList.get(j).setCount(gb.getCount()); } else if (!redisList.stream().map(redisData -> redisData.getProductId()).collect(Collectors.toList()).contains(gb.getProductId())){ redisList.add(new GroupBuyingDto(product.getProductId(), gb.getCount(), uid, product.getCategoryType(), product.getProductDescription(), product.getProductImg(), product.getProductName(), product.getProductPrice(), product.getProductStatus(), product.getProductStock())); } } } // 已选商品减少到0 for (int i = 0; i < redisList.size(); i++) { GroupBuyingDto redisDto = redisList.get(i); if (!gbList.stream().map(newGB -> newGB.getProductId()).collect(Collectors.toList()).contains(redisDto.getProductId())){ redisList.remove(i); } } } else { // 如redis中不存在拼单数据,直接添加 redisList = new LinkedList<>(); GroupJoinDto joinDto = gbList.get(0); String uid = getUid(joinDto.getToken()); ProductInfoDto product = productService.findSingleProduct(joinDto.getProductId()); redisList.add(new GroupBuyingDto(product.getProductId(), joinDto.getCount(), uid, product.getCategoryType(), product.getProductDescription(), product.getProductImg(), product.getProductName(), product.getProductPrice(), product.getProductStatus(), product.getProductStock())); } // 保存到redis redisTemplate.opsForValue().set(orderId, JsonUtils.objectToJson(redisList)); // 发送消息 for (Map.Entry<String, Set<UserGroup>> entry : sessionPool.entrySet()) { if (entry.getKey().equals(orderId)) { Set<UserGroup> users = entry.getValue(); for (UserGroup user : users) { user.getSession().getBasicRemote().sendText(JsonUtils.objectToJson(redisList)); } } } } } catch (Busin![在这里插入图片描述](https://www.www.zyiz.net/i/ll/?i=20210417224115859.gif#pic_center) essException e) { e.printStackTrace(); log.info("token不合法"); } catch (IOException e) { e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } } }
六、实现效果
实现效果如下:
在下一篇中,我们将会实现通过链接来邀请用户加入websocket实现多个用户一起操作商品的效果。
这篇关于SpringBoot + Redis尝试实现外卖拼单(二)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-19永别了,微服务架构!
- 2024-05-15鸿蒙生态设备数量超8亿台
- 2024-05-13TiDB + ES:转转业财系统亿级数据存储优化实践
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?