MQ(消息中间件):ActiveMQ、RabbitMQ、RocketMQ、kafkaMQ
解决问题:多种MQ适配(切换、维护、开发)
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
官方连接:https://spring.io/projects/spring-cloud-stream
中文操作手册:https://m.wang1314.com/doc/webapp/topic/20971999.html
标准流程套路:Binder、Channel、Source和Sink
编码API和常用注解:
RabbitMQ环境OK
新建三个模块:
pom
org.springframework.cloud
spring-cloud-starter-stream-rabbit
yml
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: #配置要绑定的rabbitmq的服务信息
defaultRabbit: #定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
output: #一个通道名称
destination: studyExchange #表示要使用Exchange名称定义
content-type: application/json #设置消息类型,本次为json,文本为“text/plain”
binder: defaultRabbit #设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 #心跳时间间隔
lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
instance-id: send-8801.com #在信息列表显示主机名称
prefer-ip-address: true #访问路径变为IP地址
主启动类StreamMQMain8801
package com.jiao.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author jyl
* @create 2021-3-15
*/
@SpringBootApplication
public class StreamMQMain8801 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8801.class,args);
}
}
业务类
package com.jiao.springcloud.service;
public interface IMessageProvider {
public String send();
}
package com.jiao.springcloud.service.impl;
import com.jiao.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import javax.annotation.Resource;
import java.util.UUID;
@EnableBinding(Source.class) //定义消息的推送管道
public class IMessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output; //消息发送管道
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
System.out.println("*********serial:"+serial);
return null;
}
}
package com.jiao.springcloud.controller;
import com.jiao.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class SendMessageController {
@Resource
private IMessageProvider messageProvider;
@GetMapping(value = "/sendMessage")
public String sendMessage(){
return messageProvider.send();
}
}
测试(启动7001、启动RabbitMQ、启动8801)
http://localhost:8801/sendMessage
pom
org.springframework.cloud
spring-cloud-starter-stream-rabbit
yml
主启动
package com.jiao.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author jyl
* @create 2021-3-15
*/
@SpringBootApplication
public class StreamMQMain8802 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8802.class,args);
}
}
业务类
package com.jiao.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void input(Message message){
System.out.println("消费者1号,----->接收到的消息:"+message.getPayload()+"\t port:"+serverPort);
}
}
测试(7001、RabbitMQ、8801、8802)
测试8801发送8802接收消息:http://localhost:8801/sendMessage
8803与8802一样,yml稍作修改。
启动(RabbitMQ、7001服务注册、8801消息生产、8802消息消费、8803消息消费)
运行后两个问题:(分组特性解决)
有重复消费问题
8801请求sendMessage,8802和8803都消费了。
解决:分组和持久化属性group(不同组可以全面消费,同一组会发生竞争关系,其中只有一个可以消费。)
8802/8803变成不同的组,group两个不同。group:(jiaoA,jiaoB)
8802修改yml:
8803修改yml:与上位置同
消费了两次。
8802/8803实现轮训分组,每次只有一个消费者,8801模块的发的消息只能被8802或者8803其中一个接收到,这样避免了重复消费。
8802/8803变成不同的组,group两个不同。group:(jiaoA,jiaoA)
结果:8801发送两次,8802消费一次,8803消费一次
链路中任何一环出现延时或者错误都会引起整个请求最后失败。(提供一套完整的服务跟踪的解决方案,兼容支持了zipkin。)
H版后不用安装Zipkin Server,直接调用jar包。
下载地址:http://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/(zipkin-server-2.12.9-exec.jar)
在下载路径启动cmd窗口:输入 java -jar zipkin-server-2.12.9-exec.jar
后台启动:
运行控制台:http://localhost:9411/zipkin
Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识。
span:表示调用链路来源,通俗讲span就是一次请求信息。
pom
org.springframework.cloud
spring-cloud-starter-zipkin
yml
...
spring:
application:
name: cloud-payment-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1 #采样率值介于0-1之间,1表示全部采集
...
业务类PaymentController
...
@GetMapping("/payment/zipkin")
public String paymentZipkin(){
return "I'm PaymentZipkin server fall back.";
}
...
pom、yml与上面一样
业务类OrderController
...
@GetMapping("/consumer/payment/zipkin")
public String paymentZipkin(){
String result = restTemplate.getForObject("http://localhost:8001"+"payment/zipkin",String.class);
return result;
}
...