Spring Cloud Stream 构建消息驱动微服务
Spring Cloud Stream 进一步封装了消息队列,可以做到代码层面对消息队列无感知。

添加依赖
消息队列是 RabbitMQ ,如果你是用的是kafka,换成对应的spring-cloud-starter-stream-kafka依赖即可
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
配置文件配置RabbitMQ的地址信息
spring-cloud-starter-stream-rabbit是Spring Cloud Stream对RabbitMQ的封装,包含对RabbitMQ的自动化配置,比如连接的RabbitMQ的默认地址localhost,默认端口5672,默认用户guest,默认密码guest,如果采用的是如上默认配置,可以不用修改配置。
配置文件放到了远端的Git,通过config server 拉取配置
server: port: 8081 spring: application: name: order datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/o2o_order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root jpa: show-sql: true rabbitmq: host: 192.168.223.139 username: guest password: guest eureka: client: service-url: defaultZone: http://localhost:8762/eureka/ env: dev-test
接口定义

Sink和Source两个接口分别定义输入通道和输出通道,Processor通过继承Source和Sink,同时具有输入通道和输出通道。就模仿Sink和Source,自定义一个消息通道
public interface MySink { // 同一个服务里面的通道名字不能一样,在不同的服务里可以相同名字的通道 // 否则启动抛出如下异常 bean definition with this name already exists String INPUT = "MyMsgInput";
@Input(MySink.INPUT)
SubscribableChannel input();
}
定义一个名为MyMsgInput的消息输入通道,@Input注解的参数则表示消息通道的名称
接收方 @EnableBinding @StreamListener

StreamReceive 用来接收RabbitMQ发送来的消息
第一步: 使用了@EnableBinding注解实现对消息通道的绑定,在该注解中还传入一个参数MySink.class,MySink是一个自定义接口,主要功能是实现对输入消息通道绑定的定义。
第二步:在StreamReceiver 类中定义processStreamMsg方法,重点是在该方法上添加@StreamListener注解,该注解表示该方法为消息中间件上数据流的事件监听器,MySink.INPUT参数表示这是input消息通道上的监听处理器。
@Component // Step1 注解 绑定消息输入的接口 @EnableBinding(MySink.class) @Slf4j public class StreamReceive { @StreamListener(MySink.INPUT) @SendTo(Source.output) public String onMessage(Object object){ log.info("Input Stream receive message :{}",object); return "receive ok"; } }
@RestController
@Slf4j
public class MsgStreamController {
@Autowired
private MySink mySink;
@GetMapping("/sendMsgByStream")
public void sendMsgByStream(){
String msg="I am one message from Spring Cloud Stream";
Message<String> message = MessageBuilder.withPayload(msg).setHeader("contentType","text/plain").build();
mySink.input().send(message);
}
}
通过 @Autowired自动注入刚才的Sink接口,然后调用 sink.input().send方法发送消息即可,启动服务,观察RabbitMQ上的队列 ,自动创建了一个

访问: http://localhost:8081/sendMsgByStream
消费组
需求: 由于服务可能会有多个实例同时在运行,我们只希望消息被一个实例所接收
启动多个服务实例
为了多启动几个节点,需要把定义在远端Git上的要加载到bootstrap.yml中的端口信息给注释掉,否则第二个端口因端口冲突起不来。
查看在Eureka Server上的注册情况
看看RabbitMQ的消息队列情况:

消息执回
消费者收到消息后给发送方一个ACK确认,该如何做呢?
@Component // Step1 注解 绑定消息输入的接口 @EnableBinding(MySink.class) @Slf4j public class StreamReceive { /** * @StreamListener - 绑定监听到指定的Exchange。 * @param object 接收到的消息内容。此参数类型必须是可序列化的。 */ @StreamListener(MySink.INPUT) @SendTo(Source.output) public String onMessage(Object object){ log.info("receive message :{}",object); return "receive ok"; } }
接收到消息后,返回给Source.OUTPUT一个消息,直接使用@SendTo直接即可,就会将返回的字符串发送给Source.OUTPUT通道
public interface Source { String output="myMsgOutput"; @Output(output) MessageChannel output(); }
写一个该消息的接收方
@Component @Slf4j @EnableBinding(Source.class) public class StreamRecevie2 { @StreamListener(Source.output) public void onMessage(String message) { log.info("Output Stream Receive:{}",message); } }
访问:http://localhost:7001/sendObject

浙公网安备 33010602011771号