消息中间件

消息中间件

什么是消息中间件

概述

  • 消息中间件可以理解成就是一个服务软件,保存信息的容器,比如生活中的快递云柜.
  • 我们把数据放到消息中间件当中, 然后通知对应的服务进行获取
  • 消息中间件是在消息的传输过程中保存信息的容器

消息中间件应用场景

  • 使用消息服务器当做大的队列使用, 先进先出, 来处理高并发写入操作
  • 使用消息服务器可以将业务系统的串行执行改为并行执行, 处理效率高, 更合理的榨取服务器的性能.

同步与异步技术

同步技术

  • dubbo是一中同步技术, 实时性高, controller调用service项目, 调用就执行,
  • 如果service项目中的代码没有执行完, controller里面的代码一致等待结果.

异步技术

  • mq消息中间件技术(jms) 是一种异步技术, 消息发送方, 将消息发送给消息服务器,
  • 消息服务器未必立即处理.什么时候去处理, 主要看消息服务器是否繁忙,
  • 消息进入服务器后会进入队列中, 先进先出.实时性不高.

JMS

概述:

  • jms的全称叫做Java message service (Java消息服务) jms是jdk底层定义的规范
  • 各大厂商都是实现这个规范的技术

jms消息服务器同类型技术

ActiveMQ
	是apache的一个比较老牌的消息中间件, 它比较均衡, 既不是最安全的, 也不是最快的.
	
RabbitMQ
	是阿里巴巴的一个消息中间件, 更适合金融类业务, 它对数据的安全性比较高.能够保证数据不丢失.
	
Kafka
	Apache下的一个子项目。特点:高吞吐,在一台普通的服务器上既可以达到10W/s的吞吐速率;适合处理海量数据。

JMS中支持的消息类型

TextMessage
	一个字符串对象
	
MapMessage
	key-value
	
ObjectMessage
	一个序列化的 Java 对象
	
BytesMessage
	一个字节的数据流
	
StreamMessage
	Java 原始值的数据流

JMS中的两种发送模式

点对点模式

  • 一个发送方, 一个接收方.
  • 也可以多个发送方, 一个接收方, 主要是接收方必须是第一个.

订阅发布模式

  • 一个发送方, 多个接收方.
  • 发送方也可以是多个, 主要看接收方, 接收方必须是多个

ActiveMQ安装

(1)将apache-activemq-5.12.0-bin.tar.gz 上传至服务器/usr/local目录下
(2)解压此文件
	cd /usr/local
	tar  zxvf  apache-activemq-5.12.0-bin.tar.gz
(3)为apache-activemq-5.12.0目录赋权
	chmod 777 apache-activemq-5.12.0
(4)进入apache-activemq-5.12.0/bin目录赋与执行权限
	cd apache-activemq-5.12.0/bin
	chmod 755 activemq 
  (5)    启动
	 ./activemq start

启动

在浏览器当中输入http://192.168.1.88:8161/
进入管理页面
用户名和密码都是 admin

说明
	Number Of Pending Messages  :等待消费的消息 这个是当前未出队列的数量。
	Number Of Consumers  :消费者 这个是消费者端的消费者数量
	Messages Enqueued  :进入队列的消息  进入队列的总数量,包括出队列的。
	Messages Dequeued  :出了队列的消息  可以理解为是消费这消费掉的数量。

快速入门

创建普通Jar工程

引入pom依赖

<dependencies>
	<dependency>
		<groupId>org.apache.activemq</groupId>
		<artifactId>activemq-client</artifactId>
		<version>5.13.4</version>
	</dependency>
</dependencies>

点对点模式Queue

创建QueueProducer

public static void main(String[] args) throws Exception{
	//1.创建连接工厂
	ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.1.88:61616");
	//2.获取连接
	Connection connection = connectionFactory.createConnection();
	//3.启动连接
	connection.start();
	//4.获取session  (参数1:是否启动事务,参数2:消息确认模式)
	Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
	//5.创建队列对象, 指定发送的队列名称, 队列名称可以随意起名, 但是发送到哪里, 就要从哪里去接收
	Queue queue = session.createQueue("test-queue");
	//6.创建消息生产者
	MessageProducer producer = session.createProducer(queue);
	//7.创建消息
	TextMessage textMessage = session.createTextMessage("Hello ActiveMQ");
	//8.发送消息
	producer.send(textMessage);
	//9.关闭资源
	producer.close();
	session.close();
	connection.close();
}

创建QueueConsumer

public static void main(String[] args) throws Exception{
	//1.创建连接工厂
	ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.1.88:61616");
	//2.获取连接
	Connection connection = connectionFactory.createConnection();
	//3.启动连接
	connection.start();
	//4.获取session  (参数1:是否启动事务,参数2:消息确认模式)
	Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
	//5.创建队列对象
	Queue queue = session.createQueue("test-queue");
	//6.创建消息消费
	MessageConsumer consumer = session.createConsumer(queue);
	//7.监听消息
	consumer.setMessageListener(new MessageListener() {
		public void onMessage(Message message) {
			TextMessage textMessage=(TextMessage)message;
			try {
				System.out.println("接收到消息:"+textMessage.getText());
			} catch (JMSException e) {
				e.printStackTrace();
			}
		}
	});
	//8.等待键盘输入
	System.in.read();
	//9.关闭资源
	consumer.close();
	session.close();
	connection.close();

}

订阅发布模式Topic

TopicConsumer1

public static void main(String[] args) throws Exception {
//1.创建连接工厂
	ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.1.88:61616");
	//2.获取连接
	Connection connection = connectionFactory.createConnection();
	//3.启动连接
	connection.start();
	//4.获取session  (参数1:是否启动事务,参数2:消息确认模式)
	Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
	//5.创建主题对象
	Topic topic = session.createTopic("test-topic");
	//6.创建消息消费
	MessageConsumer consumer = session.createConsumer(topic);
	//7.监听消息
	consumer.setMessageListener(new MessageListener() {
		public void onMessage(Message message) {
			TextMessage textMessage=(TextMessage)message;
			try {
				System.out.println("接收到消息:"+textMessage.getText());
			} catch (JMSException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
	//8.等待键盘输入
	System.in.read();
	//9.关闭资源
	consumer.close();
	session.close();
	connection.close();
}

TopicConsumer2

public static void main(String[] args) throws Exception {
	//1.创建连接工厂
	ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.1.88:61616");
	//2.获取连接
	Connection connection = connectionFactory.createConnection();
	//3.启动连接
	connection.start();
	//4.获取session  (参数1:是否启动事务,参数2:消息确认模式)
	Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
	//5.创建主题对象
	Topic topic = session.createTopic("test-topic");
	//6.创建消息消费
	MessageConsumer consumer = session.createConsumer(topic);

    //7.监听消息
    consumer.setMessageListener(new MessageListener() {
        public void onMessage(Message message) {
            TextMessage textMessage=(TextMessage)message;
            try {
                System.out.println("接收到消息:"+textMessage.getText());
            } catch (JMSException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
    //8.等待键盘输入
    System.in.read();
    //9.关闭资源
    consumer.close();
    session.close();
    connection.close();

}

TopicProducer

public static void main(String[] args) throws Exception {
	//1.创建连接工厂
	ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.1.88:61616");
	//2.获取连接
	Connection connection = connectionFactory.createConnection();
	//3.启动连接
	connection.start();
	//4.获取session  (参数1:是否启动事务,参数2:消息确认模式)
	Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
	//5.创建主题对象
	Topic topic = session.createTopic("test-topic");
	//6.创建消息生产者
	MessageProducer producer = session.createProducer(topic);
	//7.创建消息
	TextMessage textMessage = session.createTextMessage("Hello Topic ActiveMQ");
	//8.发送消息
	producer.send(textMessage);
	//9.关闭资源
	producer.close();
	session.close();
	connection.close();
}

消息中间件在工程里的应用

添加中间所需要的依赖在common工程当中

<!--activeMq -->
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-client</artifactId>
	<version>5.13.4</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
	<groupId>javax.jms</groupId>
	<artifactId>javax.jms-api</artifactId>
	<version>2.0.1</version>
</dependency>

在service_sellergoods工程当中添加配置文件

spring/applicationContext-jms.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans   
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context   
	http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://192.168.1.88:61616"/>
    </bean>

    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
    <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
    </bean>  

    <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->  
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
        <property name="connectionFactory" ref="connectionFactory"/>  
    </bean>   

    <!-- 发布订阅模式, 商品导入索引库和生成静态页面 -->
    <bean id="topicPageAndSolrDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <!--将商品上架所用的商品id发送到这个队列中-->
        <constructor-arg value="topic_page_solr"/>
    </bean>

    <!-- 点对点模式,删除索引库-->  
    <bean id="queueSolrDeleteDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <!--将商品下架所用的商品id发送到这个队列中-->
        <constructor-arg value="queue_solr_delete"/>
    </bean>  
</beans>

在修改商品状态和删除商品时发送消息

//注入属性		
@Autowired
private JmsTemplate jmsTemplate;
//为商品上架使用
@Autowired
private ActiveMQTopic topicPageAndSolrDestination;
//为商品下架使用
@Autowired
private ActiveMQQueue queueSolrDeleteDestination;
 //商品审核通过		
@Override
public void updateStatus(Long[] ids, String status) {
	if (ids != null) {
		for (final Long id : ids) {
			//1. 根据商品id修改商品对象状态码
			Goods goods  = new Goods();
			goods.setId(id);
			goods.setAuditStatus(status);
			goodsDao.updateByPrimaryKeySelective(goods);
			//2. 根据商品id修改库存集合对象状态码
			Item item = new Item();
			item.setStatus(status);
			ItemQuery query = new ItemQuery();
			ItemQuery.Criteria criteria = query.createCriteria();
			criteria.andGoodsIdEqualTo(id);
			itemDao.updateByExampleSelective(item, query);
			/**
			 * 将商品id作为消息发送给消息服务器
			 */
			if ("2".equals(status)) {
				jmsTemplate.send(topicPageAndSolrDestination, new MessageCreator() {
					@Override
					public Message createMessage(Session session) throws JMSException {
						TextMessage textMessage = session.createTextMessage(String.valueOf(id));
						return textMessage;
					}
				});
			}
		}
	}
}

//删除商品时
public void delete(Long[] ids) {
	if (ids != null) {
		for (final Long id : ids) {
			// 1. 到数据库中对商品进行逻辑删除
			Goods goods = new Goods();
			goods.setId(id);
			goods.setIsDelete("1");
			goodsDao.updateByPrimaryKeySelective(goods);
			//2 将商品id作为消息发送给消息服务器
			jmsTemplate.send(queueSolrDeleteDestination, new MessageCreator() {
				@Override
				public Message createMessage(Session session) throws JMSException {
					TextMessage textMessage = session.createTextMessage(String.valueOf(id));
					return textMessage;
				}
			});

		}
	}
}

在service_page工程当中监听消息生成静态页面

添加配置文件

spring/applicationContext-jms-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans   
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context   
	http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://192.168.1.88:61616"/>
    </bean>

    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
    <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
    </bean>  

    <!--发布订阅模式, 生成页面-->  
    <bean id="topicPageAndSolrDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <!--指定从这个队列中获取上架的商品id-->
        <constructor-arg value="topic_page_solr"/>
    </bean>    

    <!-- 发布订阅模式, 消息监听容器   生成页面 -->
    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="topicPageAndSolrDestination" />
        <property name="messageListener" ref="pageListener" />
    </bean>
    <bean id="pageListener" class="com.itxk.core.listener.PageListener"></bean>

</beans>

编写监听器

public class PageListener implements MessageListener {
    @Autowired
    private CmsService cmsService;
    @Override
    public void onMessage(Message message) {
        ActiveMQTextMessage atm = (ActiveMQTextMessage)message;
        try {
            String goodsId = atm.getText();
            Map<String, Object> goodsData = cmsService.findGoodsData(Long.parseLong(goodsId));
            cmsService.createStaticPage(Long.parseLong(goodsId), goodsData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在service_search工程当中监听消息添加和删除solr商品信息

添加配置文件

spring/applicationContext-jms-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans   
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context   
	http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://192.168.1.88:61616"/>
    </bean>

    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
    <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
    </bean>  

    <!--发布订阅模式, 将数据导入solr索引库-->  
    <bean id="topicPageAndSolrDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <!-- 指定从哪个队列中去接收上架业务的商品id -->
        <constructor-arg value="topic_page_solr"/>
    </bean>    

    <!-- 发布订阅模式, 消息监听容器, 将数据导入solr索引库 -->
    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="topicPageAndSolrDestination" />
        <property name="messageListener" ref="pageAndSolrListener" />
    </bean>
    <bean id="pageAndSolrListener" class="com.itxk.core.listener.ItemSearchListener"></bean>

    <!-- 点对点模式,删除索引库-->
    <bean id="queueSolrDeleteDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <!--指定从这个队列中去接收下架的商品id-->
        <constructor-arg value="queue_solr_delete"/>
    </bean>

    <!-- 点对点模式, 消息监听容器  删除索引库-->
    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="queueSolrDeleteDestination" />
        <property name="messageListener" ref="itemDeleteListener" />
    </bean>
    <bean id="itemDeleteListener" class="com.itxk.core.listener.ItemDeleteListener"></bean>

</beans>

添加监听器

ItemSearchListener
public class ItemSearchListener implements MessageListener {
    @Autowired
    private SolrManagerService solrManagerService;
    @Autowired
    private ItemDao itemDao;
    @Override
    public void onMessage(Message message) {
        //为了方便获取文本消息, 将原生的消息对象转换成activeMq的文本消息对象
        ActiveMQTextMessage atm = (ActiveMQTextMessage)message;
        try {
            String goodsId = atm.getText();
            ItemQuery query = new ItemQuery();
            ItemQuery.Criteria criteria = query.createCriteria();
            //查询指定商品的库存数据
            criteria.andGoodsIdEqualTo(Long.parseLong(goodsId));
            List<Item> items = itemDao.selectByExample(query);
            if (items != null) {
                for (Item item : items) {
                    //获取规格json格式字符串
                    String specJsonStr = item.getSpec();
                    Map map = JSON.parseObject(specJsonStr, Map.class);
                    item.setSpecMap(map);
                }
            }
            solrManagerService.saveItemToSolr(items);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
ItemDeleteListener
public class ItemDeleteListener implements MessageListener {
    @Autowired
    private SolrManagerService solrManagerService;
    @Override
    public void onMessage(Message message) {
        ActiveMQTextMessage atm = (ActiveMQTextMessage)message;
        try {
            String goodsId = atm.getText();
            ArrayList<Object> itemGoodsID = new ArrayList<>();
            itemGoodsID.add(Long.parseLong(goodsId));
            solrManagerService.deleteItemByGoodsId(itemGoodsID);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
posted @ 2019-10-20 21:03  海韵༒听心  阅读(327)  评论(0编辑  收藏  举报