├── .env ├── .gitignore ├── README.md ├── assets ├── banner.png ├── frame-install.png ├── lattice-relay.png └── start-direct.gif ├── connect ├── .direct.env ├── .dockerignore ├── .env ├── .gitignore ├── README.md ├── container │ ├── Dockerfile │ ├── dockerBuild.sh │ └── dockerStart.sh ├── package-lock.json ├── package.json ├── src │ ├── clients │ │ ├── createClient.ts │ │ └── createSigner.ts │ ├── core │ │ ├── createApp.ts │ │ ├── index.ts │ │ ├── spawnWorker.ts │ │ ├── startServer.ts │ │ └── utils.ts │ ├── direct.ts │ ├── index.ts │ └── services │ │ ├── useProvision.ts │ │ └── useSigning.ts └── tsconfig.json ├── mqtt-broker ├── .env ├── README.md ├── config │ ├── .gitignore │ ├── enabled_plugins │ └── rabbitmq.conf ├── container │ ├── Dockerfile │ ├── dockerBuild.sh │ └── dockerStart.sh └── metrics │ ├── .gitignore │ ├── grafana │ ├── dashboards.yml │ ├── dashboards │ │ ├── Erlang-Distribution.json │ │ ├── Erlang-Distributions-Compare.json │ │ ├── Erlang-Memory-Allocators.json │ │ ├── RabbitMQ-Overview.json │ │ ├── RabbitMQ-PerfTest.json │ │ ├── RabbitMQ-Quorum-Queues-Raft.json │ │ ├── RabbitMQ-Stream.json │ │ ├── inet_tcp_metrics.json │ │ └── rabbitmq-exporter_vs_rabbitmq-prometheus.json │ └── datasources.yml │ └── prometheus │ ├── prometheus.example.yml │ └── prometheus.yml └── pulumi ├── .gitignore ├── Pulumi.dev.yaml ├── Pulumi.prod.yaml ├── Pulumi.yaml ├── README.md ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json /.env: -------------------------------------------------------------------------------- 1 | # DCKR 2 | COMPOSE_PROJECT_NAME=lattice-connect 3 | 4 | # MQTT 5 | ERLANG_COOKIE='erlang-cookie' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | docker-compose.y*ml 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 👋 Introduction 4 | 5 | By default, [Lattice1](https://gridplus.io/lattice) devices are configured to use GridPlus' cloud services to connect apps and route messages. However, **Lattice Connect V2** allows you to disconnect your Lattice1 from the GridPlus cloud and run the services locally instead. Note that your Lattice1 still needs to be on your WiFi network in order to route local messages, as Lattice Connect V2 runs on your computer and thus must connect to your Lattice1 over LAN. 6 | 7 | This open-source project provides [Lattice1](https://gridplus.io/lattice) owners a HTTP server which they manage themselves, and that will proxy all messages between the device over their own local network, as an alternative to relying on the vendor-provided routing service. 8 | 9 | By default, communication between apps and a Lattice1 route through cloud infrastructure provided by [GridPlus](https://gridplus.io). Any messages sent to, and from, your device will always be encrypted and remain secure; however, we believe Lattice1 owners should be able to manage this service themselves, if they so choose. 10 | 11 | ### 🔗 Related Links 12 | - [📢 Discord](https://twitter.com/gridplus) 13 | - [🐤 Twitter](https://discord.gg/Bt5fVDTJb9) 14 | - [📚 Knowledge Base](https://docs.gridplus.io) 15 |   16 | 17 | ## 🤔 Why use Lattice Connect? 18 | 19 | Running _Lattice Connect_ yourself provides several advantages: 20 | 21 | - Doesn't requires running an external MQTT broker (compared to `v1`); 22 | - Offers the fastest message routing possible for a Lattice1; 23 | - Provides the highest amount of privacy available while using a Lattice1 24 | - Zero configuration changes required (i.e., no SSH'ing necessary); 25 | - Setup takes less than 5 minutes! 26 | 27 | ## 🚨 What about `v1`? 28 | This project replaces the original _[lattice-connect](https://github.com/GridPlus/lattice-connect)_, which is now archived. 29 | 30 | At the time of release, the previous software should continue working as-is; however, GridPlus will no longer offer technical support, or otherwise provide maintenance for the prior version. Thus, breaking changes that may occur as we continue improving our customers' user experience should be expected, and switching to `v2` as soon as possible is highly recommended. 31 | 32 | # ⌛️ Setup Guide 33 | 34 | ##### Estimated Time (TOTAL): 5–10 minutes 35 | 36 | It's possible to run the server: 37 | 38 | - directly on a host system using `node` v14+; or, 39 | - through a `Docker` container. 40 | 41 | > _**NOTE:** The instructions for each are nearly identical. This guide describes `node`; 42 | scripts are provided in `connect/container` that support the `Docker` method._ 43 | 44 | ### System Requirements 45 | 46 | Besides the runtime requirements, the system resources for the proxy server are trivial. It will work on any system which can run Node v14+, or Docker. 47 | 48 | The server has been tested on: 49 | 50 | - macOS v10.12; 51 | - Ubuntu 18.04; 52 | - Windows 10 53 | 54 | ## ⚙️ Configuring 55 | 56 | #### 1️⃣ Get the source code 57 | Clone the repo to the server or computer you plan to run it on: 58 | 59 | ```sh 60 | # Clone the repo: 61 | $ git clone https://github.com/GridPlus/lattice-connect-v2.git 62 | 63 | # Change your working director to the 'connect' folder: 64 | $ cd lattice-connect-v2/connect 65 | ``` 66 | 67 | #### 2️⃣ Configure the environment 68 | Edit `connect/.direct.env` and set your device's hostname: 69 | 70 | ```sh 71 | # - Open the '.direct.env' file; then, 72 | # - Replace this with your device's hostname 73 | ADMIN_CLIENT_HOST=http://GridPlus-xxxxxxxxxxx.local 74 | ``` 75 | ##### 🔍 Checking your device's hostname 76 | On Firmware v16, and above, the device's hostname is shown with the following steps: 77 | 78 | 1. **Unlock** the device; then, 79 | 2. Tap **System Preferences**; then, 80 | 3. Tap **Device Info**; then, 81 | 4. See `SSH Host`. 82 | 83 | ## 🌐 Start Proxy: Using Node 84 | From inside `connect` folder, run: 85 | 86 | ```sh 87 | # Install dependencies 88 | $ npm i 89 | 90 | # Start the proxy 91 | $ npm run start:direct 92 | 93 | # Look for confirmation... 94 | ... [!] MQTT client connected 95 | ``` 96 | 97 | 98 | 99 | ## 🐳 Start Proxy: Using Docker 100 | From inside the `connect/container` folder, run: 101 | 102 | ```sh 103 | # Script to build the container 104 | $ ./dockerBuild.sh 105 | 106 | # Script to start the proxy server 107 | $ ./dockerStart.sh 108 | ``` 109 | 110 | ## 🔬 Troubleshooting 111 | 112 | If the server fails to connect: 113 | 114 | - Double-check your `ADMIN_CLIENT_HOST` value; 115 | - Ensure `.local` is included as a suffix on the host; 116 | - `ping` your device, being certain your device is reachable before trying to run this software; 117 | - use the Lattice1 IP address as an alternative to hostname (see below); 118 | - be sure your network's firewall isn't blocking port 1883. 119 | 120 | ### Using IP Address 121 | For many of the most common network setups, the server should have no trouble finding, and connecting, to the Lattice1. However, if it's unable to connect—and you're certain you've inputted the `.local` correct—use the device's IP address instead: 122 | 123 | ```sh 124 | # - Open the '.direct.env' file; then, 125 | # - Replace this with your device's IP address. 126 | # - DON'T include '.local'; it's standard IPv4 format. 127 | ADMIN_CLIENT_HOST=http:// 128 | ``` 129 | 130 | > _**NOTE:** The IP address of the device can be determined from your network's main router or gateway appliance. Details on how to do this vary depending on your specific router or gateway appliance, and is outside the scope of this document._ 131 | 132 | # ✌️ Disconnecting Entirely from the GridPlus Cloud 133 | 134 | If you are using Lattice Connect V2 to route messages, your device will not use the GridPlus cloud, but will still be connected to it. If you would like to remove **all** connections to the GridPlus cloud, you will also need to erase some system settings and configure a custom device ID. Note that if you choose to do this, you will need to re-connect with all of your apps using the new device ID. 135 | 136 | 1. SSH into the Lattice (you can find credentials in `System Preferences -> Device Info` -- format the request like so: `ssh root@.local`) 137 | 2. Stop current processes: `service gpd stop && service mosquitto stop` 138 | 3. Update credentials: `uci set gridplus.provisionLatticeAPIURL="" && uci set gridplus.deviceID="ABCDEF" && uci set gridplus.remote_mqtt_address="foo" && uci commit` 139 | 4. Restart processes: `service gpd restart && service mosquitto restart` 140 | 141 | Give it ~30 seconds and view the `Device ID` on your Lattice menu. You should see the device ID you just configured -- this new device ID indicates that you have disconnected your Lattice from GridPlus cloud services. 142 | 143 | Note that you can set whatever `deviceID` credential you want, but you should probably use six characters to avoid any edge cases. Also note that `remote_mqtt_address` is not used when Lattice Connect V2 is routing messages, but it can't be empty or else `mosquitto` will fail to start. 144 | 145 | # 🔗 Connecting to Third Party Apps 146 | 147 | Now that Lattice Connect V2 is running on your computer, you will need to update your connections to third party apps, which may involve removing the Permission on your Lattice1 device. This section covers the most common connections: [MetaMask](https://metamask.io) and [Frame](https://frame.sh). 148 | 149 | ## 🖼 MetaMask 150 | 151 | Download the [MetaMask](https://metamask.io) extension if you don't have it already. 152 | 153 | #### Set the Lattice Relay 154 | 155 | 1. Start by removing all **Lattice1** accounts from MetaMask if present. 156 | Screenshot 2022-12-14 at 10 38 44 AM 157 | 158 | 3. Remove the MetaMask permission from your Lattice1 if present. 159 | 4. Log into the [Lattice Manager](https://lattice.gridplus.io) and remove any MetaMask entries from the **3rd Party Connections** list if present. 160 | Screenshot 2022-12-14 at 10 45 43 AM 161 | 162 | 5. In the **Settings** tab input the `http://:8080` into the **Connection Endpoint** field. 163 | 164 | Replace `` with the host running _Lattice Connect_. 165 | When running _Lattice Connect_ on the same computer you are using MetaMask, use `localhost`, otherwise use `http://:8080` where `` is the IP of the machine running **Lattice Connect**. 166 | 167 | Screenshot 2022-12-14 at 10 36 38 AM 168 | 169 | 6. Connect your Lattice to MetaMask as normal. Transaction requests will now be routed to your self-hosted endpoint. 170 | 171 | 172 | ## 🖼 Frame Wallet 173 | 174 | 175 | 176 | Download [Frame](https://frame.sh) wallet desktop app, and run the installer. 177 | 178 | #### Set the Lattice Relay 179 | 180 | From the _Settings_ panel (upper-right; slider icon): 181 | 182 | - Scroll down to the **Lattice Relay** option; then, 183 | - Click _Default_; switch to _Custom_; then, 184 | - Input the `http://:8080` 185 | 186 | Replace `RELAY_HOST` with the host running _Lattice Connect_. 187 | When running _Frame_ and _Lattice Connect_ on the same computer, use `localhost`: 188 | 189 | 190 | 191 | ## FAQ 192 | 193 | ### What do I need to do to migrate from `v1`? 194 | Nothing. If you've made changes from `SSH`, they will be ignored by `v2`. 195 | 196 | If you're adament about having factory settings, you may reset your router in the Lattice1 System Settings. Please be aware doing this will also reset your wireless routing settings, and will require reconnecting to your Wi-Fi network. 197 | 198 | ### How do I connect more than one Lattice1? 199 | Currently, the direct method supports a single Lattice1 at a time. 200 | -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GridPlus/lattice-connect-v2/646115eb82ddc53f4554748fea37ef8a777162e8/assets/banner.png -------------------------------------------------------------------------------- /assets/frame-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GridPlus/lattice-connect-v2/646115eb82ddc53f4554748fea37ef8a777162e8/assets/frame-install.png -------------------------------------------------------------------------------- /assets/lattice-relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GridPlus/lattice-connect-v2/646115eb82ddc53f4554748fea37ef8a777162e8/assets/lattice-relay.png -------------------------------------------------------------------------------- /assets/start-direct.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GridPlus/lattice-connect-v2/646115eb82ddc53f4554748fea37ef8a777162e8/assets/start-direct.gif -------------------------------------------------------------------------------- /connect/.direct.env: -------------------------------------------------------------------------------- 1 | APP_WORKER_COUNT=1 2 | 3 | # - Replace this with your device's hostname 4 | # - Should be appear as: GridPlus-xxxxxxxxxxx.local 5 | ADMIN_CLIENT_HOST=http://GridPlus-xxxxxxxxxxx.local 6 | 7 | MQTT_HTTP_PORT=1883 8 | MQTT_SKIP_USER_CHECK=1 9 | -------------------------------------------------------------------------------- /connect/.dockerignore: -------------------------------------------------------------------------------- 1 | .src/node_modules 2 | .dist 3 | .package-lock.json -------------------------------------------------------------------------------- /connect/.env: -------------------------------------------------------------------------------- 1 | APP_SERVICE_PORT=8080 2 | APP_WORKER_COUNT=2 3 | MQTT_HTTP_PORT=8883 4 | -------------------------------------------------------------------------------- /connect/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ -------------------------------------------------------------------------------- /connect/README.md: -------------------------------------------------------------------------------- 1 | # Environment Parameters 2 | 3 | > _Note: End-users should follow the instructions provided in the main folder's `README`._ 4 | 5 | ### For Internal Use 6 | 7 | - ADMIN_CLIENT_HOST 8 | - ADMIN_CLIENT_PASS 9 | - ADMIN_CLIENT_USER 10 | - APP_SERVICE_PORT 11 | - APP_WORKER_COUNT 12 | - MQTT_HTTP_PORT 13 | - MQTT_SKIP_USER_CHECK 14 | -------------------------------------------------------------------------------- /connect/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 AS base 2 | 3 | RUN apt update && \ 4 | apt install -y curl 5 | 6 | RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ 7 | apt-get install -y nodejs 8 | 9 | FROM base AS builder 10 | WORKDIR /build/connect 11 | ADD src /build/connect/src 12 | ADD package.json /build/connect 13 | ADD tsconfig.json /build/connect 14 | 15 | WORKDIR /build/connect 16 | RUN npm i && npx tsc --build tsconfig.json && npm prune --production 17 | RUN rm -rf node_modules/typescript 18 | 19 | FROM node:16-alpine AS app 20 | COPY --from=builder /build/connect/dist /app/dist 21 | COPY --from=builder /build/connect/node_modules /app/node_modules 22 | CMD NODE_ENV=production node /app/dist/index.js 23 | -------------------------------------------------------------------------------- /connect/container/dockerBuild.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | docker build \ 4 | -f Dockerfile \ 5 | ../ \ 6 | -t lattice-connect:latest 7 | -------------------------------------------------------------------------------- /connect/container/dockerStart.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | docker run \ 4 | --rm -it \ 5 | --init \ 6 | -h lattice-connect \ 7 | --env-file ../.env \ 8 | --env-file ../.direct.env \ 9 | -p 8080:8080 \ 10 | --name "lattice-connect" \ 11 | lattice-connect \ 12 | node /app/dist/direct.js -------------------------------------------------------------------------------- /connect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "private": true, 4 | "version": "0.0.1", 5 | "scripts": { 6 | "start": "NODE_ENV=development nodemon ./src/index.ts", 7 | "start:mqtt": "DEBUG='mqttjs*' NODE_ENV=development nodemon ./src/index.ts", 8 | "start:direct": "nodemon ./src/direct.ts", 9 | "start:direct:mqtt": "DEBUG='mqttjs*' NODE_ENV=development nodemon ./src/direct.ts", 10 | "prod": "tsc --build tsconfig.json; NODE_ENV=production node ./dist/index.js" 11 | }, 12 | "dependencies": { 13 | "async-mqtt": "^2.6.2", 14 | "bs58": "^5.0.0", 15 | "cors": "^2.8.5", 16 | "dotenv": "^16.0.1", 17 | "express": "^4.18.1", 18 | "express-pino-logger": "^7.0.0", 19 | "pino-http": "^8.1.1", 20 | "superagent": "^8.0.0" 21 | }, 22 | "devDependencies": { 23 | "@types/cors": "^2.8.12", 24 | "@types/express": "^4.17.13", 25 | "@types/express-pino-logger": "^4.0.3", 26 | "@types/node": "^18.0.1", 27 | "@types/superagent": "^4.1.15", 28 | "nodemon": "^2.0.19", 29 | "ts-node": "^10.8.2", 30 | "typescript": "^4.7.4" 31 | }, 32 | "nodemonConfig": { 33 | "execMap": { 34 | "ts": "node --require ts-node/register/transpile-only" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /connect/src/clients/createClient.ts: -------------------------------------------------------------------------------- 1 | import rp from 'superagent'; 2 | export interface IAdminConfig { 3 | url: URL, 4 | username: string, 5 | password: string 6 | } 7 | 8 | type APIArgs = { 9 | api: string; 10 | payload?: any; 11 | }; 12 | 13 | type APIMethod = ( 14 | { 15 | api, 16 | payload 17 | }: APIArgs) => rp.SuperAgentRequest; 18 | 19 | export interface IAdminClient { 20 | get: APIMethod, 21 | put: APIMethod 22 | } 23 | 24 | function _createClient({ url, username, password }: IAdminConfig): { 25 | client: IAdminClient 26 | } { 27 | //-------------------------------------------------- 28 | // GET 29 | //-------------------------------------------------- 30 | function get({ 31 | api, payload 32 | }: { 33 | api: string; 34 | payload?: any; 35 | }) { 36 | console.log(`${url}api/${api}`) 37 | return rp 38 | .get(`${url}api/${api}`) 39 | .auth(username, password) 40 | .set('Content-Type', 'application/json') 41 | .send(payload) 42 | } 43 | 44 | //-------------------------------------------------- 45 | // PUT 46 | //-------------------------------------------------- 47 | function put({ 48 | api, payload 49 | }: { 50 | api: string; 51 | payload?: any; 52 | }) { 53 | return rp 54 | .put(`${url}api/${api}`) 55 | .auth(username, password) 56 | .set('Content-Type', 'application/json') 57 | .send(payload); 58 | } 59 | 60 | return { client: { get, put } } 61 | } 62 | 63 | export function createClient({ url, username, password }: { 64 | url: URL | undefined, 65 | username: string | undefined, 66 | password: string | undefined 67 | }): Promise<{ client: IAdminClient }> { 68 | return new Promise((res, rej) => { 69 | if (!url) { 70 | rej(new Error(`Invalid Admin Config: HOST || ADDRESS (undefined)`)) 71 | } 72 | if (!username) { 73 | rej(new Error(`Invalid Admin Config: USERNAME (undefined)`)) 74 | } 75 | if (!password) { 76 | rej(new Error(`Invalid Admin Config: PASSWORD (undefined)`)) 77 | } 78 | 79 | res(_createClient({ 80 | url: url!, 81 | username: username!, 82 | password: password! 83 | })) 84 | }) 85 | } -------------------------------------------------------------------------------- /connect/src/clients/createSigner.ts: -------------------------------------------------------------------------------- 1 | import MQTT, { IClientOptions } from "async-mqtt" 2 | import bs58 from "bs58" 3 | import crypto from 'crypto' 4 | import { EventEmitter } from "stream" 5 | 6 | export interface ISignerClient { 7 | sendForApproval: (payload: Buffer, deviceId: string, requestId: string) => Promise, 8 | } 9 | 10 | export function createSigner(config: IClientOptions, logger?: any): { signer: ISignerClient } { 11 | //-------------------------------------------------------------------------------- 12 | // CLIENT: MQTT Connection 13 | //-------------------------------------------------------------------------------- 14 | const protocol = process.env["MQTT_HTTP_PORT"] === "8883" ? 'mqtts' : 'mqtt' 15 | const client = MQTT.connect(null, { 16 | clientId: `lattice-${bs58.encode(crypto.randomBytes(8)).slice(6).toUpperCase()}`, 17 | protocol: protocol, 18 | ...config 19 | }) 20 | 21 | //-------------------------------------------------------------------------------- 22 | // EMITTER: 'Message Received' 23 | //-------------------------------------------------------------------------------- 24 | const msgEmitter = new EventEmitter() 25 | const computeEmitterKey = (deviceId: string, requestId: string) => deviceId + requestId 26 | 27 | //-------------------------------------------------------------------------------- 28 | // TOPICS: 'TO Agent (Publish)' & 'FROM Agent (Subscribe)' 29 | //-------------------------------------------------------------------------------- 30 | const publishTopic = (deviceId, requestId) => `to_agent/${deviceId}/request/${requestId}` 31 | const subscribeTopic = (deviceId, requestId) => `from_agent/${deviceId}/response/${requestId}` 32 | 33 | //-------------------------------------------------------------------------------- 34 | // EVENT: 'Connect' 35 | //-------------------------------------------------------------------------------- 36 | client.on("connect", (stream) => { 37 | logger?.info(".: [!] MQTT client connected") 38 | }) 39 | 40 | //-------------------------------------------------------------------------------- 41 | // EVENT: 'Reconnect' 42 | //-------------------------------------------------------------------------------- 43 | client.on("reconnect", () => { 44 | logger?.info(".: [!] MQTT client reconnecting...") 45 | }) 46 | 47 | //-------------------------------------------------------------------------------- 48 | // EVENT: 'Disconnect' 49 | //-------------------------------------------------------------------------------- 50 | client.on("disconnect", (stream) => { 51 | logger?.info(".: [!] MQTT client disconnected") 52 | }) 53 | 54 | //-------------------------------------------------------------------------------- 55 | // EVENT: '(On) Message' 56 | //-------------------------------------------------------------------------------- 57 | client.on("message", (topic, payload) => { 58 | const receivedDeviceId = topic.split('/')[1] 59 | const receivedRequestId = topic.split('/')[3] 60 | const receievedPayload = payload.toString('hex') 61 | 62 | // LOG: Confirmation 63 | logger?.info(".: [!] MQTT client receieved:\n", JSON.stringify({ 64 | topic: topic, 65 | deviceId: receivedDeviceId, 66 | requestId: receivedRequestId, 67 | payload: `${payload.length} bytes` 68 | }, null, 2)) 69 | 70 | const emitterKey = computeEmitterKey(receivedDeviceId, receivedRequestId) 71 | msgEmitter.emit(emitterKey, receievedPayload) 72 | }) 73 | 74 | function sendForApproval(payload: Buffer, deviceId: string, requestId: string): Promise { 75 | return new Promise(async (res, rej) => { 76 | // LOG: Initiation 77 | logger?.info(".: [a] Processing request:\n", JSON.stringify({ 78 | deviceId: deviceId, 79 | requestId: requestId, 80 | payload: `${payload.length} bytes` 81 | }, null, 2)) 82 | 83 | try { 84 | await client.subscribe(subscribeTopic(deviceId, requestId), { qos: 1 }) 85 | await client.publish(publishTopic(deviceId, requestId), payload, { 86 | qos: 1, 87 | retain: false, 88 | dup: false 89 | }) 90 | } 91 | /** 92 | * Catch 'error' 93 | */ 94 | catch (error) { 95 | logger?.info(".: [!] Error:\n", JSON.stringify({ 96 | error 97 | })) 98 | return rej(error) 99 | } 100 | 101 | const emitterKey = computeEmitterKey(deviceId, requestId) 102 | const duration = 120000 103 | 104 | setTimeout(() => { 105 | msgEmitter.emit(emitterKey, "TIMED_OUT") 106 | }, duration) 107 | 108 | msgEmitter.once(emitterKey, async (payload) => { 109 | try { 110 | /** 111 | * Unsubscribe & parse 'response' 112 | */ 113 | await client 114 | .unsubscribe(subscribeTopic(deviceId, requestId)) 115 | .then(() => { 116 | let response = {} 117 | if (payload === "TIMED_OUT") { 118 | response = { 119 | status: 500, 120 | message: "Timed out waiting for response from device" 121 | } 122 | 123 | // LOG: Status Update 124 | logger?.info(`.: [!] Request timed out: ${JSON.stringify({ deviceId, requestId }, null, 2)}`) 125 | } 126 | else { 127 | response = { 128 | status: 200, 129 | message: payload 130 | } 131 | } 132 | // LOG: Confirmation 133 | logger?.info(".: [b] Responding with:\n", JSON.stringify({ payload: `${payload.length} bytes` }, null, 2)) 134 | return response 135 | }) 136 | .then(res) 137 | } 138 | /** 139 | * Catch 'error' 140 | */ 141 | catch (error) { 142 | logger?.info(".: [!] Error:\n", JSON.stringify({ 143 | error 144 | })) 145 | /** 146 | * REJECT: 'error' 147 | */ 148 | rej(error) 149 | } 150 | }) 151 | 152 | // LOG: Send & Wait 153 | logger?.info(`.: [?] Awaiting approval (with timeout: ${duration}ms)...`) 154 | }) 155 | } 156 | 157 | return { signer: { sendForApproval } } 158 | } -------------------------------------------------------------------------------- /connect/src/core/createApp.ts: -------------------------------------------------------------------------------- 1 | import pino, { Options } from "express-pino-logger"; 2 | import express from "express"; 3 | import bodyParser from "body-parser"; 4 | import cors from "cors"; 5 | 6 | type App = ReturnType 7 | 8 | export interface IAppConfig { 9 | logging?: Options, 10 | port?: number | string, 11 | workers?: number | string 12 | } 13 | 14 | export interface IApp extends App { 15 | config?: IAppConfig 16 | } 17 | 18 | /** 19 | * Creates an 'Express' instance. 20 | * 21 | * @param config The object that defines specific features of our app instance. 22 | * @returns An 'app' & 'logger' which is associated with the app. 23 | */ 24 | export function createApp(config: IAppConfig): Promise<{ app: IApp, logger: any }> { 25 | //---------------------------------------------------------------------------- 26 | // APP: Create 27 | //---------------------------------------------------------------------------- 28 | const app: IApp = express(); 29 | const logger = pino(config.logging) 30 | 31 | //---------------------------------------------------------------------------- 32 | // APP: Setup 33 | //---------------------------------------------------------------------------- 34 | app.use(cors()); 35 | app.use(bodyParser.json()); 36 | app.use(logger) 37 | 38 | //---------------------------------------------------------------------------- 39 | // GET: "/" 40 | //---------------------------------------------------------------------------- 41 | app.get("/", async (_, res) => { 42 | res.sendStatus(200) 43 | }) 44 | 45 | //@ts-ignore 46 | app.config = config 47 | 48 | return Promise.resolve({ app, logger: logger.logger }) 49 | } -------------------------------------------------------------------------------- /connect/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import { IApp, IAppConfig } from "./createApp" 2 | import { startServer } from "./startServer" 3 | import { spawn } from "./spawnWorker" 4 | import { Worker } from "cluster"; 5 | 6 | export interface IAppService { 7 | app: IApp, 8 | logger: any 9 | } 10 | 11 | export function startService(config: IAppConfig, useService: ((service: IAppService) => void)[]) { 12 | spawn({ 13 | upTo: config.workers, 14 | worker: (worker: Worker) => { 15 | startServer({ 16 | worker, 17 | config 18 | }) 19 | .then(server => useService.forEach(service => service(server))) 20 | .catch((err: any) => { 21 | console.error(err) 22 | process.exit(1) 23 | }) 24 | } 25 | }) 26 | } -------------------------------------------------------------------------------- /connect/src/core/spawnWorker.ts: -------------------------------------------------------------------------------- 1 | import cluster, { Worker } from "cluster" 2 | 3 | const log = { 4 | info: console.log 5 | } 6 | 7 | /** 8 | * Spawns as many workers as there are CPUs on the host system, 9 | * unless limited by `limitTo`. 10 | * 11 | * At a minimum, 1 worker is guaranteed to spawn. 12 | */ 13 | export function spawn({ upTo, worker }: { upTo?: string | number | undefined, worker: (worker: Worker) => void }) { 14 | if (cluster.isMaster) { 15 | // Spawn Processes 16 | const upperBound = require('os').cpus().length 17 | const lowerBound = upTo ?? upperBound 18 | const numWorkers = Math.max(1, Math.min(lowerBound, upperBound)) 19 | for (let i = 0; i < numWorkers; i++) { 20 | cluster.fork() 21 | } 22 | 23 | cluster.on('online', function (worker) { 24 | log.info('Worker ' + worker.id + ' is online') 25 | }) 26 | 27 | cluster.on('exit', function (worker, code, signal) { 28 | log.info(`Worker ${worker.id} died with code: ${code}, and signal: ${signal}`) 29 | if (code != 0 && code != 1) { 30 | log.info('Starting a new worker') 31 | cluster.fork() 32 | } else { 33 | log.info(`Worker ${worker.id} is not being replaced. Exiting.`) 34 | } 35 | }) 36 | } 37 | else { 38 | worker(cluster.worker!) 39 | } 40 | } -------------------------------------------------------------------------------- /connect/src/core/startServer.ts: -------------------------------------------------------------------------------- 1 | import http from "http"; 2 | import { Worker } from "cluster"; 3 | import { createApp, IAppConfig } from "./createApp"; 4 | 5 | export async function startServer({ worker, config }: { 6 | worker: Worker, 7 | config: IAppConfig 8 | }): Promise<{ app: any, logger: any }> { 9 | //---------------------------------------------------------------------------------- 10 | // APP: Port 11 | //---------------------------------------------------------------------------------- 12 | const port = config.port 13 | 14 | /** 15 | * This Promise creates a server from an already instantiated Express 'app', and 16 | * then starts listening on 'port'. 17 | * 18 | * It resolves once it begins listening; it rejects once 'server' receives its 19 | * first (and only) error, at which time the 'server' calls 'close()'. 20 | */ 21 | const createServer = ({ app, logger }) => { 22 | return new Promise<{ app: any, logger: any }>((resolve, rejected) => { 23 | //------------------------------------------------------------------------------ 24 | // HTTP: Create 'Server' 25 | //------------------------------------------------------------------------------ 26 | const server = http.createServer(app).listen(port) 27 | 28 | //------------------------------------------------------------------------------ 29 | // EVENT: 'Listening' 30 | //------------------------------------------------------------------------------ 31 | server.on('listening', () => { 32 | logger.info(`Worker ${worker.id}: listening on: ${port}`) 33 | resolve({ app, logger }) 34 | }) 35 | 36 | //------------------------------------------------------------------------------ 37 | // EVENT: 'Close' 38 | //------------------------------------------------------------------------------ 39 | server.on('close', async () => { 40 | logger.info(`Worker ${worker.id}: closing...`) 41 | }) 42 | 43 | //------------------------------------------------------------------------------ 44 | // EVENT: 'Error' 45 | //------------------------------------------------------------------------------ 46 | server.on('error', (err) => { 47 | server.close() 48 | rejected(err) 49 | }) 50 | }) 51 | } 52 | 53 | //---------------------------------------------------------------------------------- 54 | // SERVER: Create 'App'; Create 'Server' 55 | //---------------------------------------------------------------------------------- 56 | return createApp(config).then(createServer) 57 | } -------------------------------------------------------------------------------- /connect/src/core/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | import bs58 from "bs58" 3 | import crypto from 'crypto' 4 | 5 | export const getRandomUser: () => { id: string, password: string } = () => { 6 | return { 7 | id: bs58.encode(crypto.randomBytes(8)).slice(5), 8 | password: crypto.randomBytes(24).toString('hex') 9 | } 10 | } -------------------------------------------------------------------------------- /connect/src/direct.ts: -------------------------------------------------------------------------------- 1 | import cluster from "cluster" 2 | import { startService } from "./core" 3 | import { useSigning } from "./services/useSigning" 4 | 5 | if (cluster.isMaster) { 6 | require('dotenv').config({ debug: true, override: true, path: '.direct.env' }) 7 | } 8 | 9 | startService({ 10 | port: process.env.APP_SERVICE_PORT, 11 | workers: process.env.APP_WORKER_COUNT 12 | }, [useSigning]) 13 | -------------------------------------------------------------------------------- /connect/src/index.ts: -------------------------------------------------------------------------------- 1 | import { startService } from "./core" 2 | import { useProvision } from "./services/useProvision" 3 | import { useSigning } from "./services/useSigning" 4 | 5 | require('dotenv').config() 6 | 7 | startService({ 8 | port: process.env.APP_SERVICE_PORT, 9 | workers: process.env.APP_WORKER_COUNT 10 | }, [useProvision, useSigning]) -------------------------------------------------------------------------------- /connect/src/services/useProvision.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { IAppService } from "../core" 3 | import { IAdminClient, createClient } from "../clients/createClient" 4 | import { getRandomUser } from "../core/utils" 5 | 6 | export async function useProvision(service: IAppService) { 7 | const retry = { 8 | count: 0, 9 | limit: 5 10 | } 11 | 12 | const generateNextValidUser = async (client: IAdminClient, userGenerator: () => { 13 | id: string; 14 | password: string; 15 | }): Promise<{ id: string, password: string }> => { 16 | return new Promise(async (res, rej) => { 17 | const user = userGenerator() 18 | await client 19 | .get({ api: `users/${user.id}` }) 20 | .ok((res: any) => res.status == 404) 21 | .catch((error: any) => { 22 | if (error.status == 200) { 23 | // We've hit our "retry.limit". The chances of this 24 | // happening are **infinitesimally** small. 25 | //------------------------------------------------- 26 | if (retry.count >= retry.limit) { 27 | retry.count = 0 28 | return rej({ 29 | status: 500, 30 | message: "Unable to provision new user. Retry limit exceeded." 31 | }) 32 | } 33 | 34 | // Keep retrying (up to "retry.limit" times)... 35 | //---------------------------------------------- 36 | retry.count += 1 37 | return res( 38 | generateNextValidUser(client, userGenerator) 39 | ) 40 | } 41 | rej(error) 42 | }) 43 | res(user) 44 | }) 45 | } 46 | 47 | return await createClient({ 48 | url: new URL(`${process.env.ADMIN_CLIENT_HOST}`), 49 | username: process.env.ADMIN_CLIENT_USER, 50 | password: process.env.ADMIN_CLIENT_PASS 51 | }) 52 | .then(({ client }) => { 53 | service.app.get("/provision", async (req: Request, res: Response) => { 54 | await generateNextValidUser(client, getRandomUser) 55 | .then(async user => { 56 | await client.put({ 57 | api: `users/${user.id}`, 58 | payload: { 59 | password: user.password, 60 | tags: 'lattice' 61 | } 62 | }).then((_res: any) => client.put({ 63 | api: `permissions/%2F/${user.id}`, 64 | payload: { 65 | configure: '.*', 66 | write: '.*', 67 | read: '.*' 68 | } 69 | })).then((_res: any) => client.put({ 70 | api: `topic-permissions/%2F/${user.id}`, 71 | payload: { 72 | exchange: 'amq.topic', 73 | write: '.*', 74 | read: 'to_agent.{username}.*|lattice.*' 75 | } 76 | })) 77 | res.json({ 78 | user: user.id, 79 | password: user.password 80 | }) 81 | }) 82 | .catch(error => { 83 | console.error(error) 84 | res.status(500).send(error) 85 | }) 86 | }) 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /connect/src/services/useSigning.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | import { Request, Response } from "express"; 4 | import { IAppService } from "../core" 5 | import { createClient } from "../clients/createClient" 6 | import crypto from 'crypto' 7 | import cluster from "cluster"; 8 | import { createSigner } from "../clients/createSigner"; 9 | 10 | export async function useSigning(service: IAppService) { 11 | const logger = service.logger 12 | const broker = new URL(`${process.env.ADMIN_CLIENT_HOST}`) 13 | 14 | const { signer } = createSigner({ 15 | host: broker.hostname, 16 | port: parseInt(`${process.env.MQTT_HTTP_PORT}`), 17 | username: process.env.ADMIN_CLIENT_USER, 18 | password: process.env.ADMIN_CLIENT_PASS, 19 | }, logger) 20 | 21 | const getDeviceId = (req: Request): string | undefined => { 22 | return req.params?.deviceId 23 | } 24 | 25 | const generateRequestId = (): string => { 26 | return crypto.randomBytes(4).toString('hex') 27 | } 28 | 29 | const getRequestData = (req: Request): any | undefined => { 30 | return req.body.data?.data 31 | } 32 | 33 | const preflightCheck = async (deviceId: string | undefined, res: Response) => { 34 | if (process.env.MQTT_SKIP_USER_CHECK) { return } 35 | await createClient({ 36 | url: broker, 37 | username: process.env.ADMIN_CLIENT_USER, 38 | password: process.env.ADMIN_CLIENT_PASS 39 | }).then(async ({ client }) => { 40 | await client.get({ api: `users/${deviceId}` }).then(() => 41 | logger.info("[4] Device ID: VALID") 42 | ) 43 | }) 44 | } 45 | 46 | service.app.post("/:deviceId", async (req: Request, res: Response) => { 47 | const deviceId = getDeviceId(req) 48 | const requestId = generateRequestId() 49 | const requestData = getRequestData(req) 50 | 51 | logger.info("------------------------") 52 | logger.info("HANDLING MESSAGE [RELAY]") 53 | logger.info("------------------------") 54 | logger.info(`[0] Worker #${cluster.worker?.id} received payload...`) 55 | logger.info(`[1] Request ID created: '${requestId}'`) 56 | logger.info(`[2] Verifying deviceId: '${deviceId}'...`) 57 | 58 | //------------------------------------------------------- 59 | // Pre-flight check ✅ 60 | // Validate the received payload 61 | //------------------------------------------------------- 62 | if (!requestData || !deviceId) { 63 | logger.info("[!] Failed to parse request (missing 'requestData' OR 'deviceId')") 64 | logger.info(`[!] 'deviceId': ${deviceId}`) 65 | return res.status(200).send({ 66 | status: 400, 67 | message: "Invalid request" 68 | }) 69 | } 70 | 71 | //------------------------------------------------------- 72 | // Pre-flight check ✅ 73 | // Check to see if the user (deviceId) exists (OPTIONAL) 74 | //------------------------------------------------------- 75 | try { 76 | await preflightCheck(deviceId, res) 77 | } catch (error) { 78 | logger.info(`[!] Database request responded with: '${error}'`) 79 | return res.status(200).send({ 80 | status: 401, 81 | message: "Unauthorized" 82 | }) 83 | } 84 | 85 | //------------------------------------------------------- 86 | // Relay message; wait for approval 🔄 87 | //------------------------------------------------------- 88 | try { 89 | const payload = Buffer.from(requestData!) 90 | logger.info("[3] Payload: VALID") 91 | 92 | const response = await signer.sendForApproval(payload, deviceId, requestId) 93 | 94 | return res.send(response) 95 | } catch (error: any) { 96 | return res.status(500).send({ 97 | status: 500, 98 | message: `Error while attempting to relay message. Reason: '${error.message}'` 99 | }) 100 | } 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /connect/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist" /* Redirect output structure to the directory. */, 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | "noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */, 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 44 | 45 | /* Module Resolution Options */ 46 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | "resolveJsonModule": true, /* Allows importing modules with a ‘.json’ extension, which is a common practice in node projects. */ 48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | "types": [ 53 | "node" 54 | ] /* Type declaration files to be included in compilation. */, 55 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 56 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 57 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 58 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 59 | 60 | /* Source Map Options */ 61 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 62 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 63 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 64 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 65 | 66 | /* Experimental Options */ 67 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 68 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 69 | 70 | /* Advanced Options */ 71 | "skipLibCheck": true /* Skip type checking of declaration files. */, 72 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 73 | }, 74 | "include": ["src", "index.*"], 75 | "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] 76 | } 77 | -------------------------------------------------------------------------------- /mqtt-broker/.env: -------------------------------------------------------------------------------- 1 | ERLANG_COOKIE='erlang-cookie' -------------------------------------------------------------------------------- /mqtt-broker/README.md: -------------------------------------------------------------------------------- 1 | # MQTT Broker 2 | 3 | This folder is intended for the GridPlus team, and contains the source files for the RabbitMQ broker used in the _Lattice Connect_ service. -------------------------------------------------------------------------------- /mqtt-broker/config/.gitignore: -------------------------------------------------------------------------------- 1 | data.json 2 | data-*.json 3 | -------------------------------------------------------------------------------- /mqtt-broker/config/enabled_plugins: -------------------------------------------------------------------------------- 1 | [rabbitmq_management, rabbitmq_mqtt, rabbitmq_prometheus]. 2 | -------------------------------------------------------------------------------- /mqtt-broker/config/rabbitmq.conf: -------------------------------------------------------------------------------- 1 | # https://github.com/rabbitmq/rabbitmq-server/blob/master/deps/rabbit/docs/rabbitmq.conf.example# 2 | 3 | # log.file.level = error 4 | # loopback_users.guest = false 5 | 6 | # https://www.rabbitmq.com/connections.html#large-number-of-connections 7 | # sets the interval to 60 seconds 8 | collect_statistics_interval = 60000 9 | 10 | # definitions.skip_if_unchanged = true 11 | load_definitions = /etc/rabbitmq/data.json 12 | 13 | mqtt.allow_anonymous = false 14 | # mqtt.proxy_protocol = true 15 | 16 | # mqtt.listeners.tcp.1 = 127.0.0.1:1883 17 | # mqtt.listeners.tcp.2 = ::1:1883 18 | 19 | # mqtt.tcp_listen_options.backlog = 4096 20 | # mqtt.tcp_listen_options.recbuf = 131072 21 | # mqtt.tcp_listen_options.sndbuf = 131072 22 | 23 | # mqtt.tcp_listen_options.keepalive = true 24 | # mqtt.tcp_listen_options.nodelay = true 25 | 26 | # mqtt.tcp_listen_options.exit_on_close = true 27 | # mqtt.tcp_listen_options.send_timeout = 120 28 | 29 | # https://www.rabbitmq.com/networking.html#tuning-for-large-number-of-connections-tcp-buffer-size 30 | mqtt.tcp_listen_options.backlog = 128 31 | mqtt.tcp_listen_options.nodelay = true 32 | mqtt.tcp_listen_options.linger.on = true 33 | mqtt.tcp_listen_options.linger.timeout = 0 34 | mqtt.tcp_listen_options.sndbuf = 32768 35 | mqtt.tcp_listen_options.recbuf = 32768 36 | 37 | # https://www.rabbitmq.com/networking.html#dns-reverse-dns-lookups 38 | reverse_dns_lookups = false 39 | 40 | management.listener.port = 15672 41 | management.listener.ssl = false 42 | 43 | vm_memory_high_watermark.absolute = 8096MiB 44 | vm_memory_high_watermark_paging_ratio = 0.2 45 | 46 | # cluster_name = lattice-mqtt-cluster 47 | 48 | # cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config 49 | # cluster_formation.classic_config.nodes.1 = rabbit@mqtt-broker-1 50 | # cluster_formation.classic_config.nodes.2 = rabbit@mqtt-broker-2 51 | # cluster_formation.classic_config.nodes.3 = rabbit@mqtt-broker-3 52 | -------------------------------------------------------------------------------- /mqtt-broker/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rabbitmq:3.10.6-management-alpine 2 | 3 | COPY config /etc/rabbitmq/ -------------------------------------------------------------------------------- /mqtt-broker/container/dockerBuild.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | docker build -f Dockerfile ../ -t mqtt-broker:latest 4 | -------------------------------------------------------------------------------- /mqtt-broker/container/dockerStart.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | docker run \ 4 | --ulimit nofile=262144:262144 \ 5 | -m "8G" --memory-swap "-1" \ 6 | --rm -it \ 7 | --init \ 8 | -h mqtt-broker \ 9 | -p 1883:1883 \ 10 | -p 15672:15672 \ 11 | --env-file ../.env \ 12 | --name "mqtt-broker" \ 13 | mqtt-broker 14 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/.gitignore: -------------------------------------------------------------------------------- 1 | grafana/data 2 | prometheus/data 3 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/dashboards.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'rabbitmq' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: true 9 | options: 10 | path: /dashboards 11 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/dashboards/Erlang-Distributions-Compare.json: -------------------------------------------------------------------------------- 1 | { 2 | "__requires": [ 3 | { 4 | "type": "grafana", 5 | "id": "grafana", 6 | "name": "Grafana", 7 | "version": "7.0.0" 8 | }, 9 | { 10 | "type": "datasource", 11 | "id": "prometheus", 12 | "name": "prometheus", 13 | "version": "2.0.0" 14 | }, 15 | { 16 | "type": "table", 17 | "id": "table", 18 | "name": "Table", 19 | "version": "" 20 | }, 21 | { 22 | "type": "panel", 23 | "id": "graph", 24 | "name": "Graph", 25 | "version": "" 26 | }, 27 | { 28 | "type": "panel", 29 | "id": "heatmap", 30 | "name": "Heatmap", 31 | "version": "" 32 | } 33 | ], 34 | "annotations": { 35 | "list": [ 36 | { 37 | "builtIn": 1, 38 | "datasource": "-- Grafana --", 39 | "enable": true, 40 | "hide": true, 41 | "iconColor": "rgba(0, 211, 255, 1)", 42 | "name": "Annotations & Alerts", 43 | "type": "dashboard" 44 | } 45 | ] 46 | }, 47 | "editable": true, 48 | "gnetId": null, 49 | "graphTooltip": 1, 50 | "iteration": 1571066778520, 51 | "links": [], 52 | "panels": [ 53 | { 54 | "collapsed": false, 55 | "datasource": null, 56 | "gridPos": { 57 | "h": 1, 58 | "w": 24, 59 | "x": 0, 60 | "y": 0 61 | }, 62 | "id": 67, 63 | "panels": [], 64 | "title": "rabbitmq-prometheus", 65 | "type": "row" 66 | }, 67 | { 68 | "columns": [ 69 | { 70 | "text": "Min", 71 | "value": "min" 72 | }, 73 | { 74 | "text": "Max", 75 | "value": "max" 76 | }, 77 | { 78 | "text": "Avg", 79 | "value": "avg" 80 | }, 81 | { 82 | "text": "Current", 83 | "value": "current" 84 | } 85 | ], 86 | "datasource": null, 87 | "fontSize": "100%", 88 | "gridPos": { 89 | "h": 7, 90 | "w": 9, 91 | "x": 0, 92 | "y": 1 93 | }, 94 | "id": 56, 95 | "options": {}, 96 | "pageSize": null, 97 | "pluginVersion": "6.4.1", 98 | "scroll": true, 99 | "showHeader": true, 100 | "sort": { 101 | "col": 4, 102 | "desc": true 103 | }, 104 | "styles": [ 105 | { 106 | "alias": "Node -> Peer", 107 | "colorMode": null, 108 | "colors": [ 109 | "rgba(245, 54, 54, 0.9)", 110 | "rgba(237, 129, 40, 0.89)", 111 | "rgba(50, 172, 45, 0.97)" 112 | ], 113 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 114 | "decimals": 0, 115 | "mappingType": 1, 116 | "pattern": "Metric", 117 | "thresholds": [], 118 | "type": "string", 119 | "unit": "short" 120 | }, 121 | { 122 | "alias": "", 123 | "colorMode": null, 124 | "colors": [ 125 | "rgba(245, 54, 54, 0.9)", 126 | "rgba(237, 129, 40, 0.89)", 127 | "rgba(50, 172, 45, 0.97)" 128 | ], 129 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 130 | "decimals": 1, 131 | "mappingType": 1, 132 | "pattern": "/.*/", 133 | "thresholds": [], 134 | "type": "number", 135 | "unit": "Bps" 136 | } 137 | ], 138 | "targets": [ 139 | { 140 | "expr": "rate(erlang_vm_dist_send_bytes[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=~\"$rabbitmq_cluster\", namespace=\"$namespace\"}", 141 | "legendFormat": "{{rabbitmq_node}} -> {{peer}}", 142 | "refId": "A" 143 | } 144 | ], 145 | "timeFrom": null, 146 | "timeShift": null, 147 | "title": "Erlang Distribution outgoing traffic / s", 148 | "transform": "timeseries_aggregations", 149 | "type": "table" 150 | }, 151 | { 152 | "aliasColors": {}, 153 | "bars": false, 154 | "dashLength": 10, 155 | "dashes": false, 156 | "datasource": null, 157 | "description": "Erlang Distribution traffic, node network traffic and CPU + PerfTest message throughput and latency", 158 | "fill": 0, 159 | "fillGradient": 0, 160 | "gridPos": { 161 | "h": 7, 162 | "w": 15, 163 | "x": 9, 164 | "y": 1 165 | }, 166 | "id": 3, 167 | "legend": { 168 | "alignAsTable": true, 169 | "avg": false, 170 | "current": true, 171 | "max": true, 172 | "min": true, 173 | "rightSide": false, 174 | "show": false, 175 | "sort": "max", 176 | "sortDesc": true, 177 | "total": false, 178 | "values": true 179 | }, 180 | "lines": true, 181 | "linewidth": 1, 182 | "links": [], 183 | "nullPointMode": "null", 184 | "options": { 185 | "dataLinks": [] 186 | }, 187 | "percentage": false, 188 | "pointradius": 2, 189 | "points": false, 190 | "renderer": "flot", 191 | "seriesOverrides": [ 192 | { 193 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?0(\\b|\\.)/", 194 | "color": "#56A64B" 195 | }, 196 | { 197 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?1(\\b|\\.)/", 198 | "color": "#F2CC0C" 199 | }, 200 | { 201 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?2(\\b|\\.)/", 202 | "color": "#3274D9" 203 | }, 204 | { 205 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?3(\\b|\\.)/", 206 | "color": "#A352CC" 207 | }, 208 | { 209 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?4(\\b|\\.)/", 210 | "color": "#FF780A" 211 | }, 212 | { 213 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?5(\\b|\\.)/", 214 | "color": "#96D98D" 215 | }, 216 | { 217 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?6(\\b|\\.)/", 218 | "color": "#FFEE52" 219 | }, 220 | { 221 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?7(\\b|\\.)/", 222 | "color": "#8AB8FF" 223 | }, 224 | { 225 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?8(\\b|\\.)/", 226 | "color": "#CA95E5" 227 | }, 228 | { 229 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?9(\\b|\\.)/", 230 | "color": "#FFB357" 231 | } 232 | ], 233 | "spaceLength": 10, 234 | "stack": false, 235 | "steppedLine": false, 236 | "targets": [ 237 | { 238 | "expr": "rate(erlang_vm_dist_send_bytes[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=~\"$rabbitmq_cluster\", namespace=\"$namespace\"}", 239 | "format": "time_series", 240 | "intervalFactor": 1, 241 | "legendFormat": "{{rabbitmq_node}} -> {{peer}}", 242 | "refId": "A" 243 | } 244 | ], 245 | "thresholds": [], 246 | "timeFrom": null, 247 | "timeRegions": [], 248 | "timeShift": null, 249 | "title": "Erlang Distribution outgoing traffic / s", 250 | "tooltip": { 251 | "shared": true, 252 | "sort": 2, 253 | "value_type": "individual" 254 | }, 255 | "transparent": true, 256 | "type": "graph", 257 | "xaxis": { 258 | "buckets": null, 259 | "mode": "time", 260 | "name": null, 261 | "show": true, 262 | "values": [] 263 | }, 264 | "yaxes": [ 265 | { 266 | "decimals": 0, 267 | "format": "Bps", 268 | "label": null, 269 | "logBase": 1, 270 | "max": null, 271 | "min": "0", 272 | "show": true 273 | }, 274 | { 275 | "format": "short", 276 | "label": null, 277 | "logBase": 1, 278 | "max": null, 279 | "min": null, 280 | "show": true 281 | } 282 | ], 283 | "yaxis": { 284 | "align": false, 285 | "alignLevel": null 286 | } 287 | }, 288 | { 289 | "collapsed": false, 290 | "datasource": null, 291 | "gridPos": { 292 | "h": 1, 293 | "w": 24, 294 | "x": 0, 295 | "y": 8 296 | }, 297 | "id": 65, 298 | "panels": [], 299 | "title": "node-exporter_cadvisor", 300 | "type": "row" 301 | }, 302 | { 303 | "columns": [ 304 | { 305 | "text": "Min", 306 | "value": "min" 307 | }, 308 | { 309 | "text": "Max", 310 | "value": "max" 311 | }, 312 | { 313 | "text": "Avg", 314 | "value": "avg" 315 | }, 316 | { 317 | "text": "Current", 318 | "value": "current" 319 | } 320 | ], 321 | "datasource": null, 322 | "fontSize": "100%", 323 | "gridPos": { 324 | "h": 7, 325 | "w": 9, 326 | "x": 0, 327 | "y": 9 328 | }, 329 | "id": 61, 330 | "options": {}, 331 | "pageSize": null, 332 | "pluginVersion": "6.4.1", 333 | "scroll": true, 334 | "showHeader": true, 335 | "sort": { 336 | "col": 4, 337 | "desc": true 338 | }, 339 | "styles": [ 340 | { 341 | "alias": "Host", 342 | "colorMode": null, 343 | "colors": [ 344 | "rgba(245, 54, 54, 0.9)", 345 | "rgba(237, 129, 40, 0.89)", 346 | "rgba(50, 172, 45, 0.97)" 347 | ], 348 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 349 | "decimals": 0, 350 | "mappingType": 1, 351 | "pattern": "Metric", 352 | "thresholds": [], 353 | "type": "string", 354 | "unit": "short" 355 | }, 356 | { 357 | "alias": "", 358 | "colorMode": null, 359 | "colors": [ 360 | "rgba(245, 54, 54, 0.9)", 361 | "rgba(237, 129, 40, 0.89)", 362 | "rgba(50, 172, 45, 0.97)" 363 | ], 364 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 365 | "decimals": 1, 366 | "mappingType": 1, 367 | "pattern": "/.*/", 368 | "thresholds": [], 369 | "type": "number", 370 | "unit": "Bps" 371 | } 372 | ], 373 | "targets": [ 374 | { 375 | "expr": "sum by(instance) (rate(node_network_receive_bytes_total{instance=~\"$host\"}[5m]))", 376 | "legendFormat": "{{instance}}", 377 | "refId": "A" 378 | }, 379 | { 380 | "expr": "sum by(name) (rate(container_network_receive_bytes_total{name=~\"$container\"}[1m]))", 381 | "legendFormat": "{{name}}", 382 | "refId": "B" 383 | } 384 | ], 385 | "timeFrom": null, 386 | "timeShift": null, 387 | "title": "Network incoming traffic / s", 388 | "transform": "timeseries_aggregations", 389 | "type": "table" 390 | }, 391 | { 392 | "aliasColors": {}, 393 | "bars": false, 394 | "dashLength": 10, 395 | "dashes": false, 396 | "datasource": null, 397 | "decimals": null, 398 | "editable": true, 399 | "error": false, 400 | "fill": 0, 401 | "fillGradient": 0, 402 | "grid": {}, 403 | "gridPos": { 404 | "h": 7, 405 | "w": 15, 406 | "x": 9, 407 | "y": 9 408 | }, 409 | "height": "", 410 | "id": 58, 411 | "legend": { 412 | "alignAsTable": true, 413 | "avg": true, 414 | "current": false, 415 | "hideEmpty": true, 416 | "hideZero": true, 417 | "max": true, 418 | "min": true, 419 | "rightSide": true, 420 | "show": false, 421 | "sort": "max", 422 | "sortDesc": true, 423 | "total": false, 424 | "values": true 425 | }, 426 | "lines": true, 427 | "linewidth": 1, 428 | "links": [], 429 | "maxPerRow": 3, 430 | "nullPointMode": "null", 431 | "options": { 432 | "dataLinks": [] 433 | }, 434 | "percentage": false, 435 | "pointradius": 5, 436 | "points": false, 437 | "renderer": "flot", 438 | "repeatDirection": "h", 439 | "seriesOverrides": [], 440 | "spaceLength": 10, 441 | "stack": false, 442 | "steppedLine": false, 443 | "targets": [ 444 | { 445 | "calculatedInterval": "2s", 446 | "datasourceErrors": {}, 447 | "errors": {}, 448 | "expr": "sum by(instance) (rate(node_network_receive_bytes_total{instance=~\"$host\"}[5m]))", 449 | "format": "time_series", 450 | "interval": "", 451 | "intervalFactor": 1, 452 | "legendFormat": "{{instance}}", 453 | "metric": "", 454 | "refId": "A", 455 | "step": 20 456 | }, 457 | { 458 | "expr": "sum by(name) (rate(container_network_receive_bytes_total{name=~\"$container\"}[1m]))", 459 | "format": "time_series", 460 | "intervalFactor": 1, 461 | "legendFormat": "{{name}}", 462 | "refId": "B" 463 | } 464 | ], 465 | "thresholds": [], 466 | "timeFrom": null, 467 | "timeRegions": [], 468 | "timeShift": null, 469 | "title": "Network incoming traffic / s", 470 | "tooltip": { 471 | "msResolution": false, 472 | "shared": true, 473 | "sort": 2, 474 | "value_type": "individual" 475 | }, 476 | "transparent": true, 477 | "type": "graph", 478 | "xaxis": { 479 | "buckets": null, 480 | "mode": "time", 481 | "name": null, 482 | "show": true, 483 | "values": [] 484 | }, 485 | "yaxes": [ 486 | { 487 | "decimals": 1, 488 | "format": "Bps", 489 | "label": "", 490 | "logBase": 1, 491 | "max": null, 492 | "min": null, 493 | "show": true 494 | }, 495 | { 496 | "format": "short", 497 | "logBase": 1, 498 | "max": null, 499 | "min": 0, 500 | "show": false 501 | } 502 | ], 503 | "yaxis": { 504 | "align": false, 505 | "alignLevel": null 506 | } 507 | }, 508 | { 509 | "columns": [ 510 | { 511 | "text": "Min", 512 | "value": "min" 513 | }, 514 | { 515 | "text": "Max", 516 | "value": "max" 517 | }, 518 | { 519 | "text": "Avg", 520 | "value": "avg" 521 | }, 522 | { 523 | "text": "Current", 524 | "value": "current" 525 | } 526 | ], 527 | "datasource": null, 528 | "fontSize": "100%", 529 | "gridPos": { 530 | "h": 7, 531 | "w": 9, 532 | "x": 0, 533 | "y": 16 534 | }, 535 | "id": 60, 536 | "options": {}, 537 | "pageSize": null, 538 | "pluginVersion": "6.4.1", 539 | "scroll": true, 540 | "showHeader": true, 541 | "sort": { 542 | "col": 4, 543 | "desc": true 544 | }, 545 | "styles": [ 546 | { 547 | "alias": "Host", 548 | "colorMode": null, 549 | "colors": [ 550 | "rgba(245, 54, 54, 0.9)", 551 | "rgba(237, 129, 40, 0.89)", 552 | "rgba(50, 172, 45, 0.97)" 553 | ], 554 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 555 | "decimals": 0, 556 | "mappingType": 1, 557 | "pattern": "Metric", 558 | "thresholds": [], 559 | "type": "string", 560 | "unit": "short" 561 | }, 562 | { 563 | "alias": "", 564 | "colorMode": null, 565 | "colors": [ 566 | "rgba(245, 54, 54, 0.9)", 567 | "rgba(237, 129, 40, 0.89)", 568 | "rgba(50, 172, 45, 0.97)" 569 | ], 570 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 571 | "decimals": 1, 572 | "mappingType": 1, 573 | "pattern": "/.*/", 574 | "thresholds": [], 575 | "type": "number", 576 | "unit": "Bps" 577 | } 578 | ], 579 | "targets": [ 580 | { 581 | "expr": "sum by(instance) (rate(node_network_transmit_bytes_total{instance=~\"$host\"}[5m]))", 582 | "legendFormat": "{{instance}}", 583 | "refId": "A" 584 | }, 585 | { 586 | "expr": "sum by(name) (rate(container_network_transmit_bytes_total{name=~\"$container\"}[1m]))", 587 | "legendFormat": "{{name}}", 588 | "refId": "B" 589 | } 590 | ], 591 | "timeFrom": null, 592 | "timeShift": null, 593 | "title": "Network outgoing traffic / s", 594 | "transform": "timeseries_aggregations", 595 | "type": "table" 596 | }, 597 | { 598 | "aliasColors": {}, 599 | "bars": false, 600 | "dashLength": 10, 601 | "dashes": false, 602 | "datasource": null, 603 | "decimals": null, 604 | "editable": true, 605 | "error": false, 606 | "fill": 0, 607 | "fillGradient": 0, 608 | "grid": {}, 609 | "gridPos": { 610 | "h": 7, 611 | "w": 15, 612 | "x": 9, 613 | "y": 16 614 | }, 615 | "height": "", 616 | "id": 57, 617 | "legend": { 618 | "alignAsTable": true, 619 | "avg": true, 620 | "current": false, 621 | "hideEmpty": true, 622 | "hideZero": true, 623 | "max": true, 624 | "min": true, 625 | "rightSide": true, 626 | "show": false, 627 | "sort": "max", 628 | "sortDesc": true, 629 | "total": false, 630 | "values": true 631 | }, 632 | "lines": true, 633 | "linewidth": 1, 634 | "links": [], 635 | "maxPerRow": 3, 636 | "nullPointMode": "null", 637 | "options": { 638 | "dataLinks": [] 639 | }, 640 | "percentage": false, 641 | "pointradius": 5, 642 | "points": false, 643 | "renderer": "flot", 644 | "repeatDirection": "h", 645 | "seriesOverrides": [], 646 | "spaceLength": 10, 647 | "stack": false, 648 | "steppedLine": false, 649 | "targets": [ 650 | { 651 | "calculatedInterval": "2s", 652 | "datasourceErrors": {}, 653 | "errors": {}, 654 | "expr": "sum by(instance) (rate(node_network_transmit_bytes_total{instance=~\"$host\"}[5m]))", 655 | "format": "time_series", 656 | "interval": "", 657 | "intervalFactor": 1, 658 | "legendFormat": "{{instance}}", 659 | "metric": "", 660 | "refId": "A", 661 | "step": 20 662 | }, 663 | { 664 | "expr": "sum by(name) (rate(container_network_transmit_bytes_total{name=~\"$container\"}[1m]))", 665 | "format": "time_series", 666 | "intervalFactor": 1, 667 | "legendFormat": "{{name}}", 668 | "refId": "B" 669 | } 670 | ], 671 | "thresholds": [], 672 | "timeFrom": null, 673 | "timeRegions": [], 674 | "timeShift": null, 675 | "title": "Network outgoing traffic / s", 676 | "tooltip": { 677 | "msResolution": false, 678 | "shared": true, 679 | "sort": 2, 680 | "value_type": "individual" 681 | }, 682 | "transparent": true, 683 | "type": "graph", 684 | "xaxis": { 685 | "buckets": null, 686 | "mode": "time", 687 | "name": null, 688 | "show": true, 689 | "values": [] 690 | }, 691 | "yaxes": [ 692 | { 693 | "decimals": 1, 694 | "format": "Bps", 695 | "label": "", 696 | "logBase": 1, 697 | "max": null, 698 | "min": null, 699 | "show": true 700 | }, 701 | { 702 | "format": "short", 703 | "logBase": 1, 704 | "max": null, 705 | "min": 0, 706 | "show": false 707 | } 708 | ], 709 | "yaxis": { 710 | "align": false, 711 | "alignLevel": null 712 | } 713 | }, 714 | { 715 | "columns": [ 716 | { 717 | "text": "Min", 718 | "value": "min" 719 | }, 720 | { 721 | "text": "Max", 722 | "value": "max" 723 | }, 724 | { 725 | "text": "Avg", 726 | "value": "avg" 727 | }, 728 | { 729 | "text": "Current", 730 | "value": "current" 731 | } 732 | ], 733 | "datasource": null, 734 | "fontSize": "100%", 735 | "gridPos": { 736 | "h": 7, 737 | "w": 9, 738 | "x": 0, 739 | "y": 23 740 | }, 741 | "id": 59, 742 | "options": {}, 743 | "pageSize": null, 744 | "pluginVersion": "6.4.1", 745 | "scroll": true, 746 | "showHeader": true, 747 | "sort": { 748 | "col": 4, 749 | "desc": true 750 | }, 751 | "styles": [ 752 | { 753 | "alias": "Host", 754 | "colorMode": null, 755 | "colors": [ 756 | "rgba(245, 54, 54, 0.9)", 757 | "rgba(237, 129, 40, 0.89)", 758 | "rgba(50, 172, 45, 0.97)" 759 | ], 760 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 761 | "decimals": 0, 762 | "mappingType": 1, 763 | "pattern": "Metric", 764 | "thresholds": [], 765 | "type": "string", 766 | "unit": "short" 767 | }, 768 | { 769 | "alias": "", 770 | "colorMode": null, 771 | "colors": [ 772 | "rgba(245, 54, 54, 0.9)", 773 | "rgba(237, 129, 40, 0.89)", 774 | "rgba(50, 172, 45, 0.97)" 775 | ], 776 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 777 | "decimals": 0, 778 | "mappingType": 1, 779 | "pattern": "/.*/", 780 | "thresholds": [], 781 | "type": "number", 782 | "unit": "percent" 783 | } 784 | ], 785 | "targets": [ 786 | { 787 | "expr": "(max by (instance) (irate(node_cpu_seconds_total{job=\"node\", mode=~\"user|system|iowait|softirq\", instance=~\"$host\"}[5m])) * 100)", 788 | "legendFormat": "{{instance}}", 789 | "refId": "A" 790 | }, 791 | { 792 | "expr": "sum by(name) (irate(container_cpu_usage_seconds_total{name=~\"$container\"}[1m])) * 100", 793 | "legendFormat": "{{name}}", 794 | "refId": "B" 795 | } 796 | ], 797 | "timeFrom": null, 798 | "timeShift": null, 799 | "title": "CPU", 800 | "transform": "timeseries_aggregations", 801 | "type": "table" 802 | }, 803 | { 804 | "aliasColors": {}, 805 | "bars": false, 806 | "dashLength": 10, 807 | "dashes": false, 808 | "datasource": null, 809 | "decimals": null, 810 | "editable": true, 811 | "error": false, 812 | "fill": 0, 813 | "fillGradient": 0, 814 | "grid": {}, 815 | "gridPos": { 816 | "h": 7, 817 | "w": 15, 818 | "x": 9, 819 | "y": 23 820 | }, 821 | "height": "", 822 | "id": 28, 823 | "legend": { 824 | "alignAsTable": true, 825 | "avg": true, 826 | "current": false, 827 | "hideEmpty": true, 828 | "hideZero": true, 829 | "max": true, 830 | "min": true, 831 | "rightSide": true, 832 | "show": false, 833 | "sort": "max", 834 | "sortDesc": true, 835 | "total": false, 836 | "values": true 837 | }, 838 | "lines": true, 839 | "linewidth": 1, 840 | "links": [], 841 | "maxPerRow": 3, 842 | "nullPointMode": "null", 843 | "options": { 844 | "dataLinks": [] 845 | }, 846 | "percentage": false, 847 | "pointradius": 5, 848 | "points": false, 849 | "renderer": "flot", 850 | "repeat": null, 851 | "repeatDirection": "h", 852 | "seriesOverrides": [], 853 | "spaceLength": 10, 854 | "stack": false, 855 | "steppedLine": false, 856 | "targets": [ 857 | { 858 | "calculatedInterval": "2s", 859 | "datasourceErrors": {}, 860 | "errors": {}, 861 | "expr": "(max by (instance) (irate(node_cpu_seconds_total{job=\"node\", mode=~\"user|system|iowait|softirq\", instance=~\"$host\"}[5m])) * 100)", 862 | "format": "time_series", 863 | "interval": "", 864 | "intervalFactor": 1, 865 | "legendFormat": "{{instance}}", 866 | "metric": "", 867 | "refId": "A", 868 | "step": 20 869 | }, 870 | { 871 | "expr": "sum by(name) (irate(container_cpu_usage_seconds_total{name=~\"$container\"}[1m])) * 100", 872 | "format": "time_series", 873 | "hide": false, 874 | "intervalFactor": 1, 875 | "legendFormat": "{{name}}", 876 | "refId": "B" 877 | } 878 | ], 879 | "thresholds": [], 880 | "timeFrom": null, 881 | "timeRegions": [], 882 | "timeShift": null, 883 | "title": "CPU", 884 | "tooltip": { 885 | "msResolution": false, 886 | "shared": true, 887 | "sort": 2, 888 | "value_type": "individual" 889 | }, 890 | "transparent": true, 891 | "type": "graph", 892 | "xaxis": { 893 | "buckets": null, 894 | "mode": "time", 895 | "name": null, 896 | "show": true, 897 | "values": [] 898 | }, 899 | "yaxes": [ 900 | { 901 | "format": "percent", 902 | "label": "", 903 | "logBase": 1, 904 | "max": null, 905 | "min": 0, 906 | "show": true 907 | }, 908 | { 909 | "format": "short", 910 | "logBase": 1, 911 | "max": null, 912 | "min": 0, 913 | "show": true 914 | } 915 | ], 916 | "yaxis": { 917 | "align": false, 918 | "alignLevel": null 919 | } 920 | }, 921 | { 922 | "collapsed": false, 923 | "datasource": null, 924 | "gridPos": { 925 | "h": 1, 926 | "w": 24, 927 | "x": 0, 928 | "y": 30 929 | }, 930 | "id": 63, 931 | "panels": [], 932 | "title": "rabbitmq-perf-test", 933 | "type": "row" 934 | }, 935 | { 936 | "columns": [ 937 | { 938 | "text": "Min", 939 | "value": "min" 940 | }, 941 | { 942 | "text": "Max", 943 | "value": "max" 944 | }, 945 | { 946 | "text": "Avg", 947 | "value": "avg" 948 | }, 949 | { 950 | "text": "Current", 951 | "value": "current" 952 | } 953 | ], 954 | "datasource": null, 955 | "fontSize": "100%", 956 | "gridPos": { 957 | "h": 7, 958 | "w": 9, 959 | "x": 0, 960 | "y": 31 961 | }, 962 | "id": 49, 963 | "options": {}, 964 | "pageSize": null, 965 | "pluginVersion": "6.4.1", 966 | "scroll": true, 967 | "showHeader": true, 968 | "sort": { 969 | "col": 4, 970 | "desc": true 971 | }, 972 | "styles": [ 973 | { 974 | "alias": "Instance", 975 | "colorMode": null, 976 | "colors": [ 977 | "rgba(245, 54, 54, 0.9)", 978 | "rgba(237, 129, 40, 0.89)", 979 | "rgba(50, 172, 45, 0.97)" 980 | ], 981 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 982 | "decimals": 0, 983 | "mappingType": 1, 984 | "pattern": "Metric", 985 | "thresholds": [], 986 | "type": "string", 987 | "unit": "short" 988 | }, 989 | { 990 | "alias": "", 991 | "colorMode": null, 992 | "colors": [ 993 | "rgba(245, 54, 54, 0.9)", 994 | "rgba(237, 129, 40, 0.89)", 995 | "rgba(50, 172, 45, 0.97)" 996 | ], 997 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 998 | "decimals": 1, 999 | "mappingType": 1, 1000 | "pattern": "/.*/", 1001 | "thresholds": [], 1002 | "type": "number", 1003 | "unit": "short" 1004 | } 1005 | ], 1006 | "targets": [ 1007 | { 1008 | "expr": "perftest_published{instance=~\"$instance\"}", 1009 | "legendFormat": "{{instance}}", 1010 | "refId": "A" 1011 | } 1012 | ], 1013 | "timeFrom": null, 1014 | "timeShift": null, 1015 | "title": "Messages published / s", 1016 | "transform": "timeseries_aggregations", 1017 | "type": "table" 1018 | }, 1019 | { 1020 | "aliasColors": {}, 1021 | "bars": false, 1022 | "dashLength": 10, 1023 | "dashes": false, 1024 | "datasource": null, 1025 | "fill": 1, 1026 | "fillGradient": 0, 1027 | "gridPos": { 1028 | "h": 7, 1029 | "w": 15, 1030 | "x": 9, 1031 | "y": 31 1032 | }, 1033 | "id": 51, 1034 | "legend": { 1035 | "alignAsTable": true, 1036 | "avg": true, 1037 | "current": true, 1038 | "max": true, 1039 | "min": true, 1040 | "rightSide": false, 1041 | "show": false, 1042 | "sort": "avg", 1043 | "sortDesc": true, 1044 | "total": false, 1045 | "values": true 1046 | }, 1047 | "lines": true, 1048 | "linewidth": 1, 1049 | "links": [], 1050 | "nullPointMode": "null", 1051 | "options": { 1052 | "dataLinks": [] 1053 | }, 1054 | "percentage": false, 1055 | "pointradius": 5, 1056 | "points": false, 1057 | "renderer": "flot", 1058 | "seriesOverrides": [], 1059 | "spaceLength": 10, 1060 | "stack": false, 1061 | "steppedLine": false, 1062 | "targets": [ 1063 | { 1064 | "expr": "perftest_published{instance=~\"$instance\"}", 1065 | "format": "time_series", 1066 | "intervalFactor": 1, 1067 | "legendFormat": "{{instance}}", 1068 | "refId": "A" 1069 | } 1070 | ], 1071 | "thresholds": [], 1072 | "timeFrom": null, 1073 | "timeRegions": [], 1074 | "timeShift": null, 1075 | "title": "Messages published / s", 1076 | "tooltip": { 1077 | "shared": true, 1078 | "sort": 2, 1079 | "value_type": "individual" 1080 | }, 1081 | "transparent": true, 1082 | "type": "graph", 1083 | "xaxis": { 1084 | "buckets": null, 1085 | "mode": "time", 1086 | "name": null, 1087 | "show": true, 1088 | "values": [] 1089 | }, 1090 | "yaxes": [ 1091 | { 1092 | "decimals": null, 1093 | "format": "short", 1094 | "label": "", 1095 | "logBase": 1, 1096 | "max": null, 1097 | "min": "0", 1098 | "show": true 1099 | }, 1100 | { 1101 | "format": "short", 1102 | "label": null, 1103 | "logBase": 1, 1104 | "max": null, 1105 | "min": null, 1106 | "show": true 1107 | } 1108 | ], 1109 | "yaxis": { 1110 | "align": false, 1111 | "alignLevel": null 1112 | } 1113 | }, 1114 | { 1115 | "columns": [ 1116 | { 1117 | "text": "Min", 1118 | "value": "min" 1119 | }, 1120 | { 1121 | "text": "Max", 1122 | "value": "max" 1123 | }, 1124 | { 1125 | "text": "Avg", 1126 | "value": "avg" 1127 | }, 1128 | { 1129 | "text": "Current", 1130 | "value": "current" 1131 | } 1132 | ], 1133 | "datasource": null, 1134 | "fontSize": "100%", 1135 | "gridPos": { 1136 | "h": 7, 1137 | "w": 9, 1138 | "x": 0, 1139 | "y": 38 1140 | }, 1141 | "id": 55, 1142 | "options": {}, 1143 | "pageSize": null, 1144 | "pluginVersion": "6.4.1", 1145 | "scroll": true, 1146 | "showHeader": true, 1147 | "sort": { 1148 | "col": 4, 1149 | "desc": true 1150 | }, 1151 | "styles": [ 1152 | { 1153 | "alias": "Instance", 1154 | "colorMode": null, 1155 | "colors": [ 1156 | "rgba(245, 54, 54, 0.9)", 1157 | "rgba(237, 129, 40, 0.89)", 1158 | "rgba(50, 172, 45, 0.97)" 1159 | ], 1160 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1161 | "decimals": 0, 1162 | "mappingType": 1, 1163 | "pattern": "Metric", 1164 | "thresholds": [], 1165 | "type": "string", 1166 | "unit": "short" 1167 | }, 1168 | { 1169 | "alias": "", 1170 | "colorMode": null, 1171 | "colors": [ 1172 | "rgba(245, 54, 54, 0.9)", 1173 | "rgba(237, 129, 40, 0.89)", 1174 | "rgba(50, 172, 45, 0.97)" 1175 | ], 1176 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1177 | "decimals": 0, 1178 | "mappingType": 1, 1179 | "pattern": "/.*/", 1180 | "thresholds": [], 1181 | "type": "number", 1182 | "unit": "short" 1183 | } 1184 | ], 1185 | "targets": [ 1186 | { 1187 | "expr": "perftest_consumed{instance=~\"$instance\"}", 1188 | "legendFormat": "{{instance}}", 1189 | "refId": "A" 1190 | } 1191 | ], 1192 | "timeFrom": null, 1193 | "timeShift": null, 1194 | "title": "Messages consumed / s", 1195 | "transform": "timeseries_aggregations", 1196 | "type": "table" 1197 | }, 1198 | { 1199 | "aliasColors": {}, 1200 | "bars": false, 1201 | "dashLength": 10, 1202 | "dashes": false, 1203 | "datasource": null, 1204 | "fill": 1, 1205 | "fillGradient": 0, 1206 | "gridPos": { 1207 | "h": 7, 1208 | "w": 15, 1209 | "x": 9, 1210 | "y": 38 1211 | }, 1212 | "id": 53, 1213 | "legend": { 1214 | "alignAsTable": true, 1215 | "avg": true, 1216 | "current": true, 1217 | "max": true, 1218 | "min": true, 1219 | "rightSide": false, 1220 | "show": false, 1221 | "sort": "avg", 1222 | "sortDesc": true, 1223 | "total": false, 1224 | "values": true 1225 | }, 1226 | "lines": true, 1227 | "linewidth": 1, 1228 | "links": [], 1229 | "nullPointMode": "null", 1230 | "options": { 1231 | "dataLinks": [] 1232 | }, 1233 | "percentage": false, 1234 | "pointradius": 5, 1235 | "points": false, 1236 | "renderer": "flot", 1237 | "seriesOverrides": [], 1238 | "spaceLength": 10, 1239 | "stack": false, 1240 | "steppedLine": false, 1241 | "targets": [ 1242 | { 1243 | "expr": "perftest_consumed{instance=~\"$instance\"}", 1244 | "format": "time_series", 1245 | "intervalFactor": 1, 1246 | "legendFormat": "{{instance}}", 1247 | "refId": "A" 1248 | } 1249 | ], 1250 | "thresholds": [], 1251 | "timeFrom": null, 1252 | "timeRegions": [], 1253 | "timeShift": null, 1254 | "title": "Messages consumed / s", 1255 | "tooltip": { 1256 | "shared": true, 1257 | "sort": 2, 1258 | "value_type": "individual" 1259 | }, 1260 | "transparent": true, 1261 | "type": "graph", 1262 | "xaxis": { 1263 | "buckets": null, 1264 | "mode": "time", 1265 | "name": null, 1266 | "show": true, 1267 | "values": [] 1268 | }, 1269 | "yaxes": [ 1270 | { 1271 | "decimals": null, 1272 | "format": "short", 1273 | "label": "", 1274 | "logBase": 1, 1275 | "max": null, 1276 | "min": "0", 1277 | "show": true 1278 | }, 1279 | { 1280 | "format": "short", 1281 | "label": null, 1282 | "logBase": 1, 1283 | "max": null, 1284 | "min": null, 1285 | "show": true 1286 | } 1287 | ], 1288 | "yaxis": { 1289 | "align": false, 1290 | "alignLevel": null 1291 | } 1292 | }, 1293 | { 1294 | "columns": [ 1295 | { 1296 | "text": "Min", 1297 | "value": "min" 1298 | }, 1299 | { 1300 | "text": "Max", 1301 | "value": "max" 1302 | }, 1303 | { 1304 | "text": "Avg", 1305 | "value": "avg" 1306 | }, 1307 | { 1308 | "text": "Current", 1309 | "value": "current" 1310 | } 1311 | ], 1312 | "datasource": null, 1313 | "fontSize": "100%", 1314 | "gridPos": { 1315 | "h": 7, 1316 | "w": 9, 1317 | "x": 0, 1318 | "y": 45 1319 | }, 1320 | "id": 47, 1321 | "options": {}, 1322 | "pageSize": null, 1323 | "pluginVersion": "6.4.1", 1324 | "scroll": true, 1325 | "showHeader": true, 1326 | "sort": { 1327 | "col": 4, 1328 | "desc": true 1329 | }, 1330 | "styles": [ 1331 | { 1332 | "alias": "Instance", 1333 | "colorMode": null, 1334 | "colors": [ 1335 | "rgba(245, 54, 54, 0.9)", 1336 | "rgba(237, 129, 40, 0.89)", 1337 | "rgba(50, 172, 45, 0.97)" 1338 | ], 1339 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1340 | "decimals": 0, 1341 | "mappingType": 1, 1342 | "pattern": "Metric", 1343 | "thresholds": [], 1344 | "type": "string", 1345 | "unit": "short" 1346 | }, 1347 | { 1348 | "alias": "", 1349 | "colorMode": null, 1350 | "colors": [ 1351 | "rgba(245, 54, 54, 0.9)", 1352 | "rgba(237, 129, 40, 0.89)", 1353 | "rgba(50, 172, 45, 0.97)" 1354 | ], 1355 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1356 | "decimals": 1, 1357 | "mappingType": 1, 1358 | "pattern": "/.*/", 1359 | "thresholds": [], 1360 | "type": "number", 1361 | "unit": "s" 1362 | } 1363 | ], 1364 | "targets": [ 1365 | { 1366 | "expr": "perftest_latency_seconds{quantile=\"$percentile\", instance=~\"$instance\"}", 1367 | "legendFormat": "{{instance}}", 1368 | "refId": "A" 1369 | } 1370 | ], 1371 | "timeFrom": null, 1372 | "timeShift": null, 1373 | "title": "End-to-end message latency", 1374 | "transform": "timeseries_aggregations", 1375 | "type": "table" 1376 | }, 1377 | { 1378 | "aliasColors": {}, 1379 | "bars": false, 1380 | "dashLength": 10, 1381 | "dashes": false, 1382 | "datasource": null, 1383 | "fill": 1, 1384 | "fillGradient": 0, 1385 | "gridPos": { 1386 | "h": 7, 1387 | "w": 15, 1388 | "x": 9, 1389 | "y": 45 1390 | }, 1391 | "id": 45, 1392 | "legend": { 1393 | "alignAsTable": true, 1394 | "avg": true, 1395 | "current": true, 1396 | "max": true, 1397 | "min": true, 1398 | "rightSide": false, 1399 | "show": false, 1400 | "sort": "avg", 1401 | "sortDesc": true, 1402 | "total": false, 1403 | "values": true 1404 | }, 1405 | "lines": true, 1406 | "linewidth": 1, 1407 | "links": [], 1408 | "nullPointMode": "null", 1409 | "options": { 1410 | "dataLinks": [] 1411 | }, 1412 | "percentage": false, 1413 | "pointradius": 5, 1414 | "points": false, 1415 | "renderer": "flot", 1416 | "seriesOverrides": [], 1417 | "spaceLength": 10, 1418 | "stack": false, 1419 | "steppedLine": false, 1420 | "targets": [ 1421 | { 1422 | "expr": "perftest_latency_seconds{quantile=\"$percentile\", instance=~\"$instance\"}", 1423 | "format": "time_series", 1424 | "instant": false, 1425 | "interval": "1s", 1426 | "intervalFactor": 1, 1427 | "legendFormat": "{{instance}}", 1428 | "refId": "A" 1429 | } 1430 | ], 1431 | "thresholds": [], 1432 | "timeFrom": null, 1433 | "timeRegions": [], 1434 | "timeShift": null, 1435 | "title": "End-to-end message latency", 1436 | "tooltip": { 1437 | "shared": true, 1438 | "sort": 2, 1439 | "value_type": "individual" 1440 | }, 1441 | "transparent": true, 1442 | "type": "graph", 1443 | "xaxis": { 1444 | "buckets": null, 1445 | "mode": "time", 1446 | "name": null, 1447 | "show": true, 1448 | "values": [] 1449 | }, 1450 | "yaxes": [ 1451 | { 1452 | "format": "s", 1453 | "label": null, 1454 | "logBase": 1, 1455 | "max": null, 1456 | "min": "0", 1457 | "show": true 1458 | }, 1459 | { 1460 | "format": "short", 1461 | "label": null, 1462 | "logBase": 1, 1463 | "max": null, 1464 | "min": null, 1465 | "show": true 1466 | } 1467 | ], 1468 | "yaxis": { 1469 | "align": false, 1470 | "alignLevel": null 1471 | } 1472 | }, 1473 | { 1474 | "aliasColors": {}, 1475 | "bars": true, 1476 | "dashLength": 10, 1477 | "dashes": false, 1478 | "datasource": null, 1479 | "fill": 1, 1480 | "fillGradient": 0, 1481 | "gridPos": { 1482 | "h": 8, 1483 | "w": 9, 1484 | "x": 0, 1485 | "y": 52 1486 | }, 1487 | "id": 43, 1488 | "legend": { 1489 | "alignAsTable": true, 1490 | "avg": true, 1491 | "current": true, 1492 | "max": true, 1493 | "min": true, 1494 | "rightSide": false, 1495 | "show": false, 1496 | "sort": "max", 1497 | "sortDesc": true, 1498 | "total": false, 1499 | "values": true 1500 | }, 1501 | "lines": false, 1502 | "linewidth": 1, 1503 | "links": [], 1504 | "nullPointMode": "null", 1505 | "options": { 1506 | "dataLinks": [] 1507 | }, 1508 | "percentage": false, 1509 | "pointradius": 5, 1510 | "points": false, 1511 | "renderer": "flot", 1512 | "seriesOverrides": [], 1513 | "spaceLength": 10, 1514 | "stack": false, 1515 | "steppedLine": false, 1516 | "targets": [ 1517 | { 1518 | "expr": "perftest_latency_seconds{quantile=\"$percentile\", instance=~\"$instance\"}", 1519 | "format": "time_series", 1520 | "intervalFactor": 1, 1521 | "legendFormat": "{{instance}}", 1522 | "refId": "A" 1523 | } 1524 | ], 1525 | "thresholds": [], 1526 | "timeFrom": null, 1527 | "timeRegions": [], 1528 | "timeShift": null, 1529 | "title": "End-to-end message latency distribution", 1530 | "tooltip": { 1531 | "shared": false, 1532 | "sort": 0, 1533 | "value_type": "individual" 1534 | }, 1535 | "transparent": true, 1536 | "type": "graph", 1537 | "xaxis": { 1538 | "buckets": 20, 1539 | "mode": "histogram", 1540 | "name": null, 1541 | "show": true, 1542 | "values": [] 1543 | }, 1544 | "yaxes": [ 1545 | { 1546 | "format": "none", 1547 | "label": "", 1548 | "logBase": 1, 1549 | "max": null, 1550 | "min": "0", 1551 | "show": true 1552 | }, 1553 | { 1554 | "format": "short", 1555 | "label": null, 1556 | "logBase": 1, 1557 | "max": null, 1558 | "min": null, 1559 | "show": false 1560 | } 1561 | ], 1562 | "yaxis": { 1563 | "align": false, 1564 | "alignLevel": null 1565 | } 1566 | }, 1567 | { 1568 | "cards": { 1569 | "cardPadding": null, 1570 | "cardRound": null 1571 | }, 1572 | "color": { 1573 | "cardColor": "rgb(255, 255, 255)", 1574 | "colorScale": "sqrt", 1575 | "colorScheme": "interpolateBlues", 1576 | "exponent": 0.4, 1577 | "max": null, 1578 | "min": null, 1579 | "mode": "opacity" 1580 | }, 1581 | "dataFormat": "timeseries", 1582 | "datasource": null, 1583 | "gridPos": { 1584 | "h": 8, 1585 | "w": 15, 1586 | "x": 9, 1587 | "y": 52 1588 | }, 1589 | "heatmap": {}, 1590 | "hideZeroBuckets": true, 1591 | "highlightCards": true, 1592 | "id": 41, 1593 | "legend": { 1594 | "show": true 1595 | }, 1596 | "links": [], 1597 | "options": {}, 1598 | "reverseYBuckets": false, 1599 | "targets": [ 1600 | { 1601 | "expr": "perftest_latency_seconds{quantile=\"$percentile\", instance=~\"$instance\"}", 1602 | "format": "heatmap", 1603 | "intervalFactor": 1, 1604 | "refId": "A" 1605 | } 1606 | ], 1607 | "title": "End-to-end message latency distribution", 1608 | "tooltip": { 1609 | "show": true, 1610 | "showHistogram": true 1611 | }, 1612 | "transparent": true, 1613 | "type": "heatmap", 1614 | "xAxis": { 1615 | "show": true 1616 | }, 1617 | "xBucketNumber": null, 1618 | "xBucketSize": null, 1619 | "yAxis": { 1620 | "decimals": null, 1621 | "format": "s", 1622 | "logBase": 1, 1623 | "max": null, 1624 | "min": "0", 1625 | "show": true, 1626 | "splitFactor": null 1627 | }, 1628 | "yBucketBound": "auto", 1629 | "yBucketNumber": null, 1630 | "yBucketSize": null 1631 | } 1632 | ], 1633 | "refresh": "15s", 1634 | "schemaVersion": 20, 1635 | "style": "dark", 1636 | "tags": [ 1637 | "cadvisor", 1638 | "node-exporter", 1639 | "rabbitmq-perf-test", 1640 | "rabbitmq-prometheus" 1641 | ], 1642 | "templating": { 1643 | "list": [ 1644 | { 1645 | "current": { 1646 | "selected": false, 1647 | "text": "default", 1648 | "value": "default" 1649 | }, 1650 | "hide": 2, 1651 | "includeAll": false, 1652 | "label": "datasource", 1653 | "multi": false, 1654 | "name": "DS_PROMETHEUS", 1655 | "options": [], 1656 | "query": "prometheus", 1657 | "refresh": 1, 1658 | "regex": "", 1659 | "skipUrlSync": false, 1660 | "type": "datasource" 1661 | }, 1662 | { 1663 | "allValue": null, 1664 | "current": {}, 1665 | "datasource": null, 1666 | "definition": "label_values(rabbitmq_identity_info, namespace)", 1667 | "hide": 0, 1668 | "includeAll": false, 1669 | "label": "Namespace", 1670 | "multi": false, 1671 | "name": "namespace", 1672 | "options": [], 1673 | "query": "label_values(rabbitmq_identity_info, namespace)", 1674 | "refresh": 2, 1675 | "regex": "", 1676 | "skipUrlSync": false, 1677 | "sort": 1, 1678 | "tagValuesQuery": "", 1679 | "tags": [], 1680 | "tagsQuery": "", 1681 | "type": "query", 1682 | "useTags": false 1683 | }, 1684 | { 1685 | "allValue": null, 1686 | "current": { 1687 | "text": "All", 1688 | "value": [ 1689 | "$__all" 1690 | ] 1691 | }, 1692 | "datasource": null, 1693 | "definition": "label_values(rabbitmq_identity_info{namespace=\"$namespace\"}, rabbitmq_cluster)", 1694 | "hide": 0, 1695 | "includeAll": true, 1696 | "label": "RabbitMQ Cluster", 1697 | "multi": true, 1698 | "name": "rabbitmq_cluster", 1699 | "options": [], 1700 | "query": "label_values(rabbitmq_identity_info{namespace=\"$namespace\"}, rabbitmq_cluster)", 1701 | "refresh": 2, 1702 | "regex": "", 1703 | "skipUrlSync": false, 1704 | "sort": 1, 1705 | "tagValuesQuery": "", 1706 | "tags": [], 1707 | "tagsQuery": "", 1708 | "type": "query", 1709 | "useTags": false 1710 | }, 1711 | { 1712 | "allValue": null, 1713 | "current": { 1714 | "text": "All", 1715 | "value": [ 1716 | "$__all" 1717 | ] 1718 | }, 1719 | "datasource": null, 1720 | "definition": "label_values(perftest_published, instance)", 1721 | "hide": 0, 1722 | "includeAll": true, 1723 | "label": "PerfTest Instance", 1724 | "multi": true, 1725 | "name": "instance", 1726 | "options": [], 1727 | "query": "label_values(perftest_published, instance)", 1728 | "refresh": 2, 1729 | "regex": "", 1730 | "skipUrlSync": false, 1731 | "sort": 1, 1732 | "tagValuesQuery": "", 1733 | "tags": [], 1734 | "tagsQuery": "", 1735 | "type": "query", 1736 | "useTags": false 1737 | }, 1738 | { 1739 | "allValue": null, 1740 | "current": { 1741 | "text": "0.99", 1742 | "value": "0.99" 1743 | }, 1744 | "datasource": null, 1745 | "definition": "label_values(perftest_latency_seconds, quantile)", 1746 | "hide": 0, 1747 | "includeAll": false, 1748 | "label": "Percentile", 1749 | "multi": false, 1750 | "name": "percentile", 1751 | "options": [], 1752 | "query": "label_values(perftest_latency_seconds, quantile)", 1753 | "refresh": 2, 1754 | "regex": "", 1755 | "skipUrlSync": false, 1756 | "sort": 4, 1757 | "tagValuesQuery": "", 1758 | "tags": [], 1759 | "tagsQuery": "", 1760 | "type": "query", 1761 | "useTags": false 1762 | }, 1763 | { 1764 | "allValue": null, 1765 | "current": { 1766 | "text": "All", 1767 | "value": [ 1768 | "$__all" 1769 | ] 1770 | }, 1771 | "datasource": null, 1772 | "definition": "label_values(node_network_info, instance)", 1773 | "hide": 0, 1774 | "includeAll": true, 1775 | "label": "Host", 1776 | "multi": true, 1777 | "name": "host", 1778 | "options": [], 1779 | "query": "label_values(node_network_info, instance)", 1780 | "refresh": 2, 1781 | "regex": "", 1782 | "skipUrlSync": false, 1783 | "sort": 1, 1784 | "tagValuesQuery": "", 1785 | "tags": [], 1786 | "tagsQuery": "", 1787 | "type": "query", 1788 | "useTags": false 1789 | }, 1790 | { 1791 | "allValue": null, 1792 | "current": { 1793 | "text": "All", 1794 | "value": [ 1795 | "$__all" 1796 | ] 1797 | }, 1798 | "datasource": null, 1799 | "definition": "label_values(container_network_receive_bytes_total, name)", 1800 | "hide": 0, 1801 | "includeAll": true, 1802 | "label": "or Container", 1803 | "multi": true, 1804 | "name": "container", 1805 | "options": [], 1806 | "query": "label_values(container_network_receive_bytes_total, name)", 1807 | "refresh": 2, 1808 | "regex": "", 1809 | "skipUrlSync": false, 1810 | "sort": 1, 1811 | "tagValuesQuery": "", 1812 | "tags": [], 1813 | "tagsQuery": "", 1814 | "type": "query", 1815 | "useTags": false 1816 | } 1817 | ] 1818 | }, 1819 | "time": { 1820 | "from": "now-15m", 1821 | "to": "now" 1822 | }, 1823 | "timepicker": { 1824 | "refresh_intervals": [ 1825 | "15s", 1826 | "30s", 1827 | "1m", 1828 | "5m", 1829 | "10m" 1830 | ], 1831 | "time_options": [ 1832 | "5m", 1833 | "15m", 1834 | "1h", 1835 | "6h", 1836 | "12h", 1837 | "24h", 1838 | "2d", 1839 | "7d", 1840 | "30d" 1841 | ] 1842 | }, 1843 | "timezone": "", 1844 | "title": "Erlang-Distributions-Compare", 1845 | "uid": "C0jeDstZk", 1846 | "version": 20210322 1847 | } 1848 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/dashboards/RabbitMQ-PerfTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "__requires": [ 3 | { 4 | "type": "grafana", 5 | "id": "grafana", 6 | "name": "Grafana", 7 | "version": "7.0.0" 8 | }, 9 | { 10 | "type": "datasource", 11 | "id": "prometheus", 12 | "name": "prometheus", 13 | "version": "2.0.0" 14 | }, 15 | { 16 | "type": "table", 17 | "id": "table", 18 | "name": "Table", 19 | "version": "" 20 | }, 21 | { 22 | "type": "panel", 23 | "id": "graph", 24 | "name": "Graph", 25 | "version": "" 26 | }, 27 | { 28 | "type": "panel", 29 | "id": "heatmap", 30 | "name": "Heatmap", 31 | "version": "" 32 | } 33 | ], 34 | "annotations": { 35 | "list": [ 36 | { 37 | "builtIn": 1, 38 | "datasource": "-- Grafana --", 39 | "enable": true, 40 | "hide": true, 41 | "iconColor": "rgba(0, 211, 255, 1)", 42 | "name": "Annotations & Alerts", 43 | "type": "dashboard" 44 | } 45 | ] 46 | }, 47 | "editable": true, 48 | "gnetId": null, 49 | "graphTooltip": 1, 50 | "iteration": 1570184644782, 51 | "links": [], 52 | "panels": [ 53 | { 54 | "collapsed": false, 55 | "datasource": null, 56 | "description": "RabbitMQ message latency & throughput across all PerfTest instances", 57 | "gridPos": { 58 | "h": 1, 59 | "w": 24, 60 | "x": 0, 61 | "y": 0 62 | }, 63 | "id": 22, 64 | "panels": [], 65 | "title": "LATENCY", 66 | "type": "row" 67 | }, 68 | { 69 | "columns": [ 70 | { 71 | "text": "Min", 72 | "value": "min" 73 | }, 74 | { 75 | "text": "Max", 76 | "value": "max" 77 | }, 78 | { 79 | "text": "Avg", 80 | "value": "avg" 81 | }, 82 | { 83 | "text": "Current", 84 | "value": "current" 85 | } 86 | ], 87 | "datasource": null, 88 | "fontSize": "100%", 89 | "gridPos": { 90 | "h": 7, 91 | "w": 6, 92 | "x": 0, 93 | "y": 1 94 | }, 95 | "id": 33, 96 | "options": {}, 97 | "pageSize": 5, 98 | "pluginVersion": "6.4.1", 99 | "showHeader": true, 100 | "sort": { 101 | "col": 4, 102 | "desc": true 103 | }, 104 | "styles": [ 105 | { 106 | "alias": "Instance", 107 | "colorMode": null, 108 | "colors": [ 109 | "rgba(245, 54, 54, 0.9)", 110 | "rgba(237, 129, 40, 0.89)", 111 | "rgba(50, 172, 45, 0.97)" 112 | ], 113 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 114 | "decimals": 0, 115 | "mappingType": 1, 116 | "pattern": "Metric", 117 | "thresholds": [], 118 | "type": "string", 119 | "unit": "short" 120 | }, 121 | { 122 | "alias": "", 123 | "colorMode": null, 124 | "colors": [ 125 | "rgba(245, 54, 54, 0.9)", 126 | "rgba(237, 129, 40, 0.89)", 127 | "rgba(50, 172, 45, 0.97)" 128 | ], 129 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 130 | "decimals": 1, 131 | "mappingType": 1, 132 | "pattern": "/.*/", 133 | "thresholds": [], 134 | "type": "number", 135 | "unit": "s" 136 | } 137 | ], 138 | "targets": [ 139 | { 140 | "expr": "perftest_latency_seconds{quantile=\"$percentile\"}", 141 | "legendFormat": "{{instance}}", 142 | "refId": "A" 143 | } 144 | ], 145 | "timeFrom": null, 146 | "timeShift": null, 147 | "title": "End-to-end message latency", 148 | "transform": "timeseries_aggregations", 149 | "type": "table" 150 | }, 151 | { 152 | "aliasColors": {}, 153 | "bars": false, 154 | "dashLength": 10, 155 | "dashes": false, 156 | "datasource": null, 157 | "fill": 1, 158 | "fillGradient": 0, 159 | "gridPos": { 160 | "h": 7, 161 | "w": 6, 162 | "x": 6, 163 | "y": 1 164 | }, 165 | "id": 2, 166 | "legend": { 167 | "alignAsTable": true, 168 | "avg": true, 169 | "current": true, 170 | "max": true, 171 | "min": true, 172 | "rightSide": false, 173 | "show": false, 174 | "sort": "avg", 175 | "sortDesc": true, 176 | "total": false, 177 | "values": true 178 | }, 179 | "lines": true, 180 | "linewidth": 1, 181 | "links": [], 182 | "nullPointMode": "null", 183 | "options": { 184 | "dataLinks": [] 185 | }, 186 | "percentage": false, 187 | "pointradius": 5, 188 | "points": false, 189 | "renderer": "flot", 190 | "seriesOverrides": [], 191 | "spaceLength": 10, 192 | "stack": false, 193 | "steppedLine": false, 194 | "targets": [ 195 | { 196 | "expr": "perftest_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"}", 197 | "format": "time_series", 198 | "instant": false, 199 | "interval": "1s", 200 | "intervalFactor": 1, 201 | "legendFormat": "{{instance}}", 202 | "refId": "A" 203 | } 204 | ], 205 | "thresholds": [], 206 | "timeFrom": null, 207 | "timeRegions": [], 208 | "timeShift": null, 209 | "title": "End-to-end message latency", 210 | "tooltip": { 211 | "shared": true, 212 | "sort": 2, 213 | "value_type": "individual" 214 | }, 215 | "transparent": true, 216 | "type": "graph", 217 | "xaxis": { 218 | "buckets": null, 219 | "mode": "time", 220 | "name": null, 221 | "show": true, 222 | "values": [] 223 | }, 224 | "yaxes": [ 225 | { 226 | "format": "s", 227 | "label": null, 228 | "logBase": 1, 229 | "max": null, 230 | "min": "0", 231 | "show": true 232 | }, 233 | { 234 | "format": "short", 235 | "label": null, 236 | "logBase": 1, 237 | "max": null, 238 | "min": null, 239 | "show": true 240 | } 241 | ], 242 | "yaxis": { 243 | "align": false, 244 | "alignLevel": null 245 | } 246 | }, 247 | { 248 | "cards": { 249 | "cardPadding": null, 250 | "cardRound": null 251 | }, 252 | "color": { 253 | "cardColor": "rgb(255, 255, 255)", 254 | "colorScale": "sqrt", 255 | "colorScheme": "interpolateSpectral", 256 | "exponent": 0.4, 257 | "max": null, 258 | "min": null, 259 | "mode": "opacity" 260 | }, 261 | "dataFormat": "timeseries", 262 | "datasource": null, 263 | "gridPos": { 264 | "h": 7, 265 | "w": 6, 266 | "x": 12, 267 | "y": 1 268 | }, 269 | "heatmap": {}, 270 | "hideZeroBuckets": false, 271 | "highlightCards": true, 272 | "id": 16, 273 | "legend": { 274 | "show": true 275 | }, 276 | "links": [], 277 | "options": {}, 278 | "reverseYBuckets": false, 279 | "targets": [ 280 | { 281 | "expr": "perftest_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"} > 0", 282 | "format": "heatmap", 283 | "intervalFactor": 1, 284 | "refId": "A" 285 | } 286 | ], 287 | "title": "End-to-end message latency distribution", 288 | "tooltip": { 289 | "show": true, 290 | "showHistogram": true 291 | }, 292 | "transparent": true, 293 | "type": "heatmap", 294 | "xAxis": { 295 | "show": true 296 | }, 297 | "xBucketNumber": null, 298 | "xBucketSize": null, 299 | "yAxis": { 300 | "decimals": null, 301 | "format": "s", 302 | "logBase": 1, 303 | "max": null, 304 | "min": "0", 305 | "show": true, 306 | "splitFactor": null 307 | }, 308 | "yBucketBound": "auto", 309 | "yBucketNumber": null, 310 | "yBucketSize": null 311 | }, 312 | { 313 | "aliasColors": {}, 314 | "bars": true, 315 | "dashLength": 10, 316 | "dashes": false, 317 | "datasource": null, 318 | "fill": 1, 319 | "fillGradient": 0, 320 | "gridPos": { 321 | "h": 7, 322 | "w": 6, 323 | "x": 18, 324 | "y": 1 325 | }, 326 | "id": 17, 327 | "legend": { 328 | "alignAsTable": true, 329 | "avg": true, 330 | "current": true, 331 | "max": true, 332 | "min": true, 333 | "rightSide": false, 334 | "show": false, 335 | "sort": "max", 336 | "sortDesc": true, 337 | "total": false, 338 | "values": true 339 | }, 340 | "lines": false, 341 | "linewidth": 1, 342 | "links": [], 343 | "nullPointMode": "null", 344 | "options": { 345 | "dataLinks": [] 346 | }, 347 | "percentage": false, 348 | "pointradius": 5, 349 | "points": false, 350 | "renderer": "flot", 351 | "seriesOverrides": [], 352 | "spaceLength": 10, 353 | "stack": false, 354 | "steppedLine": false, 355 | "targets": [ 356 | { 357 | "expr": "perftest_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"} > 0", 358 | "format": "time_series", 359 | "intervalFactor": 1, 360 | "legendFormat": "{{instance}}", 361 | "refId": "A" 362 | } 363 | ], 364 | "thresholds": [], 365 | "timeFrom": null, 366 | "timeRegions": [], 367 | "timeShift": null, 368 | "title": "End-to-end message latency distribution", 369 | "tooltip": { 370 | "shared": false, 371 | "sort": 0, 372 | "value_type": "individual" 373 | }, 374 | "transparent": true, 375 | "type": "graph", 376 | "xaxis": { 377 | "buckets": 20, 378 | "mode": "histogram", 379 | "name": null, 380 | "show": true, 381 | "values": [] 382 | }, 383 | "yaxes": [ 384 | { 385 | "format": "none", 386 | "label": "", 387 | "logBase": 1, 388 | "max": null, 389 | "min": "0", 390 | "show": true 391 | }, 392 | { 393 | "format": "short", 394 | "label": null, 395 | "logBase": 1, 396 | "max": null, 397 | "min": null, 398 | "show": false 399 | } 400 | ], 401 | "yaxis": { 402 | "align": false, 403 | "alignLevel": null 404 | } 405 | }, 406 | { 407 | "columns": [ 408 | { 409 | "text": "Min", 410 | "value": "min" 411 | }, 412 | { 413 | "text": "Max", 414 | "value": "max" 415 | }, 416 | { 417 | "text": "Avg", 418 | "value": "avg" 419 | }, 420 | { 421 | "text": "Current", 422 | "value": "current" 423 | } 424 | ], 425 | "datasource": null, 426 | "fontSize": "100%", 427 | "gridPos": { 428 | "h": 7, 429 | "w": 6, 430 | "x": 0, 431 | "y": 8 432 | }, 433 | "id": 34, 434 | "options": {}, 435 | "pageSize": 5, 436 | "pluginVersion": "6.4.1", 437 | "showHeader": true, 438 | "sort": { 439 | "col": 4, 440 | "desc": true 441 | }, 442 | "styles": [ 443 | { 444 | "alias": "Instance", 445 | "colorMode": null, 446 | "colors": [ 447 | "rgba(245, 54, 54, 0.9)", 448 | "rgba(237, 129, 40, 0.89)", 449 | "rgba(50, 172, 45, 0.97)" 450 | ], 451 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 452 | "decimals": 0, 453 | "mappingType": 1, 454 | "pattern": "Metric", 455 | "thresholds": [], 456 | "type": "string", 457 | "unit": "short" 458 | }, 459 | { 460 | "alias": "", 461 | "colorMode": null, 462 | "colors": [ 463 | "rgba(245, 54, 54, 0.9)", 464 | "rgba(237, 129, 40, 0.89)", 465 | "rgba(50, 172, 45, 0.97)" 466 | ], 467 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 468 | "decimals": 1, 469 | "mappingType": 1, 470 | "pattern": "/.*/", 471 | "thresholds": [], 472 | "type": "number", 473 | "unit": "s" 474 | } 475 | ], 476 | "targets": [ 477 | { 478 | "expr": "perftest_confirm_latency_seconds{quantile=\"$percentile\"}", 479 | "legendFormat": "{{instance}}", 480 | "refId": "A" 481 | } 482 | ], 483 | "timeFrom": null, 484 | "timeShift": null, 485 | "title": "Publish confirm latency", 486 | "transform": "timeseries_aggregations", 487 | "type": "table" 488 | }, 489 | { 490 | "aliasColors": {}, 491 | "bars": false, 492 | "dashLength": 10, 493 | "dashes": false, 494 | "datasource": null, 495 | "fill": 1, 496 | "fillGradient": 0, 497 | "gridPos": { 498 | "h": 7, 499 | "w": 6, 500 | "x": 6, 501 | "y": 8 502 | }, 503 | "id": 37, 504 | "legend": { 505 | "alignAsTable": true, 506 | "avg": true, 507 | "current": true, 508 | "max": true, 509 | "min": true, 510 | "rightSide": false, 511 | "show": false, 512 | "sort": "avg", 513 | "sortDesc": true, 514 | "total": false, 515 | "values": true 516 | }, 517 | "lines": true, 518 | "linewidth": 1, 519 | "links": [], 520 | "nullPointMode": "null", 521 | "options": { 522 | "dataLinks": [] 523 | }, 524 | "percentage": false, 525 | "pointradius": 5, 526 | "points": false, 527 | "renderer": "flot", 528 | "seriesOverrides": [], 529 | "spaceLength": 10, 530 | "stack": false, 531 | "steppedLine": false, 532 | "targets": [ 533 | { 534 | "expr": "perftest_confirm_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"}", 535 | "format": "time_series", 536 | "instant": false, 537 | "interval": "1s", 538 | "intervalFactor": 1, 539 | "legendFormat": "{{instance}}", 540 | "refId": "A" 541 | } 542 | ], 543 | "thresholds": [], 544 | "timeFrom": null, 545 | "timeRegions": [], 546 | "timeShift": null, 547 | "title": "Publish confirm latency", 548 | "tooltip": { 549 | "shared": true, 550 | "sort": 2, 551 | "value_type": "individual" 552 | }, 553 | "transparent": true, 554 | "type": "graph", 555 | "xaxis": { 556 | "buckets": null, 557 | "mode": "time", 558 | "name": null, 559 | "show": true, 560 | "values": [] 561 | }, 562 | "yaxes": [ 563 | { 564 | "format": "s", 565 | "label": null, 566 | "logBase": 1, 567 | "max": null, 568 | "min": "0", 569 | "show": true 570 | }, 571 | { 572 | "format": "short", 573 | "label": null, 574 | "logBase": 1, 575 | "max": null, 576 | "min": null, 577 | "show": true 578 | } 579 | ], 580 | "yaxis": { 581 | "align": false, 582 | "alignLevel": null 583 | } 584 | }, 585 | { 586 | "cards": { 587 | "cardPadding": null, 588 | "cardRound": null 589 | }, 590 | "color": { 591 | "cardColor": "rgb(255, 255, 255)", 592 | "colorScale": "sqrt", 593 | "colorScheme": "interpolateSpectral", 594 | "exponent": 0.4, 595 | "max": null, 596 | "min": null, 597 | "mode": "opacity" 598 | }, 599 | "dataFormat": "timeseries", 600 | "datasource": null, 601 | "gridPos": { 602 | "h": 7, 603 | "w": 6, 604 | "x": 12, 605 | "y": 8 606 | }, 607 | "heatmap": {}, 608 | "hideZeroBuckets": false, 609 | "highlightCards": true, 610 | "id": 38, 611 | "legend": { 612 | "show": true 613 | }, 614 | "links": [], 615 | "options": {}, 616 | "reverseYBuckets": false, 617 | "targets": [ 618 | { 619 | "expr": "perftest_confirm_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"} > 0", 620 | "format": "heatmap", 621 | "intervalFactor": 1, 622 | "refId": "A" 623 | } 624 | ], 625 | "title": "Publish confirm latency distribution", 626 | "tooltip": { 627 | "show": true, 628 | "showHistogram": true 629 | }, 630 | "transparent": true, 631 | "type": "heatmap", 632 | "xAxis": { 633 | "show": true 634 | }, 635 | "xBucketNumber": null, 636 | "xBucketSize": null, 637 | "yAxis": { 638 | "decimals": null, 639 | "format": "s", 640 | "logBase": 1, 641 | "max": null, 642 | "min": "0", 643 | "show": true, 644 | "splitFactor": null 645 | }, 646 | "yBucketBound": "auto", 647 | "yBucketNumber": null, 648 | "yBucketSize": null 649 | }, 650 | { 651 | "aliasColors": {}, 652 | "bars": true, 653 | "dashLength": 10, 654 | "dashes": false, 655 | "datasource": null, 656 | "fill": 1, 657 | "fillGradient": 0, 658 | "gridPos": { 659 | "h": 7, 660 | "w": 6, 661 | "x": 18, 662 | "y": 8 663 | }, 664 | "id": 39, 665 | "legend": { 666 | "alignAsTable": true, 667 | "avg": true, 668 | "current": true, 669 | "max": true, 670 | "min": true, 671 | "rightSide": false, 672 | "show": false, 673 | "sort": "max", 674 | "sortDesc": true, 675 | "total": false, 676 | "values": true 677 | }, 678 | "lines": false, 679 | "linewidth": 1, 680 | "links": [], 681 | "nullPointMode": "null", 682 | "options": { 683 | "dataLinks": [] 684 | }, 685 | "percentage": false, 686 | "pointradius": 5, 687 | "points": false, 688 | "renderer": "flot", 689 | "seriesOverrides": [], 690 | "spaceLength": 10, 691 | "stack": false, 692 | "steppedLine": false, 693 | "targets": [ 694 | { 695 | "expr": "perftest_confirm_latency_seconds{quantile=\"$percentile\",instance=~\"$instance\"} > 0", 696 | "format": "time_series", 697 | "intervalFactor": 1, 698 | "legendFormat": "{{instance}}", 699 | "refId": "A" 700 | } 701 | ], 702 | "thresholds": [], 703 | "timeFrom": null, 704 | "timeRegions": [], 705 | "timeShift": null, 706 | "title": "Publish confirm latency distribution", 707 | "tooltip": { 708 | "shared": false, 709 | "sort": 2, 710 | "value_type": "individual" 711 | }, 712 | "transparent": true, 713 | "type": "graph", 714 | "xaxis": { 715 | "buckets": 20, 716 | "mode": "histogram", 717 | "name": null, 718 | "show": true, 719 | "values": [] 720 | }, 721 | "yaxes": [ 722 | { 723 | "format": "none", 724 | "label": "", 725 | "logBase": 1, 726 | "max": null, 727 | "min": null, 728 | "show": true 729 | }, 730 | { 731 | "format": "short", 732 | "label": null, 733 | "logBase": 1, 734 | "max": null, 735 | "min": null, 736 | "show": false 737 | } 738 | ], 739 | "yaxis": { 740 | "align": false, 741 | "alignLevel": null 742 | } 743 | }, 744 | { 745 | "collapsed": false, 746 | "datasource": null, 747 | "gridPos": { 748 | "h": 1, 749 | "w": 24, 750 | "x": 0, 751 | "y": 15 752 | }, 753 | "id": 24, 754 | "panels": [], 755 | "title": "THROUGHPUT", 756 | "type": "row" 757 | }, 758 | { 759 | "columns": [ 760 | { 761 | "text": "Min", 762 | "value": "min" 763 | }, 764 | { 765 | "text": "Max", 766 | "value": "max" 767 | }, 768 | { 769 | "text": "Avg", 770 | "value": "avg" 771 | }, 772 | { 773 | "text": "Current", 774 | "value": "current" 775 | } 776 | ], 777 | "datasource": null, 778 | "fontSize": "100%", 779 | "gridPos": { 780 | "h": 7, 781 | "w": 6, 782 | "x": 0, 783 | "y": 16 784 | }, 785 | "id": 27, 786 | "options": {}, 787 | "pageSize": 5, 788 | "pluginVersion": "6.4.1", 789 | "showHeader": true, 790 | "sort": { 791 | "col": 4, 792 | "desc": true 793 | }, 794 | "styles": [ 795 | { 796 | "alias": "Instance", 797 | "colorMode": null, 798 | "colors": [ 799 | "rgba(245, 54, 54, 0.9)", 800 | "rgba(237, 129, 40, 0.89)", 801 | "rgba(50, 172, 45, 0.97)" 802 | ], 803 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 804 | "decimals": 0, 805 | "mappingType": 1, 806 | "pattern": "Metric", 807 | "thresholds": [], 808 | "type": "string", 809 | "unit": "short" 810 | }, 811 | { 812 | "alias": "", 813 | "colorMode": null, 814 | "colors": [ 815 | "rgba(245, 54, 54, 0.9)", 816 | "rgba(237, 129, 40, 0.89)", 817 | "rgba(50, 172, 45, 0.97)" 818 | ], 819 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 820 | "decimals": 0, 821 | "mappingType": 1, 822 | "pattern": "/.*/", 823 | "thresholds": [], 824 | "type": "number", 825 | "unit": "short" 826 | } 827 | ], 828 | "targets": [ 829 | { 830 | "expr": "perftest_published", 831 | "legendFormat": "{{instance}}", 832 | "refId": "A" 833 | } 834 | ], 835 | "timeFrom": null, 836 | "timeShift": null, 837 | "title": "Messages published / s", 838 | "transform": "timeseries_aggregations", 839 | "type": "table" 840 | }, 841 | { 842 | "aliasColors": {}, 843 | "bars": false, 844 | "dashLength": 10, 845 | "dashes": false, 846 | "datasource": null, 847 | "fill": 1, 848 | "fillGradient": 0, 849 | "gridPos": { 850 | "h": 7, 851 | "w": 6, 852 | "x": 6, 853 | "y": 16 854 | }, 855 | "id": 19, 856 | "legend": { 857 | "alignAsTable": true, 858 | "avg": true, 859 | "current": true, 860 | "max": true, 861 | "min": true, 862 | "rightSide": false, 863 | "show": false, 864 | "sort": "avg", 865 | "sortDesc": true, 866 | "total": false, 867 | "values": true 868 | }, 869 | "lines": true, 870 | "linewidth": 1, 871 | "links": [], 872 | "nullPointMode": "null", 873 | "options": { 874 | "dataLinks": [] 875 | }, 876 | "percentage": false, 877 | "pointradius": 5, 878 | "points": false, 879 | "renderer": "flot", 880 | "seriesOverrides": [], 881 | "spaceLength": 10, 882 | "stack": false, 883 | "steppedLine": false, 884 | "targets": [ 885 | { 886 | "expr": "perftest_published{instance=~\"$instance\"}", 887 | "format": "time_series", 888 | "intervalFactor": 1, 889 | "legendFormat": "{{instance}}", 890 | "refId": "A" 891 | } 892 | ], 893 | "thresholds": [], 894 | "timeFrom": null, 895 | "timeRegions": [], 896 | "timeShift": null, 897 | "title": "Messages published / s", 898 | "tooltip": { 899 | "shared": true, 900 | "sort": 2, 901 | "value_type": "individual" 902 | }, 903 | "transparent": true, 904 | "type": "graph", 905 | "xaxis": { 906 | "buckets": null, 907 | "mode": "time", 908 | "name": null, 909 | "show": true, 910 | "values": [] 911 | }, 912 | "yaxes": [ 913 | { 914 | "decimals": null, 915 | "format": "short", 916 | "label": "", 917 | "logBase": 1, 918 | "max": null, 919 | "min": "0", 920 | "show": true 921 | }, 922 | { 923 | "format": "short", 924 | "label": null, 925 | "logBase": 1, 926 | "max": null, 927 | "min": null, 928 | "show": true 929 | } 930 | ], 931 | "yaxis": { 932 | "align": false, 933 | "alignLevel": null 934 | } 935 | }, 936 | { 937 | "aliasColors": {}, 938 | "bars": false, 939 | "dashLength": 10, 940 | "dashes": false, 941 | "datasource": null, 942 | "fill": 1, 943 | "fillGradient": 0, 944 | "gridPos": { 945 | "h": 7, 946 | "w": 6, 947 | "x": 12, 948 | "y": 16 949 | }, 950 | "id": 20, 951 | "legend": { 952 | "alignAsTable": true, 953 | "avg": true, 954 | "current": true, 955 | "max": true, 956 | "min": true, 957 | "rightSide": false, 958 | "show": false, 959 | "sort": "avg", 960 | "sortDesc": true, 961 | "total": false, 962 | "values": true 963 | }, 964 | "lines": true, 965 | "linewidth": 1, 966 | "links": [], 967 | "nullPointMode": "null", 968 | "options": { 969 | "dataLinks": [] 970 | }, 971 | "percentage": false, 972 | "pointradius": 5, 973 | "points": false, 974 | "renderer": "flot", 975 | "seriesOverrides": [], 976 | "spaceLength": 10, 977 | "stack": false, 978 | "steppedLine": false, 979 | "targets": [ 980 | { 981 | "expr": "perftest_consumed{instance=~\"$instance\"}", 982 | "format": "time_series", 983 | "intervalFactor": 1, 984 | "legendFormat": "{{instance}}", 985 | "refId": "A" 986 | } 987 | ], 988 | "thresholds": [], 989 | "timeFrom": null, 990 | "timeRegions": [], 991 | "timeShift": null, 992 | "title": "Messages consumed / s", 993 | "tooltip": { 994 | "shared": true, 995 | "sort": 2, 996 | "value_type": "individual" 997 | }, 998 | "transparent": true, 999 | "type": "graph", 1000 | "xaxis": { 1001 | "buckets": null, 1002 | "mode": "time", 1003 | "name": null, 1004 | "show": true, 1005 | "values": [] 1006 | }, 1007 | "yaxes": [ 1008 | { 1009 | "decimals": null, 1010 | "format": "short", 1011 | "label": "", 1012 | "logBase": 1, 1013 | "max": null, 1014 | "min": "0", 1015 | "show": true 1016 | }, 1017 | { 1018 | "format": "short", 1019 | "label": null, 1020 | "logBase": 1, 1021 | "max": null, 1022 | "min": null, 1023 | "show": true 1024 | } 1025 | ], 1026 | "yaxis": { 1027 | "align": false, 1028 | "alignLevel": null 1029 | } 1030 | }, 1031 | { 1032 | "columns": [ 1033 | { 1034 | "text": "Min", 1035 | "value": "min" 1036 | }, 1037 | { 1038 | "text": "Max", 1039 | "value": "max" 1040 | }, 1041 | { 1042 | "text": "Avg", 1043 | "value": "avg" 1044 | }, 1045 | { 1046 | "text": "Current", 1047 | "value": "current" 1048 | } 1049 | ], 1050 | "datasource": null, 1051 | "fontSize": "100%", 1052 | "gridPos": { 1053 | "h": 7, 1054 | "w": 6, 1055 | "x": 18, 1056 | "y": 16 1057 | }, 1058 | "id": 28, 1059 | "options": {}, 1060 | "pageSize": 5, 1061 | "pluginVersion": "6.4.1", 1062 | "showHeader": true, 1063 | "sort": { 1064 | "col": 4, 1065 | "desc": true 1066 | }, 1067 | "styles": [ 1068 | { 1069 | "alias": "Instance", 1070 | "colorMode": null, 1071 | "colors": [ 1072 | "rgba(245, 54, 54, 0.9)", 1073 | "rgba(237, 129, 40, 0.89)", 1074 | "rgba(50, 172, 45, 0.97)" 1075 | ], 1076 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1077 | "decimals": 0, 1078 | "mappingType": 1, 1079 | "pattern": "Metric", 1080 | "thresholds": [], 1081 | "type": "string", 1082 | "unit": "short" 1083 | }, 1084 | { 1085 | "alias": "", 1086 | "colorMode": null, 1087 | "colors": [ 1088 | "rgba(245, 54, 54, 0.9)", 1089 | "rgba(237, 129, 40, 0.89)", 1090 | "rgba(50, 172, 45, 0.97)" 1091 | ], 1092 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1093 | "decimals": 0, 1094 | "mappingType": 1, 1095 | "pattern": "/.*/", 1096 | "thresholds": [], 1097 | "type": "number", 1098 | "unit": "short" 1099 | } 1100 | ], 1101 | "targets": [ 1102 | { 1103 | "expr": "perftest_consumed", 1104 | "legendFormat": "{{instance}}", 1105 | "refId": "A" 1106 | } 1107 | ], 1108 | "timeFrom": null, 1109 | "timeShift": null, 1110 | "title": "Messages consumed / s", 1111 | "transform": "timeseries_aggregations", 1112 | "type": "table" 1113 | }, 1114 | { 1115 | "columns": [ 1116 | { 1117 | "text": "Min", 1118 | "value": "min" 1119 | }, 1120 | { 1121 | "text": "Max", 1122 | "value": "max" 1123 | }, 1124 | { 1125 | "text": "Avg", 1126 | "value": "avg" 1127 | }, 1128 | { 1129 | "text": "Current", 1130 | "value": "current" 1131 | } 1132 | ], 1133 | "datasource": null, 1134 | "fontSize": "100%", 1135 | "gridPos": { 1136 | "h": 7, 1137 | "w": 6, 1138 | "x": 0, 1139 | "y": 23 1140 | }, 1141 | "id": 30, 1142 | "options": {}, 1143 | "pageSize": 5, 1144 | "pluginVersion": "6.4.1", 1145 | "showHeader": true, 1146 | "sort": { 1147 | "col": 4, 1148 | "desc": true 1149 | }, 1150 | "styles": [ 1151 | { 1152 | "alias": "Instance", 1153 | "colorMode": null, 1154 | "colors": [ 1155 | "rgba(245, 54, 54, 0.9)", 1156 | "rgba(237, 129, 40, 0.89)", 1157 | "rgba(50, 172, 45, 0.97)" 1158 | ], 1159 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1160 | "decimals": 0, 1161 | "mappingType": 1, 1162 | "pattern": "Metric", 1163 | "thresholds": [], 1164 | "type": "string", 1165 | "unit": "short" 1166 | }, 1167 | { 1168 | "alias": "", 1169 | "colorMode": null, 1170 | "colors": [ 1171 | "rgba(245, 54, 54, 0.9)", 1172 | "rgba(237, 129, 40, 0.89)", 1173 | "rgba(50, 172, 45, 0.97)" 1174 | ], 1175 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1176 | "decimals": 0, 1177 | "mappingType": 1, 1178 | "pattern": "/.*/", 1179 | "thresholds": [], 1180 | "type": "number", 1181 | "unit": "short" 1182 | } 1183 | ], 1184 | "targets": [ 1185 | { 1186 | "expr": "perftest_confirmed", 1187 | "legendFormat": "{{instance}}", 1188 | "refId": "A" 1189 | } 1190 | ], 1191 | "timeFrom": null, 1192 | "timeShift": null, 1193 | "title": "Messages confirmed / s", 1194 | "transform": "timeseries_aggregations", 1195 | "type": "table" 1196 | }, 1197 | { 1198 | "aliasColors": {}, 1199 | "bars": false, 1200 | "dashLength": 10, 1201 | "dashes": false, 1202 | "datasource": null, 1203 | "fill": 1, 1204 | "fillGradient": 0, 1205 | "gridPos": { 1206 | "h": 7, 1207 | "w": 6, 1208 | "x": 6, 1209 | "y": 23 1210 | }, 1211 | "id": 29, 1212 | "legend": { 1213 | "alignAsTable": true, 1214 | "avg": true, 1215 | "current": true, 1216 | "max": true, 1217 | "min": true, 1218 | "rightSide": false, 1219 | "show": false, 1220 | "sort": "avg", 1221 | "sortDesc": true, 1222 | "total": false, 1223 | "values": true 1224 | }, 1225 | "lines": true, 1226 | "linewidth": 1, 1227 | "links": [], 1228 | "nullPointMode": "null", 1229 | "options": { 1230 | "dataLinks": [] 1231 | }, 1232 | "percentage": false, 1233 | "pointradius": 5, 1234 | "points": false, 1235 | "renderer": "flot", 1236 | "seriesOverrides": [], 1237 | "spaceLength": 10, 1238 | "stack": false, 1239 | "steppedLine": false, 1240 | "targets": [ 1241 | { 1242 | "expr": "perftest_confirmed{instance=~\"$instance\"}", 1243 | "format": "time_series", 1244 | "intervalFactor": 1, 1245 | "legendFormat": "{{instance}}", 1246 | "refId": "A" 1247 | } 1248 | ], 1249 | "thresholds": [], 1250 | "timeFrom": null, 1251 | "timeRegions": [], 1252 | "timeShift": null, 1253 | "title": "Messages confirmed / s", 1254 | "tooltip": { 1255 | "shared": true, 1256 | "sort": 2, 1257 | "value_type": "individual" 1258 | }, 1259 | "transparent": true, 1260 | "type": "graph", 1261 | "xaxis": { 1262 | "buckets": null, 1263 | "mode": "time", 1264 | "name": null, 1265 | "show": true, 1266 | "values": [] 1267 | }, 1268 | "yaxes": [ 1269 | { 1270 | "decimals": null, 1271 | "format": "short", 1272 | "label": "", 1273 | "logBase": 1, 1274 | "max": null, 1275 | "min": "0", 1276 | "show": true 1277 | }, 1278 | { 1279 | "format": "short", 1280 | "label": null, 1281 | "logBase": 1, 1282 | "max": null, 1283 | "min": null, 1284 | "show": true 1285 | } 1286 | ], 1287 | "yaxis": { 1288 | "align": false, 1289 | "alignLevel": null 1290 | } 1291 | }, 1292 | { 1293 | "aliasColors": {}, 1294 | "bars": false, 1295 | "dashLength": 10, 1296 | "dashes": false, 1297 | "datasource": null, 1298 | "fill": 1, 1299 | "fillGradient": 0, 1300 | "gridPos": { 1301 | "h": 7, 1302 | "w": 6, 1303 | "x": 12, 1304 | "y": 23 1305 | }, 1306 | "id": 40, 1307 | "legend": { 1308 | "alignAsTable": true, 1309 | "avg": true, 1310 | "current": true, 1311 | "max": true, 1312 | "min": true, 1313 | "rightSide": false, 1314 | "show": false, 1315 | "sort": "avg", 1316 | "sortDesc": true, 1317 | "total": false, 1318 | "values": true 1319 | }, 1320 | "lines": true, 1321 | "linewidth": 1, 1322 | "links": [], 1323 | "nullPointMode": "null", 1324 | "options": { 1325 | "dataLinks": [] 1326 | }, 1327 | "percentage": false, 1328 | "pointradius": 5, 1329 | "points": false, 1330 | "renderer": "flot", 1331 | "seriesOverrides": [], 1332 | "spaceLength": 10, 1333 | "stack": false, 1334 | "steppedLine": false, 1335 | "targets": [ 1336 | { 1337 | "expr": "perftest_nacked{instance=~\"$instance\"}", 1338 | "format": "time_series", 1339 | "intervalFactor": 1, 1340 | "legendFormat": "{{instance}}", 1341 | "refId": "A" 1342 | } 1343 | ], 1344 | "thresholds": [], 1345 | "timeFrom": null, 1346 | "timeRegions": [], 1347 | "timeShift": null, 1348 | "title": "Messages rejected / s", 1349 | "tooltip": { 1350 | "shared": true, 1351 | "sort": 2, 1352 | "value_type": "individual" 1353 | }, 1354 | "transparent": true, 1355 | "type": "graph", 1356 | "xaxis": { 1357 | "buckets": null, 1358 | "mode": "time", 1359 | "name": null, 1360 | "show": true, 1361 | "values": [] 1362 | }, 1363 | "yaxes": [ 1364 | { 1365 | "decimals": null, 1366 | "format": "short", 1367 | "label": "", 1368 | "logBase": 1, 1369 | "max": null, 1370 | "min": "0", 1371 | "show": true 1372 | }, 1373 | { 1374 | "format": "short", 1375 | "label": null, 1376 | "logBase": 1, 1377 | "max": null, 1378 | "min": null, 1379 | "show": true 1380 | } 1381 | ], 1382 | "yaxis": { 1383 | "align": false, 1384 | "alignLevel": null 1385 | } 1386 | }, 1387 | { 1388 | "columns": [ 1389 | { 1390 | "text": "Min", 1391 | "value": "min" 1392 | }, 1393 | { 1394 | "text": "Max", 1395 | "value": "max" 1396 | }, 1397 | { 1398 | "text": "Avg", 1399 | "value": "avg" 1400 | }, 1401 | { 1402 | "text": "Current", 1403 | "value": "current" 1404 | } 1405 | ], 1406 | "datasource": null, 1407 | "fontSize": "100%", 1408 | "gridPos": { 1409 | "h": 7, 1410 | "w": 6, 1411 | "x": 18, 1412 | "y": 23 1413 | }, 1414 | "id": 41, 1415 | "options": {}, 1416 | "pageSize": 5, 1417 | "pluginVersion": "6.4.1", 1418 | "showHeader": true, 1419 | "sort": { 1420 | "col": 4, 1421 | "desc": true 1422 | }, 1423 | "styles": [ 1424 | { 1425 | "alias": "Instance", 1426 | "colorMode": null, 1427 | "colors": [ 1428 | "rgba(245, 54, 54, 0.9)", 1429 | "rgba(237, 129, 40, 0.89)", 1430 | "rgba(50, 172, 45, 0.97)" 1431 | ], 1432 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1433 | "decimals": 0, 1434 | "mappingType": 1, 1435 | "pattern": "Metric", 1436 | "thresholds": [], 1437 | "type": "string", 1438 | "unit": "short" 1439 | }, 1440 | { 1441 | "alias": "", 1442 | "colorMode": null, 1443 | "colors": [ 1444 | "rgba(245, 54, 54, 0.9)", 1445 | "rgba(237, 129, 40, 0.89)", 1446 | "rgba(50, 172, 45, 0.97)" 1447 | ], 1448 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1449 | "decimals": 0, 1450 | "mappingType": 1, 1451 | "pattern": "/.*/", 1452 | "thresholds": [], 1453 | "type": "number", 1454 | "unit": "short" 1455 | } 1456 | ], 1457 | "targets": [ 1458 | { 1459 | "expr": "perftest_nacked", 1460 | "legendFormat": "{{instance}}", 1461 | "refId": "A" 1462 | } 1463 | ], 1464 | "timeFrom": null, 1465 | "timeShift": null, 1466 | "title": "Messages rejected / s", 1467 | "transform": "timeseries_aggregations", 1468 | "type": "table" 1469 | }, 1470 | { 1471 | "columns": [ 1472 | { 1473 | "text": "Min", 1474 | "value": "min" 1475 | }, 1476 | { 1477 | "text": "Max", 1478 | "value": "max" 1479 | }, 1480 | { 1481 | "text": "Avg", 1482 | "value": "avg" 1483 | }, 1484 | { 1485 | "text": "Current", 1486 | "value": "current" 1487 | } 1488 | ], 1489 | "datasource": null, 1490 | "fontSize": "100%", 1491 | "gridPos": { 1492 | "h": 7, 1493 | "w": 6, 1494 | "x": 0, 1495 | "y": 30 1496 | }, 1497 | "id": 32, 1498 | "options": {}, 1499 | "pageSize": 5, 1500 | "pluginVersion": "6.4.1", 1501 | "showHeader": true, 1502 | "sort": { 1503 | "col": 4, 1504 | "desc": true 1505 | }, 1506 | "styles": [ 1507 | { 1508 | "alias": "Instance", 1509 | "colorMode": null, 1510 | "colors": [ 1511 | "rgba(245, 54, 54, 0.9)", 1512 | "rgba(237, 129, 40, 0.89)", 1513 | "rgba(50, 172, 45, 0.97)" 1514 | ], 1515 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1516 | "decimals": 0, 1517 | "mappingType": 1, 1518 | "pattern": "Metric", 1519 | "thresholds": [], 1520 | "type": "string", 1521 | "unit": "short" 1522 | }, 1523 | { 1524 | "alias": "", 1525 | "colorMode": null, 1526 | "colors": [ 1527 | "rgba(245, 54, 54, 0.9)", 1528 | "rgba(237, 129, 40, 0.89)", 1529 | "rgba(50, 172, 45, 0.97)" 1530 | ], 1531 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 1532 | "decimals": 0, 1533 | "mappingType": 1, 1534 | "pattern": "/.*/", 1535 | "thresholds": [], 1536 | "type": "number", 1537 | "unit": "short" 1538 | } 1539 | ], 1540 | "targets": [ 1541 | { 1542 | "expr": "perftest_returned", 1543 | "legendFormat": "{{instance}}", 1544 | "refId": "A" 1545 | } 1546 | ], 1547 | "timeFrom": null, 1548 | "timeShift": null, 1549 | "title": "Messages returned / s", 1550 | "transform": "timeseries_aggregations", 1551 | "type": "table" 1552 | }, 1553 | { 1554 | "aliasColors": {}, 1555 | "bars": false, 1556 | "dashLength": 10, 1557 | "dashes": false, 1558 | "datasource": null, 1559 | "fill": 1, 1560 | "fillGradient": 0, 1561 | "gridPos": { 1562 | "h": 7, 1563 | "w": 6, 1564 | "x": 6, 1565 | "y": 30 1566 | }, 1567 | "id": 31, 1568 | "legend": { 1569 | "alignAsTable": true, 1570 | "avg": true, 1571 | "current": true, 1572 | "max": true, 1573 | "min": true, 1574 | "rightSide": false, 1575 | "show": false, 1576 | "sort": "avg", 1577 | "sortDesc": true, 1578 | "total": false, 1579 | "values": true 1580 | }, 1581 | "lines": true, 1582 | "linewidth": 1, 1583 | "links": [], 1584 | "nullPointMode": "null", 1585 | "options": { 1586 | "dataLinks": [] 1587 | }, 1588 | "percentage": false, 1589 | "pointradius": 5, 1590 | "points": false, 1591 | "renderer": "flot", 1592 | "seriesOverrides": [], 1593 | "spaceLength": 10, 1594 | "stack": false, 1595 | "steppedLine": false, 1596 | "targets": [ 1597 | { 1598 | "expr": "perftest_returned{instance=~\"$instance\"}", 1599 | "format": "time_series", 1600 | "intervalFactor": 1, 1601 | "legendFormat": "{{instance}}", 1602 | "refId": "A" 1603 | } 1604 | ], 1605 | "thresholds": [], 1606 | "timeFrom": null, 1607 | "timeRegions": [], 1608 | "timeShift": null, 1609 | "title": "Messages returned / s", 1610 | "tooltip": { 1611 | "shared": true, 1612 | "sort": 2, 1613 | "value_type": "individual" 1614 | }, 1615 | "transparent": true, 1616 | "type": "graph", 1617 | "xaxis": { 1618 | "buckets": null, 1619 | "mode": "time", 1620 | "name": null, 1621 | "show": true, 1622 | "values": [] 1623 | }, 1624 | "yaxes": [ 1625 | { 1626 | "decimals": null, 1627 | "format": "short", 1628 | "label": "", 1629 | "logBase": 1, 1630 | "max": null, 1631 | "min": "0", 1632 | "show": true 1633 | }, 1634 | { 1635 | "format": "short", 1636 | "label": null, 1637 | "logBase": 1, 1638 | "max": null, 1639 | "min": null, 1640 | "show": true 1641 | } 1642 | ], 1643 | "yaxis": { 1644 | "align": false, 1645 | "alignLevel": null 1646 | } 1647 | } 1648 | ], 1649 | "refresh": "15s", 1650 | "schemaVersion": 20, 1651 | "style": "dark", 1652 | "tags": [ 1653 | "rabbitmq-perf-test" 1654 | ], 1655 | "templating": { 1656 | "list": [ 1657 | { 1658 | "current": { 1659 | "selected": false, 1660 | "text": "default", 1661 | "value": "default" 1662 | }, 1663 | "hide": 2, 1664 | "includeAll": false, 1665 | "label": "datasource", 1666 | "multi": false, 1667 | "name": "DS_PROMETHEUS", 1668 | "options": [], 1669 | "query": "prometheus", 1670 | "refresh": 1, 1671 | "regex": "", 1672 | "skipUrlSync": false, 1673 | "type": "datasource" 1674 | }, 1675 | { 1676 | "allValue": null, 1677 | "current": { 1678 | "text": "All", 1679 | "value": [ 1680 | "$__all" 1681 | ] 1682 | }, 1683 | "datasource": null, 1684 | "definition": "label_values(perftest_published, instance)", 1685 | "hide": 0, 1686 | "includeAll": true, 1687 | "label": "PerfTest Instance", 1688 | "multi": true, 1689 | "name": "instance", 1690 | "options": [], 1691 | "query": "label_values(perftest_published, instance)", 1692 | "refresh": 2, 1693 | "regex": "", 1694 | "skipUrlSync": false, 1695 | "sort": 1, 1696 | "tagValuesQuery": "", 1697 | "tags": [], 1698 | "tagsQuery": "", 1699 | "type": "query", 1700 | "useTags": false 1701 | }, 1702 | { 1703 | "allValue": null, 1704 | "current": { 1705 | "text": "0.99", 1706 | "value": "0.99" 1707 | }, 1708 | "datasource": null, 1709 | "definition": "label_values(perftest_latency_seconds, quantile)", 1710 | "hide": 0, 1711 | "includeAll": false, 1712 | "label": "Percentile", 1713 | "multi": false, 1714 | "name": "percentile", 1715 | "options": [], 1716 | "query": "label_values(perftest_latency_seconds, quantile)", 1717 | "refresh": 2, 1718 | "regex": "", 1719 | "skipUrlSync": false, 1720 | "sort": 4, 1721 | "tagValuesQuery": "", 1722 | "tags": [], 1723 | "tagsQuery": "", 1724 | "type": "query", 1725 | "useTags": false 1726 | } 1727 | ] 1728 | }, 1729 | "time": { 1730 | "from": "now-15m", 1731 | "to": "now" 1732 | }, 1733 | "timepicker": { 1734 | "refresh_intervals": [ 1735 | "15s", 1736 | "30s", 1737 | "1m", 1738 | "5m", 1739 | "10m" 1740 | ], 1741 | "time_options": [ 1742 | "5m", 1743 | "15m", 1744 | "1h", 1745 | "6h", 1746 | "12h", 1747 | "24h", 1748 | "2d", 1749 | "7d", 1750 | "30d" 1751 | ] 1752 | }, 1753 | "timezone": "", 1754 | "title": "RabbitMQ-PerfTest", 1755 | "uid": "pK9UatSiz", 1756 | "version": 20210322 1757 | } 1758 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/dashboards/RabbitMQ-Quorum-Queues-Raft.json: -------------------------------------------------------------------------------- 1 | { 2 | "__requires": [ 3 | { 4 | "type": "grafana", 5 | "id": "grafana", 6 | "name": "Grafana", 7 | "version": "7.0.0" 8 | }, 9 | { 10 | "type": "datasource", 11 | "id": "prometheus", 12 | "name": "Prometheus", 13 | "version": "2.0.0" 14 | }, 15 | { 16 | "type": "panel", 17 | "id": "graph", 18 | "name": "Graph", 19 | "version": "" 20 | }, 21 | { 22 | "type": "panel", 23 | "id": "heatmap", 24 | "name": "Heatmap", 25 | "version": "" 26 | } 27 | ], 28 | "annotations": { 29 | "list": [ 30 | { 31 | "builtIn": 1, 32 | "datasource": "-- Grafana --", 33 | "enable": true, 34 | "hide": true, 35 | "iconColor": "rgba(0, 211, 255, 1)", 36 | "name": "Annotations & Alerts", 37 | "type": "dashboard" 38 | } 39 | ] 40 | }, 41 | "description": "Raft state for all Quorum Queues running in a RabbitMQ cluster", 42 | "editable": true, 43 | "gnetId": null, 44 | "graphTooltip": 1, 45 | "id": null, 46 | "iteration": 1581011566961, 47 | "links": [ 48 | { 49 | "icon": "doc", 50 | "tags": [], 51 | "targetBlank": true, 52 | "title": "Quorum Queues Documentation", 53 | "tooltip": "", 54 | "type": "link", 55 | "url": "https://www.rabbitmq.com/quorum-queues.html" 56 | } 57 | ], 58 | "panels": [ 59 | { 60 | "aliasColors": {}, 61 | "bars": false, 62 | "cacheTimeout": null, 63 | "dashLength": 10, 64 | "dashes": false, 65 | "datasource": null, 66 | "description": "##### Rate of Raft log operations committed\n\nThis includes all queue operations, including publishes & consumer acknowledgements.\n\nThis tracks the progress of the Raft commit index on all members, including followers.\n\nIf a RabbitMQ node does not run a Raft member, it will not report any entries committed.", 67 | "fill": 0, 68 | "fillGradient": 0, 69 | "gridPos": { 70 | "h": 9, 71 | "w": 12, 72 | "x": 0, 73 | "y": 0 74 | }, 75 | "hiddenSeries": false, 76 | "id": 64, 77 | "legend": { 78 | "alignAsTable": true, 79 | "avg": false, 80 | "current": true, 81 | "max": true, 82 | "min": false, 83 | "show": true, 84 | "sort": "current", 85 | "sortDesc": true, 86 | "total": true, 87 | "values": true 88 | }, 89 | "lines": true, 90 | "linewidth": 1, 91 | "links": [], 92 | "nullPointMode": "null", 93 | "options": { 94 | "dataLinks": [] 95 | }, 96 | "percentage": false, 97 | "pointradius": 2, 98 | "points": false, 99 | "renderer": "flot", 100 | "seriesOverrides": [ 101 | { 102 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?0(\\b|\\.)/", 103 | "color": "#56A64B" 104 | }, 105 | { 106 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?1(\\b|\\.)/", 107 | "color": "#F2CC0C" 108 | }, 109 | { 110 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?2(\\b|\\.)/", 111 | "color": "#3274D9" 112 | }, 113 | { 114 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?3(\\b|\\.)/", 115 | "color": "#A352CC" 116 | }, 117 | { 118 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?4(\\b|\\.)/", 119 | "color": "#FF780A" 120 | }, 121 | { 122 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?5(\\b|\\.)/", 123 | "color": "#96D98D" 124 | }, 125 | { 126 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?6(\\b|\\.)/", 127 | "color": "#FFEE52" 128 | }, 129 | { 130 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?7(\\b|\\.)/", 131 | "color": "#8AB8FF" 132 | }, 133 | { 134 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?8(\\b|\\.)/", 135 | "color": "#CA95E5" 136 | }, 137 | { 138 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?9(\\b|\\.)/", 139 | "color": "#FFB357" 140 | } 141 | ], 142 | "spaceLength": 10, 143 | "stack": false, 144 | "steppedLine": false, 145 | "targets": [ 146 | { 147 | "expr": "sum(rate(rabbitmq_raft_log_commit_index[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"}) by(rabbitmq_node)", 148 | "format": "time_series", 149 | "instant": false, 150 | "intervalFactor": 1, 151 | "legendFormat": "{{rabbitmq_node}}", 152 | "refId": "A" 153 | } 154 | ], 155 | "thresholds": [], 156 | "timeFrom": null, 157 | "timeRegions": [], 158 | "timeShift": null, 159 | "title": "Log entries committed / s", 160 | "tooltip": { 161 | "shared": true, 162 | "sort": 2, 163 | "value_type": "individual" 164 | }, 165 | "type": "graph", 166 | "xaxis": { 167 | "buckets": null, 168 | "mode": "time", 169 | "name": null, 170 | "show": true, 171 | "values": [] 172 | }, 173 | "yaxes": [ 174 | { 175 | "decimals": null, 176 | "format": "short", 177 | "label": null, 178 | "logBase": 1, 179 | "max": null, 180 | "min": "0", 181 | "show": true 182 | }, 183 | { 184 | "format": "short", 185 | "label": null, 186 | "logBase": 1, 187 | "max": null, 188 | "min": null, 189 | "show": true 190 | } 191 | ], 192 | "yaxis": { 193 | "align": false, 194 | "alignLevel": null 195 | } 196 | }, 197 | { 198 | "cacheTimeout": null, 199 | "cards": { 200 | "cardPadding": null, 201 | "cardRound": null 202 | }, 203 | "color": { 204 | "cardColor": "rgb(255, 255, 255)", 205 | "colorScale": "sqrt", 206 | "colorScheme": "interpolateCool", 207 | "exponent": 0.4, 208 | "mode": "opacity" 209 | }, 210 | "dataFormat": "timeseries", 211 | "datasource": null, 212 | "description": "##### Time for a log entry to be committed\n\nThis is an indicator of Raft operational overhead. Values will increase with increased load as the system trades latency for throughput.\n\nThis metric samples the time it takes for a log entry to be written to a Raft log and that entry being committed.\n\nBecause quorum queues fsync all operations to disk before committing them, they are not suitable for low-latency workloads.", 213 | "gridPos": { 214 | "h": 9, 215 | "w": 12, 216 | "x": 12, 217 | "y": 0 218 | }, 219 | "heatmap": {}, 220 | "hideZeroBuckets": false, 221 | "highlightCards": true, 222 | "id": 65, 223 | "legend": { 224 | "show": true 225 | }, 226 | "links": [], 227 | "options": {}, 228 | "reverseYBuckets": false, 229 | "targets": [ 230 | { 231 | "expr": "rabbitmq_raft_entry_commit_latency_seconds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"}", 232 | "format": "time_series", 233 | "instant": false, 234 | "intervalFactor": 1, 235 | "legendFormat": "", 236 | "refId": "A" 237 | } 238 | ], 239 | "timeFrom": null, 240 | "timeShift": null, 241 | "title": "Log entry commit latency", 242 | "tooltip": { 243 | "show": true, 244 | "showHistogram": true 245 | }, 246 | "type": "heatmap", 247 | "xAxis": { 248 | "show": true 249 | }, 250 | "xBucketNumber": null, 251 | "xBucketSize": null, 252 | "yAxis": { 253 | "decimals": null, 254 | "format": "s", 255 | "logBase": 1, 256 | "max": null, 257 | "min": "0", 258 | "show": true, 259 | "splitFactor": null 260 | }, 261 | "yBucketBound": "lower", 262 | "yBucketNumber": null, 263 | "yBucketSize": null 264 | }, 265 | { 266 | "aliasColors": {}, 267 | "bars": false, 268 | "cacheTimeout": null, 269 | "dashLength": 10, 270 | "dashes": false, 271 | "datasource": null, 272 | "description": "##### Pending Raft log entries\n\nTracks the number of Raft log entries that have been written but not yet committed.\n\nHigh & growing values may be indicative of a quorum of members not being available so that a queue can make progress.", 273 | "fill": 0, 274 | "fillGradient": 0, 275 | "gridPos": { 276 | "h": 9, 277 | "w": 12, 278 | "x": 0, 279 | "y": 9 280 | }, 281 | "hiddenSeries": false, 282 | "id": 62, 283 | "legend": { 284 | "alignAsTable": true, 285 | "avg": false, 286 | "current": true, 287 | "hideEmpty": false, 288 | "hideZero": true, 289 | "max": true, 290 | "min": false, 291 | "rightSide": false, 292 | "show": true, 293 | "sort": "total", 294 | "sortDesc": true, 295 | "total": true, 296 | "values": true 297 | }, 298 | "lines": true, 299 | "linewidth": 1, 300 | "links": [], 301 | "nullPointMode": "null", 302 | "options": { 303 | "dataLinks": [] 304 | }, 305 | "percentage": false, 306 | "pointradius": 2, 307 | "points": false, 308 | "renderer": "flot", 309 | "seriesOverrides": [ 310 | { 311 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?0(\\b|\\.)/", 312 | "color": "#56A64B" 313 | }, 314 | { 315 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?1(\\b|\\.)/", 316 | "color": "#F2CC0C" 317 | }, 318 | { 319 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?2(\\b|\\.)/", 320 | "color": "#3274D9" 321 | }, 322 | { 323 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?3(\\b|\\.)/", 324 | "color": "#A352CC" 325 | }, 326 | { 327 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?4(\\b|\\.)/", 328 | "color": "#FF780A" 329 | }, 330 | { 331 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?5(\\b|\\.)/", 332 | "color": "#96D98D" 333 | }, 334 | { 335 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?6(\\b|\\.)/", 336 | "color": "#FFEE52" 337 | }, 338 | { 339 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?7(\\b|\\.)/", 340 | "color": "#8AB8FF" 341 | }, 342 | { 343 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?8(\\b|\\.)/", 344 | "color": "#CA95E5" 345 | }, 346 | { 347 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?9(\\b|\\.)/", 348 | "color": "#FFB357" 349 | } 350 | ], 351 | "spaceLength": 10, 352 | "stack": false, 353 | "steppedLine": false, 354 | "targets": [ 355 | { 356 | "expr": "sum(\n (rabbitmq_raft_log_last_written_index * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"}) -\n (rabbitmq_raft_log_commit_index * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"})\n) by(rabbitmq_node)", 357 | "format": "time_series", 358 | "instant": false, 359 | "intervalFactor": 1, 360 | "legendFormat": "{{rabbitmq_node}}", 361 | "refId": "A" 362 | } 363 | ], 364 | "thresholds": [], 365 | "timeFrom": null, 366 | "timeRegions": [], 367 | "timeShift": null, 368 | "title": "Uncommitted log entries", 369 | "tooltip": { 370 | "shared": true, 371 | "sort": 2, 372 | "value_type": "individual" 373 | }, 374 | "type": "graph", 375 | "xaxis": { 376 | "buckets": null, 377 | "mode": "time", 378 | "name": null, 379 | "show": true, 380 | "values": [] 381 | }, 382 | "yaxes": [ 383 | { 384 | "decimals": null, 385 | "format": "short", 386 | "label": "", 387 | "logBase": 1, 388 | "max": null, 389 | "min": null, 390 | "show": true 391 | }, 392 | { 393 | "format": "short", 394 | "label": null, 395 | "logBase": 1, 396 | "max": null, 397 | "min": null, 398 | "show": true 399 | } 400 | ], 401 | "yaxis": { 402 | "align": false, 403 | "alignLevel": null 404 | } 405 | }, 406 | { 407 | "aliasColors": {}, 408 | "bars": false, 409 | "cacheTimeout": null, 410 | "dashLength": 10, 411 | "dashes": false, 412 | "datasource": null, 413 | "description": "##### Rate of Raft leader elections\n\nTracks the increments of the Raft term.\n\nSustained non-zero rates are indicative of network and/or availability issues, or queue churn. The other reason may be quorum queue declarations.\n\nValues above 0 are normal, some leader elections are expected. Sustained high values may be of concern.", 414 | "fill": 0, 415 | "fillGradient": 0, 416 | "gridPos": { 417 | "h": 9, 418 | "w": 12, 419 | "x": 12, 420 | "y": 9 421 | }, 422 | "hiddenSeries": false, 423 | "id": 63, 424 | "legend": { 425 | "alignAsTable": true, 426 | "avg": false, 427 | "current": true, 428 | "max": true, 429 | "min": false, 430 | "rightSide": false, 431 | "show": true, 432 | "sort": "total", 433 | "sortDesc": true, 434 | "total": true, 435 | "values": true 436 | }, 437 | "lines": true, 438 | "linewidth": 1, 439 | "links": [], 440 | "nullPointMode": "null", 441 | "options": { 442 | "dataLinks": [] 443 | }, 444 | "percentage": false, 445 | "pointradius": 2, 446 | "points": false, 447 | "renderer": "flot", 448 | "seriesOverrides": [ 449 | { 450 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?0(\\b|\\.)/", 451 | "color": "#56A64B" 452 | }, 453 | { 454 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?1(\\b|\\.)/", 455 | "color": "#F2CC0C" 456 | }, 457 | { 458 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?2(\\b|\\.)/", 459 | "color": "#3274D9" 460 | }, 461 | { 462 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?3(\\b|\\.)/", 463 | "color": "#A352CC" 464 | }, 465 | { 466 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?4(\\b|\\.)/", 467 | "color": "#FF780A" 468 | }, 469 | { 470 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?5(\\b|\\.)/", 471 | "color": "#96D98D" 472 | }, 473 | { 474 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?6(\\b|\\.)/", 475 | "color": "#FFEE52" 476 | }, 477 | { 478 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?7(\\b|\\.)/", 479 | "color": "#8AB8FF" 480 | }, 481 | { 482 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?8(\\b|\\.)/", 483 | "color": "#CA95E5" 484 | }, 485 | { 486 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?9(\\b|\\.)/", 487 | "color": "#FFB357" 488 | } 489 | ], 490 | "spaceLength": 10, 491 | "stack": false, 492 | "steppedLine": false, 493 | "targets": [ 494 | { 495 | "expr": "sum(rate(rabbitmq_raft_term_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"}) by(rabbitmq_node)", 496 | "format": "time_series", 497 | "instant": false, 498 | "intervalFactor": 1, 499 | "legendFormat": "{{rabbitmq_node}}", 500 | "refId": "A" 501 | } 502 | ], 503 | "thresholds": [ 504 | { 505 | "colorMode": "warning", 506 | "fill": true, 507 | "line": true, 508 | "op": "gt", 509 | "value": 3, 510 | "yaxis": "left" 511 | } 512 | ], 513 | "timeFrom": null, 514 | "timeRegions": [], 515 | "timeShift": null, 516 | "title": "Leader elections / s", 517 | "tooltip": { 518 | "shared": true, 519 | "sort": 2, 520 | "value_type": "individual" 521 | }, 522 | "type": "graph", 523 | "xaxis": { 524 | "buckets": null, 525 | "mode": "time", 526 | "name": null, 527 | "show": true, 528 | "values": [] 529 | }, 530 | "yaxes": [ 531 | { 532 | "decimals": null, 533 | "format": "short", 534 | "label": "", 535 | "logBase": 1, 536 | "max": null, 537 | "min": "0", 538 | "show": true 539 | }, 540 | { 541 | "format": "short", 542 | "label": null, 543 | "logBase": 1, 544 | "max": null, 545 | "min": null, 546 | "show": true 547 | } 548 | ], 549 | "yaxis": { 550 | "align": false, 551 | "alignLevel": null 552 | } 553 | }, 554 | { 555 | "aliasColors": {}, 556 | "bars": false, 557 | "cacheTimeout": null, 558 | "dashLength": 10, 559 | "dashes": false, 560 | "datasource": null, 561 | "description": "##### Number of entries in the Raft log\n\nTracks the number of Raft log entries since the last snapshot.\n\nLarge values can either be indicative of large quorum queue backlogs or availability problems. If the uncommitted entries metric is large as well, there is a genuine availability problem in the system.", 562 | "fill": 0, 563 | "fillGradient": 0, 564 | "gridPos": { 565 | "h": 13, 566 | "w": 24, 567 | "x": 0, 568 | "y": 18 569 | }, 570 | "hiddenSeries": false, 571 | "id": 18, 572 | "legend": { 573 | "alignAsTable": true, 574 | "avg": false, 575 | "current": true, 576 | "max": true, 577 | "min": false, 578 | "rightSide": false, 579 | "show": true, 580 | "sort": "current", 581 | "sortDesc": true, 582 | "total": true, 583 | "values": true 584 | }, 585 | "lines": true, 586 | "linewidth": 1, 587 | "links": [], 588 | "nullPointMode": "null as zero", 589 | "options": { 590 | "dataLinks": [] 591 | }, 592 | "percentage": false, 593 | "pointradius": 2, 594 | "points": false, 595 | "renderer": "flot", 596 | "seriesOverrides": [ 597 | { 598 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?0(\\b|\\.)/", 599 | "color": "#56A64B" 600 | }, 601 | { 602 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?1(\\b|\\.)/", 603 | "color": "#F2CC0C" 604 | }, 605 | { 606 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?2(\\b|\\.)/", 607 | "color": "#3274D9" 608 | }, 609 | { 610 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?3(\\b|\\.)/", 611 | "color": "#A352CC" 612 | }, 613 | { 614 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?4(\\b|\\.)/", 615 | "color": "#FF780A" 616 | }, 617 | { 618 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?5(\\b|\\.)/", 619 | "color": "#96D98D" 620 | }, 621 | { 622 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?6(\\b|\\.)/", 623 | "color": "#FFEE52" 624 | }, 625 | { 626 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?7(\\b|\\.)/", 627 | "color": "#8AB8FF" 628 | }, 629 | { 630 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?8(\\b|\\.)/", 631 | "color": "#CA95E5" 632 | }, 633 | { 634 | "alias": "/^rabbit@[a-zA-Z\\.\\-]*?9(\\b|\\.)/", 635 | "color": "#FFB357" 636 | } 637 | ], 638 | "spaceLength": 10, 639 | "stack": false, 640 | "steppedLine": false, 641 | "targets": [ 642 | { 643 | "expr": "sum(\n (rabbitmq_raft_log_last_written_index * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"}) - \n (rabbitmq_raft_log_snapshot_index * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\", namespace=\"$namespace\"})\n) by(queue, rabbitmq_node) > 5000", 644 | "hide": false, 645 | "legendFormat": "{{rabbitmq_node}} {{queue}}", 646 | "refId": "A" 647 | } 648 | ], 649 | "thresholds": [], 650 | "timeFrom": null, 651 | "timeRegions": [], 652 | "timeShift": null, 653 | "title": "Raft members with >5k entries in the log", 654 | "tooltip": { 655 | "shared": true, 656 | "sort": 2, 657 | "value_type": "individual" 658 | }, 659 | "type": "graph", 660 | "xaxis": { 661 | "buckets": null, 662 | "mode": "time", 663 | "name": null, 664 | "show": true, 665 | "values": [] 666 | }, 667 | "yaxes": [ 668 | { 669 | "decimals": null, 670 | "format": "short", 671 | "label": "", 672 | "logBase": 1, 673 | "max": null, 674 | "min": "0", 675 | "show": true 676 | }, 677 | { 678 | "format": "short", 679 | "label": null, 680 | "logBase": 1, 681 | "max": null, 682 | "min": null, 683 | "show": true 684 | } 685 | ], 686 | "yaxis": { 687 | "align": false, 688 | "alignLevel": null 689 | } 690 | } 691 | ], 692 | "refresh": "15s", 693 | "schemaVersion": 21, 694 | "style": "dark", 695 | "tags": [ 696 | "rabbitmq-prometheus" 697 | ], 698 | "templating": { 699 | "list": [ 700 | { 701 | "current": { 702 | "selected": false, 703 | "text": "default", 704 | "value": "default" 705 | }, 706 | "hide": 2, 707 | "includeAll": false, 708 | "label": "datasource", 709 | "multi": false, 710 | "name": "DS_PROMETHEUS", 711 | "options": [], 712 | "query": "prometheus", 713 | "refresh": 1, 714 | "regex": "", 715 | "skipUrlSync": false, 716 | "type": "datasource" 717 | }, 718 | { 719 | "allValue": null, 720 | "current": {}, 721 | "datasource": null, 722 | "definition": "label_values(rabbitmq_identity_info, namespace)", 723 | "hide": 0, 724 | "includeAll": false, 725 | "label": "Namespace", 726 | "multi": false, 727 | "name": "namespace", 728 | "options": [], 729 | "query": "label_values(rabbitmq_identity_info, namespace)", 730 | "refresh": 2, 731 | "regex": "", 732 | "skipUrlSync": false, 733 | "sort": 1, 734 | "tagValuesQuery": "", 735 | "tags": [], 736 | "tagsQuery": "", 737 | "type": "query", 738 | "useTags": false 739 | }, 740 | { 741 | "allValue": null, 742 | "current": { 743 | "text": "", 744 | "value": "" 745 | }, 746 | "datasource": null, 747 | "definition": "label_values(rabbitmq_identity_info{namespace=\"$namespace\"}, rabbitmq_cluster)", 748 | "hide": 0, 749 | "includeAll": false, 750 | "label": "RabbitMQ Cluster", 751 | "multi": false, 752 | "name": "rabbitmq_cluster", 753 | "options": [], 754 | "query": "label_values(rabbitmq_identity_info{namespace=\"$namespace\"}, rabbitmq_cluster)", 755 | "refresh": 2, 756 | "regex": "", 757 | "skipUrlSync": false, 758 | "sort": 0, 759 | "tagValuesQuery": "", 760 | "tags": [], 761 | "tagsQuery": "", 762 | "type": "query", 763 | "useTags": false 764 | } 765 | ] 766 | }, 767 | "time": { 768 | "from": "now-15m", 769 | "to": "now" 770 | }, 771 | "timepicker": { 772 | "refresh_intervals": [ 773 | "15s", 774 | "30s", 775 | "1m", 776 | "5m", 777 | "10m" 778 | ], 779 | "time_options": [ 780 | "5m", 781 | "15m", 782 | "1h", 783 | "6h", 784 | "12h", 785 | "24h", 786 | "2d", 787 | "7d", 788 | "30d" 789 | ] 790 | }, 791 | "timezone": "", 792 | "title": "RabbitMQ-Quorum-Queues-Raft", 793 | "uid": "f1Mee9nZz", 794 | "version": 20210322 795 | } 796 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/dashboards/rabbitmq-exporter_vs_rabbitmq-prometheus.json: -------------------------------------------------------------------------------- 1 | { 2 | "__requires": [ 3 | { 4 | "type": "grafana", 5 | "id": "grafana", 6 | "name": "Grafana", 7 | "version": "7.0.0" 8 | }, 9 | { 10 | "type": "datasource", 11 | "id": "prometheus", 12 | "name": "prometheus", 13 | "version": "2.0.0" 14 | }, 15 | { 16 | "type": "panel", 17 | "id": "graph", 18 | "name": "Graph", 19 | "version": "" 20 | } 21 | ], 22 | "annotations": { 23 | "list": [ 24 | { 25 | "builtIn": 1, 26 | "datasource": "-- Grafana --", 27 | "enable": true, 28 | "hide": true, 29 | "iconColor": "rgba(0, 211, 255, 1)", 30 | "name": "Annotations & Alerts", 31 | "type": "dashboard" 32 | } 33 | ] 34 | }, 35 | "description": "rabbitmq-exporter vs rabbitmq-prometheus", 36 | "editable": true, 37 | "gnetId": null, 38 | "graphTooltip": 1, 39 | "links": [], 40 | "panels": [ 41 | { 42 | "columns": [ 43 | { 44 | "text": "Min", 45 | "value": "min" 46 | }, 47 | { 48 | "text": "Max", 49 | "value": "max" 50 | }, 51 | { 52 | "text": "Avg", 53 | "value": "avg" 54 | }, 55 | { 56 | "text": "Current", 57 | "value": "current" 58 | } 59 | ], 60 | "datasource": null, 61 | "fontSize": "100%", 62 | "gridPos": { 63 | "h": 10, 64 | "w": 8, 65 | "x": 0, 66 | "y": 0 67 | }, 68 | "id": 5, 69 | "options": {}, 70 | "pageSize": null, 71 | "pluginVersion": "6.4.1", 72 | "scroll": true, 73 | "showHeader": true, 74 | "sort": { 75 | "col": 3, 76 | "desc": true 77 | }, 78 | "styles": [ 79 | { 80 | "alias": "", 81 | "colorMode": null, 82 | "colors": [ 83 | "rgba(245, 54, 54, 0.9)", 84 | "rgba(237, 129, 40, 0.89)", 85 | "rgba(50, 172, 45, 0.97)" 86 | ], 87 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 88 | "decimals": 0, 89 | "mappingType": 1, 90 | "pattern": "Metric", 91 | "thresholds": [], 92 | "type": "string", 93 | "unit": "short" 94 | }, 95 | { 96 | "alias": "", 97 | "colorMode": null, 98 | "colors": [ 99 | "rgba(245, 54, 54, 0.9)", 100 | "rgba(237, 129, 40, 0.89)", 101 | "rgba(50, 172, 45, 0.97)" 102 | ], 103 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 104 | "decimals": 1, 105 | "mappingType": 1, 106 | "pattern": "/.*/", 107 | "thresholds": [], 108 | "type": "number", 109 | "unit": "s" 110 | } 111 | ], 112 | "targets": [ 113 | { 114 | "expr": "scrape_duration_seconds{job=\"rabbitmq-exporter\"}", 115 | "legendFormat": "{{job}}", 116 | "refId": "A" 117 | }, 118 | { 119 | "expr": "scrape_duration_seconds{job=\"rabbitmq-server\", instance=~\".*dist-tls.*\"}", 120 | "legendFormat": "{{instance}}", 121 | "refId": "B" 122 | }, 123 | { 124 | "expr": "scrape_duration_seconds{job=\"rabbitmq-prometheus\"}", 125 | "legendFormat": "{{deployment}} {{instance}}", 126 | "refId": "C" 127 | } 128 | ], 129 | "timeFrom": null, 130 | "timeShift": null, 131 | "title": "Prometheus target scrape duration", 132 | "transform": "timeseries_aggregations", 133 | "type": "table" 134 | }, 135 | { 136 | "aliasColors": {}, 137 | "bars": false, 138 | "dashLength": 10, 139 | "dashes": false, 140 | "datasource": null, 141 | "description": "", 142 | "fill": 1, 143 | "fillGradient": 0, 144 | "gridPos": { 145 | "h": 10, 146 | "w": 16, 147 | "x": 8, 148 | "y": 0 149 | }, 150 | "id": 3, 151 | "legend": { 152 | "avg": false, 153 | "current": false, 154 | "max": false, 155 | "min": false, 156 | "show": true, 157 | "total": false, 158 | "values": false 159 | }, 160 | "lines": true, 161 | "linewidth": 1, 162 | "nullPointMode": "null", 163 | "options": { 164 | "dataLinks": [] 165 | }, 166 | "percentage": false, 167 | "pointradius": 2, 168 | "points": false, 169 | "renderer": "flot", 170 | "seriesOverrides": [], 171 | "spaceLength": 10, 172 | "stack": false, 173 | "steppedLine": false, 174 | "targets": [ 175 | { 176 | "expr": "scrape_duration_seconds{job=\"rabbitmq-exporter\"}", 177 | "legendFormat": "{{job}}", 178 | "refId": "A" 179 | }, 180 | { 181 | "expr": "scrape_duration_seconds{job=\"rabbitmq-server\", instance=~\".*dist-tls.*\"}", 182 | "legendFormat": "{{instance}}", 183 | "refId": "B" 184 | }, 185 | { 186 | "expr": "scrape_duration_seconds{job=\"rabbitmq-prometheus\"}", 187 | "legendFormat": "{{deployment}} {{instance}}", 188 | "refId": "C" 189 | } 190 | ], 191 | "thresholds": [ 192 | { 193 | "colorMode": "warning", 194 | "fill": true, 195 | "line": true, 196 | "op": "gt", 197 | "value": 10, 198 | "yaxis": "left" 199 | }, 200 | { 201 | "colorMode": "critical", 202 | "fill": true, 203 | "line": true, 204 | "op": "gt", 205 | "value": 59, 206 | "yaxis": "left" 207 | } 208 | ], 209 | "timeFrom": null, 210 | "timeRegions": [], 211 | "timeShift": null, 212 | "title": "Prometheus target scrape duration", 213 | "tooltip": { 214 | "shared": true, 215 | "sort": 2, 216 | "value_type": "individual" 217 | }, 218 | "type": "graph", 219 | "xaxis": { 220 | "buckets": null, 221 | "mode": "time", 222 | "name": null, 223 | "show": true, 224 | "values": [] 225 | }, 226 | "yaxes": [ 227 | { 228 | "format": "s", 229 | "label": null, 230 | "logBase": 1, 231 | "max": null, 232 | "min": "0", 233 | "show": true 234 | }, 235 | { 236 | "format": "short", 237 | "label": null, 238 | "logBase": 1, 239 | "max": null, 240 | "min": null, 241 | "show": true 242 | } 243 | ], 244 | "yaxis": { 245 | "align": false, 246 | "alignLevel": null 247 | } 248 | }, 249 | { 250 | "aliasColors": {}, 251 | "bars": false, 252 | "dashLength": 10, 253 | "dashes": false, 254 | "datasource": null, 255 | "fill": 1, 256 | "fillGradient": 0, 257 | "gridPos": { 258 | "h": 10, 259 | "w": 24, 260 | "x": 0, 261 | "y": 10 262 | }, 263 | "id": 2, 264 | "legend": { 265 | "avg": false, 266 | "current": false, 267 | "max": false, 268 | "min": false, 269 | "show": true, 270 | "total": false, 271 | "values": false 272 | }, 273 | "lines": true, 274 | "linewidth": 1, 275 | "nullPointMode": "null", 276 | "options": { 277 | "dataLinks": [] 278 | }, 279 | "percentage": false, 280 | "pointradius": 2, 281 | "points": false, 282 | "renderer": "flot", 283 | "seriesOverrides": [], 284 | "spaceLength": 10, 285 | "stack": false, 286 | "steppedLine": false, 287 | "targets": [ 288 | { 289 | "expr": "http_request_duration_microseconds{quantile=\"0.99\", instance=\"rabbitmq-exporter:9090\"}", 290 | "format": "heatmap", 291 | "legendFormat": "{{quantile}}th", 292 | "refId": "A" 293 | } 294 | ], 295 | "thresholds": [], 296 | "timeFrom": null, 297 | "timeRegions": [], 298 | "timeShift": null, 299 | "title": "rabbitmq-exporter - RabbitMQ HTTP API request duration", 300 | "tooltip": { 301 | "shared": true, 302 | "sort": 0, 303 | "value_type": "individual" 304 | }, 305 | "type": "graph", 306 | "xaxis": { 307 | "buckets": null, 308 | "mode": "time", 309 | "name": null, 310 | "show": true, 311 | "values": [] 312 | }, 313 | "yaxes": [ 314 | { 315 | "format": "µs", 316 | "label": null, 317 | "logBase": 1, 318 | "max": null, 319 | "min": "0", 320 | "show": true 321 | }, 322 | { 323 | "format": "short", 324 | "label": null, 325 | "logBase": 1, 326 | "max": null, 327 | "min": null, 328 | "show": true 329 | } 330 | ], 331 | "yaxis": { 332 | "align": false, 333 | "alignLevel": null 334 | } 335 | } 336 | ], 337 | "refresh": "15s", 338 | "schemaVersion": 20, 339 | "style": "dark", 340 | "tags": [ 341 | "rabbitmq-exporter", 342 | "rabbitmq-prometheus" 343 | ], 344 | "templating": { 345 | "list": [] 346 | }, 347 | "time": { 348 | "from": "now-15m", 349 | "to": "now" 350 | }, 351 | "timepicker": { 352 | "refresh_intervals": [ 353 | "15s", 354 | "30s", 355 | "1m", 356 | "5m", 357 | "10m" 358 | ], 359 | "time_options": [ 360 | "5m", 361 | "15m", 362 | "1h", 363 | "6h", 364 | "12h", 365 | "24h", 366 | "2d", 367 | "7d", 368 | "30d" 369 | ] 370 | }, 371 | "timezone": "", 372 | "title": "rabbitmq-exporter_vs_rabbitmq-prometheus", 373 | "uid": "hNmaJ2AZk", 374 | "version": 1 375 | } 376 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/grafana/datasources.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | # name of the datasource. Required 5 | - name: prometheus 6 | # datasource type. Required 7 | type: prometheus 8 | # access mode. direct or proxy. Required 9 | access: proxy 10 | # org id. will default to orgId 1 if not specified 11 | orgId: 1 12 | # url 13 | url: http://prometheus:9090 14 | # database password, if used 15 | # password: 16 | # database user, if used 17 | # user: 18 | # database name, if used 19 | # database: 20 | # enable/disable basic auth 21 | # basicAuth: 22 | # basic auth username 23 | # basicAuthUser: 24 | # basic auth password 25 | # basicAuthPassword: 26 | # enable/disable with credentials headers 27 | # withCredentials: 28 | # mark as default datasource. Max one per org 29 | isDefault: true 30 | # fields that will be converted to json and stored in json_data 31 | # jsonData: 32 | # graphiteVersion: "1.1" 33 | # tlsAuth: true 34 | # tlsAuthWithCACert: true 35 | # httpHeaderName1: "Authorization" 36 | # json object of data that will be encrypted. 37 | # secureJsonData: 38 | # tlsCACert: "..." 39 | # tlsClientCert: "..." 40 | # tlsClientKey: "..." 41 | # httpHeaderValue1: "Bearer xf5yhfkpsnmgo" 42 | version: 1 43 | # allow users to edit datasources from the UI. 44 | editable: false 45 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/prometheus/prometheus.example.yml: -------------------------------------------------------------------------------- 1 | # https://prometheus.io/docs/prometheus/latest/configuration/configuration/ 2 | global: 3 | # This is higher than RabbitMQ's collect_statistics_interval, 4 | # but still close enough to capture metrics that were refreshed within this interval 5 | # This value determines the range that we use with rate(): 6 | # https://www.robustperception.io/what-range-should-i-use-with-rate 7 | scrape_interval: 15s # Default is every 1 minute. 8 | # scrape_timeout: 10s # Default is 10 seconds. 9 | # evaluation_interval: 60s # Default is every 1 minute. 10 | 11 | # Alertmanager configuration 12 | alerting: 13 | alertmanagers: 14 | - static_configs: 15 | - targets: 16 | # - 'alertmanager:9093' 17 | 18 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 19 | rule_files: 20 | # - "first_rules.yml" 21 | # - "second_rules.yml" 22 | 23 | scrape_configs: 24 | # The job name is added as a label `job=` to any timeseries scraped from this config. 25 | - job_name: 'prometheus' 26 | static_configs: 27 | - targets: ['localhost:9090'] 28 | - job_name: 'docker' 29 | static_configs: 30 | - targets: ['docker.for.mac.localhost:9323'] 31 | - job_name: 'node-exporter' 32 | static_configs: 33 | - targets: ['node-exporter:9100'] 34 | - job_name: 'cadvisor' 35 | static_configs: 36 | - targets: ['cadvisor:8080'] 37 | - job_name: 'rabbitmq-server' 38 | static_configs: 39 | - targets: 40 | - 'rmq0:15692' 41 | - 'rmq1:15692' 42 | - 'rmq2:15692' 43 | - 'rmq0-dist-tls:15692' 44 | - 'rmq1-dist-tls:15692' 45 | - 'rmq2-dist-tls:15692' 46 | - 'rmq0-qq:15692' 47 | - 'rmq1-qq:15692' 48 | - 'rmq2-qq:15692' 49 | - 'rmq0-dist-metrics:15692' 50 | - 'rmq1-dist-metrics:15692' 51 | - 'rmq2-dist-metrics:15692' 52 | - job_name: 'rabbitmq-perf-test' 53 | static_configs: 54 | - targets: 55 | # docker-compose-overview.yml 56 | - 'basic-get:8080' 57 | - 'basic-get-auto:8080' 58 | - 'greedy-consumer:8080' 59 | - 'publisher-confirms:8080' 60 | - 'slow-consumer-persistent:8080' 61 | - 'nack:8080' 62 | - 'unroutable-return:8080' 63 | - 'unroutable-drop:8080' 64 | # docker-compose-dist-tls.yml 65 | - 'stress-dist-tls:8080' 66 | # docker-compose-qq.yml 67 | - 'qq-moderate-load:8080' 68 | - job_name: 'rabbitmq-exporter' 69 | scrape_interval: 60s 70 | scrape_timeout: 59s 71 | static_configs: 72 | - targets: 73 | # docker-compose-dist-tls.yml 74 | - 'rabbitmq-exporter:9090' 75 | -------------------------------------------------------------------------------- /mqtt-broker/metrics/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # https://prometheus.io/docs/prometheus/latest/configuration/configuration/ 2 | global: 3 | # https://www.robustperception.io/what-range-should-i-use-with-rate 4 | scrape_interval: 15s # Default is every 1 minute. 5 | 6 | scrape_configs: 7 | - job_name: 'prometheus' 8 | static_configs: 9 | - targets: ['localhost:9090'] 10 | - job_name: 'rabbitmq-server' 11 | static_configs: 12 | - targets: 13 | - 'mqtt-broker:15692' -------------------------------------------------------------------------------- /pulumi/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /pulumi/Pulumi.dev.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | aws:profile: default 3 | aws:region: us-east-1 4 | lattice-connect-v2:deploy: 5 | env: dev 6 | hostname: example.com 7 | -------------------------------------------------------------------------------- /pulumi/Pulumi.prod.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | aws:profile: gridplus-aws 3 | aws:region: us-east-1 4 | lattice-connect-v2:credentials: 5 | password: 6 | secure: AAABAOe0fS/0sZ6IomJ5h+Fep92tNtF9jt/x43FRofcL9P2Gt0YyQI5KErQGSSbZ7neQ1xR6gq0w/AdAfjaTc+CGkfjNytt3X3JQebjg4Gc= 7 | username: 8 | secure: AAABANMcQYCGwd5O/WjgfYKqalqDRn+LTJEjS3mnKWjH9gqlMAqKYvCNI2br 9 | lattice-connect-v2:deploy: 10 | env: prod 11 | hostname: connect.gridpl.us 12 | -------------------------------------------------------------------------------- /pulumi/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: lattice-connect-v2 2 | runtime: nodejs 3 | description: A minimal AWS TypeScript Pulumi program 4 | -------------------------------------------------------------------------------- /pulumi/README.md: -------------------------------------------------------------------------------- 1 | # Pulumi Deployment 2 | 3 | This folder is intended for GridPlus team, and contains the source (and scripts) used for the _Lattice Conenct_ service deployment into production. -------------------------------------------------------------------------------- /pulumi/index.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | 5 | interface Deploy { 6 | env: string; 7 | hostname: string; 8 | } 9 | 10 | interface Credentials { 11 | username: string; 12 | password: string; 13 | } 14 | 15 | let config = new pulumi.Config(); 16 | let deploy = config.requireObject("deploy") 17 | let credentials = config.requireSecretObject("credentials") 18 | 19 | // Subnets 20 | // https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/awsx/ec2/#subnets 21 | const vpc = new awsx.ec2.Vpc(`lattice-connect-${deploy.env}`, { 22 | cidrBlock: "172.0.0.0/16", 23 | subnets: [ 24 | { type: "public" }, 25 | { type: "private" }, 26 | ], 27 | }) 28 | 29 | const cluster = new awsx.ecs.Cluster("lattice-connect", { vpc, }) 30 | const repo = new awsx.ecr.Repository("lattice-connect") 31 | 32 | const mqttImage = repo.buildAndPushImage({ 33 | context: "../mqtt-broker", 34 | dockerfile: `../mqtt-broker/container/Dockerfile`, 35 | extraOptions: [ 36 | "--platform", "linux/amd64", 37 | "-t", `mqtt-broker:latest` 38 | ] 39 | }) 40 | 41 | const connectImage = repo.buildAndPushImage({ 42 | context: "../connect/", 43 | cacheFrom: { stages: ["base"] }, 44 | dockerfile: "../connect/container/Dockerfile", 45 | extraOptions: [ 46 | "--platform", "linux/amd64", 47 | "-t", "lattice-connect:latest" 48 | ] 49 | }) 50 | 51 | const domains = { 52 | "dev" : "example.com", 53 | "prod" : "*.gridpl.us" 54 | }; 55 | 56 | const amazonIssuedSSLCert = pulumi.output(aws.acm.getCertificate({ 57 | domain: deploy.hostname, 58 | mostRecent: true, 59 | types: ["AMAZON_ISSUED"], 60 | })); 61 | 62 | const loadBalancer = new awsx.lb 63 | .NetworkLoadBalancer("lattice-connect-mqtt-lb", { vpc }) 64 | 65 | const mqttSSLTCPListener = loadBalancer 66 | .createTargetGroup("lattice-connect-ssl-tg", { port: 1883, vpc }) 67 | .createListener("lattice-connect-mqtt-ssl", { 68 | protocol: "TLS", 69 | port: 8883, 70 | certificateArn: amazonIssuedSSLCert.arn 71 | }) 72 | 73 | const mqttWebListener = loadBalancer 74 | .createTargetGroup("lattice-connect-web-tg", { port: 15672, vpc }) 75 | .createListener("lattice-connect-mqtt-web-list", { 76 | protocol: "TLS", 77 | port: 15672, 78 | certificateArn: amazonIssuedSSLCert.arn 79 | }) 80 | 81 | const mqttTaskDefinition = new awsx.ecs.FargateTaskDefinition("lattice-connect-mqtt-task", { 82 | vpc, 83 | container: { 84 | image: mqttImage, 85 | memory: 8096, 86 | environment: [ 87 | { name: "ERLANG_COOKIE", value: "erlang-cookie" } 88 | ], 89 | portMappings: [ 90 | mqttSSLTCPListener, 91 | mqttWebListener 92 | ], 93 | ulimits: [ 94 | { 95 | name: "nofile", 96 | softLimit: 262144, 97 | hardLimit: 262144 98 | } 99 | ] 100 | } 101 | }) 102 | 103 | const mqttService = new awsx.ecs.FargateService("lattice-connect-mqtt-serv", { 104 | cluster, 105 | assignPublicIp: false, 106 | taskDefinition: mqttTaskDefinition 107 | }) 108 | 109 | const appServiceListener = loadBalancer 110 | .createTargetGroup("lattice-connect-app-tg", { port: 8080, vpc }) 111 | .createListener("lattice-connect-app-ssl", { 112 | protocol: "TLS", 113 | port: 443, 114 | certificateArn: amazonIssuedSSLCert.arn 115 | }) 116 | 117 | const appTaskDefinition = credentials.apply( c => 118 | new awsx.ecs.FargateTaskDefinition("lattice-connect-app-task", { 119 | vpc, 120 | container: { 121 | image: connectImage, 122 | environment: [ 123 | { name: "APP_SERVICE_PORT", value: "8080" }, 124 | { name: "ADMIN_CLIENT_HOST", value: `https://${deploy.hostname}:15672` }, 125 | { name: "ADMIN_CLIENT_USER", value: `${c.username}` }, 126 | { name: "ADMIN_CLIENT_PASS", value: `${c.password}` }, 127 | { name: "MQTT_HTTP_PORT", value: "8883" }, 128 | ], 129 | memory: 512, 130 | portMappings: [appServiceListener], 131 | ulimits: [ 132 | { 133 | name: "nofile", 134 | softLimit: 262144, 135 | hardLimit: 262144 136 | } 137 | ] 138 | } 139 | }) 140 | ) 141 | 142 | appTaskDefinition.apply(task => 143 | new awsx.ecs.FargateService("lattice-connect-app-serv", { 144 | cluster, 145 | desiredCount: 2, 146 | assignPublicIp: false, 147 | taskDefinition: task 148 | }) 149 | ) 150 | 151 | export const appDnsName = loadBalancer.loadBalancer.dnsName 152 | -------------------------------------------------------------------------------- /pulumi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lattice-connect-v2", 3 | "dependencies": { 4 | "@pulumi/aws": "^5.0.0", 5 | "@pulumi/awsx": "^0.40.0", 6 | "@pulumi/pulumi": "^3.0.0", 7 | "@pulumi/rabbitmq": "^3.2.0", 8 | "@types/superagent": "^4.1.15", 9 | "superagent": "^8.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pulumi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "outDir": "bin", 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "pretty": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitReturns": true, 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "files": [ 17 | "index.ts" 18 | ] 19 | } 20 | --------------------------------------------------------------------------------