Fork me on GitHub

Java通过消息队列与Python数据交流

需求:网页上传一张图片,图片中有些数字需要确认,Java Web的架构通过消息中间件传递到Python端进行图片识别处理。

实验需要用到的图片:
00.png

这里需要具备的知识:

  • Spring Boot
  • Python
  • RabbitMQ
  • Docker

环境:

  • Docker
  • Nginx
  • RabbitMQ

简单概括一下项目的大体结构,这里采用的是前后端分离,前端是一个简单的文件上传页面部署到Nginx,后端是Spring Boot的架构,消息队列采用的是RabbitMQ,图片识别功能交给Python处理,然后把处理好的结果返回到消息队列,最后就是Java需要结果对消息队列的消费。

java端接收图片(消息生产者) ---> 消息中间件 ---> python处理图片(消息消费者)

python处理好图片(消息生产者)---> 消息中间件 ---> java拿结果(消息消费者)

环境搭建

Docker环境安装:

# 安装yum-utils
yum install -y yum-utils device-mapper-persistent-data lvm2

# 为yum源添加docker仓库位置
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装docker
yum install docker-ce

# 启动docker
systemctl start docker

Nginx的环境我这里是安装在Window上的,具体怎么安装,请动动小手去网上搜索,这里不细讲Nginx的安装了,关于涉及到跨域和反向代理的配置可以参考我这篇博客《解决前端https访问后端http接口》【传送门】,里面有关于反向代理的配置。

Docker安装RabbitMQ:

下载rabbitmq3.9-management的docker镜像:

docker pull rabbitmq:3.9-management

使用如下命令启动RabbitMQ服务:

docker run -p 5672:5672 -p 15672:15672 --name rabbitmq \
-d rabbitmq:3.9-management

访问地址:

http://ip:15672    # 这里的ip是部署RabbitMQ服务器的ip
登录的账号密码都是guest

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test java2python</title>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
    <form enctype="multipart/form-data">
        <input type="file" id="avatar" name="uploadFile">
        <button type="button">上传</button>
    </form>
</body>
<script>
    $('button').click(function(){
        var files = $('#avatar')[0].files[0];
        var data = new FormData();
        data.append('file', files);
        $.ajax({
            url: '/api/test/file',
            type: 'POST',
            data: data,
            cache: false,
            processData: false,
            contentType: false
        });
    });
</script>
</html>

Java端

新建一个父工程,然后在父工程中新建两个子模块,一个是消息的生产者模块,另一个是消息的消费者模块。
分别在两个子模块引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

分别在子模块的resource目录application.yml文件中配置:

spring:
  application:
    name: springboot_rabbitmq # 这里可以有所不同
  rabbitmq:
    host: 192.168.1.100  # 这里根据自己的ip配置
    port: 5672
    username: guest
    password: guest
    virtual-host: /

生产消息模块

这里得留意消息中间件中的队列配置得是java2python

  • Controller层代码如下:
@Controller
@RequestMapping("/test")
public class RabbitController {
    Logger logger = LoggerFactory.getLogger(RabbitController.class);

    @Autowired
    private MessageIml messageIml;

    @PostMapping("/file")
    public void uploadHandler(@RequestParam("file") MultipartFile file) throws IOException {
        byte[] image = file.getBytes();
        messageIml.sendMessage("java2python", image);
        logger.info("成功给消息中间件发送数据");
    }
}
  • Service层代码如下:
//接口
public interface IMessage {
    /**
     * 发送消息
     * @param routerKey
     * @param data
     */
    void sendMessage(String routerKey, byte[] data);
}

//实现类
@Service
public class MessageIml implements IMessage {
    @Autowired
    private RabbitUtils rabbitUtils;

    @Override
    public void sendMessage(String routerKey, byte[] data) {
        rabbitUtils.sendMessage(routerKey, data);
    }
}

//工具类
@Component
public class RabbitUtils {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(String routingKey, byte[] data){
        rabbitTemplate.convertAndSend(routingKey, data);
    }
}

//跨域配置类
@Configuration
public class Cors {
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); //允许任何域名
        corsConfiguration.addAllowedHeader("*"); //允许任何头
        corsConfiguration.addAllowedMethod("*"); //允许任何方法
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); //注册
        return new CorsFilter(source);
    }
}

消息消费模块

这里得留意消息中间件中的队列配置得是python2java

@Component
public class RabbitCustomer {
    Logger logger = LoggerFactory.getLogger(RabbitCustomer.class);

    @RabbitHandler
    @RabbitListener(queuesToDeclare = {@Queue(value = "python2java", durable = "true", autoDelete = "false")})
    public void accept(String message){
        String content = new String(message.getBytes());
        logger.info("打印python2java 队列传来的消息:");
        logger.info("messageContent = : " + content);
    }
}

Python端

图片识别我用了一个开源的库pytesseract,具体怎么安装和使用可参考这篇博客【传送门】

安装python的RabbitMQ库:

python -m pip install pika --upgrade

rabbitAccept.py模块是从消息中间件中消费Java端生产的消息,具体如下:

#!/usr/bin/env python
from PIL import Image
import pytesseract
import pika
import rabbitSender

picSavePath = r'C:\Users\Administrator\Desktop\py\11.png'
senderOfHost = '192.168.1.100'
senderOfQueue = 'python2java'
senderOfRoutingkey = 'python2java'

def handleAndSendMessage():
    """
    Read the photo from the file system for processing and send the result as a message
    """
    image = Image.open(picSavePath)
    print("打印结果:")
    print("handle result type: "+pytesseract.image_to_string(image,lang='eng'))
    handleResult = pytesseract.image_to_string(image,lang='eng')
    print(isinstance(handleResult, str))
    sender = rabbitSender.Send(
        host=senderOfHost, 
        queue=senderOfQueue,
        routingKey=senderOfRoutingkey,
        data=bytes(handleResult, encoding='utf8'))
    sender.sendMessage()

def writeFile(path, content):
    f = open(path, 'wb')
    f.write(content)
    f.close()

def callback(ch, method, properties, body):
    writeFile(path=picSavePath, content=body)
    handleAndSendMessage()

def receiptMessage(host, queue):
    connection = pika.BlockingConnection((pika.ConnectionParameters(host)))
    channel = connection.channel()
    channel.basic_consume(queue, on_message_callback=callback, auto_ack=True)
    channel.start_consuming()
    connection.close()

if __name__ == '__main__':
    host = '192.168.1.100'
    receiptMessage(host, queue='java2python')

rabbitSender.py模块主要是处理了结果以后,把结果放到消息队列中,也就是作为消息的生产者

#!/usr/bin/env python
import pika

class Send(object):

    def __init__(self, host, queue, routingKey, data, exchange=''):
        self.host = host
        self.queue = queue
        self.exchange = exchange
        self.routingKey = routingKey
        self.data = data

    def sendMessage(self):
        connection = pika.BlockingConnection((pika.ConnectionParameters(self.host)))
        channel = connection.channel()
        channel.queue_declare(self.queue)
        channel.basic_publish(
            exchange=self.exchange, 
            routing_key=self.routingKey, 
            body=self.data)
        connection.close()

测试

  • 启动RabbitMQ服务器
  • 启动Java端的消息生产者模块
  • 启动python的消息消费者模块
  • 启动Java端的消息消费者模块
posted @ 2022-09-15 14:43  tiger_yam  阅读(94)  评论(0编辑  收藏  举报