使用 ZeroMQ 消息库在 C 和 Python 间共享数据


使用 ZeroMQ 消息库在 C 和 Python 间共享数据

文章插图
 
ZeroMQ 是一个快速灵活的消息库,用于数据收集和不同编程语言间的数据共享 。
• 来源:linux.cn • 作者:Cristiano L. Fontana • 译者:SilentDawn •
(本文字数:12873,阅读时长大约:15 分钟)
作为软件工程师,我有多次在要求完成指定任务时感到浑身一冷的经历 。其中有一次,我必须在一些新的硬件基础设施和云基础设施之间写一个接口,这些硬件需要 C 语言,而云基础设施主要是用 Python 。
实现的方式之一是 用 C 写扩展模块,Python 支持 C 扩展的调用 。快速浏览文档后发现,这需要编写大量的 C 代码 。这样做的话,在有些情况下效果还不错,但不是我喜欢的方式 。另一种方式就是将两个任务放在不同的进程中,并使用 ZeroMQ 消息库 在两者之间交换消息 。
在发现 ZeroMQ 之前,遇到这种类型的情况时,我选择了编写扩展的方式 。这种方式不算太差,但非常费时费力 。如今,为了避免那些问题,我将一个系统细分为独立的进程,通过 通信套接字 发送消息来交换信息 。这样,不同的编程语言可以共存,每个进程也变简单了,同时也容易调试 。
ZeroMQ 提供了一个更简单的过程:
  1. 编写一小段 C 代码,从硬件读取数据,然后把发现的东西作为消息发送出去 。
  2. 使用 Python 编写接口,实现新旧基础设施之间的对接 。
Pieter Hintjens 是 ZeroMQ 项目发起者之一,他是个拥有 有趣视角和作品 的非凡人物 。
准备本教程中,需要:
  • 一个 C 编译器(例如 GCC 或 Clang )
  • libzmq 库
  • Python 3
  • ZeroMQ 的 Python 封装
Fedora 系统上的安装方法:
$ dnf install clang zeromq zeromq-devel python3 python3-zmqDebian 和 Ubuntu 系统上的安装方法:
$ apt-get install clang libzmq5 libzmq3-dev python3 python3-zmq如果有问题,参考对应项目的安装指南(上面附有链接) 。
编写硬件接口库因为这里针对的是个设想的场景,本教程虚构了包含两个函数的操作库:
  • fancyhw_init() 用来初始化(设想的)硬件
  • fancyhw_read_val() 用于返回从硬件读取的数据
将库的完整代码保存到文件 libfancyhw.h 中:
#ifndef LIBFANCYHW_H#define LIBFANCYHW_H#include <stdlib.h>#include <stdint.h>// This is the fictitious hardware interfacing libraryvoid fancyhw_init(unsigned int init_param){    srand(init_param);}int16_t fancyhw_read_val(void){    return (int16_t)rand();}#endif这个库可以模拟你要在不同语言实现的组件间交换的数据,中间有个随机数发生器 。
设计 C 接口下面从包含管理数据传输的库开始,逐步实现 C 接口 。
需要的库
开始先加载必要的库(每个库的作用见代码注释):
// For printf()#include <stdio.h>// For EXIT_*#include <stdlib.h>// For memcpy()#include <string.h>// For sleep()#include <unistd.h>#include <zmq.h>#include "libfancyhw.h"必要的参数
定义 main 函数和后续过程中必要的参数:
int main(void){    const unsigned int INIT_PARAM = 12345;    const unsigned int REPETITIONS = 10;    const unsigned int PACKET_SIZE = 16;    const char *TOPIC = "fancyhw_data";    ...初始化
所有的库都需要初始化 。虚构的那个只需要一个参数:
fancyhw_init(INIT_PARAM);ZeroMQ 库需要实打实的初始化 。首先,定义对象 context,它是用来管理全部的套接字的:
void *context = zmq_ctx_new();if (!context){    printf("ERROR: ZeroMQ error occurred during zmq_ctx_new(): %sn", zmq_strerror(errno));    return EXIT_FAILURE;}之后定义用来发送数据的套接字 。ZeroMQ 支持若干种套接字,各有其用 。使用 publish 套接字(也叫 PUB 套接字),可以复制消息并分发到多个接收端 。这使得你可以让多个接收端接收同一个消息 。没有接收者的消息将被丢弃(即不会入消息队列) 。用法如下:
void *data_socket = zmq_socket(context, ZMQ_PUB);套接字需要绑定到一个具体的地址,这样客户端就知道要连接哪里了 。本例中,使用了 TCP 传输层 (当然也有 其它选项,但 TCP 是不错的默认选择):
const int rb = zmq_bind(data_socket, "tcp://*:5555");if (rb != 0){    printf("ERROR: ZeroMQ error occurred during zmq_ctx_new(): %sn", zmq_strerror(errno));    return EXIT_FAILURE;}


推荐阅读