public interface Session extends Runnable {
int AUTO_ACKNOWLEDGE = 1; // 自动签收
int CLIENT_ACKNOWLEDGE = 2; // 客户端确认签收
int DUPS_OK_ACKNOWLEDGE = 3; // 自动批量进行签收
int SESSION_TRANSACTED = 0; // 事务提交就签收
}
注意:事务会话中,如果一个事务提交成功,这个时候就签收!事务混滚,消息就会被再次传送。
没有事务的情况下,如果是 客户端确认签收,那则需要手动签收。
一般用自动 手动
message.acknowledge(); // 消息确认签收
Broker:将 ActvieMQ … 以代码的形式内嵌到我们程序中。保证随时可以启动进行使用。
官网地址:http://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection
com.fasterxml.jackson.core
jackson-databind
2.11.0
代码
public class EmbedBroker {
public static void main(String[] args) throws Exception {
// 1.创建服务
BrokerService broker = new BrokerService();
// 2、配置连接地址
// jmx是java的一个新框架
// 允许将我们所有的资源(软件和硬件)封装一个Java对象,直接暴露在服务中即可使用
broker.setUseJmx(true);
broker.addConnector("tcp://localhost:61616");
// 3、启动
broker.start();
}
}
1.导入依赖
org.springframework.boot
spring-boot-starter-activemq
org.springframework.boot
spring-boot-starter-web
2.配置yaml
server:
port: 9000
spring:
activemq:
broker-url: tcp://127.0.0.1:61616
user: admin
password: admin
jms:
pub-sub-domain: false # false 队列, true 主题
myqueue: coding-queue
比较
server.port=9090
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 在考虑结束之前等待的时间
#spring.activemq.close-timeout=15s
# 默认代理URL是否应该在内存中。如果指定了显式代理,则忽略此值。
spring.activemq.in-memory=true
# 是否在回滚回滚消息之前停止消息传递。这意味着当启用此命令时,消息顺序不会被保留。
spring.activemq.non-blocking-redelivery=false
# 等待消息发送响应的时间。设置为0等待永远。
spring.activemq.send-timeout=0
#默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置
spring.jms.pub-sub-domain=true
#账号
spring.activemq.user=admin
# 密码
spring.activemq.password=admin
# 是否信任所有包
#spring.activemq.packages.trust-all=
# 要信任的特定包的逗号分隔列表(当不信任所有包时)
#spring.activemq.packages.trusted=
# 当连接请求和池满时是否阻塞。设置false会抛“JMSException异常”。
#spring.activemq.pool.block-if-full=true
# 如果池仍然满,则在抛出异常前阻塞时间。
#spring.activemq.pool.block-if-full-timeout=-1ms
# 是否在启动时创建连接。可以在启动时用于加热池。
#spring.activemq.pool.create-connection-on-startup=true
# 是否用Pooledconnectionfactory代替普通的ConnectionFactory。
#spring.activemq.pool.enabled=false
# 连接过期超时。
#spring.activemq.pool.expiry-timeout=0ms
# 连接空闲超时
#spring.activemq.pool.idle-timeout=30s
# 连接池最大连接数
#spring.activemq.pool.max-connections=1
# 每个连接的有效会话的最大数目。
#spring.activemq.pool.maximum-active-session-per-connection=500
# 当有"JMSException"时尝试重新连接
#spring.activemq.pool.reconnect-on-exception=true
# 在空闲连接清除线程之间运行的时间。当为负数时,没有空闲连接驱逐线程运行。
#spring.activemq.pool.time-between-expiration-check=-1ms
# 是否只使用一个MessageProducer
#spring.activemq.pool.use-anonymous-producers=true
3.配置Bean
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
// 注意导包,util java中的队列
// JMS 下的队列
import javax.jms.Queue;
@Configuration
@EnableJms // 声明对 JMS 注解支持
public class ActiveMQConfig {
@Value("${myqueue}")
private String myqueue;
@Bean
public Queue queue(){
return new ActiveMQQueue(myqueue);
}
}
4.业务 生产者
@Component
public class QueueProduce {
// springboot xxxTemplate
@Autowired
private JmsMessagingTemplate template;
@Autowired
private Queue queue;
public void produceMsg(){
template.convertAndSend(queue,"produceMsg");
}
}
@RestController
public class ActiveMQController {
@Autowired
private QueueProduce queueProduce;
@RequestMapping("/queue/send")
public void queue_send(){
queueProduce.produceMsg();
System.out.println(" 消息发送成功");
}
}
@Component
public class QueueProduce {
@Autowired
private JmsMessagingTemplate template;
@Autowired
private Queue queue;
@Scheduled(fixedDelay = 3000)
public void produceMsg(){
template.convertAndSend(queue,"produceMsg");
System.out.println("定时发送消息");
}
}
@EnableScheduling
@SpringBootApplication
public class ActivemqApplication {
public static void main(String[] args) {
SpringApplication.run(ActivemqApplication.class, args);
}
}
@Configuration
public class QueueConsumer {
// 通过监听注解实现消息接收。
// 具体消费 目的地:(队列还是主题) 接收到的消息
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage) throws JMSException {
System.out.println(textMessage.getText());
}
}
@Bean
public Topic topic(){
return new ActiveMQTopic(mytopic);
}
与队列一样 把 queue 换 topic
@Component
public class TopicProduce {
@Autowired
private JmsMessagingTemplate template;
// 先启动消费者。。
@Autowired
private Topic topic;
@Scheduled(fixedDelay = 3000)
public void produceMsgScheduled(){
template.convertAndSend(topic,"produceMsgTopic");
System.out.println("系统正在定时发送消息.....");
}
}
@Configuration
public class TopicConsumer {
// 具体消费 目的地:(队列还是主题) 接收到的消息
@JmsListener(destination= "${mytopic}")
public void receive(TextMessage textMessage) throws JMSException {
// 接收消息
System.out.println(textMessage.getText());
}
}
配置:conf/activemq.xml 就是一个 Spring的配置
tcp
网络传输必须序列化,消息通过一个叫wire protocol 来序列化成字节流
wire protocol默认openwire
参数配置参考文档(一般不动)
http://activemq.apache.org/tcp-transport-reference
nio
端口需自定义
升级版
auto+nio
其他协议不太常用 用的话需更改代码
kahaDB
像redisaof日志存储 5.4默认
KahaDB特性:
1、日志的方式存储的信息
2、B-Tree 索引,快速更新
3、可以快速恢复数据
db.data 保存数据的,B-tree 所用。
db.redo 恢复消息的。自动启动然后备份
lock 锁,表示 kahadb的读写权限。
jdbc mysql
比较慢
引入三个jar 到 activemq 安装的lib 目录下
http://activemq.apache.org/persistence
LevelDB
官网地址:http://activemq.apache.org/masterslave
zookeeper集群
这篇博客linux:https://www.cnblogs.com/yangzhenlong/p/8270835.html
win上集群
1.复制3分zookeeper
2.复制zoo_sample.cfg一份更改成zoo.cfg
3.在每个apache-zookeeper-9001下建data目录
data建myid
内容对应9001对应1 , 9002对应2
4.配置zoo.cfg
分别更改端口和dataDir路径 其他不变
5.可以写个脚本 一键启动
MQ集群
node-1 61616 8161
node-2 61617 8162
node-3 61618 8163
1.复制3分mq
2、保证 brokername 相同,默认是localhost,集群的前提就是 borkerName一致。
3.修改activemq.xml文件及端口
hostname=“broker-ms1” 不同
hostname中的值需要在系统的hosts文件中配置,如果不想配置就直接写当前机器的IP
直接127.0.0.1启动时会报
C:\Windows\System32\drivers\etc
Unexpected session error: java.io.IOException: java.nio.channels.UnresolvedAddressException
以节点2为例
修改jetty.xml启动端口
问题
1.引入消息队列如何保证其高可用
持久化 事务 签收 集群
2.异步投递Async Sends
有俩种同步 开启同步 在没开启事务去持久化
useAsyncSend=true开启异步客户端要容忍消息丢失的可能
开启异步3种
1.在路径 “tcp://127.0.0.1:61616?jms.useAsyncSend=true"
2.ActiveMQConnectionFactory对象.setUseAsyncSend(true)
3.ActiveMQConnection对象.setUseAsyncSend(true)
确保成功异步回调
message.setJMSMessageID(UUID.randomUUID.toString+"…")
在消息头设置标识 回调成功失败 返回
activeMQMessageProducer.send(message,new AsyncCallback()…)
public class TestMQ {
//地址
public static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
public static void main(String[] args) throws JMSException {
//2.创建连接工厂
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//3. 获得连接
Connection connection = factory.createConnection();
connection.start();
// 4、创建会话
// 参数:事务、签收 AUTO_ACKNOWLEDGE : 自动确认
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//5.模式 队列 主题
Queue queue = session.createQueue("queue11");
//6.发送消息(生产者)
MessageProducer producer = session.createProducer(queue);
// 7、发送具体的消息到队列, 创建的消息类型是什么就些什么
// 简单文本(TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。
for (int i = 1; i <3 ; i++) {
TextMessage message= session.createTextMessage("消息"+i);
long time = 60 * 1000;// 延时1min
long period = 10 * 1000;// 每个10s
int repeat = 6;// 6次
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time);
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period);
message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
//8.发送消息
producer.send(textMessage);
}
//9.关闭流
producer.close();
session.close();
connection.close();
System.out.println("发送完毕");
}
4.分发策略
5.activemq消费重试机制
什么情况引起消息重发
用事务且在session中调用rollback()
用事务调用commit()之前关闭 或者 没有commit()
在CLIENT_ACKNOWLEDGE的传递模式下 在session中调用了recover
client acknowledge
间隔:1
次数: 6
重发6次后 消息端会给MQ发送一个“poison ack” 告诉broker别发了 broker就把消息放到DLQ(死信队列)
官网的重发 http://activemq.apache.org/redelivery-policy
6.死信队列
默认不会把非持久的死消息发送到死信队列
// “>”表示对所有队列生效,如果需要设置指定队列,则直接写队 列名称
//queuePrefix:设置死信队列前缀
//是否丢弃过期消息
7.如何保证消息不被重复消费呢?幂等性问题谈谈
用redis 把成功的一k v存入 消费前查询k是否有 来判断是否重复
如何保证消息的可靠传输?如果消息丢了怎么办