[置顶] 小白的web优化之路 三、使用MQ来实现事务异步处理
2017-06-18 17:36
447 查看
在上一篇
小白的web优化之路 二、实战使用redis来缓存列表信息 中,我们讲了使用redis来缓存列表信息,极大的提供了用户访问列表的速度,也减轻了数据库的压力,下面,我们在以一个例子来讲解MQ来实现事务的异步处理。
在使用了redis解决了列表查询问题之后,小白又遇到了一个问题,刚刚产品经理找他,要他实现一个功能,功能如下:
当用户完成某一任务时,提高用户的积分,并给此用户的所有朋友发送短信通知,告诉他们此用户完成了某任务,让他们赶紧来玩。
小白瞬间感觉难住了,提高用户的积分很简单,一个UPDATE就行,但是发送短信通知怎么办呢,假设给一个用户发送短信的时长为50ms,那么串行处理的方式十个用户就是500ms,一百个用户就是5000ms,对于一个接口来说这个时间太长了,是不能接收的,这个问题怎么解决呢?小白陷入了沉思...
过了一会,小白突然想到,我为什么要马上给用户的好友发送短信呢?就算延迟几秒接收也是可以的啊。我可以在后台创建一个队列,请求来了之后,我先把提高用户的积分,然后把要处理的内容放到队列中,然后直接返回成功就行,后台的处理程序在处理这些队列就可以了。
小白的想法也就是我们平常常说的MQ,即消息队列。在一些处理时间比较长的任务,我们可以使用MQ进行延时处理,来降低客户的请求等待时间和减轻服务器的压力。
下面,我们将以activeMQ来解决上述问题。
首先,先看看没有mq的示例代码:
在 SendMessageService 中,模拟了发送短信的延时:
可以看到,如果串行的运行的话,接口的速度太慢。
这时候即可以在接口中使用mq,即先不发送短信,而是直接放到mq中,队列中的消息会有相应的消费者去处理:
相应的消费者为:
这样会极大的提高接口速度,尤其是在好友数量很多的情况下:
示例代码已经放到github上,此工程为项目中的 web-optimize-mq 工程 ,地址为 项目源码地址
小白的web优化之路 二、实战使用redis来缓存列表信息 中,我们讲了使用redis来缓存列表信息,极大的提供了用户访问列表的速度,也减轻了数据库的压力,下面,我们在以一个例子来讲解MQ来实现事务的异步处理。
在使用了redis解决了列表查询问题之后,小白又遇到了一个问题,刚刚产品经理找他,要他实现一个功能,功能如下:
当用户完成某一任务时,提高用户的积分,并给此用户的所有朋友发送短信通知,告诉他们此用户完成了某任务,让他们赶紧来玩。
小白瞬间感觉难住了,提高用户的积分很简单,一个UPDATE就行,但是发送短信通知怎么办呢,假设给一个用户发送短信的时长为50ms,那么串行处理的方式十个用户就是500ms,一百个用户就是5000ms,对于一个接口来说这个时间太长了,是不能接收的,这个问题怎么解决呢?小白陷入了沉思...
过了一会,小白突然想到,我为什么要马上给用户的好友发送短信呢?就算延迟几秒接收也是可以的啊。我可以在后台创建一个队列,请求来了之后,我先把提高用户的积分,然后把要处理的内容放到队列中,然后直接返回成功就行,后台的处理程序在处理这些队列就可以了。
小白的想法也就是我们平常常说的MQ,即消息队列。在一些处理时间比较长的任务,我们可以使用MQ进行延时处理,来降低客户的请求等待时间和减轻服务器的压力。
下面,我们将以activeMQ来解决上述问题。
首先,先看看没有mq的示例代码:
package com.happyheng; import com.happyheng.dao.FriendDao; import com.happyheng.dao.ScoreDao; import com.happyheng.service.SendMessageService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * 发送信息的Controller * Created by happyheng on 2017/6/17. */ @RestController public class MessageController { @Resource private SendMessageService sendMessageService; @Resource private ScoreDao scoreDao; @Resource private FriendDao friendDao; @RequestMapping("/sendMessage") public String sendMessage() { // 首先提高积分 scoreDao.updateScore(); // 其次获取用户的好友手机号列表 List<String> numberList = friendDao.getUserFriendPhoneNumbers(); // 发送短信 for (String num: numberList) { sendMessageService.sendMessage(num, "你的好友完成了xx任务,快来一起玩吧"); } return "success"; } }
在 SendMessageService 中,模拟了发送短信的延时:
package com.happyheng.service; import org.springframework.stereotype.Service; /** * * Created by happyheng on 2017/6/17. */ @Service public class SendMessageService { public void sendMessage(String phoneNum, String message) { // 模拟发送短信的耗时操作 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }
可以看到,如果串行的运行的话,接口的速度太慢。
这时候即可以在接口中使用mq,即先不发送短信,而是直接放到mq中,队列中的消息会有相应的消费者去处理:
package com.happyheng; import com.happyheng.dao.FriendDao; import com.happyheng.dao.ScoreDao; import com.happyheng.service.SendMessageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.jms.*; import java.util.List; /** * 发送信息的Controller * Created by happyheng on 2017/6/17. */ @RestController public class MessageController { @Resource(name = "queueDestination") private Destination destination; @Autowired private JmsTemplate jmsTemplate; @Resource private SendMessageService sendMessageService; @Resource private ScoreDao scoreDao; @Resource private FriendDao friendDao; @RequestMapping("/sendMessageByMq") public String sendMessageByMq() { // 首先提高积分 scoreDao.updateScore(); // 其次获取用户的好友手机号列表 List<String> numberList = friendDao.getUserFriendPhoneNumbers(); // 将好友手机号直接放到mq中即可, 消费者会发送短信 for (final String num: numberList){ jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { MapMessage mapMessage = session.createMapMessage(); mapMessage.setString("num", num); mapMessage.setString("text", "你的好友完成了xx任务,快来一起玩吧"); return mapMessage; } }); } return "success"; } }
相应的消费者为:
package com.happyheng.consumer; import com.happyheng.service.SendMessageService; import javax.annotation.Resource; import javax.jms.*; /** * * Created by happyheng on 2017/6/17. */ public class MapMessageListener implements MessageListener { @Resource private SendMessageService sendMessageService; public void onMessage(Message message){ System.out.println("收到信息"); MapMessage mapMessage = (MapMessage) message; try { String num = mapMessage.getString("num"); String textMessage = mapMessage.getString("text"); System.out.println("手机号为--" + num + "--信息内容为" + textMessage); sendMessageService.sendMessage(num, textMessage); } catch (JMSException e) { e.printStackTrace(); } } }
这样会极大的提高接口速度,尤其是在好友数量很多的情况下:
示例代码已经放到github上,此工程为项目中的 web-optimize-mq 工程 ,地址为 项目源码地址
相关文章推荐
- 使用ADO.NET 实现事务处理
- 在C#中使用COM+实现事务控制(2)
- 使用Spring 2.0 新特性实现声明式事务管理-基于Annotation
- 使用事务与锁,实现一个用户取过的数据不被其他用户取到
- 转:邹建-- 使用事务与锁,实现一个用户取过的数据不被其他用户取到
- 在C#中使用COM+实现事务控制
- 使用事务与锁,实现一个用户取过的数据不被其他用户取到
- 使用TransactionScope 实现事务管理
- 使用ADO.NET 实现事务处理
- (ZT)使用JOTM实现分布式事务管理(多数据源)
- 在C#中使用COM+实现事务控制(1)
- 在C#中使用COM+实现事务控制
- 使用Ado.Net进行简单事务处理的四种实现及比较
- 使用Spring 2.0 新特性实现声明式事务管理-基于XML Schema
- 在C#中使用COM+实现事务控制
- zt在C#中使用COM+实现事务控制
- 使用JOTM实现分布式事务管理(多数据源)
- 使用事务与锁,实现一个用户取过的数据不被其他用户取到
- 使用事务范围实现隐式事务
- 使用事务与锁,实现一个用户取过的数据不被其他用户取到