Java通过消息队列与Python数据交流
需求:网页上传一张图片,图片中有些数字需要确认,Java Web的架构通过消息中间件传递到Python端进行图片识别处理。
实验需要用到的图片:
这里需要具备的知识:
- 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端的消息消费者模块
作者:tiger_yam
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。