feat: Flask consumer urllib + RabbitMQ HTTP API
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
"""Consumer — RabbitMQ HTTP API → Redis + MongoDB (no AMQP libs)"""
|
||||
import json, os, base64, threading, time
|
||||
from urllib.request import Request, urlopen
|
||||
from flask import Flask, jsonify
|
||||
import redis, pymongo
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
RMQ_HOST = os.environ["RMQ_HOST"]
|
||||
RMQ_USER = os.environ["RMQ_USER"]
|
||||
RMQ_PASS = os.environ["RMQ_PASS"]
|
||||
RMQ_QUEUE = os.environ["RMQ_QUEUE"]
|
||||
RMQ_API = f"http://{RMQ_HOST}:15672/api"
|
||||
AUTH = base64.b64encode(f"{RMQ_USER}:{RMQ_PASS}".encode()).decode()
|
||||
|
||||
REDIS_HOST = os.environ["REDIS_HOST"]
|
||||
REDIS_PORT = int(os.environ.get("REDIS_PORT", 6379))
|
||||
REDIS_PASS = os.environ.get("REDIS_PASS", "")
|
||||
|
||||
MONGO_URI = os.environ["MONGO_URI"]
|
||||
MONGO_DB = os.environ.get("MONGO_DB", "iot")
|
||||
|
||||
consumed = 0; errors = 0
|
||||
r = None; mongo = None
|
||||
|
||||
def get_redis():
|
||||
global r
|
||||
if r is None:
|
||||
r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASS or None, decode_responses=True)
|
||||
print("[CONSUMER] Redis connected")
|
||||
return r
|
||||
|
||||
def get_mongo():
|
||||
global mongo
|
||||
if mongo is None:
|
||||
mongo = pymongo.MongoClient(MONGO_URI)[MONGO_DB]["iot_events"]
|
||||
mongo.create_index("timestamp", expireAfterSeconds=604800)
|
||||
print("[CONSUMER] MongoDB connected")
|
||||
return mongo
|
||||
|
||||
def rmq_get(url):
|
||||
req = Request(url, headers={"Authorization": f"Basic {AUTH}"})
|
||||
return json.loads(urlopen(req, timeout=5).read())
|
||||
|
||||
def store(event):
|
||||
rd = get_redis()
|
||||
rd.hset("iot:latest", event["sensor_id"], json.dumps({
|
||||
"value": event["value"], "unit": event["unit"], "location": event["location"], "timestamp": event["timestamp"],
|
||||
}))
|
||||
rd.hincrby("iot:counters", event["device_type"], 1)
|
||||
rd.zadd("iot:recent", {json.dumps(event): 0})
|
||||
rd.zremrangebyrank("iot:recent", 0, -1001)
|
||||
get_mongo().insert_one({
|
||||
"event_id": event["event_id"], "sensor_id": event["sensor_id"],
|
||||
"device_type": event["device_type"], "location": event["location"],
|
||||
"value": event["value"], "unit": event["unit"], "timestamp": event["timestamp"],
|
||||
})
|
||||
|
||||
def poll_loop():
|
||||
global consumed, errors
|
||||
# Wait for RabbitMQ to be ready
|
||||
time.sleep(10)
|
||||
while True:
|
||||
try:
|
||||
# Get messages from queue via HTTP API
|
||||
msgs = rmq_get(f"{RMQ_API}/queues/%2F/{RMQ_QUEUE}/get")
|
||||
if msgs:
|
||||
for m in msgs:
|
||||
if m.get("payload"):
|
||||
event = json.loads(base64.b64decode(m["payload"]).decode())
|
||||
store(event)
|
||||
consumed += 1
|
||||
# Ack via API
|
||||
rmq_get(f"{RMQ_API}/queues/%2F/{RMQ_QUEUE}/get?requeue=false&count=1")
|
||||
if consumed > 0 and consumed % 10 == 0:
|
||||
print(f"[CONSUMER] Processed {consumed}")
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f"[CONSUMER] Error: {e}")
|
||||
time.sleep(2)
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return jsonify({"service":"iot-consumer","status":"running","queue":RMQ_QUEUE,"consumed":consumed,"errors":errors})
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
return jsonify({"status":"ok","consumed":consumed})
|
||||
|
||||
if __name__ == "__main__":
|
||||
threading.Thread(target=poll_loop, daemon=True).start()
|
||||
app.run(host="0.0.0.0", port=int(os.environ.get("PORT",5000)))
|
||||
Reference in New Issue
Block a user