-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This document describes the Proxy Service, a GoLang-based service designed to manage incoming HTTP requests, route them based on flexible configurations, and handle responses efficiently.
- What is Proxy Service
- Key Features
- PostgreSQL Use Case
- Execution
- Configuration
- Supported Services
- Request from Proxy Service Format
- Response for Proxy Service Format
- Proxy Tasks[]
- Worker Types
- Core Components
- Middlewares
- File Manager
The Proxy Service is a GoLang application designed for managing incoming HTTP requests.
It uses a Proxy Config to define API requests and specifies where and under what conditions to direct them.
The Proxy Service then performs the following steps:
- Receives the request.
- Optionally verifies authorization cookies.
- Adds
request_context(with IP, user-agent, etc.) and other configurable parameters to the request body. - Applies request balancing rules to distribute requests evenly among multiple servers.
- Calls the necessary service, such as a NodeJS application or a PostgreSQL function.
- Processes the response. If the response contains Proxy Tasks (in the
proxy:[]section), it executes these tasks, either synchronously (waiting for a response) or in the background.
- Flexible interaction between various services.
- Load balancing across servers.
- Multi-threaded transaction execution.
- Background mode support ensures fast client response.
- Support for EventStream for server-client communication.
- Collector for gathering data from multiple transactions with subsequent single transaction updates.
- Task scheduler with multiple operating modes.
- Enhanced security through supporting a single entry point to services. For example, to work with a database, it's sufficient to create a user with permissions only to run one function, without access to tables.
- Collects metrics for further visualization via Grafana and Prometheus.
With the Proxy Service, you can develop powerful high-load systems with business logic residing in the database. This approach ensures high performance: data is processed where it resides.
The use of Collectors allows processing up to 1 million transactions with data updates per second.
To implement this approach, the following steps are necessary:
- Develop a data architecture that accounts for asynchronous updates – so that data is updated not directly via
INSERT/UPDATE, but through Collectors. - Split long-running transactions into multiple steps using Proxy Tasks.
- Implement external calls (e.g., to ChatGPT) also via Proxy Tasks.
Examples of configurations can be found in the "config" folder.
The main sections of the config are:
databaseserverworkercollectorsmetrics
| Service Name | Keys | Description |
|---|---|---|
db |
apicall string - name of the function in the database that the Proxy Service calls |
Execute a request in the database |
http |
method string - request methodurl string - request URLheaders object - Headersbody string|object - request bodytimeout string - timeout (e.g., "30s", but not more than 600s) |
Execute an HTTP request |
eventstream |
es_id string - connection identifierpayload string - request bodyes_list array - array of connection identifiers |
Send a message to the client via EventStream |
file |
Write file:savePath string - path to save the file with body contentbody string - file content to save at savePathfilename string - filename for Content-Disposition headerRead file: readPath string - path to read the fileheaders object - object with headers, including Content-Type
|
Read/write a file |
metrics |
measurer string - metric collector typemetric string - metric namelabels object - set of labels for the metricvalue float - metric value |
Send value to metrics |
collector |
collector string - collector namedata object - data to send to the Uniproxy collector |
Send data to the Uniproxy collector |
command |
command []string - array of command arguments to execute on the server |
Executes a CLI command on the server, as the system user running the proxy |
{
"api_call": "<http API path>",
"request_context": {
"ip": "<ip_address>",
"ua": "<user_agent>"
},
"<any other keys>": "..."
}{
"error": {
"code": "<http code>",
"message": "<any text message>"
},
"http_response": {
"http_code": "<http code>",
"header": {
"<object with headers>": "..."
},
"body": {
"<object with API response>": "..."
}
},
"proxy_tasks": [
"<array with Proxy Tasks>"
],
"proxy_options": {
"rollback": "1 = rollback a database transaction | 0 (default)"
}
}An array of tasks for the Proxy Service. Tasks can be executed sequentially, waiting for a response, or in parallel, in background mode.
Each task is described by the following parameters:
{
"service": "<service type as described above>",
"data": {
"api_call": "<API url to call>",
"<any parameters for the API request>": "..."
},
"options": {
"pauseBefore": "<period to pause before the task, e.g. “10ms”. 0 = no pause>",
"pauseAfter": "<period to pause after the task, e.g. “10ms”. 0 = no pause>",
"timeout": "<timeout to wait for response from service, e.g. “5s”>",
"responseTo": "<key to save the service response>",
"collectedTo": "<key in request to send collected responses>",
"toClient": "<true = direct response to client without any transformation>",
"taskGroup": "\"self\" (default) | \"nowait\" | \"<group key>\""
}
}taskGroup Options:
-
self: Wait for response and return to client (if it's the last "self" task). -
nowait: Do not wait for a response, run in a separate goroutine. -
<group key>: Run all tasks with the same<group key>in parallel goroutines and wait for all responses.
-
task_ticker: Periodic task execution at a fixed interval, regardless of when the previous task finished (task initiator - worker). -
task_wait: Periodic task execution at a fixed interval, taking into account the completion of the previous task (task initiator - worker). -
task_idle_wait: If theproxyTasks[]array is empty, it waits for a specified period. If not empty, it executes the task immediately again (task initiator - worker). -
notify: Listener for PostgreSQL database notifications (pg_notifycalls) (task initiator - database).
- Main service: Contains a set of processes, within which taskers are launched, and within those, workers are launched.
- Worker: Each worker runs a channel into which the tasker throws tasks for execution.
- Tasker: A thread within a process that puts periodic tasks (requests to the database) into a channel (which listeners listen to) for execution.
- Listener: Listens to the channel (where the tasker puts tasks) and executes the received task.
{
"name": "upload",
"folder": "/files",
"mimext": {
"image/*": "jpg",
"text/csv": "csv",
"image/jpg": "jpg",
"image/png": "png",
"image/jpeg": "jpg",
"text/plain": "txt",
"image/jpe?g": "jpg",
"application/pdf": "pdf",
"application/vnd": {
"openxmlformats-officedocument": {
"wordprocessingml": {
"document": "doc"
}
}
},
"application/zip": "zip",
"application/gzip": "gz",
"application/x-7z-compressed": "7z"
},
"source": "file",
"fileMode": 777,
"destination": "folder",
"nameTemplate": "user-file-*.$ext",
"requestField": "upload"
}The Proxy Service interacts with two main folders:
-
/files: Folder for internal files, used for data upload/download. -
/public: Folder for public static files, images, scripts, JSON config files from the DB, and others.
To send a required file to the client, simply complete the transaction with the following Proxy Tasks[]:
[
{
"data": {
"readPath": "/files/a.txt"
},
"options": {
"taskGroup": "self"
},
"service": "file"
}
]To write data to a required file, simply complete the transaction with the following Proxy Tasks[]:
{
"data": {
"savePath": "/files/a.txt",
"body": "<textual content>",
"body_base64": "<base64 content, only if body is not specified>"
},
"options": {
"taskGroup": "self"
},
"service": "file"
}