├── .env.dist ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── config ├── config_dev.toml └── config_prod.toml ├── docker-compose.yml ├── docker ├── docker-compose.test.yml ├── images │ ├── consumer │ │ └── Dockerfile │ └── rabbitmq │ │ └── Dockerfile └── scripts │ └── consumer │ └── start.sh ├── src ├── client │ ├── consumer │ │ ├── channel.rs │ │ ├── connection.rs │ │ ├── message.rs │ │ └── mod.rs │ ├── executor │ │ ├── events.rs │ │ ├── mod.rs │ │ └── waiter.rs │ └── mod.rs ├── config │ ├── database │ │ ├── mod.rs │ │ └── schema.rs │ ├── file.rs │ ├── mod.rs │ └── queue │ │ ├── config.rs │ │ ├── mod.rs │ │ └── model.rs ├── lib.rs ├── main.rs └── utils.rs └── tests ├── client.rs └── config.rs /.env.dist: -------------------------------------------------------------------------------- 1 | RABBITMQ_PORT=5672 2 | RABBITMQ_PUBLIC_PORT=5672 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /target/ 3 | /logs 4 | **/*.rs.bk 5 | config/config.toml 6 | old 7 | .env 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: focal 2 | 3 | language: rust 4 | 5 | sudo: required 6 | 7 | rust: 8 | - nightly 9 | 10 | cache: cargo 11 | 12 | addons: 13 | apt: 14 | update: true 15 | packages: 16 | - rabbitmq-server 17 | - cmake 18 | - build-essential 19 | - libssl-dev 20 | - libmysqlclient-dev 21 | 22 | after_success: | 23 | if [[ "$TRAVIS_RUST_VERSION" == nightly ]]; then 24 | `RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --force cargo-tarpaulin` 25 | cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID 26 | fi 27 | 28 | branches: 29 | only: 30 | - master 31 | - develop 32 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.18" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "amq-protocol" 14 | version = "6.0.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "3c222de30b345b19470d9091414c5bda68cd845c44e8dde2f874373e0b247a54" 17 | dependencies = [ 18 | "amq-protocol-tcp", 19 | "amq-protocol-types", 20 | "amq-protocol-uri", 21 | "cookie-factory", 22 | "nom", 23 | ] 24 | 25 | [[package]] 26 | name = "amq-protocol-tcp" 27 | version = "6.0.3" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "bee3c947a2c5b40b6e506453eeff96998bb2caa2ea616ed028ba1bafbc2b16ce" 30 | dependencies = [ 31 | "amq-protocol-uri", 32 | "tcp-stream", 33 | "tracing", 34 | ] 35 | 36 | [[package]] 37 | name = "amq-protocol-types" 38 | version = "6.0.3" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "d198cde4bde0eadf1a946e4f0d113ff471bf8abe4ef728181d7320461da8a3e4" 41 | dependencies = [ 42 | "cookie-factory", 43 | "nom", 44 | "serde", 45 | "serde_json", 46 | ] 47 | 48 | [[package]] 49 | name = "amq-protocol-uri" 50 | version = "6.0.3" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "9f843ece1f9a66cebbccf4162b5505e96e93598d3b2678ac56ad19d60642bd45" 53 | dependencies = [ 54 | "percent-encoding 2.1.0", 55 | "url 2.2.2", 56 | ] 57 | 58 | [[package]] 59 | name = "ansi_term" 60 | version = "0.11.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 63 | dependencies = [ 64 | "winapi", 65 | ] 66 | 67 | [[package]] 68 | name = "arrayvec" 69 | version = "0.5.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 72 | 73 | [[package]] 74 | name = "async-channel" 75 | version = "1.6.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 78 | dependencies = [ 79 | "concurrent-queue", 80 | "event-listener", 81 | "futures-core", 82 | ] 83 | 84 | [[package]] 85 | name = "async-executor" 86 | version = "1.4.1" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 89 | dependencies = [ 90 | "async-task", 91 | "concurrent-queue", 92 | "fastrand", 93 | "futures-lite", 94 | "once_cell", 95 | "slab", 96 | ] 97 | 98 | [[package]] 99 | name = "async-global-executor" 100 | version = "2.0.2" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" 103 | dependencies = [ 104 | "async-channel", 105 | "async-executor", 106 | "async-io", 107 | "async-mutex", 108 | "blocking", 109 | "futures-lite", 110 | "num_cpus", 111 | "once_cell", 112 | ] 113 | 114 | [[package]] 115 | name = "async-io" 116 | version = "1.4.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "4bbfd5cf2794b1e908ea8457e6c45f8f8f1f6ec5f74617bf4662623f47503c3b" 119 | dependencies = [ 120 | "concurrent-queue", 121 | "fastrand", 122 | "futures-lite", 123 | "libc", 124 | "log", 125 | "once_cell", 126 | "parking", 127 | "polling", 128 | "slab", 129 | "socket2", 130 | "waker-fn", 131 | "winapi", 132 | ] 133 | 134 | [[package]] 135 | name = "async-lock" 136 | version = "2.4.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" 139 | dependencies = [ 140 | "event-listener", 141 | ] 142 | 143 | [[package]] 144 | name = "async-mutex" 145 | version = "1.4.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" 148 | dependencies = [ 149 | "event-listener", 150 | ] 151 | 152 | [[package]] 153 | name = "async-std" 154 | version = "1.9.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" 157 | dependencies = [ 158 | "async-channel", 159 | "async-global-executor", 160 | "async-io", 161 | "async-lock", 162 | "crossbeam-utils", 163 | "futures-channel", 164 | "futures-core", 165 | "futures-io", 166 | "futures-lite", 167 | "gloo-timers", 168 | "kv-log-macro", 169 | "log", 170 | "memchr", 171 | "num_cpus", 172 | "once_cell", 173 | "pin-project-lite", 174 | "pin-utils", 175 | "slab", 176 | "wasm-bindgen-futures", 177 | ] 178 | 179 | [[package]] 180 | name = "async-task" 181 | version = "4.0.3" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" 184 | 185 | [[package]] 186 | name = "atomic-waker" 187 | version = "1.0.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 190 | 191 | [[package]] 192 | name = "atty" 193 | version = "0.2.14" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 196 | dependencies = [ 197 | "hermit-abi", 198 | "libc", 199 | "winapi", 200 | ] 201 | 202 | [[package]] 203 | name = "autocfg" 204 | version = "1.0.1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 207 | 208 | [[package]] 209 | name = "base64" 210 | version = "0.13.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 213 | 214 | [[package]] 215 | name = "bitflags" 216 | version = "1.2.1" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 219 | 220 | [[package]] 221 | name = "bitvec" 222 | version = "0.19.5" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" 225 | dependencies = [ 226 | "funty", 227 | "radium", 228 | "tap", 229 | "wyz", 230 | ] 231 | 232 | [[package]] 233 | name = "blocking" 234 | version = "1.0.2" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" 237 | dependencies = [ 238 | "async-channel", 239 | "async-task", 240 | "atomic-waker", 241 | "fastrand", 242 | "futures-lite", 243 | "once_cell", 244 | ] 245 | 246 | [[package]] 247 | name = "bumpalo" 248 | version = "3.6.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" 251 | 252 | [[package]] 253 | name = "byteorder" 254 | version = "1.4.3" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 257 | 258 | [[package]] 259 | name = "bytes" 260 | version = "1.0.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 263 | 264 | [[package]] 265 | name = "cache-padded" 266 | version = "1.1.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" 269 | 270 | [[package]] 271 | name = "cc" 272 | version = "1.0.68" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" 275 | 276 | [[package]] 277 | name = "cfg-if" 278 | version = "1.0.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 281 | 282 | [[package]] 283 | name = "chrono" 284 | version = "0.4.19" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 287 | dependencies = [ 288 | "libc", 289 | "num-integer", 290 | "num-traits", 291 | "serde", 292 | "time", 293 | "winapi", 294 | ] 295 | 296 | [[package]] 297 | name = "clap" 298 | version = "2.33.3" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 301 | dependencies = [ 302 | "ansi_term", 303 | "atty", 304 | "bitflags", 305 | "strsim", 306 | "textwrap", 307 | "unicode-width", 308 | "vec_map", 309 | ] 310 | 311 | [[package]] 312 | name = "concurrent-queue" 313 | version = "1.2.2" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 316 | dependencies = [ 317 | "cache-padded", 318 | ] 319 | 320 | [[package]] 321 | name = "cookie-factory" 322 | version = "0.3.2" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" 325 | 326 | [[package]] 327 | name = "core-foundation" 328 | version = "0.9.1" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 331 | dependencies = [ 332 | "core-foundation-sys", 333 | "libc", 334 | ] 335 | 336 | [[package]] 337 | name = "core-foundation-sys" 338 | version = "0.8.2" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 341 | 342 | [[package]] 343 | name = "crossbeam-channel" 344 | version = "0.5.1" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 347 | dependencies = [ 348 | "cfg-if", 349 | "crossbeam-utils", 350 | ] 351 | 352 | [[package]] 353 | name = "crossbeam-utils" 354 | version = "0.8.4" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" 357 | dependencies = [ 358 | "autocfg", 359 | "cfg-if", 360 | "lazy_static", 361 | ] 362 | 363 | [[package]] 364 | name = "crystalsoft-utils" 365 | version = "0.1.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "85a8ec66fa7486297a4e828f5cabe4e34a4b4934058010753767421bd3717ad7" 368 | 369 | [[package]] 370 | name = "ctor" 371 | version = "0.1.20" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" 374 | dependencies = [ 375 | "quote", 376 | "syn", 377 | ] 378 | 379 | [[package]] 380 | name = "diesel" 381 | version = "1.4.6" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "047bfc4d5c3bd2ef6ca6f981941046113524b9a9f9a7cbdfdd7ff40f58e6f542" 384 | dependencies = [ 385 | "byteorder", 386 | "chrono", 387 | "diesel_derives", 388 | "mysqlclient-sys", 389 | "r2d2", 390 | "url 1.7.2", 391 | ] 392 | 393 | [[package]] 394 | name = "diesel_derives" 395 | version = "1.4.1" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" 398 | dependencies = [ 399 | "proc-macro2", 400 | "quote", 401 | "syn", 402 | ] 403 | 404 | [[package]] 405 | name = "doc-comment" 406 | version = "0.3.3" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 409 | 410 | [[package]] 411 | name = "env_logger" 412 | version = "0.8.3" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" 415 | dependencies = [ 416 | "atty", 417 | "humantime", 418 | "log", 419 | "regex", 420 | "termcolor", 421 | ] 422 | 423 | [[package]] 424 | name = "event-listener" 425 | version = "2.5.1" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" 428 | 429 | [[package]] 430 | name = "fastrand" 431 | version = "1.4.1" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" 434 | dependencies = [ 435 | "instant", 436 | ] 437 | 438 | [[package]] 439 | name = "foreign-types" 440 | version = "0.3.2" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 443 | dependencies = [ 444 | "foreign-types-shared", 445 | ] 446 | 447 | [[package]] 448 | name = "foreign-types-shared" 449 | version = "0.1.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 452 | 453 | [[package]] 454 | name = "form_urlencoded" 455 | version = "1.0.1" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 458 | dependencies = [ 459 | "matches", 460 | "percent-encoding 2.1.0", 461 | ] 462 | 463 | [[package]] 464 | name = "funty" 465 | version = "1.1.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" 468 | 469 | [[package]] 470 | name = "futures" 471 | version = "0.3.15" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" 474 | dependencies = [ 475 | "futures-channel", 476 | "futures-core", 477 | "futures-executor", 478 | "futures-io", 479 | "futures-sink", 480 | "futures-task", 481 | "futures-util", 482 | ] 483 | 484 | [[package]] 485 | name = "futures-channel" 486 | version = "0.3.15" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" 489 | dependencies = [ 490 | "futures-core", 491 | "futures-sink", 492 | ] 493 | 494 | [[package]] 495 | name = "futures-core" 496 | version = "0.3.15" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" 499 | 500 | [[package]] 501 | name = "futures-executor" 502 | version = "0.3.15" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" 505 | dependencies = [ 506 | "futures-core", 507 | "futures-task", 508 | "futures-util", 509 | ] 510 | 511 | [[package]] 512 | name = "futures-io" 513 | version = "0.3.15" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" 516 | 517 | [[package]] 518 | name = "futures-lite" 519 | version = "1.11.3" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" 522 | dependencies = [ 523 | "fastrand", 524 | "futures-core", 525 | "futures-io", 526 | "memchr", 527 | "parking", 528 | "pin-project-lite", 529 | "waker-fn", 530 | ] 531 | 532 | [[package]] 533 | name = "futures-macro" 534 | version = "0.3.15" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" 537 | dependencies = [ 538 | "autocfg", 539 | "proc-macro-hack", 540 | "proc-macro2", 541 | "quote", 542 | "syn", 543 | ] 544 | 545 | [[package]] 546 | name = "futures-sink" 547 | version = "0.3.15" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" 550 | 551 | [[package]] 552 | name = "futures-task" 553 | version = "0.3.15" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" 556 | 557 | [[package]] 558 | name = "futures-util" 559 | version = "0.3.15" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" 562 | dependencies = [ 563 | "autocfg", 564 | "futures-channel", 565 | "futures-core", 566 | "futures-io", 567 | "futures-macro", 568 | "futures-sink", 569 | "futures-task", 570 | "memchr", 571 | "pin-project-lite", 572 | "pin-utils", 573 | "proc-macro-hack", 574 | "proc-macro-nested", 575 | "slab", 576 | ] 577 | 578 | [[package]] 579 | name = "getrandom" 580 | version = "0.2.3" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 583 | dependencies = [ 584 | "cfg-if", 585 | "libc", 586 | "wasi", 587 | ] 588 | 589 | [[package]] 590 | name = "gloo-timers" 591 | version = "0.2.1" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" 594 | dependencies = [ 595 | "futures-channel", 596 | "futures-core", 597 | "js-sys", 598 | "wasm-bindgen", 599 | "web-sys", 600 | ] 601 | 602 | [[package]] 603 | name = "hermit-abi" 604 | version = "0.1.18" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 607 | dependencies = [ 608 | "libc", 609 | ] 610 | 611 | [[package]] 612 | name = "humantime" 613 | version = "2.1.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 616 | 617 | [[package]] 618 | name = "idna" 619 | version = "0.1.5" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 622 | dependencies = [ 623 | "matches", 624 | "unicode-bidi", 625 | "unicode-normalization", 626 | ] 627 | 628 | [[package]] 629 | name = "idna" 630 | version = "0.2.3" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 633 | dependencies = [ 634 | "matches", 635 | "unicode-bidi", 636 | "unicode-normalization", 637 | ] 638 | 639 | [[package]] 640 | name = "instant" 641 | version = "0.1.9" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 644 | dependencies = [ 645 | "cfg-if", 646 | ] 647 | 648 | [[package]] 649 | name = "itoa" 650 | version = "0.4.7" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 653 | 654 | [[package]] 655 | name = "js-sys" 656 | version = "0.3.51" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 659 | dependencies = [ 660 | "wasm-bindgen", 661 | ] 662 | 663 | [[package]] 664 | name = "kv-log-macro" 665 | version = "1.0.7" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 668 | dependencies = [ 669 | "log", 670 | ] 671 | 672 | [[package]] 673 | name = "lapin" 674 | version = "1.7.1" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "5b9d21a790e85496a97d9b82821aa39c59579433e9ad29d7c1ab54d9807ebfcc" 677 | dependencies = [ 678 | "amq-protocol", 679 | "async-task", 680 | "crossbeam-channel", 681 | "futures-core", 682 | "log", 683 | "mio", 684 | "parking_lot", 685 | "pinky-swear", 686 | "serde", 687 | ] 688 | 689 | [[package]] 690 | name = "lazy_static" 691 | version = "1.4.0" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 694 | 695 | [[package]] 696 | name = "lexical-core" 697 | version = "0.7.6" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 700 | dependencies = [ 701 | "arrayvec", 702 | "bitflags", 703 | "cfg-if", 704 | "ryu", 705 | "static_assertions", 706 | ] 707 | 708 | [[package]] 709 | name = "libc" 710 | version = "0.2.95" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" 713 | 714 | [[package]] 715 | name = "lock_api" 716 | version = "0.4.4" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 719 | dependencies = [ 720 | "scopeguard", 721 | ] 722 | 723 | [[package]] 724 | name = "log" 725 | version = "0.4.14" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 728 | dependencies = [ 729 | "cfg-if", 730 | "value-bag", 731 | ] 732 | 733 | [[package]] 734 | name = "matches" 735 | version = "0.1.8" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 738 | 739 | [[package]] 740 | name = "memchr" 741 | version = "2.4.0" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 744 | 745 | [[package]] 746 | name = "mio" 747 | version = "0.7.11" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" 750 | dependencies = [ 751 | "libc", 752 | "log", 753 | "miow", 754 | "ntapi", 755 | "winapi", 756 | ] 757 | 758 | [[package]] 759 | name = "miow" 760 | version = "0.3.7" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 763 | dependencies = [ 764 | "winapi", 765 | ] 766 | 767 | [[package]] 768 | name = "mysqlclient-sys" 769 | version = "0.2.4" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "7e9637d93448044078aaafea7419aed69d301b4a12bcc4aa0ae856eb169bef85" 772 | dependencies = [ 773 | "pkg-config", 774 | "vcpkg", 775 | ] 776 | 777 | [[package]] 778 | name = "native-tls" 779 | version = "0.2.7" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" 782 | dependencies = [ 783 | "lazy_static", 784 | "libc", 785 | "log", 786 | "openssl", 787 | "openssl-probe", 788 | "openssl-sys", 789 | "schannel", 790 | "security-framework", 791 | "security-framework-sys", 792 | "tempfile", 793 | ] 794 | 795 | [[package]] 796 | name = "nom" 797 | version = "6.1.2" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" 800 | dependencies = [ 801 | "bitvec", 802 | "funty", 803 | "lexical-core", 804 | "memchr", 805 | "version_check", 806 | ] 807 | 808 | [[package]] 809 | name = "ntapi" 810 | version = "0.3.6" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 813 | dependencies = [ 814 | "winapi", 815 | ] 816 | 817 | [[package]] 818 | name = "num-integer" 819 | version = "0.1.44" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 822 | dependencies = [ 823 | "autocfg", 824 | "num-traits", 825 | ] 826 | 827 | [[package]] 828 | name = "num-traits" 829 | version = "0.2.14" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 832 | dependencies = [ 833 | "autocfg", 834 | ] 835 | 836 | [[package]] 837 | name = "num_cpus" 838 | version = "1.13.0" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 841 | dependencies = [ 842 | "hermit-abi", 843 | "libc", 844 | ] 845 | 846 | [[package]] 847 | name = "once_cell" 848 | version = "1.7.2" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 851 | 852 | [[package]] 853 | name = "openssl" 854 | version = "0.10.34" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" 857 | dependencies = [ 858 | "bitflags", 859 | "cfg-if", 860 | "foreign-types", 861 | "libc", 862 | "once_cell", 863 | "openssl-sys", 864 | ] 865 | 866 | [[package]] 867 | name = "openssl-probe" 868 | version = "0.1.4" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 871 | 872 | [[package]] 873 | name = "openssl-sys" 874 | version = "0.9.63" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" 877 | dependencies = [ 878 | "autocfg", 879 | "cc", 880 | "libc", 881 | "pkg-config", 882 | "vcpkg", 883 | ] 884 | 885 | [[package]] 886 | name = "parking" 887 | version = "2.0.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 890 | 891 | [[package]] 892 | name = "parking_lot" 893 | version = "0.11.1" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 896 | dependencies = [ 897 | "instant", 898 | "lock_api", 899 | "parking_lot_core", 900 | ] 901 | 902 | [[package]] 903 | name = "parking_lot_core" 904 | version = "0.8.3" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 907 | dependencies = [ 908 | "cfg-if", 909 | "instant", 910 | "libc", 911 | "redox_syscall", 912 | "smallvec", 913 | "winapi", 914 | ] 915 | 916 | [[package]] 917 | name = "pem" 918 | version = "0.8.3" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" 921 | dependencies = [ 922 | "base64", 923 | "once_cell", 924 | "regex", 925 | ] 926 | 927 | [[package]] 928 | name = "percent-encoding" 929 | version = "1.0.1" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 932 | 933 | [[package]] 934 | name = "percent-encoding" 935 | version = "2.1.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 938 | 939 | [[package]] 940 | name = "pin-project-lite" 941 | version = "0.2.6" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 944 | 945 | [[package]] 946 | name = "pin-utils" 947 | version = "0.1.0" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 950 | 951 | [[package]] 952 | name = "pinky-swear" 953 | version = "4.4.0" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "9bf8cda6f8e1500338634e4e3ce90ac59eb7929a1e088b6946c742be1cc44dc1" 956 | dependencies = [ 957 | "doc-comment", 958 | "parking_lot", 959 | "tracing", 960 | ] 961 | 962 | [[package]] 963 | name = "pkg-config" 964 | version = "0.3.19" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 967 | 968 | [[package]] 969 | name = "polling" 970 | version = "2.0.3" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b" 973 | dependencies = [ 974 | "cfg-if", 975 | "libc", 976 | "log", 977 | "wepoll-sys", 978 | "winapi", 979 | ] 980 | 981 | [[package]] 982 | name = "ppv-lite86" 983 | version = "0.2.10" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 986 | 987 | [[package]] 988 | name = "proc-macro-hack" 989 | version = "0.5.19" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 992 | 993 | [[package]] 994 | name = "proc-macro-nested" 995 | version = "0.1.7" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 998 | 999 | [[package]] 1000 | name = "proc-macro2" 1001 | version = "1.0.27" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 1004 | dependencies = [ 1005 | "unicode-xid", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "quote" 1010 | version = "1.0.9" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1013 | dependencies = [ 1014 | "proc-macro2", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "r2d2" 1019 | version = "0.8.9" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" 1022 | dependencies = [ 1023 | "log", 1024 | "parking_lot", 1025 | "scheduled-thread-pool", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "rabbitmq-consumer" 1030 | version = "1.2.3" 1031 | dependencies = [ 1032 | "async-std", 1033 | "base64", 1034 | "chrono", 1035 | "clap", 1036 | "crystalsoft-utils", 1037 | "diesel", 1038 | "env_logger", 1039 | "futures", 1040 | "lapin", 1041 | "log", 1042 | "serde", 1043 | "serde_derive", 1044 | "tokio", 1045 | "tokio-stream", 1046 | "toml", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "radium" 1051 | version = "0.5.3" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" 1054 | 1055 | [[package]] 1056 | name = "rand" 1057 | version = "0.8.3" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 1060 | dependencies = [ 1061 | "libc", 1062 | "rand_chacha", 1063 | "rand_core", 1064 | "rand_hc", 1065 | ] 1066 | 1067 | [[package]] 1068 | name = "rand_chacha" 1069 | version = "0.3.0" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" 1072 | dependencies = [ 1073 | "ppv-lite86", 1074 | "rand_core", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "rand_core" 1079 | version = "0.6.2" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 1082 | dependencies = [ 1083 | "getrandom", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "rand_hc" 1088 | version = "0.3.0" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 1091 | dependencies = [ 1092 | "rand_core", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "redox_syscall" 1097 | version = "0.2.8" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" 1100 | dependencies = [ 1101 | "bitflags", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "regex" 1106 | version = "1.5.4" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1109 | dependencies = [ 1110 | "aho-corasick", 1111 | "memchr", 1112 | "regex-syntax", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "regex-syntax" 1117 | version = "0.6.25" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1120 | 1121 | [[package]] 1122 | name = "remove_dir_all" 1123 | version = "0.5.3" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1126 | dependencies = [ 1127 | "winapi", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "ryu" 1132 | version = "1.0.5" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1135 | 1136 | [[package]] 1137 | name = "schannel" 1138 | version = "0.1.19" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1141 | dependencies = [ 1142 | "lazy_static", 1143 | "winapi", 1144 | ] 1145 | 1146 | [[package]] 1147 | name = "scheduled-thread-pool" 1148 | version = "0.2.5" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" 1151 | dependencies = [ 1152 | "parking_lot", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "scopeguard" 1157 | version = "1.1.0" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1160 | 1161 | [[package]] 1162 | name = "security-framework" 1163 | version = "2.2.0" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" 1166 | dependencies = [ 1167 | "bitflags", 1168 | "core-foundation", 1169 | "core-foundation-sys", 1170 | "libc", 1171 | "security-framework-sys", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "security-framework-sys" 1176 | version = "2.2.0" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" 1179 | dependencies = [ 1180 | "core-foundation-sys", 1181 | "libc", 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "serde" 1186 | version = "1.0.126" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 1189 | dependencies = [ 1190 | "serde_derive", 1191 | ] 1192 | 1193 | [[package]] 1194 | name = "serde_derive" 1195 | version = "1.0.126" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 1198 | dependencies = [ 1199 | "proc-macro2", 1200 | "quote", 1201 | "syn", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "serde_json" 1206 | version = "1.0.64" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 1209 | dependencies = [ 1210 | "itoa", 1211 | "ryu", 1212 | "serde", 1213 | ] 1214 | 1215 | [[package]] 1216 | name = "signal-hook-registry" 1217 | version = "1.3.0" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1220 | dependencies = [ 1221 | "libc", 1222 | ] 1223 | 1224 | [[package]] 1225 | name = "slab" 1226 | version = "0.4.3" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" 1229 | 1230 | [[package]] 1231 | name = "smallvec" 1232 | version = "1.6.1" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1235 | 1236 | [[package]] 1237 | name = "socket2" 1238 | version = "0.4.0" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" 1241 | dependencies = [ 1242 | "libc", 1243 | "winapi", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "static_assertions" 1248 | version = "1.1.0" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1251 | 1252 | [[package]] 1253 | name = "strsim" 1254 | version = "0.8.0" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1257 | 1258 | [[package]] 1259 | name = "syn" 1260 | version = "1.0.72" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" 1263 | dependencies = [ 1264 | "proc-macro2", 1265 | "quote", 1266 | "unicode-xid", 1267 | ] 1268 | 1269 | [[package]] 1270 | name = "tap" 1271 | version = "1.0.1" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 1274 | 1275 | [[package]] 1276 | name = "tcp-stream" 1277 | version = "0.20.7" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "9a29af643fc448ecb2d9b8a74aa2c60c72c36cb447bcf490b866797b58f00f13" 1280 | dependencies = [ 1281 | "cfg-if", 1282 | "mio", 1283 | "native-tls", 1284 | "pem", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "tempfile" 1289 | version = "3.2.0" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1292 | dependencies = [ 1293 | "cfg-if", 1294 | "libc", 1295 | "rand", 1296 | "redox_syscall", 1297 | "remove_dir_all", 1298 | "winapi", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "termcolor" 1303 | version = "1.1.2" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1306 | dependencies = [ 1307 | "winapi-util", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "textwrap" 1312 | version = "0.11.0" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1315 | dependencies = [ 1316 | "unicode-width", 1317 | ] 1318 | 1319 | [[package]] 1320 | name = "time" 1321 | version = "0.1.43" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1324 | dependencies = [ 1325 | "libc", 1326 | "winapi", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "tinyvec" 1331 | version = "1.2.0" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" 1334 | dependencies = [ 1335 | "tinyvec_macros", 1336 | ] 1337 | 1338 | [[package]] 1339 | name = "tinyvec_macros" 1340 | version = "0.1.0" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1343 | 1344 | [[package]] 1345 | name = "tokio" 1346 | version = "1.6.0" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "bd3076b5c8cc18138b8f8814895c11eb4de37114a5d127bafdc5e55798ceef37" 1349 | dependencies = [ 1350 | "autocfg", 1351 | "bytes", 1352 | "libc", 1353 | "memchr", 1354 | "mio", 1355 | "num_cpus", 1356 | "once_cell", 1357 | "parking_lot", 1358 | "pin-project-lite", 1359 | "signal-hook-registry", 1360 | "tokio-macros", 1361 | "winapi", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "tokio-macros" 1366 | version = "1.2.0" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" 1369 | dependencies = [ 1370 | "proc-macro2", 1371 | "quote", 1372 | "syn", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "tokio-stream" 1377 | version = "0.1.6" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066" 1380 | dependencies = [ 1381 | "futures-core", 1382 | "pin-project-lite", 1383 | "tokio", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "toml" 1388 | version = "0.5.8" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1391 | dependencies = [ 1392 | "serde", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "tracing" 1397 | version = "0.1.26" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" 1400 | dependencies = [ 1401 | "cfg-if", 1402 | "pin-project-lite", 1403 | "tracing-core", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "tracing-core" 1408 | version = "0.1.18" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" 1411 | 1412 | [[package]] 1413 | name = "unicode-bidi" 1414 | version = "0.3.5" 1415 | source = "registry+https://github.com/rust-lang/crates.io-index" 1416 | checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" 1417 | dependencies = [ 1418 | "matches", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "unicode-normalization" 1423 | version = "0.1.17" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" 1426 | dependencies = [ 1427 | "tinyvec", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "unicode-width" 1432 | version = "0.1.8" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1435 | 1436 | [[package]] 1437 | name = "unicode-xid" 1438 | version = "0.2.2" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1441 | 1442 | [[package]] 1443 | name = "url" 1444 | version = "1.7.2" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 1447 | dependencies = [ 1448 | "idna 0.1.5", 1449 | "matches", 1450 | "percent-encoding 1.0.1", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "url" 1455 | version = "2.2.2" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1458 | dependencies = [ 1459 | "form_urlencoded", 1460 | "idna 0.2.3", 1461 | "matches", 1462 | "percent-encoding 2.1.0", 1463 | ] 1464 | 1465 | [[package]] 1466 | name = "value-bag" 1467 | version = "1.0.0-alpha.7" 1468 | source = "registry+https://github.com/rust-lang/crates.io-index" 1469 | checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" 1470 | dependencies = [ 1471 | "ctor", 1472 | "version_check", 1473 | ] 1474 | 1475 | [[package]] 1476 | name = "vcpkg" 1477 | version = "0.2.13" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" 1480 | 1481 | [[package]] 1482 | name = "vec_map" 1483 | version = "0.8.2" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1486 | 1487 | [[package]] 1488 | name = "version_check" 1489 | version = "0.9.3" 1490 | source = "registry+https://github.com/rust-lang/crates.io-index" 1491 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1492 | 1493 | [[package]] 1494 | name = "waker-fn" 1495 | version = "1.1.0" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 1498 | 1499 | [[package]] 1500 | name = "wasi" 1501 | version = "0.10.2+wasi-snapshot-preview1" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1504 | 1505 | [[package]] 1506 | name = "wasm-bindgen" 1507 | version = "0.2.74" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 1510 | dependencies = [ 1511 | "cfg-if", 1512 | "wasm-bindgen-macro", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "wasm-bindgen-backend" 1517 | version = "0.2.74" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 1520 | dependencies = [ 1521 | "bumpalo", 1522 | "lazy_static", 1523 | "log", 1524 | "proc-macro2", 1525 | "quote", 1526 | "syn", 1527 | "wasm-bindgen-shared", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "wasm-bindgen-futures" 1532 | version = "0.4.24" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" 1535 | dependencies = [ 1536 | "cfg-if", 1537 | "js-sys", 1538 | "wasm-bindgen", 1539 | "web-sys", 1540 | ] 1541 | 1542 | [[package]] 1543 | name = "wasm-bindgen-macro" 1544 | version = "0.2.74" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 1547 | dependencies = [ 1548 | "quote", 1549 | "wasm-bindgen-macro-support", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "wasm-bindgen-macro-support" 1554 | version = "0.2.74" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 1557 | dependencies = [ 1558 | "proc-macro2", 1559 | "quote", 1560 | "syn", 1561 | "wasm-bindgen-backend", 1562 | "wasm-bindgen-shared", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "wasm-bindgen-shared" 1567 | version = "0.2.74" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 1570 | 1571 | [[package]] 1572 | name = "web-sys" 1573 | version = "0.3.51" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 1576 | dependencies = [ 1577 | "js-sys", 1578 | "wasm-bindgen", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "wepoll-sys" 1583 | version = "3.0.1" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" 1586 | dependencies = [ 1587 | "cc", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "winapi" 1592 | version = "0.3.9" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1595 | dependencies = [ 1596 | "winapi-i686-pc-windows-gnu", 1597 | "winapi-x86_64-pc-windows-gnu", 1598 | ] 1599 | 1600 | [[package]] 1601 | name = "winapi-i686-pc-windows-gnu" 1602 | version = "0.4.0" 1603 | source = "registry+https://github.com/rust-lang/crates.io-index" 1604 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1605 | 1606 | [[package]] 1607 | name = "winapi-util" 1608 | version = "0.1.5" 1609 | source = "registry+https://github.com/rust-lang/crates.io-index" 1610 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1611 | dependencies = [ 1612 | "winapi", 1613 | ] 1614 | 1615 | [[package]] 1616 | name = "winapi-x86_64-pc-windows-gnu" 1617 | version = "0.4.0" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1620 | 1621 | [[package]] 1622 | name = "wyz" 1623 | version = "0.2.0" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" 1626 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rabbitmq-consumer" 3 | version = "1.2.3" 4 | authors = ["Dario Cancelliere "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "rabbitmq_consumer_lib" 9 | path = "src/lib.rs" 10 | 11 | [profile.release] 12 | lto = true 13 | 14 | [dependencies] 15 | async-std = "^1.9" 16 | tokio = { version = "^1.6", features = ["full"] } 17 | tokio-stream = "^0.1" 18 | futures = "^0.3" 19 | lapin = "^1.7" 20 | toml = "^0.5" 21 | serde = "^1.0" 22 | serde_derive = "^1.0" 23 | diesel = { version = "^1.4", features = ["mysql", "chrono", "r2d2"] } 24 | chrono = { version = "^0.4", features = ["serde"] } 25 | log = "^0.4" 26 | env_logger = "^0.8" 27 | base64 = "^0.13" 28 | clap = "^2.33" 29 | crystalsoft-utils = "^0.1" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Facile.it 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ consumer 2 | [![Build Status](https://travis-ci.com/facile-it/rabbitmq-consumer.svg?branch=master)](https://travis-ci.com/facile-it/rabbitmq-consumer) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | 5 | A configurable RabbitMQ consumer made in Rust, useful for a stable and reliable CLI commands processor. 6 | 7 | ## Version 1.2.0 and 1.2.3 warning 8 | 9 | In order to use this version correctly and only if you use the MySQL configuration for queues, you have to update your `queues` table schema adding a new `nack_code` and `prefetch_count` integer columns: 10 | 11 | ```sql 12 | ALTER TABLE queues ADD nack_code INT(11) DEFAULT 2 NULL; 13 | ALTER TABLE queues ADD prefetch_count INT(11) DEFAULT 1 NULL; 14 | ``` 15 | 16 | ## Installation 17 | 18 | You can either compile it yourself, or download a precompiled binary from [here](https://github.com/facile-it/rabbitmq-consumer/releases). 19 | 20 | ### Compiling 21 | 22 | You need the Rust nightly environment, and you should be familiar with the language, in order to build a binary. 23 | 24 | #### Installing Rust 25 | 26 | In order to install the Rust toolchain with `rustc` (compiler), `rustup` and `Cargo`, execute this command: 27 | `curl https://sh.rustup.rs -sSf | sh` 28 | 29 | #### Introducing Cargo 30 | 31 | Cargo is a dependency manager for Rust. You can create new projects, require new crates (libraries) or update existing crates. 32 | 33 | #### Building 34 | 35 | Just run `cargo +nightly build` inside the project folder to build a debug binary. 36 | 37 | You can even build a release binary (slower but optimized build) using `cargo +nightly build --release` 38 | 39 | NB: You need the `libmysqlclient-dev` package (for Ubuntu) or `mysql-client` (for macOS) in order to build and statically link the binary. 40 | 41 | WARNING: Nightly Rust version is required for this project in order to compile (caused by the static linking of some non-bundles libraries) 42 | 43 | #### Testing 44 | 45 | In order to launch tests you need to run the container first: 46 | 47 | `docker-compose -f docker-compose.yml -f docker/docker-compose.test.yml up -d` 48 | 49 | Please be sure to create your own `config.toml` inside the `config` directory before running the container. 50 | 51 | Then simply launch: `cargo test +nightly` 52 | 53 | ## Usage 54 | 55 | Run without arguments to start with default configuration or with `--help` to show the help summary: 56 | 57 | ``` 58 | $ rabbitmq-consumer 59 | rabbitmq-consumer 1.2.3 60 | 61 | A configurable RabbitMQ consumer made in Rust, useful for a stable and reliable CLI commands processor. 62 | 63 | USAGE: 64 | rabbitmq-consumer [OPTIONS] 65 | 66 | FLAGS: 67 | -h, --help Prints help information 68 | -V, --version Prints version information 69 | 70 | OPTIONS: 71 | -e, --env Environment for configuration file loading 72 | -p, --path Base config file path 73 | ``` 74 | 75 | ## Process shutdown 76 | 77 | The consumer handles the SIGTERM and SIGINT signals, so when a signal is received, the consumer will be gracefully canceled. 78 | 79 | # Configuration 80 | The consumer loads the standard configuration file, located at `config/config.toml`, at runtime. Pass the `--env` parameter in order to load a custom configuration file, for example: 81 | 82 | ``` 83 | $ rabbitmq-consumer --env dev 84 | ``` 85 | 86 | This will load the file located at `config/config_dev.toml`, if exists, otherwise the consumer will fallback to the default `config/config.toml` file. 87 | 88 | You can also specify the configuration file base path with the `--path` option. 89 | 90 | ## config.toml 91 | ### [rabbit] section 92 | This section describes the connection to the AMQP server. 93 | 94 | > `host = "localhost"` 95 | >> This defines the host domain or IP of the AMQP server: if the consumer fails to connect to this host, it will throw an error, and retry automatically. 96 | 97 | > `port = 5672` 98 | >> This is the port of the AMQP server. 99 | 100 | > `username = "guest"` 101 | >> This is the username of the AMQP server. 102 | 103 | > `password = "guest"` 104 | >> This is the password of the AMQP server. 105 | 106 | > `vhost = "/"` 107 | >> This is the vhost to use for the current user and session. 108 | 109 | > `queue_prefix = "queue_"` 110 | >> This is the internal prefix that the application will use to configure queues. 111 | 112 | > `reconnections = 0` 113 | >> By default the consumer will try to reconnect to AMQP server automatically and indefinitely (default 0 value), change this value to limit reconnection retries. 114 | 115 | ### [[rabbit.queues]] section 116 | This section (a TOML array) defines all queues and consumers. 117 | 118 | > `id = 1` 119 | >> The virtual ID of this queue: must be different for each queue. 120 | 121 | > `prefetch_count = 1` 122 | >> If specified, limits the number of unacknowledged messages on a channel (or connection) when consuming (default is 1) 123 | 124 | > `queue_name = "example"` 125 | >> The internal name of the queue, also used in combination with the prefix to generate the channel and queue name. 126 | 127 | > `consumer_name = "example"` 128 | >> The internal name of the consumer: "_consumer_NUMBER" string will be attached after the name. 129 | 130 | > `command = "php ../../bin/console example:command"` 131 | >> The command executed for each received message; for example if the message contains `--id 1234`, the final executed command will be: 132 | `php ../../bin/console example:command --id 1234`, you can attach any parameter using the content of the message, or pass a base64 encoded string for serialized data. 133 | 134 | > `command_timeout = 30` 135 | >> If specified, the command will be executed with a custom timeout: default is 30 (value is in minutes). 136 | 137 | > `base64 = false` 138 | >> If enabled, the consumer will send a base64 encoded message with a single "--body" parameter with the encoded data. 139 | 140 | > `start_hour = "00:00:00"` 141 | >> The start hour of consumer activity. 142 | 143 | > `end_hour = "23:59:59"` 144 | >> The end hour of consumer activity: the consumer will be stopped automatically until the start hour is reached again. 145 | 146 | > `count = 1` 147 | >> Specify how many consumers to run for this queue. WARNING: use multiple counts only on a shared instance, for a container based utilization, is recommended to split the load between replicas or more containers. 148 | 149 | > `nack_code = 2` 150 | >> If specified, the not requeue logic (nack not requeue) applies only to this specific exit code, all other exit code will requeue the message. 151 | 152 | > `retry_wait = 120` 153 | >> The waiting time for the retry mode: default is 120 (value is in seconds). 154 | 155 | > `retry_mode = "incremental"` 156 | >> The retry mode can be "incremental", "static" or "ignored": in "incremental" the waiting time is multiplied each time by 2; in "static" the waiting time is fixed; in "ignored" no retry will be attempted. 157 | 158 | > `enabled = true` 159 | >> Enable or disable the queue. 160 | 161 | ### [database] section 162 | This section defines the MySQL configuration. 163 | 164 | > `enabled = true` 165 | >> Enable or disable MySQL connection: if disabled, the "Static configuration" will be used. 166 | 167 | > `host = "localhost"` 168 | >> The MySQL server host. 169 | 170 | > `port = 3306` 171 | >> The MySQL server port. 172 | 173 | > `user = "user"` 174 | >> The MySQL server user. 175 | 176 | > `password = "password"` 177 | >> The MySQL server password. 178 | 179 | > `db_name = "database"` 180 | >> The MySQL server database name to use. 181 | 182 | > `retries = 3` 183 | >> Specify how many retries of MySQL if connection is lost before ending the process 184 | 185 | ## Environment variables 186 | You can use environment variables for every configuration of the TOML file just using the `"$VARIABLE_NAME"` syntax as parameter value. 187 | It's mandatory to use quotes in order to avoid spaced parameters and other issues. 188 | 189 | ## Queues and consumers 190 | There are two ways for configuring queues: 191 | 192 | * Database configuration (using MySQL integration) 193 | * Static configuration 194 | 195 | In __Database configuration__, you can enable or disable a queue at runtime, change its operation time and consumers count. 196 | 197 | You can enable the Database configuration by switching the `enabled` option to `true` in the `[database]` section. 198 | 199 | WARNING: If you use the MySQL connection, the consumer can fails more frequently due to the dependency of the MySQL server connection. 200 | 201 | When a database configuration is enabled, the consumer will fetch a table named `queues` and will load all the configuration values from there. This is the query for creating the needed table: 202 | 203 | ```sql 204 | CREATE TABLE queues 205 | ( 206 | id INT AUTO_INCREMENT 207 | PRIMARY KEY, 208 | prefetch_count INT(11) DEFAULT 1 NULL, 209 | queue_name VARCHAR(255) NOT NULL, 210 | consumer_name VARCHAR(255) NOT NULL, 211 | command VARCHAR(250) NOT NULL, 212 | command_timeout BIGINT UNSIGNED NULL, 213 | base64 TINYINT(1) DEFAULT 0 NOT NULL, 214 | start_hour TIME NOT NULL, 215 | end_hour TIME NOT NULL, 216 | count INT(11) DEFAULT 1 NOT NULL, 217 | nack_code INT(11) DEFAULT 2 NULL, 218 | retry_wait BIGINT UNSIGNED DEFAULT 120 NOT NULL, 219 | retry_mode VARCHAR(50) DEFAULT 'incremental' NOT NULL, 220 | enabled TINYINT(1) NOT NULL 221 | ) 222 | ENGINE = InnoDB; 223 | ``` 224 | 225 | You can skip this if you don't care about changing the values at runtime. Just set the `enabled` flag to `false` on the database section and use the __Static configuration__ as follows: 226 | 227 | ```toml 228 | [[rabbit.queues]] 229 | id = 1 230 | queue_name = "example" 231 | consumer_name = "example" 232 | command = "command_to_execute" 233 | command_timeout = 30 234 | base64 = false 235 | start_hour = "00:00:00" 236 | end_hour = "23:59:59" 237 | count = 1 238 | retry_wait = 120 239 | retry_mode = "incremental" 240 | enabled = true 241 | 242 | [[rabbit.queues]] 243 | id = 2 244 | queue_name = "example2" 245 | consumer_name = "example2" 246 | command = "command_to_execute2" 247 | command_timeout = 15 248 | base64 = false 249 | start_hour = "00:00:00" 250 | end_hour = "23:59:59" 251 | count = 1 252 | retry_wait = 120 253 | retry_mode = "incremental" 254 | enabled = true 255 | 256 | [[rabbit.queues]] 257 | id = 3 258 | queue_name = "example3" 259 | consumer_name = "example3" 260 | command = "command_to_execute3" 261 | command_timeout = 20 262 | base64 = false 263 | start_hour = "00:00:00" 264 | end_hour = "23:59:59" 265 | count = 1 266 | retry_wait = 120 267 | retry_mode = "incremental" 268 | enabled = true 269 | ``` 270 | 271 | You can set as many consumer as you need: these consumers will run on the same process asynchronously using one thread. 272 | 273 | ## Retry logic with exit codes 274 | The consumer will handle specific OS process exit codes in order to apply a retry logic. This only applies with "incremental" or "static" retry modes. 275 | 276 | **List of exit codes** 277 | 278 | | Exit Code | Action | 279 | |:---------:|---------------------------------------| 280 | | 0 | Acknowledgement | 281 | | 1 | Negative acknowledgement and re-queue | 282 | | 2 | Negative acknowledgement | 283 | 284 | # Thanks to 285 | * [Tokio](https://github.com/tokio-rs/tokio) 286 | * [Lapin](https://github.com/CleverCloud/lapin) 287 | * [Diesel](https://github.com/diesel-rs/diesel) 288 | * [TOML](https://github.com/alexcrichton/toml-rs) 289 | * [Chrono](https://github.com/chronotope/chrono) 290 | * [Base64](https://github.com/marshallpierce/rust-base64) 291 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | if "release" == env::var("PROFILE").unwrap_or_else(|_| "".into()) { 5 | println!(r"cargo:rustc-link-lib=static=mysqlclient"); 6 | println!(r"cargo:rustc-link-lib=static:-bundle=stdc++"); 7 | println!(r"cargo:rustc-link-lib=static=z"); 8 | println!(r"cargo:rustc-link-lib=static=ssl"); 9 | println!(r"cargo:rustc-link-lib=static=crypto"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /config/config_dev.toml: -------------------------------------------------------------------------------- 1 | [rabbit] 2 | host = "127.0.0.1" 3 | port = 5672 4 | username = "guest" 5 | password = "guest" 6 | vhost = "/" 7 | queue_prefix = "queue_" 8 | reconnections = 0 9 | 10 | [[rabbit.queues]] 11 | id = 1 12 | queue_name = "example" 13 | consumer_name = "example" 14 | command = "echo 1" 15 | command_timeout = 30 16 | base64 = false 17 | start_hour = "00:00:00" 18 | end_hour = "23:59:59" 19 | count = 1 20 | retry_wait = 120 21 | retry_mode = "incremental" 22 | enabled = true 23 | 24 | [database] 25 | enabled = false 26 | host = "" 27 | port = 3306 28 | user = "" 29 | password = "" 30 | db_name = "" 31 | retries = 3 32 | -------------------------------------------------------------------------------- /config/config_prod.toml: -------------------------------------------------------------------------------- 1 | [rabbit] 2 | host = "localhost" 3 | port = 5672 4 | username = "guest" 5 | password = "guest" 6 | vhost = "/" 7 | queue_prefix = "queue_" 8 | reconnections = 0 9 | 10 | [[rabbit.queues]] 11 | id = 1 12 | queue_name = "example" 13 | consumer_name = "example" 14 | command = "echo 1" 15 | command_timeout = 30 16 | base64 = false 17 | start_hour = "00:00:00" 18 | end_hour = "23:59:59" 19 | count = 1 20 | retry_wait = 120 21 | retry_mode = "incremental" 22 | enabled = true 23 | 24 | [database] 25 | enabled = false 26 | host = "" 27 | port = 3306 28 | user = "" 29 | password = "" 30 | db_name = "" 31 | retries = 3 32 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | consumer: 4 | build: docker/images/consumer 5 | container_name: consumer 6 | working_dir: /home 7 | volumes: 8 | - "./config/config.toml:/home/config/config.toml" 9 | - "./target/debug/rabbitmq-consumer:/home/rabbitmq-consumer" 10 | - "./docker/scripts/consumer/start.sh:/home/rabbitmq-consumer.sh" 11 | - "./logs:/home/logs" 12 | ulimits: 13 | nproc: 1000000 14 | nofile: 15 | soft: 1000000 16 | hard: 1000000 17 | env_file: .env 18 | command: > 19 | /bin/sh -c "chmod a+x rabbitmq-consumer && chmod a+x rabbitmq-consumer.sh && ./rabbitmq-consumer.sh" 20 | restart: always 21 | depends_on: 22 | - rabbitmq 23 | -------------------------------------------------------------------------------- /docker/docker-compose.test.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | db: 4 | image: mariadb:latest 5 | container_name: r_db 6 | environment: 7 | TZ: Europe/Rome 8 | MYSQL_DATABASE: test 9 | MYSQL_ROOT_PASSWORD: root 10 | MYSQL_TCP_PORT: 3306 11 | expose: 12 | - 3306 13 | ports: 14 | - 3312:3306 15 | 16 | rabbitmq: 17 | build: ./docker/images/rabbitmq 18 | container_name: rabbitmq 19 | expose: 20 | - $RABBITMQ_PORT 21 | ports: 22 | - $RABBITMQ_PUBLIC_PORT:$RABBITMQ_PORT 23 | - 15672:15672 24 | -------------------------------------------------------------------------------- /docker/images/consumer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:focal 2 | LABEL maintainer='emulator@hotmail.it' 3 | 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | ENV TZ=Europe/Rome 6 | ENV TZDIR=/usr/share/zoneinfo 7 | 8 | RUN apt-get update && apt-get install -y \ 9 | tzdata ca-certificates 10 | 11 | RUN ln -fs $TZDIR/$TZ /etc/localtime \ 12 | && dpkg-reconfigure -f noninteractive tzdata 13 | -------------------------------------------------------------------------------- /docker/images/rabbitmq/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rabbitmq:management 2 | LABEL maintainer='emulator@hotmail.it' 3 | -------------------------------------------------------------------------------- /docker/scripts/consumer/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while true; do 4 | ./rabbitmq-consumer | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' >> logs/rabbitmq-consumer.log 5 | 6 | echo "Process crashed with code $?. Restarting..." >&2 7 | 8 | sleep 1 9 | done 10 | -------------------------------------------------------------------------------- /src/client/consumer/channel.rs: -------------------------------------------------------------------------------- 1 | use async_std::sync::Arc; 2 | 3 | use log::info; 4 | 5 | use lapin::options::{BasicQosOptions, QueueDeclareOptions}; 6 | use lapin::{types::FieldTable, Channel as LapinChannel, Connection, Error as LapinError, Queue}; 7 | 8 | use crate::config::queue::config::QueueConfig; 9 | 10 | type ChannelResult = Result<(LapinChannel, Queue), LapinError>; 11 | 12 | pub struct Channel {} 13 | 14 | impl Channel { 15 | pub async fn get_queue>( 16 | connection: Arc, 17 | queue: QueueConfig, 18 | prefix: S, 19 | ) -> ChannelResult { 20 | let channel = connection.create_channel().await?; 21 | channel 22 | .basic_qos( 23 | queue.prefetch_count.unwrap_or(1) as u16, 24 | BasicQosOptions { 25 | ..Default::default() 26 | }, 27 | ) 28 | .await?; 29 | 30 | info!( 31 | "[{}] Created channel with id: {}", 32 | queue.queue_name, 33 | channel.id() 34 | ); 35 | 36 | let queue = channel 37 | .queue_declare( 38 | &format!("{}{}", prefix.as_ref(), queue.queue_name), 39 | QueueDeclareOptions { 40 | durable: true, 41 | auto_delete: false, 42 | ..Default::default() 43 | }, 44 | FieldTable::default(), 45 | ) 46 | .await?; 47 | 48 | Ok((channel, queue)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/client/consumer/connection.rs: -------------------------------------------------------------------------------- 1 | use async_std::sync::Arc; 2 | 3 | use log::info; 4 | 5 | use lapin::{ 6 | uri::AMQPAuthority, uri::AMQPUri, uri::AMQPUserInfo, Connection as LapinConnection, 7 | ConnectionProperties, Error, 8 | }; 9 | 10 | use crate::config::RabbitConfig; 11 | 12 | #[derive(Debug, Clone)] 13 | pub enum ConnectionError { 14 | NotConnected, 15 | LapinError(Error), 16 | } 17 | 18 | type ConnectionResult = Result, ConnectionError>; 19 | 20 | pub struct Connection { 21 | config: RabbitConfig, 22 | lapin: ConnectionResult, 23 | } 24 | 25 | impl Connection { 26 | pub fn new(config: RabbitConfig) -> Self { 27 | Self { 28 | config, 29 | lapin: Err(ConnectionError::NotConnected), 30 | } 31 | } 32 | 33 | pub async fn get_connection(&mut self) -> ConnectionResult { 34 | if let Ok(ref lapin) = self.lapin { 35 | if lapin.status().errored() { 36 | self.lapin = Err(ConnectionError::NotConnected); 37 | } 38 | } 39 | 40 | if self.lapin.is_err() { 41 | info!( 42 | "Connecting to RabbitMQ at {}:{}...", 43 | self.config.host, self.config.port 44 | ); 45 | 46 | match LapinConnection::connect_uri( 47 | AMQPUri { 48 | scheme: Default::default(), 49 | authority: AMQPAuthority { 50 | userinfo: AMQPUserInfo { 51 | username: self.config.username.clone(), 52 | password: self.config.password.clone(), 53 | }, 54 | host: self.config.host.clone(), 55 | port: self.config.port, 56 | }, 57 | vhost: self.config.vhost.clone(), 58 | query: Default::default(), 59 | }, 60 | ConnectionProperties::default(), 61 | ) 62 | .await 63 | { 64 | Ok(connection) => { 65 | self.lapin = Ok(Arc::new(connection)); 66 | } 67 | Err(e) => { 68 | self.lapin = Err(ConnectionError::LapinError(e)); 69 | } 70 | } 71 | } 72 | 73 | self.lapin.clone() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/client/consumer/message.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::io; 3 | use std::process::Output; 4 | use std::str; 5 | 6 | use async_std::sync::{Arc, RwLock}; 7 | 8 | use log::{error, info}; 9 | 10 | use tokio::process::Command; 11 | 12 | use futures::future::select_all; 13 | use futures::{FutureExt, TryFutureExt}; 14 | 15 | use lapin::message::Delivery; 16 | use lapin::options::{BasicAckOptions, BasicRejectOptions}; 17 | use lapin::{Channel, Error as LapinError}; 18 | 19 | use base64::encode as base64_encode; 20 | 21 | use crate::client::consumer::DEFAULT_WAIT_PART; 22 | use crate::config::queue::config::QueueConfig; 23 | use crate::config::queue::{Queue, RetryMode, RetryType}; 24 | use crate::utils; 25 | 26 | #[derive(Debug)] 27 | pub enum MessageError { 28 | LapinError(LapinError), 29 | } 30 | 31 | type MessageResult = Result; 32 | 33 | pub enum CommandResult { 34 | Timeout, 35 | Output(io::Result), 36 | } 37 | 38 | struct MessageCommand { 39 | command: Command, 40 | human: String, 41 | } 42 | 43 | pub struct Message { 44 | queue: Arc>, 45 | } 46 | 47 | const ACKNOWLEDGEMENT: i32 = 0; 48 | const NEGATIVE_ACKNOWLEDGEMENT_AND_RE_QUEUE: i32 = 1; 49 | const NEGATIVE_ACKNOWLEDGEMENT: i32 = 2; 50 | 51 | impl Message { 52 | pub fn new(queue: Arc>) -> Self { 53 | Self { queue } 54 | } 55 | 56 | pub async fn handle_message( 57 | &self, 58 | index: i32, 59 | queue_config: &QueueConfig, 60 | channel: &Channel, 61 | delivery: Delivery, 62 | ) -> MessageResult<()> { 63 | let msg = { 64 | match str::from_utf8(&delivery.data) { 65 | Ok(msg) => { 66 | if queue_config.base64 { 67 | base64_encode(msg) 68 | } else { 69 | msg.to_string().replace("\"", "") 70 | } 71 | } 72 | Err(_) => "".into(), 73 | } 74 | }; 75 | 76 | let message_command = { 77 | let cmd = self.queue.write().await.get_command(queue_config.id); 78 | let mut human_command = cmd.clone(); 79 | let mut arguments = cmd.split(' ').collect::>(); 80 | let mut command = Command::new(arguments.pop_front().unwrap()); 81 | let mut additional_arguments = VecDeque::new(); 82 | if queue_config.base64 { 83 | human_command.push_str(&format!(" --body {}", msg)); 84 | 85 | additional_arguments.push_back("--body"); 86 | additional_arguments.push_back(msg.as_str()); 87 | } else { 88 | human_command.push_str(&format!(" {}", msg)); 89 | 90 | additional_arguments.extend(msg.split(' ').collect::>()); 91 | } 92 | 93 | arguments.append(&mut additional_arguments); 94 | command.args(arguments); 95 | 96 | MessageCommand { 97 | command, 98 | human: human_command, 99 | } 100 | }; 101 | 102 | info!( 103 | "[{}] Executing command \"{}\" on consumer #{}", 104 | queue_config.queue_name, message_command.human, index 105 | ); 106 | 107 | self.process_message(index, queue_config, msg, message_command, channel, delivery) 108 | .await 109 | } 110 | 111 | async fn process_message( 112 | &self, 113 | index: i32, 114 | queue_config: &QueueConfig, 115 | msg: String, 116 | mut message_command: MessageCommand, 117 | channel: &Channel, 118 | delivery: Delivery, 119 | ) -> MessageResult<()> { 120 | let timeout = utils::wait( 121 | self.queue 122 | .write() 123 | .await 124 | .get_command_timeout(queue_config.id), 125 | ) 126 | .map(|_| CommandResult::Timeout); 127 | let output = message_command.command.output().map(CommandResult::Output); 128 | 129 | let (res, _, _) = select_all(vec![timeout.boxed(), output.boxed()]).await; 130 | match res { 131 | CommandResult::Output(output) => { 132 | let retry_type = self.queue.write().await.get_retry_type(queue_config.id); 133 | match output { 134 | Ok(output) => match retry_type { 135 | RetryType::Ignored => { 136 | channel 137 | .basic_ack( 138 | delivery.delivery_tag, 139 | BasicAckOptions { multiple: false }, 140 | ) 141 | .map_err(MessageError::LapinError) 142 | .await?; 143 | 144 | info!( 145 | "[{}] Command \"{}\" executed on consumer #{} and result ignored, message removed.", 146 | queue_config.queue_name, 147 | message_command.human, 148 | index 149 | ); 150 | } 151 | _ => { 152 | let exit_code = 153 | output.status.code().unwrap_or(NEGATIVE_ACKNOWLEDGEMENT); 154 | 155 | let exit_code = if let Some(nack_code) = queue_config.nack_code { 156 | if nack_code == exit_code { 157 | NEGATIVE_ACKNOWLEDGEMENT 158 | } else if exit_code == ACKNOWLEDGEMENT { 159 | exit_code 160 | } else { 161 | NEGATIVE_ACKNOWLEDGEMENT_AND_RE_QUEUE 162 | } 163 | } else { 164 | exit_code 165 | }; 166 | 167 | match exit_code { 168 | ACKNOWLEDGEMENT => { 169 | channel 170 | .basic_ack( 171 | delivery.delivery_tag, 172 | BasicAckOptions { multiple: false }, 173 | ) 174 | .map_err(MessageError::LapinError) 175 | .await?; 176 | 177 | info!( 178 | "[{}] Command \"{}\" succeeded on consumer #{}, message removed.", 179 | queue_config.queue_name, message_command.human, index 180 | ); 181 | 182 | self.queue.write().await.set_queue_wait( 183 | queue_config.id, 184 | queue_config.retry_wait, 185 | index, 186 | RetryMode::Normal, 187 | ); 188 | } 189 | NEGATIVE_ACKNOWLEDGEMENT_AND_RE_QUEUE => { 190 | channel 191 | .basic_reject( 192 | delivery.delivery_tag, 193 | BasicRejectOptions { requeue: true }, 194 | ) 195 | .map_err(MessageError::LapinError) 196 | .await?; 197 | 198 | info!( 199 | "[{}] Command \"{}\" failed on consumer #{}, message rejected and requeued. Output:\n{:#?}", 200 | queue_config.queue_name, 201 | message_command.human, 202 | index, 203 | output 204 | ); 205 | 206 | let ms = self 207 | .queue 208 | .write() 209 | .await 210 | .get_queue_wait(queue_config.id, index); 211 | 212 | info!( 213 | "[{}] Waiting {} milliseconds for consumer #{}...", 214 | queue_config.queue_name, ms, index 215 | ); 216 | 217 | self.wait_db(index, queue_config).await; 218 | 219 | self.queue.write().await.set_queue_wait( 220 | queue_config.id, 221 | ms, 222 | index, 223 | RetryMode::Retry, 224 | ); 225 | } 226 | _ => { 227 | channel 228 | .basic_reject( 229 | delivery.delivery_tag, 230 | BasicRejectOptions { requeue: false }, 231 | ) 232 | .map_err(MessageError::LapinError) 233 | .await?; 234 | 235 | info!( 236 | "[{}] Command \"{}\" failed on consumer #{}, message rejected. Output:\n{:#?}", 237 | queue_config.queue_name, 238 | message_command.human, 239 | index, 240 | output 241 | ); 242 | } 243 | } 244 | } 245 | }, 246 | Err(e) => { 247 | channel 248 | .basic_reject( 249 | delivery.delivery_tag, 250 | BasicRejectOptions { requeue: false }, 251 | ) 252 | .map_err(MessageError::LapinError) 253 | .await?; 254 | 255 | error!( 256 | "[{}] Error {:?} executing the command \"{}\" on consumer #{}, message \"{}\" rejected...", 257 | queue_config.queue_name, 258 | e, 259 | message_command.human, 260 | index, 261 | msg 262 | ); 263 | } 264 | } 265 | 266 | Ok(()) 267 | } 268 | CommandResult::Timeout => { 269 | channel 270 | .basic_reject(delivery.delivery_tag, BasicRejectOptions { requeue: true }) 271 | .map_err(MessageError::LapinError) 272 | .await?; 273 | 274 | info!( 275 | "[{}] Timeout occurred executing the command \"{}\" on consumer #{}, message \"{}\" rejected and requeued...", 276 | queue_config.queue_name, 277 | message_command.human, 278 | index, 279 | msg 280 | ); 281 | 282 | Ok(()) 283 | } 284 | } 285 | } 286 | 287 | async fn wait_db(&self, index: i32, queue_config: &QueueConfig) { 288 | while async { 289 | let is_enabled = self.queue.write().await.is_enabled(queue_config.id); 290 | if is_enabled { 291 | let waiting = self 292 | .queue 293 | .write() 294 | .await 295 | .get_queue_wait(queue_config.id, index) as i64 296 | - DEFAULT_WAIT_PART as i64; 297 | 298 | if waiting <= 0 { 299 | self.queue.write().await.set_queue_wait( 300 | queue_config.id, 301 | queue_config.retry_wait, 302 | index, 303 | RetryMode::Normal, 304 | ); 305 | 306 | None 307 | } else { 308 | self.queue.write().await.set_queue_wait( 309 | queue_config.id, 310 | waiting as u64, 311 | index, 312 | RetryMode::Forced, 313 | ); 314 | 315 | utils::wait(DEFAULT_WAIT_PART).await; 316 | Some(()) 317 | } 318 | } else { 319 | None 320 | } 321 | } 322 | .await 323 | .is_some() 324 | {} 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/client/consumer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | pub mod connection; 3 | mod message; 4 | 5 | use async_std::sync::{Arc, RwLock}; 6 | 7 | use log::{error, info}; 8 | 9 | use tokio::signal::unix::{signal, SignalKind}; 10 | use tokio_stream::StreamExt; 11 | 12 | use futures::future::select_all; 13 | use futures::future::FutureExt; 14 | 15 | use lapin::options::{BasicCancelOptions, BasicConsumeOptions, BasicRecoverOptions}; 16 | use lapin::{types::FieldTable, Channel as LapinChannel, Error as LapinError, Queue as LapinQueue}; 17 | 18 | use crate::client::consumer::channel::Channel; 19 | use crate::client::consumer::connection::{Connection, ConnectionError}; 20 | use crate::client::consumer::message::{Message, MessageError}; 21 | use crate::client::executor::events::{Events, EventsHandler}; 22 | use crate::config::database::Database; 23 | use crate::config::file::File; 24 | use crate::config::queue::config::QueueConfig; 25 | use crate::config::queue::Queue; 26 | use crate::config::Config; 27 | use crate::utils; 28 | 29 | const CONSUMER_WAIT: u64 = 60000; 30 | const DEFAULT_WAIT_PART: u64 = 1000; 31 | 32 | #[derive(Debug, PartialEq)] 33 | pub enum ConsumerStatus { 34 | ConsumerChanged, 35 | CountChanged, 36 | GenericOk, 37 | Killed, 38 | } 39 | 40 | #[derive(Debug)] 41 | pub enum ConsumerError { 42 | LapinError(LapinError), 43 | IoError(std::io::Error), 44 | ConnectionError(ConnectionError), 45 | MessageError(MessageError), 46 | } 47 | 48 | type ConsumerResult = Result; 49 | 50 | pub struct Consumer { 51 | queue: Arc>, 52 | connection: Connection, 53 | message: Message, 54 | hooks: Vec>>, 55 | config: Config, 56 | } 57 | 58 | impl Consumer { 59 | pub fn new(config: Config) -> Self { 60 | let queue = Arc::new(RwLock::new(Queue::new({ 61 | if config.database.enabled { 62 | Box::new(Database::new(config.database.clone())) 63 | } else { 64 | Box::new(File::new(config.rabbit.queues.clone())) 65 | } 66 | }))); 67 | 68 | Self { 69 | queue: queue.clone(), 70 | connection: Connection::new(config.rabbit.clone()), 71 | message: Message::new(queue), 72 | hooks: Vec::new(), 73 | config, 74 | } 75 | } 76 | 77 | pub async fn run(&mut self) -> ConsumerResult { 78 | match self.connection.get_connection().await { 79 | Ok(connection) => { 80 | for hook in &self.hooks { 81 | hook.write() 82 | .await 83 | .on_connect(&self.config.rabbit.host, self.config.rabbit.port); 84 | } 85 | 86 | let mut sigint = signal(SignalKind::interrupt()).map_err(ConsumerError::IoError)?; 87 | let sigint = sigint.recv().map(|_| Ok(ConsumerStatus::Killed)); 88 | let mut sigquit = signal(SignalKind::quit()).map_err(ConsumerError::IoError)?; 89 | let sigquit = sigquit.recv().map(|_| Ok(ConsumerStatus::Killed)); 90 | let mut sigterm = 91 | signal(SignalKind::terminate()).map_err(ConsumerError::IoError)?; 92 | let sigterm = sigterm.recv().map(|_| Ok(ConsumerStatus::Killed)); 93 | 94 | let mut futures = vec![sigint.boxed(), sigquit.boxed(), sigterm.boxed()]; 95 | 96 | info!("Managing queues..."); 97 | 98 | let queues = self.queue.write().await.get_queues(); 99 | if queues.is_empty() { 100 | panic!("Can't load consumers due to empty queues"); 101 | } 102 | 103 | for queue in queues { 104 | for index in 0..queue.count { 105 | futures.push( 106 | match Channel::get_queue( 107 | connection.clone(), 108 | queue.clone(), 109 | self.config.rabbit.queue_prefix.clone(), 110 | ) 111 | .await 112 | { 113 | Ok((c, q)) => { 114 | info!("[{}] Queue created", queue.queue_name); 115 | 116 | self.consume(index, queue.clone(), c, q).boxed() 117 | } 118 | Err(e) => async { Err(ConsumerError::LapinError(e)) }.boxed(), 119 | }, 120 | ); 121 | } 122 | } 123 | 124 | let (res, _, _) = select_all(futures).await; 125 | 126 | res 127 | } 128 | Err(e) => Err(ConsumerError::ConnectionError(e)), 129 | } 130 | } 131 | 132 | pub async fn consume( 133 | &self, 134 | index: i32, 135 | queue_config: QueueConfig, 136 | channel: LapinChannel, 137 | queue: LapinQueue, 138 | ) -> ConsumerResult { 139 | let consumer_name = format!("{}_consumer_{}", queue_config.consumer_name, index); 140 | 141 | if !self.queue.write().await.is_enabled(queue_config.id) { 142 | info!( 143 | "[{}] Consumer #{} with \"{}\" not enabled, waiting...", 144 | queue_config.queue_name, index, consumer_name 145 | ); 146 | } 147 | 148 | self.check_consumer(&queue_config).await; 149 | 150 | let consumer = channel 151 | .basic_consume( 152 | queue.name().as_str(), 153 | &consumer_name, 154 | BasicConsumeOptions { 155 | ..Default::default() 156 | }, 157 | FieldTable::default(), 158 | ) 159 | .await; 160 | 161 | match consumer { 162 | Ok(mut consumer) => { 163 | info!( 164 | "[{}] Consumer #{} declared \"{}\"", 165 | queue_config.queue_name, index, consumer_name 166 | ); 167 | 168 | while let Some(delivery) = consumer.next().await { 169 | match delivery { 170 | Ok((channel, delivery)) => { 171 | let is_changed = self 172 | .queue 173 | .write() 174 | .await 175 | .is_changed(queue_config.id, queue_config.count); 176 | let is_enabled = self.queue.write().await.is_enabled(queue_config.id); 177 | 178 | if !is_changed && is_enabled { 179 | self.message 180 | .handle_message(index, &queue_config, &channel, delivery) 181 | .await 182 | .map_err(ConsumerError::MessageError)?; 183 | } 184 | 185 | if !is_enabled { 186 | if !is_changed { 187 | if channel 188 | .basic_cancel( 189 | &consumer_name, 190 | BasicCancelOptions { nowait: false }, 191 | ) 192 | .await 193 | .is_err() 194 | { 195 | error!( 196 | "[{}] Error canceling the consumer #{}, returning...", 197 | queue_config.queue_name, index 198 | ); 199 | } else { 200 | utils::wait(DEFAULT_WAIT_PART).await; 201 | } 202 | } 203 | 204 | if channel 205 | .basic_recover(BasicRecoverOptions { requeue: true }) 206 | .await 207 | .is_err() 208 | { 209 | error!( 210 | "[{}] Error recovering message for consumer #{}, message is not ackable...", 211 | queue_config.queue_name, 212 | index 213 | ); 214 | 215 | return Ok(ConsumerStatus::GenericOk); 216 | } 217 | 218 | info!( 219 | "[{}] Consumer #{} not active, messages recovered and consumer canceled...", 220 | queue_config.queue_name, 221 | index 222 | ); 223 | 224 | return Ok(ConsumerStatus::ConsumerChanged); 225 | } else if is_changed { 226 | info!( 227 | "[{}] Consumers count changed, messages recovered...", 228 | queue_config.queue_name 229 | ); 230 | 231 | return Ok(ConsumerStatus::CountChanged); 232 | } 233 | } 234 | Err(e) => { 235 | error!("[{}] Error getting messages.", queue_config.queue_name); 236 | 237 | return Err(ConsumerError::LapinError(e)); 238 | } 239 | } 240 | } 241 | 242 | info!("Messages have been processed."); 243 | 244 | Ok(ConsumerStatus::GenericOk) 245 | } 246 | Err(e) => Err(ConsumerError::LapinError(e)), 247 | } 248 | } 249 | 250 | async fn check_consumer(&self, queue_config: &QueueConfig) { 251 | while async { 252 | if !self.queue.write().await.is_enabled(queue_config.id) { 253 | utils::wait(CONSUMER_WAIT).await; 254 | 255 | Some(()) 256 | } else { 257 | None 258 | } 259 | } 260 | .await 261 | .is_some() 262 | {} 263 | } 264 | } 265 | 266 | impl EventsHandler for Consumer { 267 | fn add_events_hook(mut self, hook: Arc>) -> Self { 268 | self.hooks.push(hook); 269 | 270 | self 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/client/executor/events.rs: -------------------------------------------------------------------------------- 1 | use async_std::sync::{Arc, RwLock}; 2 | 3 | #[allow(unused_variables)] 4 | pub trait Events: Send + Sync { 5 | fn on_connect(&mut self, host: &str, port: u16) {} 6 | fn on_error(&mut self, error: &str) {} 7 | } 8 | 9 | pub trait EventsHandler { 10 | fn add_events_hook(self, hook: Arc>) -> Self; 11 | } 12 | -------------------------------------------------------------------------------- /src/client/executor/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | mod waiter; 3 | 4 | use async_std::sync::{Arc, RwLock}; 5 | 6 | use crate::client::consumer::{Consumer, ConsumerError, ConsumerStatus}; 7 | use crate::client::executor::events::{Events, EventsHandler}; 8 | use crate::client::executor::waiter::Waiter; 9 | use crate::config::Config; 10 | 11 | #[derive(Debug)] 12 | pub enum ExecutorStatus { 13 | Restart, 14 | Exit, 15 | Killed, 16 | } 17 | 18 | #[derive(Debug)] 19 | pub enum ExecutorError { 20 | Error(ConsumerError), 21 | Wait(ConsumerError, u64), 22 | } 23 | 24 | type ExecutorResult = Result; 25 | 26 | pub struct Executor { 27 | waiter: Arc>, 28 | consumer: Consumer, 29 | } 30 | 31 | impl Executor { 32 | pub fn new(config: Config) -> Self { 33 | let waiter = Arc::new(RwLock::new(Waiter::new( 34 | config.rabbit.reconnections.unwrap_or(0), 35 | ))); 36 | 37 | Executor { 38 | waiter: waiter.clone(), 39 | consumer: Consumer::new(config).add_events_hook(waiter), 40 | } 41 | } 42 | 43 | pub async fn execute(&mut self) -> ExecutorResult { 44 | match self.consumer.run().await { 45 | Ok(ConsumerStatus::CountChanged) => Ok(ExecutorStatus::Restart), 46 | Ok(ConsumerStatus::ConsumerChanged) => Ok(ExecutorStatus::Restart), 47 | Ok(ConsumerStatus::GenericOk) => Ok(ExecutorStatus::Exit), 48 | Ok(ConsumerStatus::Killed) => Ok(ExecutorStatus::Killed), 49 | Err(e) => { 50 | let mut waiter = self.waiter.write().await; 51 | waiter.on_error(&format!("{:?}", e)); 52 | 53 | if waiter.is_to_close() { 54 | return Err(ExecutorError::Error(e)); 55 | } 56 | 57 | Err(ExecutorError::Wait(e, waiter.waiting)) 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/client/executor/waiter.rs: -------------------------------------------------------------------------------- 1 | use crate::client::executor::events::Events; 2 | 3 | pub struct Waiter { 4 | connections: i32, 5 | pub waiting: u64, 6 | waiting_times: i32, 7 | } 8 | 9 | impl Waiter { 10 | const LOOP_WAIT: u64 = 500; 11 | 12 | pub fn new(connections: i32) -> Self { 13 | Waiter { 14 | connections, 15 | waiting: Self::LOOP_WAIT, 16 | waiting_times: 0, 17 | } 18 | } 19 | 20 | pub fn is_to_close(&self) -> bool { 21 | if self.connections > 0 { 22 | self.waiting_times >= self.connections 23 | } else { 24 | false 25 | } 26 | } 27 | } 28 | 29 | impl Events for Waiter { 30 | fn on_connect(&mut self, _host: &str, _port: u16) { 31 | self.waiting = Self::LOOP_WAIT; 32 | self.waiting_times = 0; 33 | } 34 | 35 | fn on_error(&mut self, _error: &str) { 36 | if self.connections > 0 && self.waiting_times < self.connections { 37 | self.waiting_times += 1; 38 | } 39 | 40 | if self.waiting < (u64::max_value() / 2) { 41 | self.waiting *= 2; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/client/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod consumer; 2 | mod executor; 3 | 4 | use std::thread; 5 | use std::time::Duration; 6 | 7 | use log::{error, info}; 8 | 9 | use crate::client::consumer::ConsumerError; 10 | use crate::client::executor::{Executor, ExecutorError, ExecutorStatus}; 11 | use crate::config::Config; 12 | 13 | type ClientResult = Result; 14 | 15 | pub struct Client { 16 | executor: Executor, 17 | } 18 | 19 | impl Client { 20 | pub fn new>(environment: S, path: S) -> Self { 21 | Client { 22 | executor: Executor::new(Config::new(environment, path)), 23 | } 24 | } 25 | 26 | pub async fn run(&mut self) -> ClientResult<()> { 27 | loop { 28 | match self.executor.execute().await { 29 | Ok(ExecutorStatus::Restart) => info!("Consumer count changed, restarting..."), 30 | Ok(ExecutorStatus::Exit) => { 31 | info!("Process finished, exiting..."); 32 | 33 | break; 34 | } 35 | Ok(ExecutorStatus::Killed) => { 36 | info!("Process killed, exiting..."); 37 | 38 | break; 39 | } 40 | Err(ExecutorError::Wait(error, waiting)) => { 41 | error!("Error ({:?}), waiting {} seconds...", error, waiting / 1000); 42 | 43 | thread::sleep(Duration::from_millis(waiting)); 44 | } 45 | Err(ExecutorError::Error(e)) => { 46 | error!("Error ({:?}), exiting...", e); 47 | 48 | return Err(e); 49 | } 50 | } 51 | } 52 | 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/config/database/mod.rs: -------------------------------------------------------------------------------- 1 | mod schema; 2 | 3 | use log::info; 4 | 5 | use diesel::mysql::MysqlConnection; 6 | use diesel::prelude::*; 7 | use diesel::r2d2::{ConnectionManager, Pool}; 8 | 9 | use crate::config::database::schema::queues; 10 | use crate::config::queue::config::QueueConfig; 11 | use crate::config::queue::model::QueueModel; 12 | use crate::config::DatabaseConfig; 13 | 14 | pub struct Database { 15 | pub pool: Pool>, 16 | config: DatabaseConfig, 17 | } 18 | 19 | impl Database { 20 | const DEFAULT_PORT: i32 = 3306; 21 | const DEFAULT_RETRIES: i32 = 3; 22 | 23 | pub fn new(config: DatabaseConfig) -> Self { 24 | Database { 25 | pool: Database::pool(config.clone()), 26 | config, 27 | } 28 | } 29 | 30 | pub fn pool(config: DatabaseConfig) -> Pool> { 31 | let database_url = format!( 32 | "mysql://{}:{}@{}:{}/{}", 33 | config.user, 34 | config.password, 35 | config.host, 36 | config.port.unwrap_or(Self::DEFAULT_PORT), 37 | config.db_name 38 | ); 39 | 40 | info!( 41 | "Connecting to MySQL at {}:{}...", 42 | config.host, 43 | config.port.unwrap_or(Self::DEFAULT_PORT) 44 | ); 45 | 46 | let manager = ConnectionManager::::new(database_url); 47 | Pool::builder().build(manager).unwrap_or_else(|e| { 48 | panic!( 49 | "Error {:?} connecting to host {} with db name {}.", 50 | e, config.host, config.db_name 51 | ) 52 | }) 53 | } 54 | 55 | pub fn reconnect(&mut self) { 56 | self.pool = Database::pool(self.config.clone()); 57 | } 58 | } 59 | 60 | impl QueueModel for Database { 61 | fn get_queues(&mut self) -> Vec { 62 | for i in 1..self.config.retries.unwrap_or(Self::DEFAULT_RETRIES) { 63 | match self.pool.get() { 64 | Ok(connection) => match queues::dsl::queues.load::(&connection) { 65 | Ok(rs) => return rs, 66 | Err(e) => { 67 | if i == 1 { 68 | self.reconnect(); 69 | } else { 70 | panic!("Error checking Database: {:?}", e); 71 | } 72 | } 73 | }, 74 | Err(e) => { 75 | if i == 1 { 76 | self.reconnect(); 77 | } else { 78 | panic!("Error pooling Database: {:?}", e); 79 | } 80 | } 81 | } 82 | } 83 | 84 | vec![] 85 | } 86 | 87 | fn get_queue(&mut self, id: i32) -> Option { 88 | for i in 1..self.config.retries.unwrap_or(Self::DEFAULT_RETRIES) { 89 | match self.pool.get() { 90 | Ok(connection) => match queues::dsl::queues 91 | .filter(queues::dsl::id.eq(id)) 92 | .limit(1) 93 | .get_result::(&connection) 94 | { 95 | Ok(c) => { 96 | return Some(c); 97 | } 98 | Err(e) => { 99 | if i == 1 { 100 | self.reconnect(); 101 | } else { 102 | panic!("Error checking Database: {:?}", e); 103 | } 104 | } 105 | }, 106 | Err(e) => { 107 | if i == 1 { 108 | self.reconnect(); 109 | } else { 110 | panic!("Error pooling Database: {:?}", e); 111 | } 112 | } 113 | } 114 | } 115 | 116 | None 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/config/database/schema.rs: -------------------------------------------------------------------------------- 1 | table! { 2 | queues { 3 | id -> Integer, 4 | prefetch_count -> Nullable, 5 | queue_name -> Varchar, 6 | consumer_name -> Varchar, 7 | command -> Varchar, 8 | command_timeout -> Nullable>, 9 | base64 -> Bool, 10 | start_hour -> Nullable