一、简介
1、异步
客户请求不会阻塞进程,服务端的响应是可以非即时的。
2、异步的常见形态
- 通知
- 请求/异步响应
- 消息
3、MQ应用场景
- 异步处理:比如用户注册之后,需要发送短信或者邮件,注册信息写入数据裤之后通过异步消息,让短信服务和邮件服务去做他们的事,提升用户体验。
- 流量削峰:秒杀系统,在应用前端加入消息队列,从而控制活动的人数,加入队列长度超过最大,应该直接抛弃用户请求,或者直接跳转到错误页面,
- 日志处理
- 应用解藕:用户下单后,订单服务将消息写入消息队列,商品服务来订阅这个下单的消息,采用拉或者推的方式获取下单的信息
二、RabbitMQ的基本使用
- pom.xml
<!-- 引入bus-amqp 消息队列-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
- bootstrap.yml
#消息队列
rabbitmq:
host: 192.168.3.233
username: guest
password: guest
port: 5672
- MqReceiver
/**
* @Title 接收MQ消息
* @Author zwj
* @Date 2018/11/26
* @Description
*/
@Slf4j
@Component
public class MqReceiver {
//@RabbitListener(queues = "myQueue") //手动创建队列
//2.自动创建队列
//@RabbitListener(queuesToDeclare = @Queue("myQueue"))
//3.自动创建,Exchange和Queue绑定
@RabbitListener(bindings = @QueueBinding(
value = @Queue("myQueue"),
exchange = @Exchange("myExchange")
))
public void process(String message){
log.info("MqReceiver:{}",message);
}
/**
* 数码供应商服务
* */
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange("myOrder"),
key = "computer",
value = @Queue("computerOrder")
))
public void processComputer(String message){
log.info("computer MqReceiver:{}",message);
}
/**
* 水果供应商服务
* */
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange("myOrder"),
key = "fruit",
value = @Queue("fruitOrder")
))
public void processFruit(String message){
log.info("fruit MqReceiver:{}",message);
}
}
- sendMassage
/**
* @Title 发送消息测试
* @Author zwj
* @Date 2018/11/26
* @Description
*/
@Component
public class MqReceiverTest extends ServerApplicationTests {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
public void send() {
amqpTemplate.convertAndSend("myQueue","now"+new Date());
}
@Test
public void sendOrder() {
amqpTemplate.convertAndSend("myOrder","computer","now"+new Date());
}
}
三、Spring Cloud Stream的使用
Spring Cloud Stream 为微服务应用构建消息驱动能力的框架,应用程序通过input,output来与Stream中的Binder交互,Stream中的Binder与中间件(Middleware)交互,Binder是stream的抽象概念,是应用与消息中间件的粘合剂,使用Stream最大的方便之处莫过于对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至做到动态切换中间件。
Stream支持的中间件只有两种:RabbitMQ、Kadka
- pom.xml
<!-- 引入Spring Cloud Stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- bootstrap.yml
spring:
cloud:
stream:
bindings:
input: #消息名字
group: order #消息分组 项目不管启动多少实例,都只有一个实例接收消息
content-type: application/json #序列化
#消息队列
rabbitmq:
host: 192.168.3.233
username: guest
password: guest
port: 5672
- StreamClient
/**
* @Title
* @Author zwj
* @Date 2018/11/26
* @Description
*/
public interface StreamClient {
//报错:Invalid bean definition with name 'myMessageOrdersssss' defined in com.imooc.order.message.StreamClient: bean definition with this name already exists
//解决方法:@Input和@Output不可一样,同一服务里面的信道名字不能一样,在不同的服务里可以相同名字的信道
String INPUT = "input";
String OUTPUT = "output";
@Input(StreamClient.INPUT)
SubscribableChannel input();
@Output(StreamClient.OUTPUT)
MessageChannel output();
String INPUT2 = "input2";
String OUTPUT2 = "output2";
@Input(StreamClient.INPUT2)
SubscribableChannel input2();
@Output(StreamClient.OUTPUT2)
MessageChannel output2();
}
- StreamReceiver
/**
* @Title
* @Author zwj
* @Date 2018/11/26
* @Description
*/
@Component
@EnableBinding(StreamClient.class)
@Slf4j
public class StreamReceiver {
// @StreamListener(StreamClient.INPUT)
// public void process(Object message){
// System.out.println(message);
// log.info("StreamReceiver:{}",message);
// }
/**
* 接收orderDTO对象消息
* */
@StreamListener(value = StreamClient.INPUT)
@SendTo(StreamClient.INPUT2) //消息发送给谁
public String process(OrderDTO message){
log.info("StreamReceiver:{}",message);
return"收到了";
}
/**
* 接收到消息之后返回的消息
* */
@StreamListener(value = StreamClient.INPUT2)
public void process2(OrderDTO message){
log.info("StreamReceiver2:{}",message);
}
}
- SendMessageController
/**
* @Title
* @Author zwj
* @Date 2018/11/26
* @Description
*/
@RestController
public class SendMessageController {
@Autowired
private StreamClient streamClient;
// @GetMapping("/sendMessage")
// public void process(){
// String message = "now "+new Date();
// streamClient.input().send(MessageBuilder.withPayload(message).build());
// }
@GetMapping("/sendMessage")
public void process(){
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOrderId("123456");
streamClient.input().send(MessageBuilder.withPayload(orderDTO).build());
}
}