本文目录导读:

Django 服务发现在不同的架构场景下有不同的集成方式,以下是几种主流方案的详细指南:
使用 Consul(最常用方案)
安装依赖
pip install python-consul django-consul
Django 配置 (settings.py)
# Consul 配置 CONSUL_HOST = '127.0.0.1' CONSUL_PORT = 8500 # 服务注册配置 SERVICE_NAME = 'my-django-app' SERVICE_PORT = 8000 SERVICE_TAGS = ['django', 'api', 'v1'] # 健康检查配置 HEALTH_CHECK_URL = f'/health/' HEALTH_INTERVAL = '10s' HEALTH_TIMEOUT = '5s'
服务注册与发现模块 (services/consul.py)
import consul
from django.conf import settings
class ConsulService:
def __init__(self):
self.client = consul.Consul(
host=settings.CONSUL_HOST,
port=settings.CONSUL_PORT
)
def register_service(self):
"""注册服务"""
service_id = f"{settings.SERVICE_NAME}-{socket.gethostname()}-{settings.SERVICE_PORT}"
self.client.agent.service.register(
name=settings.SERVICE_NAME,
service_id=service_id,
address=socket.gethostbyname(socket.gethostname()),
port=settings.SERVICE_PORT,
tags=settings.SERVICE_TAGS,
check=consul.Check.http(
f'http://localhost:{settings.SERVICE_PORT}{settings.HEALTH_CHECK_URL}',
interval=settings.HEALTH_INTERVAL,
timeout=settings.HEALTH_TIMEOUT
)
)
return service_id
def deregister_service(self, service_id):
"""注销服务"""
self.client.agent.service.deregister(service_id)
def discover_service(self, service_name=None):
"""发现服务"""
services = self.client.agent.services()
if service_name:
return {k: v for k, v in services.items()
if v['Service'] == (service_name or settings.SERVICE_NAME)}
return services
Django App 初始化 (apps.py)
from django.apps import AppConfig
import atexit
import socket
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
if not hasattr(self, '_service_registered'):
from .services.consul import ConsulService
consul_service = ConsulService()
service_id = consul_service.register_service()
# 程序退出时取消注册
atexit.register(consul_service.deregister_service, service_id)
self._service_registered = True
健康检查视图 (views.py)
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from django.db import connection
@require_GET
def health_check(request):
"""健康检查接口"""
status = 'healthy'
try:
# 检查数据库连接
connection.ensure_connection()
except Exception as e:
status = 'unhealthy'
return JsonResponse({
'status': status,
'service': settings.SERVICE_NAME
})
使用 Etcd
安装依赖
pip install python-etcd3
Django 配置 (settings.py)
ETCD_HOST = '127.0.0.1' ETCD_PORT = 2379 SERVICE_TTL = 30 # 秒
服务注册模块 (services/etcd.py)
import etcd3
import json
from django.conf import settings
class EtcdService:
def __init__(self):
self.client = etcd3.client(
host=settings.ETCD_HOST,
port=settings.ETCD_PORT
)
def register_service(self):
"""注册服务并设置租约"""
lease = self.client.lease(settings.SERVICE_TTL)
service_info = {
'name': settings.SERVICE_NAME,
'address': socket.gethostbyname(socket.gethostname()),
'port': settings.SERVICE_PORT,
'tags': settings.SERVICE_TAGS
}
key = f"/services/{settings.SERVICE_NAME}/{socket.gethostname()}:{settings.SERVICE_PORT}"
self.client.put(key, json.dumps(service_info), lease=lease)
# 启动自动续约
lease.refresh()
return lease
def discover_service(self, service_name=None):
"""发现服务"""
prefix = f"/services/{service_name or settings.SERVICE_NAME}"
services = []
for value, metadata in self.client.get_prefix(prefix):
service = json.loads(value)
services.append(service)
return services
使用 Kubernetes API(原生方案)
安装依赖
pip install kubernetes
服务发现配置 (services/k8s.py)
from kubernetes import client, config
import os
class K8sServiceDiscovery:
def __init__(self):
# 在K8s集群内运行
config.load_incluster_config()
self.api = client.CoreV1Api()
def discover_service(self, service_name, namespace='default'):
"""通过K8s API发现服务"""
services = self.api.list_namespaced_service(
namespace=namespace,
field_selector=f'metadata.name={service_name}'
)
endpoints = []
for svc in services.items:
# 获取Endpoints
endpoints_obj = self.api.read_namespaced_endpoints(
svc.metadata.name,
namespace
)
if endpoints_obj.subsets:
for subset in endpoints_obj.subsets:
for address in subset.addresses:
endpoints.append({
'host': address.ip,
'port': subset.ports[0].port
})
return endpoints
def get_service_url(self, service_name, namespace='default'):
"""获取服务URL"""
endpoints = self.discover_service(service_name, namespace)
if endpoints:
# 负载均衡选择
import random
endpoint = random.choice(endpoints)
return f"http://{endpoint['host']}:{endpoint['port']}"
return None
环境变量方式(简化版)
# Django settings.py
import os
# Kubernetes会自动注入环境变量
# 格式: {SERVICE_NAME}_SERVICE_HOST 和 {SERVICE_NAME}_SERVICE_PORT
def get_service_url(service_name):
"""通过环境变量获取服务地址"""
host = os.environ.get(f'{service_name.upper()}_SERVICE_HOST')
port = os.environ.get(f'{service_name.upper()}_SERVICE_PORT')
if host and port:
return f"http://{host}:{port}"
return None
# 使用示例
USER_SERVICE_URL = get_service_url('user-service')
使用 Eureka(Spring Cloud 生态)
安装依赖
pip install py_eureka_client
Django 配置 (settings.py)
EUREKA_SERVER = 'http://eureka-server:8761/eureka/' EUREKA_APP_NAME = 'django-app' EUREKA_INSTANCE_HOST = 'localhost' EUREKA_INSTANCE_PORT = 8000
服务注册模块 (services/eureka.py)
import py_eureka_client.eureka_client as eureka_client
from django.conf import settings
class EurekaService:
def __init__(self):
self.client = None
def init_eureka(self):
"""初始化Eureka客户端"""
eureka_client.init(
eureka_server=settings.EUREKA_SERVER,
app_name=settings.EUREKA_APP_NAME,
instance_port=settings.EUREKA_INSTANCE_PORT,
instance_host=settings.EUREKA_INSTANCE_HOST
)
def discover_service(self, app_name):
"""发现服务"""
instances = eureka_client.get_instances(app_name)
return instances
客户端负载均衡
使用 Round Robin 算法
import itertools
from typing import Dict, List
class RoundRobinBalancer:
def __init__(self):
self._services: Dict[str, List] = {}
self._iterators = {}
def update_services(self, service_name: str, endpoints: List):
"""更新服务列表"""
self._services[service_name] = endpoints
if service_name not in self._iterators:
self._iterators[service_name] = itertools.cycle(endpoints)
def get_next(self, service_name: str):
"""获取下一个可用服务"""
if service_name not in self._services:
return None
endpoints = self._services[service_name]
if not endpoints:
return None
return next(self._iterators[service_name])
集成到 Django 中间件
# middleware/service_discovery.py
from django.http import JsonResponse
from .services.consul import ConsulService
class ServiceDiscoveryMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.consul = ConsulService()
def __call__(self, request):
# 在请求处理前更新服务列表
if hasattr(request, 'service_discovery'):
services = self.consul.discover_service()
request.service_list = services
response = self.get_response(request)
return response
生产环境最佳实践
Docker Compose 示例
version: '3.8'
services:
consul:
image: consul:1.15
ports:
- "8500:8500"
command: agent -dev -client=0.0.0.0
django-app:
build: .
ports:
- "8000:8000"
environment:
- CONSUL_HOST=consul
- DJANGO_SETTINGS_MODULE=myproject.settings.production
depends_on:
- consul
command: >
sh -c "python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
监控与告警
# monitoring/health_check.py
from prometheus_client import start_http_server, Gauge
import threading
class ServiceMonitor:
def __init__(self):
self.service_status = Gauge(
'django_service_status',
'Service registration status',
['service_name']
)
def start(self, port=8001):
"""启动监控HTTP服务"""
threading.Thread(
target=start_http_server,
args=(port,),
daemon=True
).start()
def update_metrics(self, service_name, status):
"""更新服务指标"""
self.service_status.labels(
service_name=service_name
).set(1 if status else 0)
推荐方案
- 小规模部署:使用 Consul,配置简单,功能完整
- Kubernetes 环境:使用 K8s 内置服务发现
- Spring Cloud 生态:使用 Eureka
- 需要强一致性:使用 Etcd
选择方案时需要考虑团队技术栈、运维能力和系统复杂度。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播