spring-ready-python 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spring_ready_python-0.1.0/.github/workflows/python-publish.yml +69 -0
- spring_ready_python-0.1.0/.gitignore +70 -0
- spring_ready_python-0.1.0/Dockerfile +29 -0
- spring_ready_python-0.1.0/PKG-INFO +459 -0
- spring_ready_python-0.1.0/README.md +421 -0
- spring_ready_python-0.1.0/docker-compose.yml +38 -0
- spring_ready_python-0.1.0/example.py +170 -0
- spring_ready_python-0.1.0/pyproject.toml +73 -0
- spring_ready_python-0.1.0/spring_ready/__init__.py +30 -0
- spring_ready_python-0.1.0/spring_ready/actuator/__init__.py +68 -0
- spring_ready_python-0.1.0/spring_ready/actuator/auditevents.py +114 -0
- spring_ready_python-0.1.0/spring_ready/actuator/beans.py +141 -0
- spring_ready_python-0.1.0/spring_ready/actuator/caches.py +111 -0
- spring_ready_python-0.1.0/spring_ready/actuator/configprops.py +109 -0
- spring_ready_python-0.1.0/spring_ready/actuator/discovery.py +131 -0
- spring_ready_python-0.1.0/spring_ready/actuator/env.py +205 -0
- spring_ready_python-0.1.0/spring_ready/actuator/health.py +145 -0
- spring_ready_python-0.1.0/spring_ready/actuator/heapdump.py +80 -0
- spring_ready_python-0.1.0/spring_ready/actuator/httptrace.py +138 -0
- spring_ready_python-0.1.0/spring_ready/actuator/info.py +123 -0
- spring_ready_python-0.1.0/spring_ready/actuator/logfile.py +201 -0
- spring_ready_python-0.1.0/spring_ready/actuator/loggers.py +184 -0
- spring_ready_python-0.1.0/spring_ready/actuator/mappings.py +88 -0
- spring_ready_python-0.1.0/spring_ready/actuator/metrics.py +260 -0
- spring_ready_python-0.1.0/spring_ready/actuator/prometheus.py +354 -0
- spring_ready_python-0.1.0/spring_ready/actuator/refresh.py +86 -0
- spring_ready_python-0.1.0/spring_ready/actuator/scheduledtasks.py +95 -0
- spring_ready_python-0.1.0/spring_ready/actuator/threaddump.py +116 -0
- spring_ready_python-0.1.0/spring_ready/config/__init__.py +5 -0
- spring_ready_python-0.1.0/spring_ready/config/loader.py +215 -0
- spring_ready_python-0.1.0/spring_ready/core.py +479 -0
- spring_ready_python-0.1.0/spring_ready/eureka/__init__.py +15 -0
- spring_ready_python-0.1.0/spring_ready/eureka/client.py +291 -0
- spring_ready_python-0.1.0/spring_ready/eureka/discovery.py +198 -0
- spring_ready_python-0.1.0/spring_ready/eureka/instance.py +200 -0
- spring_ready_python-0.1.0/spring_ready/eureka/registry.py +191 -0
- spring_ready_python-0.1.0/spring_ready/exceptions.py +26 -0
- spring_ready_python-0.1.0/spring_ready/integrations/__init__.py +8 -0
- spring_ready_python-0.1.0/spring_ready/integrations/fastapi.py +521 -0
- spring_ready_python-0.1.0/spring_ready/retry.py +97 -0
- spring_ready_python-0.1.0/tests/test_basic.py +155 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# This workflow will upload a Python Package to PyPI when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Upload Python Package
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release-build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.x"
|
|
28
|
+
|
|
29
|
+
- name: Build release distributions
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install build
|
|
32
|
+
python -m build
|
|
33
|
+
|
|
34
|
+
- name: Upload distributions
|
|
35
|
+
uses: actions/upload-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
name: release-dists
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
pypi-publish:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
needs:
|
|
43
|
+
- release-build
|
|
44
|
+
permissions:
|
|
45
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
46
|
+
id-token: write
|
|
47
|
+
|
|
48
|
+
# Dedicated environments with protections for publishing are strongly recommended.
|
|
49
|
+
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
|
|
50
|
+
environment:
|
|
51
|
+
name: pypi
|
|
52
|
+
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
|
|
53
|
+
# url: https://pypi.org/p/YOURPROJECT
|
|
54
|
+
#
|
|
55
|
+
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
|
|
56
|
+
# ALTERNATIVE: exactly, uncomment the following line instead:
|
|
57
|
+
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
|
|
58
|
+
|
|
59
|
+
steps:
|
|
60
|
+
- name: Retrieve release distributions
|
|
61
|
+
uses: actions/download-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: release-dists
|
|
64
|
+
path: dist/
|
|
65
|
+
|
|
66
|
+
- name: Publish release distributions to PyPI
|
|
67
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
68
|
+
with:
|
|
69
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Unit test / coverage reports
|
|
35
|
+
htmlcov/
|
|
36
|
+
.tox/
|
|
37
|
+
.nox/
|
|
38
|
+
.coverage
|
|
39
|
+
.coverage.*
|
|
40
|
+
.cache
|
|
41
|
+
nosetests.xml
|
|
42
|
+
coverage.xml
|
|
43
|
+
*.cover
|
|
44
|
+
*.py,cover
|
|
45
|
+
.hypothesis/
|
|
46
|
+
.pytest_cache/
|
|
47
|
+
|
|
48
|
+
# Environments
|
|
49
|
+
.env
|
|
50
|
+
.venv
|
|
51
|
+
env/
|
|
52
|
+
venv/
|
|
53
|
+
ENV/
|
|
54
|
+
env.bak/
|
|
55
|
+
venv.bak/
|
|
56
|
+
|
|
57
|
+
# IDEs
|
|
58
|
+
.idea/
|
|
59
|
+
.vscode/
|
|
60
|
+
*.swp
|
|
61
|
+
*.swo
|
|
62
|
+
*~
|
|
63
|
+
|
|
64
|
+
# macOS
|
|
65
|
+
.DS_Store
|
|
66
|
+
|
|
67
|
+
# Project specific
|
|
68
|
+
*.log
|
|
69
|
+
/logs/
|
|
70
|
+
/.claude/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
LABEL maintainer="your-email@example.com"
|
|
4
|
+
LABEL description="Spring-Ready Python Microservice"
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Install dependencies
|
|
9
|
+
COPY requirements.txt .
|
|
10
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
11
|
+
|
|
12
|
+
# Copy application code
|
|
13
|
+
COPY . .
|
|
14
|
+
|
|
15
|
+
# Environment variables (can be overridden)
|
|
16
|
+
ENV SPRING_APPLICATION_NAME=python-service
|
|
17
|
+
ENV APP_PORT=8080
|
|
18
|
+
ENV SPRING_PROFILES_ACTIVE=production
|
|
19
|
+
ENV EUREKA_SERVER_URL=http://eureka:8761/eureka/
|
|
20
|
+
|
|
21
|
+
# Expose port
|
|
22
|
+
EXPOSE 8080
|
|
23
|
+
|
|
24
|
+
# Health check
|
|
25
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
|
26
|
+
CMD python -c "import requests; requests.get('http://localhost:8080/actuator/health').raise_for_status()"
|
|
27
|
+
|
|
28
|
+
# Run the application
|
|
29
|
+
CMD ["uvicorn", "example:app", "--host", "0.0.0.0", "--port", "8080"]
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spring-ready-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Make Python FastAPI apps work with Spring Boot ecosystem (Eureka, Config Server, Actuator)
|
|
5
|
+
Project-URL: Homepage, https://github.com/tcivie/spring-ready-python
|
|
6
|
+
Project-URL: Issues, https://github.com/tcivie/spring-ready-python/issues
|
|
7
|
+
Author-email: Gleb Tcivie <gleb@tcivie.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: actuator,config-server,eureka,fastapi,microservices,prometheus,spring-boot
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: FastAPI
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: fastapi>=0.100.0
|
|
23
|
+
Requires-Dist: requests>=2.31.0
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == 'all'
|
|
26
|
+
Requires-Dist: spring-config-client-python>=0.1.0; extra == 'all'
|
|
27
|
+
Provides-Extra: config
|
|
28
|
+
Requires-Dist: spring-config-client-python>=0.1.0; extra == 'config'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: httpx>=0.24.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: uvicorn>=0.23.0; extra == 'dev'
|
|
35
|
+
Provides-Extra: metrics
|
|
36
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == 'metrics'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# Spring-Ready Python
|
|
40
|
+
|
|
41
|
+
Make your Python FastAPI app work seamlessly with Spring Boot ecosystem: Eureka, Config Server, Spring Boot Admin, and Prometheus.
|
|
42
|
+
|
|
43
|
+
## Why This Exists
|
|
44
|
+
|
|
45
|
+
You have a Spring Boot microservices architecture with Eureka, Config Server, Spring Boot Admin, and Prometheus monitoring. You want to add a Python service but keep everything working together. This library makes that dead simple.
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
✅ **Eureka Service Registration** - Automatic registration with heartbeat
|
|
50
|
+
✅ **Config Server Integration** - Load config from Spring Cloud Config with Eureka discovery
|
|
51
|
+
✅ **Actuator Endpoints** - `/actuator/health`, `/actuator/info`, `/actuator/prometheus`
|
|
52
|
+
✅ **FastAPI Integration** - Drop-in support for FastAPI apps
|
|
53
|
+
✅ **Fail-Fast Behavior** - Matches Spring Boot's startup failure handling
|
|
54
|
+
✅ **Exponential Backoff Retry** - Retry logic for Eureka and Config Server
|
|
55
|
+
✅ **Zero Magic** - Own implementation, no hidden dependencies
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install spring-ready-python
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Optional dependencies:
|
|
64
|
+
```bash
|
|
65
|
+
# For Config Server support
|
|
66
|
+
pip install spring-config-client-python
|
|
67
|
+
|
|
68
|
+
# For Prometheus metrics
|
|
69
|
+
pip install prometheus-client
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from spring_ready import SpringReadyApp
|
|
76
|
+
from fastapi import FastAPI
|
|
77
|
+
|
|
78
|
+
# Create FastAPI app
|
|
79
|
+
app = FastAPI()
|
|
80
|
+
|
|
81
|
+
# Add Spring integration
|
|
82
|
+
spring_app = SpringReadyApp(app)
|
|
83
|
+
spring_app.start()
|
|
84
|
+
|
|
85
|
+
# Your routes
|
|
86
|
+
@app.get("/")
|
|
87
|
+
def read_root():
|
|
88
|
+
return {"message": "Hello from Spring-Ready Python!"}
|
|
89
|
+
|
|
90
|
+
# Run with uvicorn
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
import uvicorn
|
|
93
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That's it. Your app now:
|
|
97
|
+
- Registers with Eureka
|
|
98
|
+
- Loads config from Config Server (discovered via Eureka)
|
|
99
|
+
- Exposes `/actuator/health`, `/actuator/info`, `/actuator/prometheus`
|
|
100
|
+
- Shows up in Spring Boot Admin
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
Set via environment variables (matching Spring Boot conventions):
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Application
|
|
108
|
+
SPRING_APPLICATION_NAME=my-python-service
|
|
109
|
+
APP_PORT=8080
|
|
110
|
+
SPRING_PROFILES_ACTIVE=production
|
|
111
|
+
|
|
112
|
+
# Eureka
|
|
113
|
+
EUREKA_SERVER_URL=http://eureka:8761/eureka/
|
|
114
|
+
EUREKA_INSTANCE_IP=192.168.1.100 # Optional: custom IP for registration
|
|
115
|
+
EUREKA_INSTANCE_HOSTNAME=my-service.example.com # Optional: custom hostname
|
|
116
|
+
|
|
117
|
+
# Config Server (optional - will be discovered from Eureka)
|
|
118
|
+
CONFIG_SERVER_URI=http://config-server:8888
|
|
119
|
+
CONFIG_SERVER_USERNAME=admin
|
|
120
|
+
CONFIG_SERVER_PASSWORD=secret
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Environment Variables Reference
|
|
124
|
+
|
|
125
|
+
| Variable | Description | Default | Example |
|
|
126
|
+
|----------|-------------|---------|---------|
|
|
127
|
+
| `SPRING_APPLICATION_NAME` | Application name | `python-service` | `my-python-service` |
|
|
128
|
+
| `APP_PORT` | Application port | `8080` | `8080` |
|
|
129
|
+
| `SPRING_PROFILES_ACTIVE` | Active profile | `default` | `production` |
|
|
130
|
+
| `EUREKA_SERVER_URL` | Eureka server URL(s) | `http://localhost:8761/eureka/` | `http://eureka:8761/eureka/` |
|
|
131
|
+
| `EUREKA_INSTANCE_IP` | Custom IP for registration | Auto-detected | `192.168.1.100` |
|
|
132
|
+
| `EUREKA_INSTANCE_HOSTNAME` | Custom hostname | Auto-detected | `my-service.local` |
|
|
133
|
+
| `CONFIG_SERVER_URI` | Config Server URL | Discovered from Eureka | `http://config:8888` |
|
|
134
|
+
| `CONFIG_SERVER_SERVICE_ID` | Config Server service ID | `CONFIG-SERVER` | `CONFIG-SERVER` |
|
|
135
|
+
| `CONFIG_SERVER_USERNAME` | Config Server username | None | `admin` |
|
|
136
|
+
| `CONFIG_SERVER_PASSWORD` | Config Server password | None | `secret` |
|
|
137
|
+
|
|
138
|
+
## Docker Example
|
|
139
|
+
|
|
140
|
+
```dockerfile
|
|
141
|
+
FROM python:3.11-slim
|
|
142
|
+
|
|
143
|
+
WORKDIR /app
|
|
144
|
+
COPY requirements.txt .
|
|
145
|
+
RUN pip install -r requirements.txt
|
|
146
|
+
|
|
147
|
+
COPY . .
|
|
148
|
+
|
|
149
|
+
ENV SPRING_APPLICATION_NAME=my-service
|
|
150
|
+
ENV EUREKA_SERVER_URL=http://eureka:8761/eureka/
|
|
151
|
+
ENV SPRING_PROFILES_ACTIVE=production
|
|
152
|
+
|
|
153
|
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
# docker-compose.yml
|
|
158
|
+
services:
|
|
159
|
+
my-service:
|
|
160
|
+
build: .
|
|
161
|
+
environment:
|
|
162
|
+
- EUREKA_SERVER_URL=http://eureka:8761/eureka/
|
|
163
|
+
- SPRING_PROFILES_ACTIVE=production
|
|
164
|
+
ports:
|
|
165
|
+
- "8080:8080"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Advanced Usage
|
|
169
|
+
|
|
170
|
+
### Custom Health Checks
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from spring_ready import SpringReadyApp
|
|
174
|
+
|
|
175
|
+
spring_app = SpringReadyApp(app)
|
|
176
|
+
spring_app.start()
|
|
177
|
+
|
|
178
|
+
# Add custom health check
|
|
179
|
+
def check_database():
|
|
180
|
+
# Your database check logic
|
|
181
|
+
return database.is_connected()
|
|
182
|
+
|
|
183
|
+
spring_app.health_endpoint.add_check("database", check_database)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Service Discovery
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# Find another service registered in Eureka
|
|
190
|
+
config_server_url = spring_app.service_discovery.get_service_url("CONFIG-SERVER")
|
|
191
|
+
|
|
192
|
+
# Get all instances of a service
|
|
193
|
+
instances = spring_app.service_discovery.get_instances("my-other-service")
|
|
194
|
+
|
|
195
|
+
for instance in instances:
|
|
196
|
+
print(f"Instance: {instance.base_url}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Manual Configuration
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from spring_ready import SpringReadyApp
|
|
203
|
+
|
|
204
|
+
spring_app = SpringReadyApp(
|
|
205
|
+
app=app,
|
|
206
|
+
app_name="my-service",
|
|
207
|
+
app_port=8080,
|
|
208
|
+
eureka_servers=["http://eureka1:8761/eureka/", "http://eureka2:8761/eureka/"],
|
|
209
|
+
profile="production",
|
|
210
|
+
fail_fast=True,
|
|
211
|
+
prefer_ip_address=True
|
|
212
|
+
)
|
|
213
|
+
spring_app.start()
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Custom IP Address Registration
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from spring_ready import SpringReadyApp
|
|
220
|
+
|
|
221
|
+
# Specify a custom IP address for Eureka registration
|
|
222
|
+
# Useful in Docker/Kubernetes when you need to advertise a specific IP
|
|
223
|
+
spring_app = SpringReadyApp(
|
|
224
|
+
app=app,
|
|
225
|
+
app_name="my-service",
|
|
226
|
+
instance_ip="192.168.1.100", # Custom IP address
|
|
227
|
+
instance_hostname="my-service.example.com" # Optional: custom hostname
|
|
228
|
+
)
|
|
229
|
+
spring_app.start()
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Or via environment variables:
|
|
233
|
+
```bash
|
|
234
|
+
export EUREKA_INSTANCE_IP=192.168.1.100
|
|
235
|
+
export EUREKA_INSTANCE_HOSTNAME=my-service.example.com
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## How It Works
|
|
239
|
+
|
|
240
|
+
### Startup Sequence
|
|
241
|
+
|
|
242
|
+
1. **Eureka Registration**: Registers with Eureka (with retry/backoff)
|
|
243
|
+
2. **Config Discovery**: Discovers Config Server from Eureka
|
|
244
|
+
3. **Config Loading**: Loads configuration into `os.environ`
|
|
245
|
+
4. **Actuator Setup**: Exposes health, info, and metrics endpoints
|
|
246
|
+
5. **Heartbeat**: Starts background thread for Eureka heartbeats (every 30s)
|
|
247
|
+
|
|
248
|
+
### Shutdown Sequence
|
|
249
|
+
|
|
250
|
+
1. Stops heartbeat thread
|
|
251
|
+
2. Deregisters from Eureka
|
|
252
|
+
3. Clean exit
|
|
253
|
+
|
|
254
|
+
### Behavior Matching Spring Boot
|
|
255
|
+
|
|
256
|
+
| Feature | Spring Boot | spring-ready-python |
|
|
257
|
+
|---------|-------------|---------------------|
|
|
258
|
+
| Fail-fast on startup | ✓ | ✓ |
|
|
259
|
+
| Exponential backoff retry | ✓ | ✓ |
|
|
260
|
+
| Eureka heartbeat (30s) | ✓ | ✓ |
|
|
261
|
+
| Config Server discovery | ✓ | ✓ |
|
|
262
|
+
| Actuator endpoints | ✓ | ✓ |
|
|
263
|
+
| Graceful shutdown | ✓ | ✓ |
|
|
264
|
+
|
|
265
|
+
## Actuator Endpoints
|
|
266
|
+
|
|
267
|
+
### GET /actuator/health
|
|
268
|
+
|
|
269
|
+
Returns application health status:
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"status": "UP",
|
|
274
|
+
"components": {
|
|
275
|
+
"diskSpace": {"status": "UP"},
|
|
276
|
+
"eureka": {"status": "UP"},
|
|
277
|
+
"ping": {"status": "UP"}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### GET /actuator/info
|
|
283
|
+
|
|
284
|
+
Returns application metadata:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"app": {
|
|
289
|
+
"name": "my-python-service",
|
|
290
|
+
"version": "1.0.0"
|
|
291
|
+
},
|
|
292
|
+
"python": {
|
|
293
|
+
"version": "3.11.5",
|
|
294
|
+
"runtime": {
|
|
295
|
+
"name": "CPython",
|
|
296
|
+
"version": "3.11.5"
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### GET /actuator/prometheus
|
|
303
|
+
|
|
304
|
+
Returns Prometheus metrics in exposition format:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
# HELP python_info Python platform information
|
|
308
|
+
# TYPE python_info gauge
|
|
309
|
+
python_info{implementation="CPython",version="3.11.5"} 1.0
|
|
310
|
+
...
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Troubleshooting
|
|
314
|
+
|
|
315
|
+
### No Registration Attempts to Eureka Server
|
|
316
|
+
|
|
317
|
+
**Symptom**: You don't see any connection attempts to your Eureka server in logs or network monitoring.
|
|
318
|
+
|
|
319
|
+
**Cause**: The application is likely using the default Eureka URL (`http://localhost:8761/eureka/`) instead of your server.
|
|
320
|
+
|
|
321
|
+
**Solution**:
|
|
322
|
+
|
|
323
|
+
1. **Set the environment variable** (Recommended):
|
|
324
|
+
```bash
|
|
325
|
+
export EUREKA_SERVER_URL=http://10.10.0.1:8761/eureka/
|
|
326
|
+
python your_app.py
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
2. **Or pass it explicitly in code**:
|
|
330
|
+
```python
|
|
331
|
+
spring_app = SpringReadyApp(
|
|
332
|
+
app,
|
|
333
|
+
eureka_servers=["http://10.10.0.1:8761/eureka/"]
|
|
334
|
+
)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
3. **Verify your configuration**:
|
|
338
|
+
```python
|
|
339
|
+
import os
|
|
340
|
+
print(f"EUREKA_SERVER_URL: {os.getenv('EUREKA_SERVER_URL')}")
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
4. **Check the startup logs**: Look for this message:
|
|
344
|
+
```
|
|
345
|
+
INFO:spring_ready.core:Configured Eureka server(s): http://10.10.0.1:8761/eureka/
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
If you see:
|
|
349
|
+
```
|
|
350
|
+
WARNING:spring_ready.core:Using default Eureka server URL (http://localhost:8761/eureka/)
|
|
351
|
+
```
|
|
352
|
+
Then you need to set `EUREKA_SERVER_URL`.
|
|
353
|
+
|
|
354
|
+
5. **Enable debug logging** to see detailed registration attempts:
|
|
355
|
+
```python
|
|
356
|
+
import logging
|
|
357
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
6. **Verify network connectivity**:
|
|
361
|
+
```bash
|
|
362
|
+
curl http://10.10.0.1:8761/eureka/apps
|
|
363
|
+
```
|
|
364
|
+
Should return XML/JSON with registered applications.
|
|
365
|
+
|
|
366
|
+
**Common mistakes**:
|
|
367
|
+
- Forgetting to set `EUREKA_SERVER_URL` in Docker/Kubernetes deployments
|
|
368
|
+
- Setting the variable in your IDE but not in the runtime environment
|
|
369
|
+
- Using `localhost` when running in containers (use service names or IPs instead)
|
|
370
|
+
|
|
371
|
+
### Eureka Registration Fails
|
|
372
|
+
|
|
373
|
+
Check Eureka server is reachable:
|
|
374
|
+
```bash
|
|
375
|
+
curl http://eureka:8761/eureka/apps
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Set `fail_fast=False` to start anyway:
|
|
379
|
+
```python
|
|
380
|
+
spring_app = SpringReadyApp(app, fail_fast=False)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Config Server Not Found
|
|
384
|
+
|
|
385
|
+
Ensure Config Server is registered in Eureka:
|
|
386
|
+
```bash
|
|
387
|
+
curl http://eureka:8761/eureka/apps/CONFIG-SERVER
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Or provide direct URL:
|
|
391
|
+
```bash
|
|
392
|
+
export CONFIG_SERVER_URI=http://config-server:8888
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Port Already in Use
|
|
396
|
+
|
|
397
|
+
Change the port:
|
|
398
|
+
```python
|
|
399
|
+
spring_app = SpringReadyApp(app, app_port=8081)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Or via environment:
|
|
403
|
+
```bash
|
|
404
|
+
export APP_PORT=8081
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Differences from Spring Boot
|
|
408
|
+
|
|
409
|
+
| Aspect | Spring Boot | Python |
|
|
410
|
+
|--------|-------------|---------|
|
|
411
|
+
| Metrics names | `jvm.memory.used` | `process_virtual_memory_bytes` |
|
|
412
|
+
| Runtime info | Java/JVM | Python/CPython |
|
|
413
|
+
| Config refresh | `/actuator/refresh` | Not supported (restart required) |
|
|
414
|
+
| Auto-configuration | Annotations | Explicit initialization |
|
|
415
|
+
|
|
416
|
+
## Requirements
|
|
417
|
+
|
|
418
|
+
- Python 3.9+
|
|
419
|
+
- FastAPI
|
|
420
|
+
- requests
|
|
421
|
+
|
|
422
|
+
Optional:
|
|
423
|
+
- `spring-config-client-python` for Config Server support
|
|
424
|
+
- `prometheus-client` for metrics
|
|
425
|
+
|
|
426
|
+
## License
|
|
427
|
+
|
|
428
|
+
MIT
|
|
429
|
+
|
|
430
|
+
## Contributing
|
|
431
|
+
|
|
432
|
+
Contributions welcome! This library is intentionally simple and focused. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
433
|
+
|
|
434
|
+
## Comparison with Spring Boot
|
|
435
|
+
|
|
436
|
+
```kotlin
|
|
437
|
+
// Spring Boot (Kotlin)
|
|
438
|
+
@SpringBootApplication
|
|
439
|
+
@EnableDiscoveryClient
|
|
440
|
+
class MyApplication
|
|
441
|
+
|
|
442
|
+
// Configuration in application.yml
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
```python
|
|
446
|
+
# Python equivalent
|
|
447
|
+
from spring_ready import SpringReadyApp
|
|
448
|
+
from fastapi import FastAPI
|
|
449
|
+
|
|
450
|
+
app = FastAPI()
|
|
451
|
+
spring_app = SpringReadyApp(app)
|
|
452
|
+
spring_app.start()
|
|
453
|
+
|
|
454
|
+
# Configuration from environment variables
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
**Note**: This library focuses on making Python apps work with existing Spring Boot infrastructure. It's not a replacement for Spring Boot itself.
|