什么是消息中间件
面向消息的中间件,发送者将消息发送给消息服务器,消息服务器将消息存放在队列中,在合适的时候在将消息转发给接收者。
这种模式下,发送和接收是异步的,发送者无需等待,二者的生命周期未必相同,发送消息的时候接受者不一定运行,接收消息的时候发送者也不一定运行,一对多通信,对于一个消息可以有多高接收者。
jms
jms是java的消息服务,jms的客户端之间可以通过jms服务进行异步的消息传输。
消息模型
点对点(p2p):每个消息只有一个消费者,一旦被消费,消息就不在消息队列中。发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列。接收者在成功接收消息之后需要向队列应答成功。
发布订阅:每个消息可以有多个消费,发布者和订阅者之间有时间上的依赖性,针对某个主题的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
实现点对点通讯模式
//生产者
public class Producer {
public static void main(String[] args) throws Exception {
//创建一个activemq工厂
ActiveMQConnectionFactory activeFactory =
new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,"tcp://127.0.0.1:61616");
//获取一个activemq连接
Connection connection = activeFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
// Destination :消息的目的地;消息发送给谁.
//创建队列的名字
Destination destinstion = session.createQueue("activemq_a1");
//消息生产者
MessageProducer messageProducer = session.createProducer(destinstion);
//设置不持久化
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
for(int i = 1; i<= 5; i++) {
send(session,messageProducer,i);
}
}
public static void send(Session session, MessageProducer mp, int i) throws Exception {
TextMessage textMessage = session.createTextMessage("消息:" + i);
mp.send(textMessage);
}
}
//消费者
public class Consumer {
public static void main(String[] args) throws Exception {
ActiveMQConnectionFactory activemqConnectionFactory =
new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,"tcp://127.0.0.1:61616");
Connection connection = activemqConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("activemq_a1");
MessageConsumer messageConsumer = session.createConsumer(destination);
while(true) {
TextMessage textMessage = (TextMessage) messageConsumer.receive();
if(null != textMessage) {
System.out.println(textMessage.getText());
}
}
}
}
实现发布订阅模式
//发布者
public class Producer {
public static void main(String[] args) throws Exception {
System.out.println("发布者启动。。。");
ActiveMQConnectionFactory activemqConnectionFactory =
new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,"tcp://127.0.0.1:61616");
Connection connection = activemqConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(null);
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
for(int i =1; i<=5; i++) {
send(session,messageProducer,i);
}
}
public static void send(Session session, MessageProducer mp, int i) throws Exception {
TextMessage textMessage = session.createTextMessage("消息:" + i);
Destination d = session.createTopic("activemy_a1_topic");
mp.send(d,textMessage);
}
}
public class Consumer {
public static void main(String[] args) throws Exception {
System.out.println("订阅者1启动。。。");
ActiveMQConnectionFactory activemqConnectionFactory =
new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
Connection connection = activemqConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("activemy_a1_topic");
MessageConsumer mc = session.createConsumer(topic);
while(true) {
TextMessage textMessage = (TextMessage) mc.receive();
if(null != textMessage) {
System.out.println(textMessage.getText());
}
}
}
}
JMS消息可靠机制
ActiveMQ消息签收机制:客户端成功接收一条消息的标志是一条消息被签收,成功应答。
消息的签收分两种
1.带事物的session,如果session带有事物,并且事物成功提交,则消息被自动签收,如果事物回滚,则消息会被再次传送。
2.不带事物的session,不带事物的session的签收方式,取决于session的配置。
Session.AUTO_ACKNOWLEDGE 消息自动签收
Session.GLIENT_ACKNOWLEDGE 手动签收 textMessage.acknowledge();手动签收
Session.DUPS_OK_ACKNOWLEDGE 不是必须签收,消息可能会重复发送。
//生产者不开启session,客户端必须手动签收模式
Session session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
//消费者手动签收
textMessage.acknowledge();
//生产者不开启session,客户端自动签收模式
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//事物消息
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//提交事物
session.commit();
ActiveMQ持久化
//设置消息持久化
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
使用MQ注意事项
1.消费者代码不要抛出异常,否则activmq默认有重试机制
2.如果代码发生异常,需要发布版本才可以解决的问题,不要使用重试机制,采用日志记录方式,定时job进行补偿
3.如果不需要发布版本解决的问题,可以采用重试机制进行补偿。
消息幂等性,不被重复消费
产生原因:网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费。
解决方案:使用全局messageid判断消费方便使用同一个,解决幂等性。