java和python之间数据交互,不同语言间传输数据:使用RabbitMQ

问题描述

项目过程中可能会遇到,java从数据库取了很多数据,但java本身不方便处理,所以传递给python去处理,如何传?这里我结合很多已存在的方法,然后再谈谈怎么用rabbitmq来操作。
这里会举简单的例子示范,当然前提你需要了解基本的java,python代码编写,大致了解下队列、rabbitmq是啥,还有相关的文件操作、json等等。

方法

方法一:java直接执行python脚本.py文件,把数据放到参数里传递

举例说明:
java代码,

    @Test
	public void test()  {
        String pythonPath="/Library/Frameworks/Python.framework/Versions/3.6/bin/python3 ";
        String filePath="/Users/guang/Documents/Python_Project/Test/test/receiver.py ";

        //首先定义个list,赋值。
        List<Integer> list1 = new ArrayList<Integer>();
        int i = 1;
        while(i<=10000) {
        	list1.add(i);
        	i++;
        }
        
        //调用python脚本并传递list
        try {
            Process process = Runtime.getRuntime().exec(
                    pythonPath + filePath + list1);
            BufferedReader in=new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line=in.readLine())!=null){
                System.out.println(line);
            }
            in.close();
            int re=process.waitFor();
            System.out.println(re==1?"----状态码1----运行失败":"----状态码0----运行成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
      
	}

python代码,

if __name__ == '__main__':
    print("------------- start -------------")
    # print("len(sys.argv[1:])==", len(sys.argv[1:]))
    print("sys.argv[1:]==", sys.argv[1:])

运行结果:

在这里插入图片描述
这样的方式有两个问题:
1、参数传输过程中形式会发生变化,比如上面我实际想传一个list过去,但通过sys.argv[1:]可以看到,拿到的数据是一个被加工过的字符串形式,这样你又要花费大量精力去切割或者其他操作来把它在python中变成一个列表;
2、你不妨试着把数据量调大10倍,从10000 -> 100000,发现会报下面的错误,因为传不了那么大的。
在这里插入图片描述

方法二:通过第三方文件作为中间站

(因为遇到项目的特殊性,这里举的例子是把大量数据存储到了excel中),即增加了写入和读取的过程。

数据准备:
为了方便测试,这里模拟java从数据库取出的每条数据是这么个简单的形式:
某地区|某年指标1|某年指标2|某年指标3| …
在这里插入图片描述模拟生成数据代码:

public class testObj {
	
	private String place;
	private List<Integer> datalist;
	
	public String getPlace() {
		return place;
	}
	public void setPlace(String place) {
		this.place = place;
	}
	public List<Integer> getDatalist() {
		return datalist;
	}
	public void setDatalist(List<Integer> datalist) {
		this.datalist = datalist;
	}
	public testObj(String place, List<Integer> datalist) {
		super();
		this.place = place;
		this.datalist = datalist;
	}

}
public class MockData {
	
	public static List<testObj> mockData() {
		List<testObj> list_obj = new ArrayList<testObj>();
		for (int i = 0; i < 50000; i++) {  //模拟5万行,每行70列
			List<Integer> list_int = new ArrayList<Integer>();
            for(int j=1949; j<=2019; j++) {
            	list_int.add(j);
        	}
			testObj obj = new testObj("address: 中国"+i+"省,"+i+"市,"+i+"县,"+"某水利部门", list_int);
			list_obj.add(obj);
        }
		return list_obj;
	}

}

写入excel:

public class writeToExcel {
	
	public static void writeExcel(List<testObj> list, String path) {
        try {
            // Excel底部的表名
            String sheetn = "sheet1";
            // 用JXL向新建的文件中添加内容
            File myFilePath = new File(path);
            if (!myFilePath.exists())
                myFilePath.createNewFile();
            OutputStream outf = new FileOutputStream(path);
            WritableWorkbook wwb = Workbook.createWorkbook(outf);
            jxl.write.WritableSheet writesheet = wwb.createSheet(sheetn, 1);
            // 内容添加
            for (int i = 0; i < list.size(); i++) {
            	testObj obj = (testObj)list.get(i);
            	Label label = new Label(0, i, obj.getPlace()); 
                writesheet.addCell(label);
            	List<Integer> numbersList = obj.getDatalist();
            	for(int j=1;j<numbersList.size();j++) {
                    writesheet.addCell(new jxl.write.Number(j,i,(Integer)numbersList.get(j)));
            	}
            }
            wwb.write();
            wwb.close();
        } catch (WriteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	
	public static void main(String[] args) {
		
		long startTime=System.currentTimeMillis(); //获取开始时间
		
        //数据准备
        List<testObj> list = MockData.mockData();
        //存放路径
        String path;
        try {
        	path = "/Users/guang/Documents/Java_Project/testdemo/src/main/java/com/test/testdemo/test.xls";
            //System.out.println(path);
            writeExcel(list, path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        long endTime=System.currentTimeMillis(); //获取结束时间
        
        System.out.println("程序运行时间: "+(endTime-startTime)/1000.0+"s");
        
    }
}

在这里插入图片描述
excel里已存在这么多模拟数据,

在这里插入图片描述python读excel:

import xlrd
import datetime

if __name__ == '__main__':
    starttime = datetime.datetime.now()
    workbook = xlrd.open_workbook('/Users/guang/Documents/Java_Project/testdemo/src/main/java/com/test/testdemo/test.xls')
    sheet = workbook.sheets()[0]
    endtime = datetime.datetime.now()
    print("拿到数据时间:{}".format(endtime - starttime))
    nrows = sheet.nrows
    print('数据行数:{}'.format(nrows))

![
这种方法其实是可取的,在数据量不是很大的情况下,效率和后面的rabbitmq差别并不是很大,但是数据量特别大的情况下速度可能会慢,在本例中有时候还要去考虑excel单个sheet的行列最大限制容量问题。

方法三:使用消息队列的方式,RabbitMQ

基本概念简述:
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信。

在这里插入图片描述
MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。RabbitMQ是一个在AMQP基础上完成的,可复用的企业消息系统。

在这里插入图片描述
上图是简单抽象的描述,具体到 rabbitmq 有很多详细的概念,这里不一一详述。当然,我们上面也说过 ,rabbitmq 是 AMQP 协议的一个开源实现,所以其内部实际上也是 AMQP 中的基本概念,包括:Message(消息)、Publisher(消息生产者),Exchange(交换器),Binding(绑定),Queue(消息队列),Connection(网络连接),Channel(信道),Consumer(消息消费者),Virtual Host(虚拟主机),Broker(消息队列服务器)。

使用说明:
首先,安装rabbitmq,这个可以参考网上相关教程比较简单,可以安装在本地,也可以其他地方,而对于我们这里java/python传输问题上,区别其实也就是代码中的host到底是localhost还是你的其他ip地址192.168.***.***, 因为我们是来告诉大家怎么传输,所以安装过程这里不一一赘述,大家可以去网络上了解一下。我是单独安装在虚拟机的ubuntu上的,安装成功后,主机也可以访问管理界面了:
在这里插入图片描述
当然我们这里只是简单的传数据问题,也不需要用这个复杂界面管理啥,有兴趣的同学可以去详细了解下rabbitmq综合的知识。
代码部分(数据我们依旧是用上面的模拟数据):
java代码(即生产者),

public class Producer1 {
	
	public final static String QUEUE_NAME="data";
	
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException{
        
    	long startTime=System.currentTimeMillis(); //获取开始时间
		
        //数据准备
        List<testObj> list = MockData.mockData();
    	
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ相关信息
        factory.setHost("192.168.43.211");
        factory.setUsername("test");
        factory.setPassword("123456");
        //factory.setPort(5672);
        //创建一个新的连接
        Connection connection = factory.newConnection();
        //创建一个通道
        Channel channel = connection.createChannel();
        //声明一个队列        
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        
        List<testObj> list1 = new ArrayList<testObj>();
        list1 = new MockData().mockData();
        
        //发送消息到队列中
        ObjectMapper mapper=new ObjectMapper();
        String message = mapper.writeValueAsString(list1);
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        
        //关闭通道和连接
        channel.close();
        connection.close();
        
        long endTime=System.currentTimeMillis(); //获取结束时间  
        System.out.println("程序运行时间: "+(endTime-startTime)/1000.0+"s");
        
        
    }

}

在这里插入图片描述
这时候可以看到rabbitmq管理界面消息队列queue那里出现了我们定义的data队列,被java发送上去了
在这里插入图片描述
python代码(即消费者),

if __name__ == '__main__':
    starttime = datetime.datetime.now()
    # 创建socket链接
    credentials = pika.PlainCredentials('test', '123456')
    connection = pika.BlockingConnection(pika.ConnectionParameters(
     '192.168.43.211', 5672, '/', credentials))
    # 创建管道
    channel = connection.channel()
    # 创建队列
    queue_name = 'data'
    channel.queue_declare(queue_name)
    # 如果接受到消息就调用回调函数,准备接受消息

    # 声明回调函数
    def callback(ch, method, properties, body):
        message = json.loads(body.decode())
        endtime = datetime.datetime.now()
        print("拿数据时间:{}".format(endtime - starttime))
        list = message
        for i in list:
            print(i)

    channel.basic_consume(callback, queue=queue_name, no_ack=False)
    channel.start_consuming()

结果:
在这里插入图片描述
在这里插入图片描述我们可以看到python顺利拿到java传输的数据,至于你拿到数据后,后面要做什么复杂的操作、分析,那就是看你自己的需要了。
另外,你不妨尝试把模拟数据条数提高到10万条甚至更多,前两种方法未必能得到有效的支持,但rabbitmq基本能保持稳定有效。

总结

在解决不同语言程序之间数据传输问题上,方法各异,rabbitmq是一个很好的选择,处理数据量大,且从时间效率上来说也快(单从我的数据测试结果上来看整个过程速度提高3倍左右,当然其他复杂结构数据可能稍有差异)。但如果你的数据量并不巨大,其他方法也可行,根据不同情况,做最好的选择保证代价最低一定是我们优先考虑的事儿。
最后声明,本篇文章只是针对java向python传输数据的几种方法进行分析,所以里面用到的一些知识不面面详解,具体可以去参考网上相关教程。另外,由于不同机器、不同系统环境下的差异是不确定性的,所以数据测试结果可能和其他环境下有所差异,纯属正常,本文仅供参考,请根据你自己的想法去合理选择。若有更好的方法,欢迎大家一起交流共同改进!谢谢!本篇文章著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

猜你喜欢

转载自blog.csdn.net/pig2guang/article/details/84382375
今日推荐