SpringMVC + RabbitMQ实战(XML配置rabbit,监听多条队列)

一、为什么使用SpringMVC整合RabbitMQ?

由于springboot项目rabbit-rfc与sapjco3.jar的不兼容性,具体表现为:在Linux上执行java -jar rabbit-rfc.jar时,出现以下错误:

It is not allowed to rename or repackage the original archive “sapjco3.jar”

这是因为springboot项目依赖jar包的默认打包规则即为Maven的默认打包规则:

%artifactId%-%version%.jar

所以当sapjco3在maven中的dependency如下时:

 <dependency>
     <groupId>com.sap</groupId>
     <artifactId>sapjco3</artifactId>
     <version>3.0.12</version>
     <scope>system</scope>
     <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/sapjco3.jar</systemPath>
 </dependency>

sapjco3.jar最终在package项目jar包时会被重新打包命名为sapjco3-3.0.12.jar,所以才会出现上述不允许重命名或重打包sapjco3.jar的错误。
具体参考stackoverflow的这个问题:
https://stackoverflow.com/questions/36924961/sapjco3-jar-has-issues-with-mavens-naming-convention-springboot
解决此问题的方法之一就是不要让sapjco3.jar在打包或运行时被重命名,所以使用SpringMVC + 本地Tomcat的办法,在IntelliJ Idea 中配置为发布模式war,便可以在项目启动时在target目录中生成以项目名命名的war包。
在这里插入图片描述
附:IntelliJ Idea war和war exploded的区别

是选择war还是war exploded 这里首先看一下他们两个的区别:
war模式:将WEB工程以包的形式上传到服务器 ; war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器;
(1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布;
(2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat
部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
(3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面什么的东西才可以及时的显示出来。

二、项目实战

项目架构参考我的博文《RabbitMQ中间件方案:BPM-SAP的应用解耦和用户并发量控制》、SpringMVC的其他配置参考《SpringMVC + Spring + Hibernate实战(通用配置)》。
以下两个项目需要引入必要的依赖:

    <dependency>
      <groupId>org.springframework.amqp</groupId>
      <artifactId>spring-rabbit</artifactId>
      <version>2.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.4.3</version>
    </dependency>

版本号没必要拘泥,进入maven repository官网,引入最新版本的依赖即可。

项目一:api-rabbit-mvc,生产requestQueues,消费responseQueues

创建5条request_queue,并监听5条response_queue,监听类(消费者,相当于springboot-amqp starter中的@RabbitListener)为ResponseMapConsumer,监听方法为listenResponseMap(相当于@RabbitHandler)。将请求Json数据requestMap发送到请求队列request_queue i(1 ~ 5),并监听response_queue 1 - 5,只要监听到response queue i,能通过UUID和序列号i的对应校验,就将监听得到的SAP响应Json数据返回给前端或BPM的HttpClient。

applicationContext.xml
说明:
首先使用context:component-scan扫描到com.jake下所有被上下文注解标记的类,如(@Controller、@Service、@Repository、@Component等),context:annotation-config用于开启上下文注解,与context:component-scan功能重复。其中,rabbit:queue用于自动创建队列;message-converter使用了Jackson2JsonMessageConverter类,用于传递Json数据(本项目中暂未使用);rabbit-listener中使用queue-names属性监听了多条队列,队列名之间以逗号分隔;ref为消费者类名在上下文注解中的别名(默认类名首字母小写),method为其中处理监听队列携带数据的具体方法(参数与队列携带参数类型相同)。由于没有使用rabbit:binding标签,所以所有创建的rabbit消息队列都被绑定到RabbitMQ服务器默认的直连交换机AMQP Default上。

 <context:component-scan base-package="com.jake"/>
 <!--<context:annotation-config/>-->
 <rabbit:connection-factory
         id="rabbitConnectionFactory" host="127.0.0.1"
         username="guest" password="guest" port="5672"/>
 <rabbit:admin connection-factory="rabbitConnectionFactory"/>
 <rabbit:queue id="requestQueue1" name="REQUEST_QUEUE1"/>
 <rabbit:queue id="requestQueue2" name="REQUEST_QUEUE2"/>
 <rabbit:queue id="requestQueue3" name="REQUEST_QUEUE3"/>
 <rabbit:queue id="requestQueue4" name="REQUEST_QUEUE4"/>
 <rabbit:queue id="requestQueue5" name="REQUEST_QUEUE5"/>
 <bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
 <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" message-converter="jackson2JsonMessageConverter"/>
 <rabbit:listener-container message-converter="jackson2JsonMessageConverter">
     <rabbit:listener queue-names="RESPONSE_QUEUE1, RESPONSE_QUEUE2, RESPONSE_QUEUE3, RESPONSE_QUEUE4, RESPONSE_QUEUE5"
                      ref="responseMapConsumer" method="listenResponseMap"/>
 </rabbit:listener-container>

生产者

package com.jake.apirabbitmvc.producer;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class RequestMapProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendRequestMap(String queueName, Map<String, Object> requestMap) {
        rabbitTemplate.convertAndSend(queueName, requestMap);
    }

}

消费者

package com.jake.apirabbitmvc.consumer;

import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class ResponseMapConsumer {

    @Getter
    @Setter
    private Map<String, Object> responseMap;

    public void listenResponseMap(Map<String, Object> responseMap) {
        setResponseMap(responseMap);
    }

}

控制层
调用生产者的sendRequestMap方法发送请求Json数据,调用消费者的getResponseMap方法实时获取响应Json数据。

package com.aac.apirabbitmvc.controller;

import com.aac.apirabbitmvc.constant.Queues;
import com.aac.apirabbitmvc.consumer.ResponseMapConsumer;
import com.aac.apirabbitmvc.producer.RequestMapProducer;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping(value = "/rfc")
public class BpmToRfcController {

    @Autowired
    private RequestMapProducer requestMapProducer;

    @Autowired
    private ResponseMapConsumer responseMapConsumer;

    @RequestMapping(value = "/json")
    public Map<String, Object> getResponseMap(@RequestBody Map<String, Object> requestMap) throws InterruptedException {
        String requestID = UUID.randomUUID().toString();
        int reqQNo = RandomUtils.nextInt(1, 6);
        requestMap.put("requestID", requestID);
        requestMap.put("reqQNo", reqQNo);
        requestMapProducer.sendRequestMap(Queues.REQUEST_QUEUE + reqQNo, requestMap);
        Map<String, Object> responseMap = responseMapConsumer.getResponseMap();
        // 不断获取@RabbitListener监听到的响应数据,响应数据不为NULL时退出循环。
        while (responseMap == null) {
            Thread.sleep(100);
            responseMap = responseMapConsumer.getResponseMap();
        }
        String responseID = (String) responseMap.get("responseID");
        int resQNo = (int) responseMap.get("resQNo");
        // 不断获取@RabbitListener监听到的响应数据,当标记值相同时退出循环。
        while (!(StringUtils.equals(requestID, responseID) && reqQNo == resQNo)) {
            Thread.sleep(100);
            responseMap = responseMapConsumer.getResponseMap();
            responseID = (String) responseMap.get("responseID");
            resQNo = (int) responseMap.get("resQNo");
        }
        return responseMap;
    }

}

项目二:rfc-rabbit-mvc,生产responseQueues,消费requestQueues

创建5条response_queue,并监听5条request_queue,监听类(消费者,相当于springboot-amqp starter中的@RabbitListener)为RequestMapConsumer,监听方法为listenRequestMap(相当于@RabbitHandler)。只要监听到request_queue 1 - 5中的任意一条(假设为i),就获取其中的请求Json数据,通过其中的参数调用RFC接口,获取SAP的响应Json数据responseMap,将该Json数据发送到response_queue i(1 ~ 5)。

applicationContext.xml

<context:component-scan base-package="com.jake"/>
<!--<context:annotation-config/>-->
<rabbit:connection-factory
        id="rabbitConnectionFactory" host="127.0.0.1"
        username="guest" password="guest" port="5672"/>
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<rabbit:queue id="responseQueue1" name="RESPONSE_QUEUE1"/>
<rabbit:queue id="responseQueue2" name="RESPONSE_QUEUE2"/>
<rabbit:queue id="responseQueue3" name="RESPONSE_QUEUE3"/>
<rabbit:queue id="responseQueue4" name="RESPONSE_QUEUE4"/>
<rabbit:queue id="responseQueue5" name="RESPONSE_QUEUE5"/>
<bean id="jackson2JsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" message-converter="jackson2JsonMessageConverter"/>
<rabbit:listener-container message-converter="jackson2JsonMessageConverter">
    <rabbit:listener queue-names="REQUEST_QUEUE1, REQUEST_QUEUE2, REQUEST_QUEUE3, REQUEST_QUEUE4, REQUEST_QUEUE5"
                     ref="requestMapConsumer" method="listenRequestMap"/>
</rabbit:listener-container>

生产者

package com.jake.rfcrabbitmvc.producer;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class RequestMapProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendResponseMap(String queueName, Map<String, Object> responseMap) {
        rabbitTemplate.convertAndSend(queueName, responseMap);
    }

}

消费者

package com.jake.rfcrabbitmvc.consumer;

import com.jake.rfcrabbitmvc.constant.Queues;
import com.jake.rfcrabbitmvc.producer.RequestMapProducer;
import com.jake.rfcrabbitmvc.service.BpmToSapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class RequestMapConsumer {

    @Autowired
    private RequestMapProducer requestMapProducer;

    @Autowired
    private BpmToSapService bpmToSapService;

    public void listenRequestMap(Map<String, Object> requestMap) {
        Map<String, Object> responseMap = bpmToSapService.getSapJson(requestMap);
        String requestID = (String) requestMap.get("requestID");
        int reqQNo = (int) requestMap.get("reqQNo");
        responseMap.put("responseID", requestID);
        responseMap.put("resQNo", reqQNo);
        requestMapProducer.sendResponseMap(Queues.RESPONSE_QUEUE + reqQNo, responseMap);
    }

}

根据请求Json数据获取SAP响应Json数据的bpmToSapService.getSapJson(requestMap)方法参考我的博客:《访问SAP统一RFC连接接口(RESTFUL风格)》

三、发布war包至Linux

在localhost使用postman测试,能够正常获取SAP的响应Json数据后,将两个项目的war包上传至Linux服务器Tomcat的webapps的目录下,bin目录执行./startup.sh,运行项目。
访问linux-server-ip:8080可以访问Tomcat主页。
携带请求Json数据访问linux-server-ip:8080/api-rabbit-mvc/rfc/json.action无法获取响应Json数据,分析可能的原因是当响应Json数据为null时,控制层就不返回数据。由此可以推断出api项目无法监听到rfc项目返回的Json数据。
携带请求Json数据直接访问linux-server-ip:8080/rfc-rabbit-mvc/rfc/json.action,发现报类似以下的错误:ClassNotFound:JCoConnection does not exist,明显缺少了sapjco3.jar和libsapjco3.so文件,百度谷歌搜索:sapjco3 linux,找到在Linux环境下配置有关sapjco的环境变量的方法。
参考博客:https://tw.saowen.com/a/47f989f244ed24d772d18b69c08feee2015966eb4b1ecbafbb6e22da32a8bae3
如果已经配置好了,依然报错,那么执行source /etc/profile使环境变量重新生效即可。
再访问linux-server-ip:8080/rfc-rabbit-mvc/rfc/json.action就可以获取Json数据了。

猜你喜欢

转载自blog.csdn.net/qq_15329947/article/details/86022174
今日推荐