This commit is contained in:
2025-09-30 14:15:21 +05:30
parent 0a146c2169
commit c21ec7ba42
8 changed files with 326 additions and 1160 deletions
+8 -176
View File
@@ -6,12 +6,11 @@
DATE:
Create: Monday, 29th Sept., 2025
Create: Tuesday, 30th Sept., 2025
OBJECTIVE:
To have a centralized Socket.IO app from where several namespaces can be registered. This is kind of like how
you can have one Quart app and register several blueprints.
Simple helper functions to work with the Socket.IO server process.
REFERENCES:
@@ -36,49 +35,14 @@ import sys
sys.path.append(".")
sys.path.append("..")
# System-level activities:
import io
import os
import random
# My utils:
from utils_v2.string import json
from utils_v2.string import regex
from utils_v2.system import files
from utils_v2.date_time import date_time
from utils_v2.database.async_mongo_v2 import AsyncMongo
from utils_v2.queue.kafka.controllers.async_kafka import ConsumerKafka, get_ssl_context
from utils_v2.cache.async_redis_cache_v3 import AsyncRedisCache
from utils_v2.serialization.json_serializer import JSONSerializer
# To make HTTP calls:
import httpx
# To work with date and time:
import datetime
import time
# Models:
from models.core.user import CoreUserInfoModel
from models.finstitutions.trading.ticks import TradingTick
# To work with SocketIO:
import socket
import socketio
# To maintain the app's state:
from wsio_v2.app_state import AppState
from wsio_v2.test.test_ns import TestNamespace
from wsio_v2.test.echo import echo
# For asynchronous activities:
import asyncio
# To work with various datatypes:
from typing import List
# Debugging:
from icecream import IceCreamDebugger
# *****************************************************************************************************************
@@ -88,42 +52,7 @@ from icecream import IceCreamDebugger
# *****************************************************************************************************************
# A custom class to maintain the app's state:
app_state = AppState()
# Debugging:
app_state.printer = IceCreamDebugger(prefix = "WSIO | ", includeContext = True)
app_state.no_context_printer = IceCreamDebugger(prefix = "WSIO | ", includeContext = False)
# To make API calls:
app_state.http_client = httpx.AsyncClient(
limits = httpx.Limits(
max_connections = 100, # ............ Maximum number of connections allowed in the pool.
max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive.
),
timeout = httpx.Timeout(
pool = 120.0, # .... Time to wait for a free connection from the pool.
connect = 2.5, # ... Time to wait for establishing a connection to the server.
write = 10.0, # .... Time to wait for sending data.
read = 9.9 # ....... Time to wait for receiving data.
)
)
# General:
app_state.SERVER_HOSTNAME = str(socket.gethostname())
app_state.ALLOWED_ORIGINS = []
# Namespaces:
app_state.NAMESPACE_DEFAULT = "/"
app_state.NAMESPACE_TEST = "/test"
app_state.NAMESPACE_TICKS = "/ticks"
app_state.NAMESPACE_ORDER_UPDATES = "/order-updates"
# Events:
app_state.EVENT_CONNECT = "connect"
app_state.EVENT_DISCONNECT = "disconnect"
app_state.EVENT_ECHO = "echo"
app_state.EVENT_TICKS = "ticks"
# --- Nothing Yet
# *****************************************************************************************************************
@@ -133,13 +62,7 @@ app_state.EVENT_TICKS = "ticks"
# *****************************************************************************************************************
# For SocketIO:
sio = socketio.AsyncServer(
cors_allowed_origins = "*",
async_mode = "asgi"
)
app = socketio.ASGIApp(sio)
sio.register_namespace(TestNamespace("/test"))
# --- Nothing Yet
# *****************************************************************************************************************
@@ -149,7 +72,7 @@ sio.register_namespace(TestNamespace("/test"))
# *****************************************************************************************************************
def origin_is_allowed(origin: str) -> bool:
def origin_is_allowed(origin: str, app_state: AppState) -> bool:
"""
To check if a given origin is in the allowed list.
@@ -182,7 +105,8 @@ def origin_is_allowed(origin: str) -> bool:
async def init(
script_id: str,
debug: bool
debug: bool,
app_state: AppState,
):
"""
@@ -192,12 +116,6 @@ async def init(
:return: True if initialized successfully, else False.
"""
# Declare the required global variables:
# global SCRIPT_DATA
# global ALLOWED_ORIGINS
# global redis_cache
# global kafka_consumer
# Basic stuff:
if debug: app_state.printer.enable()
app_state.printer("Initializing.")
@@ -300,29 +218,6 @@ async def init(
return True
# ---------------------------------------------------------------------------------------------------------------------
def register_namespaces():
"""
A quick function that registers all the namespaces to the same Socket.IO app.
"""
app_state.printer("Registering namespaces.")
sio.register_namespace(TestNamespace("/test"))
app_state.printer("Namespaces registered.")
# ---------------------------------------------------------------------------------------------------------------------
@sio.on(event = app_state.EVENT_ECHO, namespace = app_state.NAMESPACE_DEFAULT)
async def echo(sid, data):
print(f"Received (default ns) from {sid}: {data}")
await sio.emit("echo", data, to = sid)
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
@@ -332,67 +227,4 @@ async def echo(sid, data):
if __name__ == "__main__":
app_state.printer("Main.")
# To get args from the terminal:
import argparse
# To run the ASGI:
import uvicorn
from multiprocessing import freeze_support
# Get the config from the command-line:
parser = argparse.ArgumentParser(description = f"SocketIO to serve live market data (and a general passthrough).")
parser.add_argument(
"-w", "--workers",
type = int,
help = "The no. of threads to spin up for this instance!",
default = 2
)
parser.add_argument(
"-a", "--host",
type = str,
help = "The host for the app. e.g.: '0.0.0.0' or '127.0.0.1'.",
default = "0.0.0.0"
)
parser.add_argument(
"-p", "--port",
type = int,
help = "The port no. to bind the app to.",
default = 8080
)
parser.add_argument(
"-s", "--script-id",
type = str,
help = "The id of this script (will affect the loaded config)."
)
parser.add_argument(
"-d", "--debug",
action = "store_true",
help = "Whether, or not, you want to see debugging messages in the terminal.",
default = False
)
args = parser.parse_args()
# Note down the config;
os.environ["SCRIPT_ID"] = args.script_id
os.environ["DEBUG"] = str(args.debug)
# Startup message:
app_state.printer.enable()
app_state.printer(str(args.debug))
if str(args.debug).lower().find("false") >= 0: app_state.printer.disable()
# Register all namespaces:
app_state.init_func = init
app_state.origin_check_func = origin_is_allowed
# register_namespaces()
# Run the gateway:
freeze_support()
uvicorn.run(
app = "app:app",
workers = args.workers,
host = args.host,
port = args.port
)
pass