Skip to content

Commit 0635490

Browse files
committed
Add pubsub functionality via MQTT
Signed-off-by: Rene Schwermer <rene.schwermer@tum.de>
1 parent 44b9b78 commit 0635490

8 files changed

Lines changed: 395 additions & 42 deletions

File tree

CMakeLists.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,19 @@ endif()
4141

4242
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
4343

44-
set(GEDS_EXTRA_COMPILER_FLAGS -Wall -Wextra -Werror) # -Wpedantic # error: ISO C++ does not support ‘__int128’ for ‘type name’ [-Werror=pedantic]
44+
# Added for pub/sub service
45+
# Requires to remove GEDS_EXTRA_COMPILER_FLAGS -Werror
46+
include(FetchContent)
47+
FetchContent_Declare(MQTT
48+
GIT_REPOSITORY https://github.com/eclipse/paho.mqtt.c.git
49+
GIT_TAG v1.3.8)
50+
FetchContent_MakeAvailable(MQTT)
51+
FetchContent_Declare(MQTT_CXX
52+
GIT_REPOSITORY https://github.com/eclipse/paho.mqtt.cpp
53+
GIT_TAG v1.2.0)
54+
FetchContent_MakeAvailable(MQTT_CXX)
55+
56+
set(GEDS_EXTRA_COMPILER_FLAGS -Wall -Wextra) # -Wpedantic # error: ISO C++ does not support ‘__int128’ for ‘type name’ [-Werror=pedantic]
4557
set(GEDS_EXTRA_LINKER_FLAGS)
4658

4759
# if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")

doc/BUILDING.md

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
1-
# Building
2-
3-
## CMake
4-
Install CMake > 3.20.
5-
6-
- Build commands:
7-
```bash
8-
cmake -DCMAKE_BUILD_TYPE=Debug -S . -B $BUILD_DIR
9-
cmake --build $BUILD_DIR
10-
```
11-
12-
- Test commands:
13-
```bash
14-
cmake --build $BUILD_DIR -t test
15-
```
16-
17-
- Install command:
18-
```bash
19-
cmake --install $BUILD_DIR --prefix $INSTALL_DIR --component geds
20-
```
21-
22-
## Docker
23-
24-
`build-docker.sh` builds a docker container with GRPC and a build of GEDS in `/usr/local/opt/geds`.
25-
26-
## Dependencies
27-
28-
### MacOS
1+
# Building GEDS
2+
3+
- [Workflow](#workflow)
4+
- [Instructions for MacOS](#instructions-for-macos)
5+
- [Instructions for Windows](#instructions-for-windows)
6+
- [Instructions for Linux](#instructions-for-linux)
7+
- [Deploying via Docker](#deploying-via-docker)
8+
- [Deploying via Ansible](#deploying-via-ansible)
9+
10+
## Workflow <a name="workflow"></a>
11+
The general workflow of building GEDS from source is:
12+
1. Pull GEDS repository: `git pull https://github.com/IBM/GEDS.git`
13+
2. Install dependencies, e.g. `cmake` version > 3.20 (check via `cmake --version`)
14+
3. Create `build` and `install` directory in the GEDS folder and set environment variables: `export $BUILD_DIR=~/GEDS/build` & `export $INSTALL_DIR=~/GEDS/bin`
15+
4. Build Boost
16+
5. Build AWS SDK
17+
6. Build GEDS
18+
7. Install GEDS
19+
20+
## Instructions for MacOS <a name="instructions-for-macos"></a>
2921

3022
Install the following dependencies through homebrew:
3123

@@ -54,23 +46,41 @@ Finally build it with:
5446
cmake --build . --target all
5547
```
5648

57-
### Linux
49+
## Instructions for Windows <a name="instructions-for-windows"></a>
50+
Coming
5851

59-
Install the following dependencies:
52+
## Instructions for Linux <a name="instructions-for-linux"></a>
53+
Install GEDS dependencies:
6054

6155
```
62-
apt-get install -y \
63-
clang \
64-
curl wget \
65-
build-essential gcc ninja-build \
66-
openjdk-11-jdk \
67-
python3.9 python3.9-dev python3-distutils
56+
sudo apt install -y clang curl wget build-essential gcc ninja-build openjdk-11-jdk python3-dev python3-distutils cmake
6857
```
6958

70-
and a recent version (>= 3.20) of CMake:
59+
CMake version >= 3.20:
7160
```
7261
CMAKE_VERSION=3.22.4
7362
wget --quiet -O cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz \
7463
&& tar xf cmake.tar.gz --strip-components=1 -C /usr/local/ \
7564
&& rm cmake.tar.gz
7665
```
66+
67+
Install AWS SDK dependecies:
68+
```
69+
sudo apt install libcurl4-openssl-dev libssl-de uuid-dev zlib1g-dev libpulse-dev
70+
```
71+
72+
Build AWS SDK: `/bin/bash build-aws-sdk.sh`
73+
74+
Build Boost: `/bin/bash build-boost.sh`
75+
76+
Build GEDS:
77+
1. Check if environment variables are correctly set via `printenv | grep BUILD_DIR` and `printenv | grep INSTALL_DIR`
78+
2. `cmake -DCMAKE_BUILD_TYPE=Debug -S . -B $BUILD_DIR`
79+
3. `cmake --build $BUILD_DIR -j 4` (-j specifies the number of cores to use)
80+
4. `cmake --install $BUILD_DIR --prefix $INSTALL_DIR --component geds`
81+
82+
## Deploying via Docker <a name="deploying-via-docker"></a>
83+
`build-docker.sh` builds a docker container with GRPC and a build of GEDS in `/usr/local/opt/geds`.
84+
85+
## Deploying via Ansible <a name="deploying-via-ansible"></a>
86+
We offer an Ansible playbook to automate GEDS building from source on multiple clients.

doc/geds_ansible.yml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
- hosts: geds
3+
name: Update all apt packages
4+
become: false
5+
vars:
6+
ansible_python_interpreter: /usr/bin/python3
7+
remote_home: "{{ ansible_env.HOME }}"
8+
9+
tasks:
10+
- name: Update and upgrade
11+
tags: update
12+
become: true
13+
apt:
14+
upgrade: yes
15+
update_cache: yes
16+
17+
- name: Reboot
18+
tags: reboot
19+
become: true
20+
reboot:
21+
22+
- name: Install GEDS dependencies
23+
tags: dependencies
24+
become: true
25+
apt:
26+
pkg:
27+
- clang
28+
- curl
29+
- wget
30+
- build-essential
31+
- gcc
32+
- ninja-build
33+
- openjdk-11-jdk
34+
- python3-dev
35+
- python3-distutils
36+
- cmake
37+
state: latest
38+
update_cache: yes
39+
40+
- name: Create GEDS directory
41+
tags: git
42+
become: false
43+
file:
44+
path: "{{ remote_home }}/GEDS"
45+
state: directory
46+
47+
- name: Git clone GEDS
48+
tags: git
49+
become: false
50+
ansible.builtin.git:
51+
repo: "https://github.com/IBM/GEDS.git"
52+
dest: "{{ remote_home }}/GEDS/"
53+
54+
- name: AWS dependencies
55+
tags: aws
56+
become: true
57+
apt:
58+
pkg:
59+
- libcurl4-openssl-dev
60+
- libssl-dev
61+
- uuid-dev
62+
- zlib1g-dev
63+
- libpulse-dev
64+
state: latest
65+
update_cache: yes
66+
67+
- name: Build AWS
68+
tags: aws
69+
become: false
70+
ansible.builtin.command: /bin/bash build-aws-sdk.sh
71+
async: 3600
72+
poll: 30
73+
args:
74+
chdir: "{{ remote_home }}/GEDS"
75+
76+
- name: Build boost
77+
tags: boost
78+
become: false
79+
ansible.builtin.command: /bin/bash build-boost.sh
80+
args:
81+
chdir: "{{ remote_home }}/GEDS"
82+
83+
- name: Create GEDS build directory
84+
tags: geds
85+
become: false
86+
file:
87+
path: "{{ remote_home }}/GEDS/build"
88+
state: directory
89+
90+
- name: Build GEDS
91+
tags: geds
92+
become: false
93+
ansible.builtin.command: cmake -DCMAKE_BUILD_TYPE=Debug -S . -B $BUILD_DIR
94+
args:
95+
chdir: "{{ remote_home }}/GEDS"
96+
environment:
97+
BUILD_DIR: "{{ remote_home }}/GEDS/build"
98+
99+
- name: Build GEDS
100+
tags: geds
101+
become: false
102+
ansible.builtin.command: cmake --build $BUILD_DIR -j 4
103+
args:
104+
chdir: "{{ remote_home }}/GEDS"
105+
environment:
106+
BUILD_DIR: "{{ remote_home }}/GEDS/build"
107+
108+
- name: Create GEDS install directory
109+
tags: geds
110+
become: false
111+
file:
112+
path: "{{ remote_home }}/GEDS/bin"
113+
state: directory
114+
115+
- name: Install GEDS
116+
tags: geds
117+
become: false
118+
ansible.builtin.command: cmake --install $BUILD_DIR --prefix $INSTALL_DIR --component geds
119+
args:
120+
chdir: "{{ remote_home }}/GEDS"
121+
environment:
122+
BUILD_DIR: "{{ remote_home }}/GEDS/build"
123+
INSTALL_DIR: "{{ remote_home }}/GEDS/bin"

src/metadataservice/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(SOURCES
1010
ObjectStoreHandler.h
1111
S3Helper.cpp
1212
S3Helper.h
13+
PubSubMQTT.h
1314
)
1415

1516
add_library(libmetadataservice STATIC ${SOURCES})
@@ -19,6 +20,7 @@ target_link_libraries(libmetadataservice
1920
geds_utility
2021
geds_proto
2122
geds_s3
23+
paho-mqttpp3
2224
)
2325
target_compile_options(libmetadataservice PUBLIC ${GEDS_EXTRA_COMPILER_FLAGS})
2426
target_compile_definitions(libmetadataservice

src/metadataservice/PubSubMQTT.h

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* Copyright 2022- IBM Inc. All rights reserved
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "mqtt/async_client.h"
7+
8+
9+
// https://github.com/eclipse/paho.mqtt.cpp/issues/141
10+
mqtt::async_client_ptr createClient(std::string serverAddress,
11+
std::string clientID){
12+
// Use MQTT v5 to enable no_local:
13+
// This flag avoids receiving messages from the same host by telling
14+
// the broker not to send messages received with a client ID to a
15+
// subscriber with the same client ID
16+
17+
mqtt::create_options createOpts = mqtt::create_options();
18+
createOpts.set_mqtt_verison(5);
19+
std::string persistDir= "None";
20+
auto client_ptr = std::make_shared<mqtt::async_client>(serverAddress,
21+
clientID,
22+
createOpts,
23+
persistDir);
24+
std::cout << "Created MQTT client to " + serverAddress + " and ID " + clientID << std::endl;
25+
return client_ptr;
26+
}
27+
28+
mqtt::async_client_ptr connectClient(std::shared_ptr<mqtt::async_client> client_ptr,
29+
std::string node){
30+
auto connOpts = std::make_shared<mqtt::connect_options>();
31+
32+
if (node == "server"){
33+
connOpts->set_keep_alive_interval(20);
34+
connOpts->set_clean_session(false);
35+
connOpts->set_automatic_reconnect(true);
36+
connOpts->set_mqtt_version(5);
37+
}
38+
if (node == "client"){
39+
connOpts->set_clean_session(false);
40+
connOpts->set_mqtt_version(5);
41+
}
42+
client_ptr->connect(*connOpts)->wait();
43+
std::cout << "Connected MQTT client" << std::endl;
44+
return client_ptr;
45+
}
46+
47+
void publishData(mqtt::async_client_ptr client_ptr,
48+
std::string topic,
49+
int QoS,
50+
std::string data){
51+
mqtt::topic top(*client_ptr, topic, QoS, true);
52+
mqtt::message_ptr message = mqtt::make_message(topic, data);
53+
message->set_qos(QoS);
54+
message->set_payload("A single message");
55+
56+
client_ptr->publish(message);
57+
std::cout << "Published data to " + topic << std::endl;
58+
}
59+
60+
mqtt::async_client_ptr subscribe(mqtt::async_client_ptr client_ptr,
61+
std::string topic,
62+
int QoS){
63+
mqtt::subscribe_options subOpts;
64+
subOpts.set_no_local(true); // Only works with MQTT v5
65+
66+
client_ptr->subscribe(topic, QoS, subOpts)->wait();
67+
std::cout << "Subscribed to " + topic << std::endl;
68+
return client_ptr;
69+
}
70+
71+
void unsubscribe(mqtt::async_client_ptr client_ptr,
72+
std::string topic){
73+
client_ptr->unsubscribe(topic)->wait();
74+
client_ptr->stop_consuming();
75+
std::cout << "Unsubscribed from " + topic << std::endl;
76+
}
77+
78+
std::tuple<std::string, std::string> consumeMessage(mqtt::async_client_ptr client_ptr){
79+
client_ptr->start_consuming();
80+
auto msg = client_ptr->consume_message();
81+
// msg->get_payload()
82+
return std::make_tuple(msg->get_topic(), msg->to_string());
83+
}
84+
85+
void disconnectClient(mqtt::async_client_ptr client_ptr){
86+
client_ptr->disconnect()->wait();
87+
std::cout << "Disconnected client" << std::endl;
88+
}
89+
90+
91+
// Example publisher
92+
// ---------------------------------------------------------------
93+
// #include "PubSub.h"
94+
//
95+
// int main(int argc, char **argv) {
96+
// mqtt::async_client_ptr client_ptr = createClient("tcp://localhost:1883",
97+
// "mds_server");
98+
// std::string node_type = "server";
99+
// mqtt::async_client_ptr connected_client_ptr = connectClient(client_ptr,
100+
// node_type);
101+
// publishData(connected_client_ptr, "home/file1", 1, "Hello World");
102+
// disconnectClient(connected_client_ptr);
103+
// return 0;
104+
// }
105+
106+
// Example subscriber
107+
// ---------------------------------------------------------------
108+
// #include "PubSub.h"
109+
//
110+
// int main(void)
111+
// {
112+
// mqtt::async_client_ptr client_ptr = createClient("tcp://localhost:1883",
113+
// "mds_client");
114+
// std::string node_type = "client";
115+
// mqtt::async_client_ptr connected_client_ptr = connectClient(client_ptr,
116+
// node_type);
117+
// mqtt::async_client_ptr subscribed_client_ptr = subscribe(connected_client_ptr,
118+
// "home/file1", 1);
119+
// while (true) {
120+
// auto msg = consumeMessage(subscribed_client_ptr);
121+
// std::string topic = std::get<0>(msg);
122+
// std::string payload = std::get<1>(msg);
123+
//
124+
// if (payload.empty()){
125+
// break;
126+
// }
127+
// std::cout << topic + " " + payload << std::endl;
128+
// }
129+
// return 0;
130+
// }

0 commit comments

Comments
 (0)