A Python client for Apollo configuration service implemented using the official Apollo HTTP API. Tested with Python 3.13 and the latest version of Apollo.
Key Features:
- Real-time Updates: Supports Apollo configuration retrieval and real-time updates.
- Secret Support: Supports Apollo applications with or without secret keys.
- Distributed Deployment: Supports configuration retrieval for Apollo applications in distributed deployments.
- Fault Tolerance: Automatically switches to the next available service node when an Apollo service node is unavailable.
- Async Support: Provides an asynchronous client for async configuration retrieval.
- Multiple Configuration Methods: Supports configuration via environment variables, .env files, or direct parameters.
Apollo's official website recommends three Python clients, each with the following issues:
-
Client 1 is no longer functional and hasn't been maintained since 2019.
-
Client 2 uses the telnetlib library, which is deprecated in Python 3.13. Additionally, some URLs in the project are now 404. After reviewing the latest Apollo documentation, updates were needed. Instead of submitting a PR with extensive changes, I created a new project.
-
Client 3 is marked as deprecated in its README.
According to Apollo's official documentation on Overall Design:
- Meta Server encapsulates Eureka's service discovery interface.
- Client accesses Meta Server through domain name to get Config Service list (IP+Port), then directly accesses the server through IP+Port to get configuration information.
This project is implemented based on this principle. The service discovery interface is defined in the source code here.
Install via pip:
pip install pyapollo-zenkilanOr add to requirements.txt:
pyapollo-zenkilan>=0.2.0
Parameter descriptions:
- meta_server_address: Apollo server address.
- app_id: Apollo application ID.
- app_secret: Apollo application secret key.
pyapollo supports multiple configuration methods, listed in order of priority (highest to lowest):
- Direct Parameters: Pass parameters directly when creating the client
- Using ApolloSettingsConfig: Create a settings object and pass it to the client
- Environment Variables: Use environment variables with the APOLLO_ prefix
- .env File: Create a .env file in the project root directory
Create a .env file in your project root directory with the following content:
# Required Apollo Settings
APOLLO_META_SERVER_ADDRESS=http://localhost:8080
APOLLO_APP_ID=your-app-id
# Authentication Settings
APOLLO_USING_APP_SECRET=true
APOLLO_APP_SECRET=your-app-secret
# Optional Settings
APOLLO_CLUSTER=default
APOLLO_ENV=DEV
APOLLO_NAMESPACES=application,common,database
APOLLO_TIMEOUT=10
APOLLO_CYCLE_TIME=30
A .env.example file is provided as a reference.
In addition to the default .env file in the project root, you can specify a custom .env file path:
from pyapollo.settings import ApolloSettingsConfig
from pyapollo.client import ApolloClient
# Load from custom .env file path
settings = ApolloSettingsConfig.from_env_file("path/to/custom.env")
client = ApolloClient(settings=settings)Set environment variables directly:
# Required settings
export APOLLO_META_SERVER_ADDRESS=http://localhost:8080
export APOLLO_APP_ID=your-app-id
# Authentication settings (optional)
export APOLLO_USING_APP_SECRET=true
export APOLLO_APP_SECRET=your-app-secret
# Other optional settings
export APOLLO_CLUSTER=default
export APOLLO_ENV=DEV
export APOLLO_NAMESPACES=application,common,database
export APOLLO_TIMEOUT=10
export APOLLO_CYCLE_TIME=30| Environment Variable | Description | Default | Required |
|---|---|---|---|
| APOLLO_META_SERVER_ADDRESS | Apollo server address | - | Yes |
| APOLLO_APP_ID | Apollo application ID | - | Yes |
| APOLLO_USING_APP_SECRET | Whether to use secret authentication | false | No |
| APOLLO_APP_SECRET | Apollo application secret key | - | Only if USING_APP_SECRET=true |
| APOLLO_CLUSTER | Cluster name | default | No |
| APOLLO_ENV | Environment name | DEV | No |
| APOLLO_NAMESPACES | Comma-separated list of namespaces | application | No |
| APOLLO_TIMEOUT | Request timeout in seconds | 10 | No |
| APOLLO_CYCLE_TIME | Configuration refresh cycle in seconds | 30 | No |
| APOLLO_CACHE_FILE_DIR_PATH | Cache file directory path | - | No |
| APOLLO_IP | Client IP address | - | No |
from pyapollo.settings import ApolloSettingsConfig
from pyapollo.client import ApolloClient
# Create settings object
settings = ApolloSettingsConfig(
meta_server_address="http://localhost:8080",
app_id="your-app-id",
using_app_secret=True,
app_secret="your-app-secret"
)
# Pass settings to client
client = ApolloClient(settings=settings)from pyapollo.client import ApolloClient
# Method 1: Direct parameters
meta_server_address = "https://your-apollo/meta-server-address"
app_id="your-apollo-app-id"
app_secret="your-apollo-app-secret"
# Apollo client with authentication
apollo = ApolloClient(
meta_server_address=meta_server_address,
app_id=app_id,
app_secret=app_secret,
)
# Apollo client without authentication
apollo = ApolloClient(
meta_server_address=meta_server_address,
app_id=app_id,
)
# Method 2: Load from environment variables or .env file
apollo = ApolloClient() # Automatically loads from environment or .env file
# Get text format configuration
val = apollo.get_value("text_key")
print(val)
# Get JSON format configuration
json_val = apollo.get_json_value("json_key")
print(json_val)import asyncio
from pyapollo.async_client import AsyncApolloClient
async def main():
meta_server_address = "https://your-apollo/meta-server-address"
app_id="your-apollo-app-id"
app_secret="your-apollo-app-secret"
# Apollo async client with authentication
async with AsyncApolloClient(
meta_server_address=meta_server_address,
app_id=app_id,
app_secret=app_secret,
) as client:
# Get text format configuration
val = await client.get_json_value("json_key")
print(val)
# Get JSON format configuration
json_val = await client.get_value("text_key")
print(json_val)
# Apollo async client without authentication
async with AsyncApolloClient(
meta_server_address=meta_server_address,
app_id=app_id,
) as client:
# Get text format configuration
val = await client.get_json_value("json_key")
print(val)
# Get JSON format configuration
json_val = await client.get_value("text_key")
print(json_val)
# Async client with custom config server
async with AsyncApolloClient(
app_id=app_id,
config_server_host="http://config-server.example.com",
config_server_port=8080
) as client:
# Dynamically update configuration
await client.update_config(
timeout=120,
namespaces=["application", "cache"]
)
val = await client.get_value("sample_key")
print(val)
if __name__ == "__main__":
asyncio.run(main())pyapollo supports dynamic updates of client configuration parameters at runtime without needing to recreate the client instance:
from pyapollo.client import ApolloClient
# Create client
client = ApolloClient(
meta_server_address="http://localhost:8080",
app_id="my-app",
timeout=30,
cycle_time=60
)
# Update single parameter
client.update_config(timeout=120)
# Batch update multiple parameters
client.update_config(
timeout=60,
cycle_time=30,
cluster="production"
)
# Update namespaces
client.update_config(
namespaces=["application", "redis", "database"]
)
# Environment switching
client.update_config(
meta_server_address="http://prod-apollo:8080",
env="PROD",
cluster="production"
)
# Add authentication
client.update_config(
app_secret="your-secret-key"
)
# Custom config server (bypass meta server discovery)
client.update_config(
config_server_host="http://config.example.com",
config_server_port=8080
)
# View current configuration
config = client.get_current_config()
print(config)Besides using meta server for automatic config server discovery, you can directly specify config server address:
from pyapollo.client import ApolloClient
# Connect directly to known config server, no meta server needed
client = ApolloClient(
app_id="my-app",
config_server_host="http://config-server.example.com",
config_server_port=8080
)
# Switch config server at runtime
client.update_config(
config_server_host="http://backup-config.example.com",
config_server_port=9090
)
# Switch back to meta server discovery (need to provide meta_server_address)
client.update_config(
meta_server_address="http://meta-server.example.com",
config_server_host=None, # Clear custom config
config_server_port=None
)| Parameter | Type | Description |
|---|---|---|
| meta_server_address | str | Apollo server address |
| app_id | str | Application ID |
| app_secret | str | None | Application secret |
| cluster | str | Cluster name |
| env | str | Environment name |
| namespaces | List[str] | List of namespaces |
| ip | str | None | Client IP address |
| timeout | int | Request timeout in seconds |
| cycle_time | int | Configuration refresh cycle |
| cache_file_dir_path | str | None | Cache file directory path |
| config_server_host | str | None | Custom config server host |
| config_server_port | int | None | Custom config server port |
- meta_server_address: Re-fetches config server list after update
- app_id: Reinitializes cache file paths after update
- namespaces: Clears cache for removed namespaces after update
- cycle_time: Restarts polling thread after update
- cache_file_dir_path: Reinitializes cache directory after update
Parameter updates include comprehensive validation:
try:
# Invalid parameters will raise ValueError
client.update_config(timeout=-1) # Negative timeout
client.update_config(namespaces=[]) # Empty namespace list
client.update_config(app_id="") # Empty app ID
except ValueError as e:
print(f"Parameter validation failed: {e}")The project provides multiple example scripts demonstrating different configuration and usage methods:
python examples/sync_demo.pypython examples/async_demo.py# Detailed configuration update demonstration
python examples/update_config_demo.py
# Simple configuration update examples
python examples/simple_update_examples.py
# Custom config server examples
python examples/custom_config_server_demo.py
# Async client custom config server examples
python examples/async_custom_config_server_demo.py# First set the required environment variables
export APOLLO_META_SERVER_ADDRESS=http://localhost:8080
export APOLLO_APP_ID=your-app-id
# Run the example (with default key name 'sample_key')
python examples/env_demo.py
# Run the example (with specified configuration key)
python examples/env_demo.py --key your_config_key
# Run the example (with different text and JSON configuration keys)
python examples/env_demo.py --key your_config_key --json-key your_json_key# Make sure you have a .env file in the project root or examples/test.env
# Using default key names
python examples/dotenv_demo.py
# Specify configuration key
python examples/dotenv_demo.py --key your_config_key
# Specify different text and JSON configuration keys
python examples/dotenv_demo.py --key your_config_key --json-key your_json_key