目录
一:消息驱动
前面几章,我们说完了cloud的基本组件。下面我们就聊聊cloud的一些非必须但是重要的组件。第一个组件就是springcloud stream。stream是基于kafka或者rabbitmq的构建于Spring Integration的消息机制的应用。简而言之,就是封装了kafka或者rabbitmq中间组件的消息层应用。几个重要的概念:
~Binder:这是个抽象概念,我更愿意叫其消息绑定器。在stream中,就是通过其来外观表现通道管理和组件类型
~Publish-Subscribe:消息的驱动方式。在这里,我们不需要去在意其消息件的实现,独立创建一个通道,将消息发送到通道中,
发送给全部或者指定的部分。
~Consumer Groups:消息分组,这其实是kafka中的概念,只不过在stream中扩展了rabbitmq中概念。
~Durability:消息选择持久化
~Bindings:stream的绑定,将消息中间件和stream的配置器连接起来,对内封装。
二:搭建简单的消息发送-接受者
1.消息发送者(生产者)
(1)aoolication.yml
spring:
application:
name: provider-demo
cloud:
stream:
binders:
test:
type: rabbit ###声明消息组件类型
environment:
spring:
rabbitmq: ###消息链接
addresses: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
testOutPut: ###声明消息通道,注意这里声明一定后面的代码中一定是这个通道名称
destination: testRabbit
content-type: application/json
default-binder: test
server:
port: 8079
(2)消息通道和消息发送
package springcloud.stream.mq;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
/**
* 消息通道设计
*
*
* @author monxz
*
* @date 2019年6月20日
*/
public interface MqMessageSource {
String TEST_OUT_PUT = "testOutPut"; //这里的名称和yml配置中的声明一致
@Output(TEST_OUT_PUT)
MessageChannel testOutPut();
}
package springcloud.stream.mq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
/**
*
*消息发送
*
* @author monxz
*
* @date 2019年6月20日
*/
@EnableBinding(MqMessageSource.class)
public class MqMessageProducer {
@Autowired
@Output(MqMessageSource.TEST_OUT_PUT)
private MessageChannel channel;
public void sendMsg(String msg) {
channel.send(MessageBuilder.withPayload(msg).build());
System.err.println("消息发送成功00:"+msg);
}
}
(3)Controller实现消息发送
package springcloud.stream.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springcloud.stream.domain.User;
import springcloud.stream.mq.MqMessageProducer;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private MqMessageProducer mqMessageProducer;
@GetMapping(value = "/testMq")
public String testMq(@RequestParam("msg")String msg){
mqMessageProducer.sendMsg(msg);
return "发送成功";
}
}
2.消息接受者(消费者)
(1)application.yml
spring:
application:
name: consumer-demo
cloud:
stream:
binders:
test:
type: rabbit
environment:
spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
testInPut: ###接受者通道名称
destination: testRabbit
content-type: application/json
default-binder: test
server:
port: 8089
(2)消息接收
/**
* 消息接收通道
*
*
* @author monxz
*
* @date 2019年6月20日
*/
public interface MqMessageSource {
String TEST_IN_PUT = "testInPut";
@Input(TEST_IN_PUT)
SubscribableChannel testInPut();
}/**
* 消息接收通道
*
*
* @author monxz
*
* @date 2019年6月20日
*/
public interface MqMessageSource {
String TEST_IN_PUT = "testInPut";
@Input(TEST_IN_PUT)
SubscribableChannel testInPut();
}
package cn.org.zhixiang.mq;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
/**
* 消息接收
*
*
* @author monxz
*
* @date 2019年6月20日
*/
@EnableBinding(MqMessageSource.class)
public class MqMessageConsumer {
@StreamListener(MqMessageSource.TEST_IN_PUT)
public void messageInPut(Message<String> message) {
System.err.println(" 消息(01)接收成功:" + message.getPayload());
}
}
三:消息分组
说明:上述中实现了消息传递是一种临时的,而在我们需要持久化是和分组时。
(1)消息生产者(同上一样)
(2)消息消费者(除了yml配置有点不一样)
spring:
application:
name: consumer-demo
cloud:
stream:
binders:
test:
type: rabbit
environment:
spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
testInPut:
destination: testRabbit
content-type: application/json
default-binder: test
group: group2 ###新增分组
server:
port: 8090
复制客户端4分,分成2各组,测试发现一个组只能有一个接收消息
四:指定消息接收
说明:指定消息接受者
1.消息生产者
(1)yml配置
spring:
application:
name: provider-demo
cloud:
stream:
binders:
test:
type: rabbit
environment:
spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
testOutPut:
destination: testRabbit
content-type: application/json
default-binder: test
producer: #添加消息指定
partitionKeyExpression: headers['partitionKey'] #头部设定 partitionKey 属性
partitionCount: 2 #个数
server:
port: 8079
(2)消息发送中配置
package springcloud.stream.mq;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
public interface MqMessageSource {
String TEST_OUT_PUT = "testOutPut";
@Output(TEST_OUT_PUT)
MessageChannel testOutPut();
}
package springcloud.stream.mq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
@EnableBinding(MqMessageSource.class)
public class MqMessageProducer {
@Autowired
@Output(MqMessageSource.TEST_OUT_PUT)
private MessageChannel channel;
public void sendMsg(String msg,Integer parkey) {
channel.send(MessageBuilder.withPayload(msg).setHeader("partitionKey", parkey).build()); //配置属性
System.err.println("消息发送成功00:"+msg);
}
}
(3).controller
package springcloud.stream.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springcloud.stream.domain.User;
import springcloud.stream.mq.MqMessageProducer;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private MqMessageProducer mqMessageProducer;
@GetMapping(value = "/testMq")
public String testMq(@RequestParam("msg")String msg,@RequestParam("partitionKey")Integer partitionKey){
mqMessageProducer.sendMsg(msg,partitionKey);
return "发送成功";
}
}
2.消息接受者
spring:
application:
name: consumer-demo
cloud:
stream:
binders:
test:
type: rabbit
environment:
spring:
rabbitmq:
addresses: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
testInPut:
destination: testRabbit
content-type: application/json
default-binder: test
consumer:
partitioned: true #消息接收
instance-count: 2 #个数
instance-index: 1 #索引
server:
port: 8092
其他没变。注意服务端的partitionKey属性对应着 instance-index的值
五:消息返回
说明:消息接收完成返回给服务端接收完成
1.生产者:这里其实消息生产者就变成了变成上面的消息接受者
/**
*消息接受者,返回客户端
* 接收到消息并且返回
* @param message
* @return
*/
@StreamListener(MqMessageSource.TEST_IN_PUT)
@SendTo({"msg_return"})
public String messageInPut(Message<String> message) {
System.err.println(" 消息(04)接收成功:" + message.getPayload());
return "消息04接收到信息,并添加返回";
}
2.消费者
package springcloud.stream.mq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Component;
/**
* d
*
* @author syj
* CreateTime 2018/12/03
* describe:
*/
@Component
@EnableBinding(MqMessageSource.class)
public class MqMessageProducer {
@Autowired
@Output(MqMessageSource.TEST_OUT_PUT)
private MessageChannel channel;
public void sendMsg(String msg,Integer parkey) {
channel.send(MessageBuilder.withPayload(msg).setHeader("partitionKey", parkey).build());
System.err.println("消息发送成功00:"+msg);
}
@StreamListener(MqMessageSource.TEST_IN_PUT) //这里的名称桶@SendTo里面的值一样
public void getReturnMsg(Message<String> message) {
System.err.println("消息是否已经被消费:"+message.getPayload());
}
}
六:动态消息通道绑定
说明:动态绑定消息通道,代码改于(二),
1.修改生产者
package springcloud.stream.mq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;
@Component
@EnableBinding
public class MqMessageProducer {
//动态绑定的发送者
@Autowired
private BinderAwareChannelResolver reslover;
//通过dest指定某一个通道
public void DySendMsg(String msg,String dest) {
System.err.println("消息发送");
reslover.resolveDestination(dest).send(
MessageBuilder.withPayload(msg)
.build()
);
}
}
3.修改消费者
package springcloud.mq;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface MqMessageSource {
//动态绑定通道,定义2个通道
String Dy_c1="dy-c1";
String Dy_c2="dy-c2";
@Input(Dy_c1)
SubscribableChannel Dyc1();
@Input(Dy_c2)
SubscribableChannel Dyc2();
}
package springcloud.mq;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Component;
/**
* 定义通道监听
*
*
* @author monxz
*
* @date 2019年6月20日
*/
@EnableBinding(MqMessageSource.class)
public class MqMessageConsumer {
/**
* 动态消息1
* @param message
*/
@StreamListener(MqMessageSource.Dy_c1)
public void dyc1(Message<String> message) {
System.err.println("动态通道接收到消息,内容为:"+message.getPayload());
}
/**
* 动态消息2
* @param message
*/
@StreamListener(MqMessageSource.Dy_c2)
public void dyc2(Message<String> message) {
System.err.println("动态通道接收到消息,内容为:"+message.getPayload());
}
}