虚拟机镜像网盘路径
链接:百度网盘 请输入提取码 提取码:egno
618M:Virtualbox用的,纯净的RockyLinux
3.5G:VMware用的,安装好所有软件的RockyLinux
4.1G:Virtualbox用的,安装好所有软件的RockyLinux
当接收消息队列中信息的模块运行发送异常时,怎么完成事务的回滚?
当消息队列中(stock)发生异常时,在异常处理的代码中,我们可以向消息的发送者(order)发送消息,然后通知发送者(order)处理,消息的发送者(order)接收到消息后,一般要手写代码回滚,如果回滚代码过程中再发生异常,就又要思考回滚方式,如果一直用消息队列传递消息的话,可能发生异常的情况是无止境的
所以我们在处理消息队列异常时,经常会设置一个"死信队列"
死信队列没有任何处理者,通常情况下会有专人周期性的处理死信队列的消息
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。Kafka最初是由LinkedIn开发,并随后于2011年初开源。
Kafka是一个结构相对简单的消息队列(MQ)软件
kafka软件结构图
Kafka Cluster(Kafka集群)
Partition(分片)
Producer:消息的发送方,也就是消息的来源,Kafka中的生产者
order就是消息的发送方,在Dubbo中order是消费者,这个身份变化了
Consumer:消息的接收方,也是消息的目标,Kafka中的消费者
stock就是消息的接收方,在Dubbo中stock是生产者,这个身份变化了
Topic:话题或主题的意思,消息的收发双方要依据同一个话题名称,才不会将信息错发给别人
Record:消息记录,就是生产者和消费者传递的信息内容,保存在指定的Topic中
Kafka作为消息队列,它和其他同类产品相比,突出的特点就是性能强大
Kafka将消息队列中的信息保存在硬盘中
Kafka对硬盘的读取规则进行优化后,效率能够接近内存
硬盘的优化规则主要依靠"顺序读写,零拷贝,日志压缩等技术"
Kafka处理队列中数据的默认设置:
必须将我们kafka软件的解压位置设置在一个根目录,文件夹名称尽量短(例如:kafka)
然后路径不要有空格和中文
我们要创建一个空目录用于保存Kafka运行过程中产生的数据
本次创建名称为data的空目录
下面进行Kafka启动前的配置
先到D:\kafka\config下配置有文件zookeeper.properties
找到dataDir属性修改如下
dataDir=D:/data
修改完毕之后要Ctrl+S进行保存,否则修改无效!!!!
注意D盘和data文件夹名称,匹配自己电脑的真实路径和文件夹名称
还要修改server.properties配置文件
log.dirs=D:/data
修改注意事项和上面相同
要想启动Kafka必须先启动Zookeeper
zoo:动物园
keeper:园长
可以引申为管理动物的人
Linux服务器中安装的各种软件,很多都是有动物形象的
如果这些软件在Linux中需要修改配置信息的话,就需要进入这个软件,去修改配置,每个软件都需要单独修改配置的话,工作量很大
我们使用Zookeeper之后,可以创建一个新的管理各种软件配置的文件管理系统
Linux系统中各个软件的配置文件集中到Zookeeper中
实现在Zookeeper中,可以修改服务器系统中的各个软件配置信息
长此以往,很多软件就删除了自己写配置文件的功能,而直接从Zookeeper中获取
Kafka就是需要将配置编写在Zookeeper中的软件之一
所以要先启动zookeeper才能启动kafka
进入路径D:\kafka\bin\windows
输入cmd进入dos命令行
D:\kafka\bin\windows>zookeeper-server-start.bat ..\..\config\zookeeper.properties
总体方式一样,输入不同指令
D:\kafka\bin\windows>kafka-server-start.bat ..\..\config\server.properties
2.5.4附录
Mac系统启动Kafka服务命令(参考):
# 进入Kafka文件夹
cd Documents/kafka_2.13-2.4.1/bin/
# 动Zookeeper服务
./zookeeper-server-start.sh -daemon ../config/zookeeper.properties
# 启动Kafka服务
./kafka-server-start.sh -daemon ../config/server.properties
Mac系统关闭Kafka服务命令(参考):
# 关闭Kafka服务
./kafka-server-stop.sh
# 启动Zookeeper服务
./zookeeper-server-stop.sh
在启动kafka时有一个常见错误
wmic不是内部或外部命令
这样的提示,需要安装wmic命令,安装方式参考
我在cmd输入wmic,提示说不是内部或外部命令,也不是可运行的程序或批处理文件,请问如何解决_百度知道
启动的zookeeper和kafka的窗口不要关闭
我们在csmall项目中编写一个kafka使用的演示
csmall-cart-webapi模块
添加依赖
org.springframework.kafka
spring-kafka
com.google.code.gson
gson
修改yml文件进行配置
spring:
kafka:
# 定义kafka的配置信息:确定kafka的ip和端口
bootstrap-servers: localhost:9092
# consumer.group-id必须设置的配置
# 意思是"话题分组",这个配置的目的是为了区分不同的项目而配置的
# 本质上,这个分组名称会前缀在所有话题名名称之前,例如话题名称为message,真正发送时csmall.message
consumer:
group-id: csmall
在SpringBoot启动类中添加启动Kafka的注解
@SpringBootApplication
@EnableDubbo
// 启动支持Kafka的功能
@EnableKafka
// 为了测试Kafka发送消息的功能
// 我们利用SpringBoot自带的调用工具,周期性的向kafka发送消息
// 下面的注解和Kafka软件没有直接必然的依赖关系
@EnableScheduling
public class CsmallCartWebapiApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallCartWebapiApplication.class, args);
}
}
下面我们就可以实现周期性的向kafka发送消息并接收的操作了
编写消息的发送
cart-webapi包下创建kafka包
包中创建Producer类来发送消息
// 这个类实现周期性向Kafka发送消息
// 因为Spring的任务调度需要将当类保存到Spring容器,所以要加下面的注解
@Component
public class Producer {
// 直接获取Spring容器中支持向Kafka发送消息的对象
// 这个对象会在SpringBoot启动时自动的装配到Spring容器
// KafkaTemplate<[话题的类型],[消息的类型]>
@Autowired
private KafkaTemplate kafkaTemplate;
// 实现每隔10秒向Kafka发送消息
int i=1;
// 设置每隔10秒(10000毫秒)运行一次的调度注解
@Scheduled(fixedRate = 10000)
// SpringBoot启动后,每隔10秒运行一次下面的方法
public void sendMessage(){
// 实例化一个Cart对象,赋值并发送给Kafka
Cart cart=new Cart();
cart.setId(i++);
cart.setCommodityCode("PC100");
cart.setUserId("UU100");
cart.setPrice(RandomUtils.nextInt(90)+10);
cart.setCount(RandomUtils.nextInt(10)+1);
// "{"id":"1","price":"58",...}"
// 利用Gson依赖,将cart对象转换为上面样式的json格式字符串
Gson gson=new Gson();
String json=gson.toJson(cart);
System.out.println("要发送的信息为:"+json);
// 执行发送
kafkaTemplate.send("myCart",json);
}
}
kafka包中创建一个叫Consumer的类来接收消息
接收消息的类可以是本模块的类,也可以是其它模块的类,编写的代码是完全一致
// 需要接收kafka的消息,因为kafkaTemple是Spring容器管理的对象
// 所以消息的接收功能,也要保存到Spring容器中
@Component
public class Consumer {
// SpringKafka接收消息依靠框架提供的"监听机制"
// 框架中有一个线程,一直实时关注Kafka的消息接收
// 如果我们指定的话题名称(myCart)接收了消息,那么这条线程就会自动调用下面的方法
@KafkaListener(topics = "myCart")
// 下面方法的参数也是来自监听机制,也就是myCart话题接收到的消息
public void received(ConsumerRecord record){
// 接收消息的方法参数必须是ConsumerRecord
// 泛型类型和发送消息时指定的泛型一致<[话题名称的类型],[消息的类型]>
// record就是消息本身,可以从这个对象中获得具体消息内容
String json=record.value();
// 将json格式的字符串转换为java对象
Gson gson=new Gson();
// 执行转换
Cart cart=gson.fromJson(json,Cart.class);
//{"id":2,"commodityCode":"PC100","price":61,"count":9,"userId":"UU100"}
System.out.println(cart);
}
}
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。 AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。 RabbitMQ 最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
1.可靠性(Reliability) RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
2.灵活的路由(Flexible Routing) 在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
3.消息集群(Clustering) 多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker
4.高可用(Highly Available Queues) 队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
5.多种协议(Multi-protocol) RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
6.多语言客户端(Many Clients) RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
7.管理界面(Management UI) RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。
8.跟踪机制(Tracing) 如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
9.插件机制(Plugin System) RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
苹果mac系统的同学直接苍老师网站看MacOS安装RabbitMQ的技术贴
RabbitMQ是Erlang语言开发的,所以要先安装Erlang语言的运行环境
下载Erlang的官方路径
OTP Versions Tree
安装的话就是双击
安装过程中都可以使用默认设置,需要注意的是
不要安装在中文路径和有空格的路径下!!!
下载RabbitMQ的官方网址
Installing on Windows — RabbitMQ
安装也是双击即可
不要安装在中文路径和有空格的路径下!!!
要想运行RabbitMQ必须保证系统有Erlang的环境变量
配置Erlang环境变量
把安装Erlang的bin目录配置在环境变量Path的属性中
启动RabbitMQ
找到RabbitMQ的安装目录
可能是:
D:\tools\rabbit\rabbitmq_server-3.10.1\sbin
具体路径根据自己的情况寻找
地址栏运行cmd
输入启动指令如下
D:\tools\rabbit\rabbitmq_server-3.10.1\sbin>rabbitmq-plugins enable rabbitmq_management
结果如下
运行完成后,验证启动状态
RabbitMQ自带一个管理的界面,所以我们可以访问这个界面来验证它的运行状态
http://localhost:15672
登录界面用户名密码
guest
guest
登录成功后看到RabbitMQ运行的状态
如果启动失败,可以手动启动RabbitMQ
参考路径如下
RabbitMQ环境的搭建和报错,差点让我放弃,怀着试试的心态
RabbitMQ软件支持很多种消息队列的发送方式的
使用的比较多的是路由模式
和Kafka不同,Kafka是使用话题名称来收发信息,结构简单
RabbitMQ是使用交换机\路由key指定要发送消息的队列
消息的发送者发送消息时,需要指定交换机和路由key名称
消息的接收方接收消息时,只需要指定队列的名称
在编写代码上,相比于Kafka,每个业务要编写一个配置类
这个配置类中要绑定交换机和路由key的关系,以及路由Key和队列的关系
csmall-stock-webapi项目中测试RabbitMQ
可以利用之前我们使用Quartz实现的每隔一段时间输出当前日期信息的方法改为发送消息
添加依赖
org.springframework.boot
spring-boot-starter-amqp
yml文件配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 设置虚拟host /表示当前目录 设置是固定的
virtual-host: /
交换机\路由Key\队列的配置类
RabbitMQ要求我们在java代码级别设置交换机\路由Key\队列的关系
我们在quartz包下,创建config包
包中创建配置信息类RabbitMQConfig
// 这个类是配置RabbitMQ中交换机,路由Key和队列的配置类
// 交换机和队列是实际对象,而路由key只是关系,他们都需要保存到Spring容器来管理,才能生效
@Configuration
public class RabbitMQConfig {
// 需要涉及的交换机\路由Key\队列的名称都需要定义常量来声明
public static final String STOCK_EX="stock_ex";
public static final String STOCK_ROUT="stock_rout";
public static final String STOCK_QUEUE="stock_queue";
// 声明交换机对象,保存到Spring容器
// 根据实际需求生成交换机的数量
@Bean
public DirectExchange stockDirectExchange(){
return new DirectExchange(STOCK_EX);
}
// 声明队列对象,保存Spring容器
@Bean
public Queue stockQueue(){
return new Queue(STOCK_QUEUE);
}
// 声明路由key的绑定关系,路由Key不是实体对象,本质上是一种关系的记录
// 所以要声明哪个交换机绑定了哪个队列
@Bean
public Binding stockBinding(){
return BindingBuilder.bind(
stockQueue()).to(stockDirectExchange()).with(STOCK_ROUT);
}
}
RabbitMQ发送消息
我们在QuartzJob类中输出时间的代码后继续编写代码
实现RabbitMQ消息的发送
public class QuartzJob implements Job {
// 向RabbitMQ发送消息的对象
// 也是通过配置之后,SpringBoot启动创建的
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 实现一个简单的任务做演示
// 例如输出当前时间
System.out.println("-------------------"+ LocalDateTime.now() +
"--------------------");
// 实例化Stock对象
Stock stock=new Stock();
stock.setId(20);
stock.setCommodityCode("PC100");
stock.setReduceCount(10);
// 利用RabbitTemplate发送消息
// convertAndSend([交换机名称],[路由Key名称],[要发送的消息])
rabbitTemplate.convertAndSend(RabbitMQConfig.STOCK_EX,
RabbitMQConfig.STOCK_ROUT,stock);
System.out.println("发送消息完成:"+stock);
}
}
我们可以通过修改QuartzConfig类中的Cron表达式修改调用的周期
CronScheduleBuilder cron=
CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
按上面的cron修改之后,会每隔10秒运行一次发送消息的操作
接收RabbitMQ的消息
quartz包下再创建一个新的类用于接收信息
RabbitMQConsumer代码如下
// 当前类也要实例化对象,保存到Spring容器才能实现监听效果
@Component
// 和Kafka不同,RabbitMQ的监听器注解需要编写在类上
@RabbitListener(queues = {RabbitMQConfig.STOCK_QUEUE})
public class RabbitMQConsumer {
// 类上编写监听,但是不能直接确实是类中的哪个方法
// 所以我们需要在具体执行队列中消息处理的方法上添加指定注解
// 这样当队列中有消息时,就会自动运行这个方法
// 当前类只允许一个方法添加这个注解
// 参数直接声明发送的对象类型即可
@RabbitHandler
public void process(Stock stock){
System.out.println("消息的接收完成,内容:"+stock);
}
}
启动Nacos\RabbitMQ\Seata
启动stock-webapi
根据Cron表达式,消息会在0/10/20/30/40/50秒数时运行
测试成功表示一切正常
课堂作业
在csmall-business模块中
创建一个rabbit包
包中创建RabbitConfig类配置交换机\路由Key\和队列的配置(允许复制)
创建一个消息的发送者(建议使用Spring任务调度)RabbitProducer
每隔10秒发送一个Order对象到RabbitMQ
最后编写一个类接收RabbitMQ中的信息RabbitConsumer